Determining size of selection of multiple objects (Illustrator CS)

I’m trying to write a script to output a PDF file.

The basic premise is we prefer to use Distiller to create PDFs since they have proven to be more cross-platform and printer tolerant than ones made directly from native applications like Illustrator and InDesign.

So we prefer to output to PostScript, the put those into Distiller with one of three custom-made profiles. Nice, consistant, compatible PDFs every time. Except that they are a royal PITA to do that way.

About a month ago, my first “big script” for the department was big hit. We felt comfortable enough to offer to do more of this kind of automation work as a regular service, and when we asked for project ideas, automating PDF creation was tops on the list.

So far, thanks to scouring MacScripter, I’ve got Illustrator creating an appropriate PostScript file. However, there is a wrinkle I can’t get past.

Because of the way we work, rarely are our PDFs in sizes like Letter, Tabloid, etc. Almost always “custom” and very rarely is the actual image the size of the page. We prefer our PDFs to be cropped to the outermost bounds of the visible artwork, not the page size.

We can’t use EPS like we used to (EPS exports to Distiller automatically “crop” like this), because of the transparency features of the new Adobe CS applications.

When we print these files, if you select “Custom” in the page size, Illustrator automatically puts in the “cropped bounds” we’re looking for, but I can’t seem to mimick this behavior via scripting of the PostScript export itself. I’ve also tried Prefab’s UI Actions, but Illustrator is being stubborn in this regard.

My other idea was to figure out the outer bounds myself. The easiest way seemed to use Select All and then getting information on this selection (similar to what displays in the Info palette when you Select All). However, any attempt to get dimensions from a “Select All” selection yield the dimensions from a single sub-object in the selection (the one with the lowest ID number near as I can tell).

I had thought to Select All, then use Group, but then I’d make a mess of the artwork in the process (since our artists rely heavily on layers).

This is what would seem logical, and it does correctly “Select All,” that’s as far as I’ve gotten. :confused:


set selection to every page item of current document
set artwork_width to width of selection as string
set artwork_height to height of selection as string

I’m a little annoyed that the solution to this might be purchasing a book everyone keeps referencing, just to do something that should be more clear in their Library entires. I will if I have to, but if anyone else knows how to do this, I’m all ears.

–Kevin

Kevin,

I’m not sure this will be of help or not, but the objects would have to be grouped in order to get a width and height of all items together. As you said though this would possibly mess up your art. Having said that, what you could do would be a be to get the position of every item and then compare the highest, lowest, farthest left, and farthest right. The lower left corner of every object is where the position comes from, so farthest left and lowest is easy to figure out. The highest and farthest right would mean you’d have to get the height and width of each object and add it to the appropriate position number. This would have to be a loop, so if there are many objects, it would be sluggish. If you have a lot of text converted to outlines then make sure you group the text if possible. Anyway…

If you put all your objects into a list then do something like this. . .

tell application "Illustrator CS"
	activate
	set theList to every page item of current document
	set c to count theList
	display dialog c
	tell current document
		repeat with i from 1 to (count theList)
			repeat with anItem in theList
				set selection to page item i
				set x to item 1 of (position of selection)
				set y to item 2 of (position of selection)
				if x is less than item 1 of position of anItem then
					set leastX to x
				else if x is greater than item 1 of position of anItem then
					set greatX to (x + (width of anItem))
				end if
				if y is less than item 2 of position of anItem then
					set leastY to item 2 of position of anItem
				else if y is greater than item 2 of position of anItem then
					set greatY to (y + (height of anItem))
				end if
			end repeat
		end repeat
	end tell
	display dialog (leastX & " " & greatX & " " & leastY & " " & greatY) as string
end tell

This was a hurry up script so I’m sure it can be optimized.

PreTech

PreTech,

Tried your script and it’s not returning proper dimensions. I’m trying to step through your code, but don’t quite see what the error is. I’m using Illustrator’s “Info” palette as the reference. I even took my multiple test shapes and coverted them to a single shape and still keep getting the wrong numbers out of your script, which is kinda odd.

On my test shape it overshot by more than 3/4 of an inch horizontally and about 4 inches vertically. Assuming of course that Illustrator is returning numbers in “points” (1 point, or “0p1,” = 1/72 of an inch).

Am guessing either something I missed in the coordinate math (my test case has negative coordinates) or Illustrator is not giving an expected value.

I’ll comb through it this afternoon, if you beat me to it…

(And mucho thanks…I think this is the right track, just some subtlty missing…)

EDIT: [removed my edits…quick math check says this should work even for negative coordinates…so still befuddled]

Kevin,

Sorry, my finding least and greatest code is way off,:frowning: I’ll work on it. It shouldn’t be to bad. I was in a hurry.

PreTech

Think I got it. I just needed your insight on the “how” to do it, by checking every object. Little worried about the speed on a document with hundreds of objects, but one thing at a time. :wink:

Wrong or not you got my brain down the right set of tracks, which is half the battle with no one else to bounce ideas off of. :slight_smile:

I also had to jostle my brain for doing coordinate math. What we’re really doing is moving the dot around to find the top left and bottom right coordinates of the “bounding box” defined as every object on the page. With that in mind, I came-up with this, and it seems to be working no matter where I put the zero point, what order the page objects are in, and always returns positive numbers.


tell application "Illustrator CS"
	activate
	set theList to every page item of current document
	set c to count theList
	
	tell current document
		
		--initialize coordinates with first object
		set selection to page item 1
		set leastX to item 1 of (position of selection)
		set greatX to (item 1 of (position of selection)) + (width of selection)
		set leastY to item 2 of (position of selection)
		set greatY to (item 2 of (position of selection)) - (height of selection)
		
		repeat with i from 1 to c
			set selection to page item i
			
			--update top left
			set xa to item 1 of (position of selection)
			set ya to item 2 of (position of selection)
			if xa is less than leastX then set leastX to xa
			if ya is greater than leastY then set leastY to ya
			
			--update bottom right
			set xb to xa + (width of selection)
			set yb to ya - (height of selection)
			if xb is greater than greatX then set greatX to xb
			if yb is less than greatY then set greatY to yb
			
		end repeat
	end tell
	
	set doc_width to (greatX - leastX)
	set doc_height to (leastY - greatY)
end tell

Now whether this is the most efficient way to do the code is another story. I tend to sacrifice making it ultra-compact in favor of readability in case I have to recall what the heck I was doing months down the road.

Thoughts?

Kevin,

I can’t test this now (I’m at home and do not have CS here). I believe this should work. The finding the least and greatest part works in a plain applescript using a record list. It was busy at work today, or at least that’s my story and I’m stickin’ to it.:smiley:

tell application "Illustrator CS"
	activate
	set theList to every page item of current document
	set leastX to item 1 of position of item 1 of theList
	set leastY to item 2 of position of item 1 of theList
	set greatX to item 1 of position of item 1 of theList
	set greatY to item 2 of position of item 1 of theList
	set gWidth to 0
	set gHeight to 0
	tell current document
			repeat with anItem in theList
                            set thePos to position of anItem
				if leastX is greater than item 1 of thePos then
				set leastX to item 1 of thePos
				end if
				if leastY is greater than item 2 of thePos then
				set leastY to item 2 of thePos
				end if
				if leastX is less than item 1 of thePos
				then set greatX to item 1 of thePos
				set gWidth to width of anItem
				end if
				if leastY is less than item 2 of thePos then
				set greatY to item 2 of thePos
				set gHeight to height of anItem
				end if
			end repeat
	end tell
	set greatX to greatX + gWidth
	set greatY to greatY + gHeight
	display dialog (leastX & " " & greatX & " " & leastY & " " & greatY) as string
end tell

PreTech

Kevin: Try this on for size…


tell document 1 of application "Illustrator CS"
	set tempGroup to make new group item with properties {name:"TempGroup"} -- Create a temporary container group
	display dialog "Ready to gather data." -- This is UI shenanigans to let the user know the next process may take a second or two (or 30!).
	duplicate every page item to beginning of group item "TempGroup" -- copy instances to it
	set {w, h} to {width, height} of tempGroup -- get properties
	delete tempGroup -- delete it.
	-- No page  item was harmed in the operation of this script!
	
	display dialog "Width of group: " & (w / 72) & return & "Height of Group: " & (h / 72)
--Do what ever you want from here. Dialog returns inches as real.
end tell

Essentially, you’re just creating a dummy group to get the properties. All art on all layers (and sublayers, and sub-sublayers) remain intact. Then delete the dummy when you’re done.

Granted this is not the fastest with item heavy documents but I don’t think you’ll find it terribly slow. I ran it on a document with 1,946 objects spread over 3 layers and one sublayer and it completed within 30 seconds. This is on a 400 MHz G4 with 704 MB RAM.

Hope it helps.

Jim Neumann
BLUEFROG

I tried this at home and at work with Illustrator CS1 and a worst-case graphics file and it crashes Illustrator and gives the wrong dimensions. It seems to crash with the line “delete tempGroup”, since if I change the dialog output on the last dialog to the Finder, it does output results. So the results, while wrong, are available just before Illustrator crashes.

As an aside, how did you get a count of all the objects in the file?

I may have to break-down and find a creative and non-destructive way to do Group, get the dimensions, then revert somehow. Right now I think the following methodology:

–Make script drag-n-drop only.
–Script will not process a file if it is already open (is there a way to check this at the Finder?)
–Script will open file, group, get dimensions, then close without saving.

Just seems like such a kludge. You’d think “total dimensions of current selection” would be available, given that the Info palette has it handy. bleh

Calvin,

Have you considered using the geometric bounds or visible bounds properties of the document?

Of course this type of script will depend on your origin being at the bottom left and the transformation point also being at the bottom left.

Illustrator uses Points for page co-ordinates so the resulting width and height variables will need to be converted to another measurement unit if you are not using points.

Example code below …

tell application "Adobe Illustrator"
	activate
	set {leftEdge, topEdge, rightEdge, bottomEdge} to geometric bounds of document 1
	set theWidth to (rightEdge - leftEdge)
	set theHeight to (topEdge - bottomEdge)
end tell

Toby

Kevin,

Sorry about my attempts earlier. I was in a hurry and didn’t even come close to getting the code correct for the least and greatest. Oh well…:frowning:

I tried Bluefrog’s script here in CS1 and it works fine. Just in case you didn’t realize it, his script changes the dimensions from points to inches. Is that why you’re getting the incorrect dimensions? Or maybe you have text frames with a lot of spaces?

In your questions you mention that you open the file and then close it without saving. If that is the case, then you could do the grouping of the items without any temporary group that would then need to be deleted.

As far as any files that are open already, you could use:

tell application "Illustrator CS"
close every document saving no
end tell

at the front of the script. Of course you might want to place a dialog stating that all files will be closed without saving first so it can be canceled if there is something important you want to save.

PreTech

Toby, im not 100% but don’t think your solution would work if the page items contain masks that extend beyond visual artwork and the same for visual bounds only calculates the increase caused by stroke widths to artwork. Could well be wrong as I have not had the time to test.

Can’t count on either. This automation will be used by 40+ graphic artists, production designers, etc. whom have shown me in the past that I can “count on” little with these kinds of things. To be honest, I wouldn’t be writing this script if I could count on them creating PDFs the way we’ve told them to. :wink:

Also, the transformation point defaults to the center of any object, correct, even with a group? Sounds like even with that technique I’d be grouping the object to get a single geometric bound. So far all of these properties have applied either to a group, or if multiple objects are selected, the topmost object.

Unless there is a handy way to move the origin and transformation points to the proper location before starting, but that sounds like the same problem…need to know the bounds first.

I’m going to order the Adobe script books and maybe they’ll help clarify the mess. Seems overkill, but…shrug

I just have this nagging suspicion that with “Javascript” in the title I’ll be playing the same games I had to with Photoshop and using “do script” to a JavaScript…which is kinda klunky IMHO.

Not really, but I’m betting I have alot more than the nearly 2,000 objects he tested against, or more complex objects. The file also contains lots of transparency effects. I tried it on two different Macs with the same results (CS1 and Tiger, but different builds and hardware)…crashtacular.

I’m guessing it’s a complexity issue of some sort.

Yeah and after beating my head against it for two hours, I couldn’t even manage that simple chore. Was able to do the “select all” portion but all attempt to group them failed. There is just something about Adobe’s syntax I can’t wrap my head around. Even Excel is easier to understand than Illustrator. Well for me, anyway. O.o

If they’d just make Illustrator recordable (either by Script Editor or the way they do it in Photoshop with the Javascript watcher plugin), I could pick-apart their syntax and go nuts. I’ve done this with Excel…either record it via Script Editor or using the Macro maker and then work backwards.

The problem here is I need to avoid dialogs and avoid trashing anything. So I can’t do either. groan

If I have dialogs, the perception is that it’s slower than doing it by hand (or “their own way”), but if I close an unsaved file, I’ll get the riot act read to ME, not the artist. So I have to assume worst-case and go on that basis.

Not that I don’t appreciate the suggestion. :slight_smile:

Does the Finder keep a “this file is open” property I can check against?

Have to wonder if the crashes I’m getting might be caused by masks. Have to realize I’m dealing with consumer packaging mechanicals and the aritsts really push the limits of the software. Masks, thousands of objects, gradients, layered transparency, groups and subgroups, typographic effects, large placed Photoshop images, etc.

Hi Calvin

Not sure if you have tried this but have you thought of creating the custom print set up you
need has an illustrator action.
I know actions aren’t the best but they can be exported to all other machines the script would need to run on!
I just recreated what you describe in your first post on this subject and it worked you can then just call
your action from the script…

Hope this helps

Kevin,

Here’s a snippet that might help in getting the file that may be open to the front.

set theChoice to choose file with multiple selections allowed
tell application "Illustrator CS"
	repeat with anItem in theChoice
		open anItem
		set t to anItem
		tell application "Finder" to set nme to name of t
		if name of current document is not nme then set current document to nme
		-- do other actions to the file here
	end repeat
end tell

I don’t know how the files are selected, but you could use something like this and put all file names in a list and use that name list to make sure which file is being worked on.

Kevin,

If an action, as Pidge1 has mentioned, does not work, the only solution I can see is to go back to comparing objects and their positions. This will not modify anything and nothing gets deleted. It will be slow, especially if you have art with thousands of objects. :frowning:

tell application "Illustrator CS"
	set thisDoc to current document
	tell thisDoc
		set obj to every page item
		set leastx to item 1 of position of page item 1
		set leasty to item 2 of position of page item 1
		set greatx to (leastx + (width of page item 1))
		set greaty to (leasty + (height of page item 1))
		repeat with anObj in obj
			set pos to position of anObj
			set x to item 1 of pos
			set y to item 2 of pos
			set w to width of anObj
			set h to height of anObj
			if x is less than leastx then
				set leastx to x
			end if
			if (y - h) is less than leasty then
				set leasty to (y - h)
			end if
			if (x + w) is greater than greatx then
				set greatx to (x + w)
			end if
			if y is greater than greaty then
				set greaty to y
			end if
		end repeat
	end tell
end tell

(I had to post this 'cause I knew it wasn’t as hard as my head said it was! :D)

PreTech

Not sure quite what you mean. If you mean scripting the user interface, that was actually my first take on the problem. But Illustrator isn’t watchable or recordable, and it doesn’t seem to be responding to UI scripting that I can tell, despite finding the appropriate UI elements with Prefab’s UI Browser.

(It’s very possible I’m just not grasping UI scripting either, being my first attempt. If someone could perhaps lend me a hand at something like doing “Select All” then “Group” then I could probably figure the rest out myself. I’ll even give you the Prefab Browser output for the UI elements if it’d help!)

We don’t create PDFs straight from Illustrator, but use PostScript then use Distiller…it results in better PDFs for our purposes. So using any pre-defined Print setups only solves a tiny part of the problem (i.e. making the PostScript file). We want to script it from beginning to end to eliminate users “doing their own thing.” Right now I have 30+ artists all generating PDFs in likely about as many ways, rather than using our written instructions. If we give them a “path of least resistance” we’re betting on much better compliance.

I really must apologize if I seem to be “doing things the hard way” or seem a bit deflective…my hands are a bit tied on the “why” which limits the “how” and makes the solution a bit tricky.

If I misunderstood all together, then I apologize, and really need clarification of what you mean. :wink:

–Kevin

Calvin, personally I try to keep well clear of UI scripting where possible esspecially with Adobe’s lack of support for its interface elements. But here’s a few samples that work fine for me in CS1, the first one is your group request:

tell application "Illustrator CS"
	activate
	tell application "System Events"
		tell process "Illustrator CS"
			tell menu bar 1
				click menu item "All" of menu "Select"
				delay 1
				click menu item "Group" of menu "Object"
				delay 1
				click menu item "Deselect" of menu "Select"
			end tell
		end tell
	end tell
end tell

This second one is for document size as this is r/o property in illustrator

tell application "Illustrator CS"
	activate
	set docRef to the current document
	tell docRef
		tell application "System Events"
			tell process "Illustrator CS"
				key down command
				key down option
				keystroke "p"
				key up command
				key up option
				delay 1
				keystroke tab
				keystroke "300" --Your measurements in ruler units. not points as the rest of illustrator scripting
				keystroke tab
				keystroke "300" --Your measurements in ruler units. not points as the rest of illustrator scripting
				keystroke return
			end tell
		end tell
	end tell
end tell

And lastly document color space again r/o property.

tell application "Illustrator CS"
	activate
	set docRef to the current document
	tell docRef
		if color space of docRef is not CMYK then
			tell application "System Events"
				tell process "Illustrator CS"
					tell menu bar 1
						click menu item "CMYK Color" of menu "Document Color Mode" of menu item "Document Color Mode" of menu "File"
					end tell
				end tell
			end tell
		end if
	end tell
end tell

Hi Calvin

Sorry if i was a bit vague. i’ll try and explain!
What you need to do is create an illustrator action. in the “actions pallette”
This will record your actions or steps in the application.
Actions in illustrator will record the print process from what i can gather, (briefly tested it)
So you go thru the process of creating your postcript file thru the print dialogs while illustrator records it.
then you call it from your script.
example:

tell application "Illustrator CS"
	activate
	do script "Delete Unused Palette Items" from "Default Actions" without dialogs
end tell

run this script it, and watch your swatches pallette if you haven’t deleted them all ready this will clean up your swatches,
and also as long as you haven’t cleaned your default actions out either!!! it will run a default action that comes with illustrator.
hope this helps

after a bit more research i’ve found out you would have to create a print preset to get the result i think you are after. in which case this can be called
directly from your script thru the print options, or you could still record it using an action and just select the preset.