Adobe Illustrator

Adobe Illustrator has a surprisingly robust and flexible scripting API. Objects of all types are cleanly delineated and the feature set goes pretty deep. The hardest part, I found, was getting used to the different record types used between colors and object frames vs. geometric bounds. Some consistency or clearer (read as: any) documentation in the Dictionary would help, I don’t care which, but these aren’t atypical issues for Adobe products.

Artboards (CS4)

I could be wrong, but artboards look to be a new feature in Illustrator. In some recent work I ran afoul of them, and I’m still trying to figure what is the point of this “feature”. The dimension information for them look to be {x || y, height, width, x || y}, but the Dictionary is not clear on which are exactly x and y. The following snippet really just shows how to access and change them quickly and cleanly.

Set all artboards to visible bounds of content within the document (CS4)

on FixArtboard(TheDocument)
	tell application "Adobe Illustrator"
		tell TheDocument
			set visibleBounds to visible bounds
			set theArtboards to artboards
			set lastArtboard to count theArtboards
			repeat with thisArtboard from 1 to lastArtboard
				set theArtboard to item thisArtboard of theArtboards
				set artboard rectangle of theArtboard to visibleBounds
			end repeat
		end tell
	end tell
end FixArtboard

Set all artboards to a specific size (CS4)

This may seem obvious, but I find it sometimes helps to see things in context rather than a detached block of code.\

on FixArtboard(theDocument)
	tell application "Adobe Illustrator"
		tell theDocument
			-- artboards > artboard 1 > artboard rectangle : {x||y, height, width, x||y} the docs aren't clear on what's what here
			set theArtboards to artboards
			set lastArtboard to count theArtboards
			repeat with thisArtboard from 1 to lastArtboard
				set theArtboard to item thisArtboard of theArtboards
				set artboard rectangle of theArtboard to {600.0, 288.0, 432.0, 960.0}
			end repeat
		end tell
	end tell
end FixArtboard

Convert CMYK Spot Color to plain CMYK Color (CS4)

The thing to note about this is that applying a color to a path item is as simple as applying a record, and while the color values are floats, the class has to be declared in the new record and is an enumerated value. In other words, colors are merely records and not custom objects in Illustrator.

on ConvertCMYKSpotColorToPlainCMYKInPathItem(thePathItem) -- (path item) as void
	tell application "Adobe Illustrator"
		
		set theFillColor to fill color of thePathItem
		set theFillColorClass to class of theFillColor as string
		
		if theFillColorClass = "spot color info" then
			
			set theSpotColor to spot of theFillColor
			set theColor to color of theSpotColor
			
			set theColorClass to class of theColor as string
			
			if theColorClass = "CMYK color info" then
								
				set vBlack to black of theColor
				set vCyan to cyan of theColor
				set vMagenta to magenta of theColor
				set vYellow to yellow of theColor
				
				set fill color of thePathItem to {black:vBlack, cyan:vCyan, magenta:vMagenta, yellow:vYellow, class:CMYK color info}
				
			end if
		end if
	end tell
end ConvertCMYKSpotColorToPlainCMYKInPathItem

Matching Colors (CS4)

This subroutine was written in response to an error found in a book where a bunch of art contained text that was a 4-color mix when all it should ever be is 100% black.

But finding a color and checking it’s values isn’t as obvious as one might think. In Illustrator, if you look at a color and see these values in the GUI’s palette…

  • C:66.8%
  • M:57.8%
  • Y:66.4%
  • K:50.3%

…they are actually…

  • C:66.800004243851%
  • M:57.809996604919%
  • Y:66.409999132156%
  • K:50.38999915123%

…in the scripting API. When looking for a color it is crucial to get the exact values before starting the comparison.

To complicate matters further, I wasn’t able to do a direct comparison of the values (color = value), I had to use a scorecard method by comparing a given value against a (very) narrow range as shown below.

on IsColorRichBlack(theFillColor) -- (fill color as record) as boolean
	tell application "Adobe Illustrator"
		set vCyan to cyan of theFillColor
		set vMagenta to magenta of theFillColor
		set vYellow to yellow of theFillColor
		set vBlack to black of theFillColor

		set theColorMatchScore to 0

		if (vCyan ? 66.8) and (vCyan ? 66.9) then
			set theColorMatchScore to theColorMatchScore + 1
		else
			set theColorMatchScore to theColorMatchScore - 1
		end if

		if (vMagenta ? 57.8) and (vMagenta ? 57.9) then
			set theColorMatchScore to theColorMatchScore + 1
		else
			set theColorMatchScore to theColorMatchScore - 1
		end if

		if (vYellow ? 66.4) and (vYellow ? 66.5) then
			set theColorMatchScore to theColorMatchScore + 1
		else
			set theColorMatchScore to theColorMatchScore - 1
		end if

		if (vBlack ? 50.3) and (vBlack ? 50.4) then
			set theColorMatchScore to theColorMatchScore + 1
		else
			set theColorMatchScore to theColorMatchScore - 1
		end if

		if theColorMatchScore = 4 then
			return true
		end if
		return false
	end tell
end IsColorRichBlack

Is Stroke Color Black In Page Item (CS4)

This is a variation on IsColorRichBlack, but instead of looking at the fill for a specific black this looks into just the stroke for any variation of black. This was created to help resolve a problem with overprinting because most objects need overprinting off whereas black text (in labels) and strokes (in leader lines) need to have overprinting on.
on IsStrokeColorBlackInPageItem(thePageItem) — (fill color as record) as booleantell application “Adobe Illustrator”tryset theStrokeColor to stroke color of thePageItemon errorreturn true — don’t touch anything, just in caseend tryset vCyan to cyan of theStrokeColorset vMagenta to magenta of theStrokeColorset vYellow to yellow of theStrokeColorset vBlack to black of theStrokeColorif (vBlack = 100.0) thenreturn trueend ifset theColorMatchScore to 0if (vCyan ≥ 66.8) and (vCyan ≤ 66.9) thenset theColorMatchScore to theColorMatchScore + 1elseset theColorMatchScore to theColorMatchScore – 1end ifif (vMagenta ≥ 57.8) and (vMagenta ≤ 57.9) thenset theColorMatchScore to theColorMatchScore + 1elseset theColorMatchScore to theColorMatchScore – 1end ifif (vYellow ≥ 66.4) and (vYellow ≤ 66.5) thenset theColorMatchScore to theColorMatchScore + 1elseset theColorMatchScore to theColorMatchScore – 1end ifif (vBlack ≥ 50.3) and (vBlack ≤ 50.4) thenset theColorMatchScore to theColorMatchScore + 1elseset theColorMatchScore to theColorMatchScore – 1end ifif theColorMatchScore = 4 thenreturn trueend ifreturn falseend tellend IsStrokeColorBlackInPageItem

Disable Overprints In PageItem (CS4)

This is used in conjunction withIsStrokeColorBlackInPageItem(PageItem) to handle overprinting issues. In almost every case, overprinting should be applied to black paths and text.

on DisableOverprintsInPageItem(PageItem)
	tell application "Adobe Illustrator"
		try
			set fill overprint of PageItem to false
			
			set strokeColorIsBlack to IsStrokeColorBlackInPageItem(PageItem) of me
			if strokeColorIsBlack is false then
				set stroke overprint of PageItem to false
			end if
			
		on error the error_message number the error_number
			-- do something with the error because some objects can’t accept the command, typically nothing just to be sure nothing wonky happens in the layout.
		end try
	end tell
end DisableOverprintsInPageItem

Get all page items within a document (CS4)

I think most of the time, getting the page items at the document level is enough for most jobs, but sometimes every object needs to be touched for one reason or another. This takes a page item, gets all of the child page items, and even goes into found group items. I’m not sure why, but simply passing a document object to this won’t work.

property pAtomicPageItems : {}

tell application "Adobe Illustrator"
	open theEpsFile without dialogs
	
	set TheDocument to document 1
	
	set pageItemCount to (count page items in TheDocument)
	
	if pageItemCount > 0 then
		set firstPageItem to 1
		set thisPageItem to firstPageItem
		repeat with thisPageItem from 1 to pageItemCount
			set thePageItem to page item thisPageItem of TheDocument
			set end of pAtomicPageItems to thePageItem
			my GetPageItemsInPageItem(thePageItem)
		end repeat
	end if
	
	set groupItemCount to (count group items) in TheDocument
	
	if groupItemCount > 0 then
		set firstGroupItem to 1
		set thisGroupItem to firstGroupItem
		repeat with thisGroupItem from firstGroupItem to groupItemCount
			try
				set theGroupItem to item thisGroupItem of group items of TheDocument
				my GetPageItemsInPageItem(theGroupItem)
			end try
		end repeat
	end if
	
	(* do something with the collected page items *)

end tell

on GetPageItemsInPageItem(ParentPageItem) -- (page item) as list
	tell application "Adobe Illustrator"
		try
			set pageItemCount to (count page items in ParentPageItem)
			
			if pageItemCount > 0 then
				set firstPageItem to 1
				set thisPageItem to firstPageItem
				repeat with thisPageItem from 1 to pageItemCount
					set thePageItem to page item thisPageItem of ParentPageItem
					set end of pAtomicPageItems to thePageItem
					my GetPageItemsInPageItem(thePageItem)
				end repeat
			end if
			
			set groupItemCount to (count group items) in ParentPageItem
			
			if groupItemCount > 0 then
				set firstGroupItem to 1
				set thisGroupItem to firstGroupItem
				repeat with thisGroupItem from firstGroupItem to groupItemCount
					set theGroupItem to item thisGroupItem of group items of ParentPageItem
					my GetPageItemsInPageItem(theGroupItem)
				end repeat
			end if
			
		end try
		
	end tell
end GetPageItemsInPageItem

Parse Path Item (CS4)

This is a general purpose subroutine that shows examples of the various things that can be checked on path item in Illustrator. A lot of what is above started out here in one form or another.

on ParsePathItem(thePathItem) -- (page item) as void
	tell application "Adobe Illustrator"
		
		set theStrokeWidth to width of thePathItem
		
		-- set the width to the minimum for all strokes regardless of color.
		if theStrokeWidth > 0.0 and theStrokeWidth < 0.25 then
			try
				set width of thePathItem to 0.25
			on error the error_message number the error_number
				log "ERROR: set width of thePathItem to 0.25: " & error_number & ", " & error_message
			end try
		end if
		
		set theStrokeColor to stroke color of thePathItem
		set kCurrentStrokeColor to theStrokeColor
		
		set theStrokeColorClass to class of theStrokeColor as string
		
		if theStrokeColorClass contains "no color info" then
			-- do nothing
		else
			
			if theStrokeColorClass contains "CMYK" then -- not finding this
				
				(* Analyze the color to see what needs to happen next *)
				
				set cVal to cyan of theStrokeColor
				set mVal to magenta of theStrokeColor
				set yVal to yellow of theStrokeColor
				set kVal to black of theStrokeColor
				
				set inkCoverage to cVal + mVal + yVal + kVal
				
				if cVal = 0.0 and mVal = 0.0 and yVal = 0.0 and kVal = 100.0 then
					-- do nothing
					
				else if inkCoverage ≥ 350.0 then -- the color is 4-color black
					my SetPathItemStrokeColorToK100(thePathItem)
					
				else -- the color is not black, but it is 4-color and the stroke needs to be bigger.
					
					if theStrokeWidth ≥ 0.25 and theStrokeWidth < 0.5 then
						try
							set width of thePathItem to 0.5
						on error the error_message number the error_number
							log "ERROR:set width of thePathItem to 0.5: " & error_number & ", " & error_message
						end try
					end if -- theStrokeWidth
				end if -- color check
			else if theStrokeColorClass contains "spot" then
				
				(* If the name contains "Black", then change the color C0 M0 Y0 K100 *)
				
				set theSpotColor to spot of theStrokeColor
				set theSpotColorName to name of theSpotColor
				
				if theSpotColorName contains "black" then
					my SetPathItemStrokeColorToK100(thePathItem)
				end if
			end if
			
		end if -- theStrokeColor is"no color info" then
		
	end tell -- application
end ParsePathItem