InDesign Sort Romans

Okay, So,
I have an Indesign script, you drop pdf files directly onto it,
it sorts pages (say 0001_#.pdf, 0002_#.pdf, i_#.pdf, ii_#.pdf)
then opens indesign and places them in a new document according to user input and dimensions of the pdf files.
Problem is, the roman numeral labeled pages come first in the new document even though they are alphabetically last.
How can this be corrected?


property kNumPages : 999
property kSaveLastDoc : false
property widthDefault : 8
property heightDefault : 10
property percentDefault : 100
property defaultBigger : 1

global gDocNew, gDocName, gCurPage, gCurSpread, gCurBox, gNumBoxes, gDocWidth, gDocHeight, gMargin, grfkheight, grfkwidth, BindOffset, gScale, myDialog, pagesFacing, gBleedSize, theBleed
global gFirstOne, gLastOne, gIsMultiDoc, gDocJustMade, gFileType, gWdOrigin, gHtOrigin, gMakeSmaller
global keepWidth, SecWidth, SecHtOrig, gPleaseRotate, theRectangle, OrigRulOrig, gMasterOffset, positionGuide, positionLeft, ImagesPerPage, gVertOffset

on open droppedItems
	display dialog "For best results, all files should have the same dimensions and that the type file comes alphabetically directly following the image file. Also, all files must be in the same folder, remember to drop all files on script - not folder."
	doInitStuff()
	set gRecordNumber to count droppedItems
	set droppedItems to my quickSort(droppedItems, 1, gRecordNumber)
	repeat with tItem in droppedItems
		set itsInfo to (info for file tItem)
		if folder of itsInfo then -- folders dropped
			searchFolder(tItem as string)
		else -- files dropped
			if gCurBox > ImagesPerPage then
				doNewPage()
			end if
			doImageStuff(tItem as string)
		end if
	end repeat
	tell application "Adobe InDesign CC 2019"
		tell page gCurPage of document gDocName
			if (gCurPage mod 2) = 0 then
				tell theRectangle
					set currentBounds to {-gBleedSize, -gBleedSize, (gDocHeight + gBleedSize), (gDocWidth + gBleedSize)}
					set geometric bounds to currentBounds
				end tell
			end if
		end tell
	end tell
	if kSaveLastDoc and gIsMultiDoc then
		doSaveDoc()
	end if
	tell application "Adobe InDesign CC 2019"
		tell view preferences of document gDocName
			set horizontal measurement units to inches
			set vertical measurement units to inches
		end tell
		if pagesFacing is true then
			tell document preferences of document gDocName
				set facing pages to true
			end tell
		end if
	end tell
	
	with timeout of 600000 seconds
		activate
		display dialog "Completed!" buttons {"Okay"} default button 1 with icon 2
	end timeout
	
	tell application "Adobe InDesign CC 2019"
		set gDocName to activate
	end tell
	
end open

on doInitStuff()
	set gIsMultiDoc to false
	set gDocJustMade to true
	set pagesFacing to false
	set positionGuide to ""
	set positionLeft to ""
	set gDocWidth to 720
	set gDocHeight to 720
	set gCurBox to 1
	tell application "Adobe InDesign CC 2019"
		my myDialog()
		my doNewDoc()
		
	end tell

	tell application "Finder"
		set positionGuide to activate
		set positionGuide to the button returned of (display dialog "Which aspect of the page should remain as it was before?" buttons {"Gutter", "Thumb Edge", "Center"} default button 1)
		--if the button returned of (display dialog "Which aspect of the page should remain as it was before?" buttons {"Gutter", "Thumb Edge", "Center"}) is "Gutter" then	
		if positionGuide = "Gutter" then
			set positionLeft to 1
		else
			if positionGuide = "Thumb Edge" then
				--if the button returned of (display dialog "Which aspect of the page should remain as it was before?" buttons {"Gutter", "Thumb Edge", "Center"}) is "Thumb Edge" then
				set positionLeft to 0
			else
				set positionGuide to "Center"
			end if
		end if
	end tell
end doInitStuff

on myDialog()
	tell application "Adobe InDesign CC 2019"
		set theDialog to make dialog with properties {can cancel:true}
		activate
		tell theDialog
			set myDialogColumn to make dialog column
			tell myDialogColumn
				set myBorderPanel0 to make border panel
				tell myBorderPanel0
					make static text with properties {static label:"Items per page:"}
					set myItemNum to make dialog column
					tell myItemNum
						set itemNum to make real editbox with properties {edit value:1, small nudge:1, minimum value:1, maximum value:15, min width:60}
					end tell
				end tell
				set myBorderPanel1 to make border panel
				tell myBorderPanel1
					make static text with properties {static label:"New Width:"}
					set myCW to make dialog column
					tell myCW
						set theWidth to make measurement editbox with properties {edit value:576, edit units:inches, small nudge:0.5, minimum value:1, maximum value:2000, min width:60}
					end tell
				end tell
				set myBorderPanel2 to make border panel
				tell myBorderPanel2
					make static text with properties {static label:"New Height:"}
					set myCH to make dialog column
					tell myCH
						set theHeight to make measurement editbox with properties {edit value:720, edit units:inches, small nudge:0.5, minimum value:1, maximum value:2000, min width:60}
					end tell
				end tell
				set myBorderPanel3 to make border panel
				tell myBorderPanel3
					make static text with properties {static label:"Bleed"}
					set myB to make dialog column
					tell myB
						set theBleed to make measurement editbox with properties {edit value:9, edit units:inches, small nudge:0.5, minimum value:0, maximum value:72, min width:60}
					end tell
				end tell
				set myBorderPanel4 to make border panel
				tell myBorderPanel4
					make static text with properties {static label:"Percentage:"}
					set myPC to make dialog column
					tell myPC
						set thePercentage to make real editbox with properties {edit value:100, small nudge:1, minimum value:10, maximum value:500, min width:60}
					end tell
				end tell
				set myBorderPanel4a to make border panel
				tell myBorderPanel4a
					make static text with properties {static label:"Vertical Offset"}
					set myOffS to make dialog column
					tell myOffS
						set theOffset to make measurement editbox with properties {edit value:0, edit units:inches, small nudge:0.5, minimum value:-144, maximum value:144, min width:60}
					end tell
				end tell
				set myBorderPanel5 to make border panel
				tell myBorderPanel5
					make static text with properties {static label:"Images being placed are how much bigger than original trim size?"}
					set myM to make dialog column
					tell myM
						set theDifference to make measurement editbox with properties {edit value:72, edit units:inches, small nudge:0.5, minimum value:0, maximum value:288, min width:60}
					end tell
				end tell
				set myBorderPanel6 to make border panel
				tell myBorderPanel6
					make static text with properties {static label:"Facing Pages:"}
					set myFP to make dialog column
					tell myFP
						set doPagesFace to make checkbox control with properties {min width:45}
						tell myBorderPanel6
							set checked state of doPagesFace to true
						end tell
					end tell
				end tell
			end tell
		end tell
		set myResult to show theDialog
		if myResult = true then
			set ImagesPerPage to (edit value of itemNum) as real
			set gDocWidth to (edit value of theWidth) as real
			set gDocHeight to (edit value of theHeight) as real
			set gBleedSize to (edit value of theBleed) as real
			set newPercentage to (edit value of thePercentage) as real
			set gVertOffset to (edit value of theOffset) as real
			set amtBigger to (edit value of theDifference) as real
			set pagesFacing to (checked state of doPagesFace)
		end if
		set gScale to newPercentage
		set gMasterOffset to ((amtBigger / 2) * (gScale / 100))
		destroy theDialog
		
	end tell
end myDialog

on quickSort(a, L, R)
	set ctr to L
	set n to R
	set aRef to item L of a as string
	repeat while ctr < n
		repeat while (aRef < item n of a) and (ctr < n)
			set n to n - 1
		end repeat
		if n is not ctr then
			set item ctr of a to item n of a
			set ctr to ctr + 1
		end if
		repeat while (aRef > item ctr of a) and (ctr < n)
			set ctr to ctr + 1
		end repeat
		if n is not ctr then
			set item n of a to item ctr of a
			set n to n - 1
		end if
	end repeat
	set item n of a to aRef
	if L < n then
		quickSort(a, L, n - 1)
	end if
	if R > ctr then
		quickSort(a, ctr + 1, R)
	end if
	return a
end quickSort


on doNewDoc()
	tell application "Adobe InDesign CC 2019"
		set gCurPage to 1
		set gCurBox to 1
		set gDocJustMade to true
		tell view preferences
			set horizontal measurement units to points
			set vertical measurement units to points
		end tell
		set gDocNew to make new document with properties {document preferences:{facing pages:"true", document bleed bottom offset:gBleedSize, document bleed inside or left offset:gBleedSize, document bleed top offset:gBleedSize, document bleed outside or right offset:gBleedSize, document bleed uniform size:true}}
		set gDocName to name of gDocNew
		tell document preferences of gDocNew
			set facing pages to false
			set page width to (gDocWidth & " pt" as string)
			set page height to (gDocHeight & " pt" as string)
		end tell
		activate
		tell document gDocName
			tell view preferences
				set horizontal measurement units to points
				set vertical measurement units to points
			end tell
			repeat with i from 1 to ImagesPerPage
				set theLayer to "Layer " & i
				if exists layer theLayer then
					--set theLayer to theLayer
				else
					make layer with properties {name:theLayer, visible:true, locked:false}
				end if
			end repeat
		end tell
	end tell
end doNewDoc

on doImageStuff(tFile)
	set theImage to (tFile)
	set imageName to (tFile) as alias
	set fileInfo to info for imageName
	
	tell application "Adobe InDesign CC 2019"
		set oldCrops to PDF crop of PDF place preferences
		set PDF crop of PDF place preferences to crop media
		tell page gCurPage of document gDocName
			set theBounds to {"0 pt", "0 pt", "144 pt", "144 pt"}
			set theLayer to ("Layer " & gCurBox)
			set theRectangle to make rectangle with properties ¬
				{geometric bounds:theBounds, name:"pic" & gCurBox, fill color:"None", stroke color:"None", item layer:theLayer}
			with timeout of 600000 seconds
				place imageName on theRectangle with properties {horizontal scale:gScale, vertical scale:gScale}
			end timeout
			tell theRectangle to fit given frame to content
			set grfkbnds to geometric bounds of theRectangle
			set grfkwidth to ((item 4 of grfkbnds) - (item 2 of grfkbnds)) as real
			set grfkheight to ((item 3 of grfkbnds) - (item 1 of grfkbnds)) as real
			
		end tell
		tell page gCurPage of document gDocName
			tell theRectangle
				if positionGuide = "Center" then
					set geometric bounds to {(((gDocHeight - grfkheight) / 2) + gVertOffset), ((gDocWidth - grfkwidth) / 2), ((((gDocHeight - grfkheight) / 2) + gVertOffset) + grfkheight), (((gDocWidth - grfkwidth) / 2) + grfkwidth)}
					tell theRectangle to fit given content to frame
				else
					if (gCurPage mod 2) = positionLeft then
						set geometric bounds to {(((gDocHeight - grfkheight) / 2) + gVertOffset), -gMasterOffset, ((((gDocHeight - grfkheight) / 2) + grfkheight) + gVertOffset), (-gMasterOffset + grfkwidth)}
						tell theRectangle to fit given content to frame
					else
						set geometric bounds to {(((gDocHeight - grfkheight) / 2) + gVertOffset), (gMasterOffset + gDocWidth - grfkwidth), ((((gDocHeight - grfkheight) / 2) + grfkheight) + gVertOffset), (gMasterOffset + gDocWidth)}
						tell theRectangle to fit given content to frame
					end if
				end if
				if pagesFacing is true then
					if gCurPage > 1 then
						if (gCurPage mod 2) = 0 then
							set currentBounds to {-gBleedSize, -gBleedSize, (gDocHeight + gBleedSize), gDocWidth}
							set geometric bounds to currentBounds
						else
							set currentBounds to {-gBleedSize, 0, (gDocHeight + gBleedSize), (gDocWidth + gBleedSize)}
							set geometric bounds to currentBounds
						end if
					else
						set currentBounds to {-gBleedSize, -gBleedSize, (gDocHeight + gBleedSize), (gDocWidth + gBleedSize)}
						set geometric bounds to currentBounds
					end if
				else
					set currentBounds to {-gBleedSize, -gBleedSize, (gDocHeight + gBleedSize), (gDocWidth + gBleedSize)}
					set geometric bounds to currentBounds
				end if
			end tell
		end tell
		set PDF crop of PDF place preferences to oldCrops
	end tell
	set gCurBox to gCurBox + 1
	set gLastOne to name of fileInfo
end doImageStuff

on doNewPage()
	set gCurBox to 1
	if (gCurPage ≥ kNumPages) then
		set gIsMultiDoc to true
		doSaveDoc()
		doNewDoc()
	else
		set gCurPage to gCurPage + 1
		tell application "Adobe InDesign CC 2019"
			tell document 1
				make page at end
			end tell
		end tell
	end if
end doNewPage

on doSaveDoc()
	tell application "Finder"
		if not (folder kSavePath exists) then
			set kSavePath to (choose folder with prompt "Choose location to save new indd file")
		end if
	end tell
	tell application "Adobe InDesign CC 2019"
		save document gDocName in (kSavePath & gFirstOne & "-" & gLastOne) without template
		close document 1 saving no
	end tell
end doSaveDoc

Hi. I can’t tell what code may even be relevant to the question; perhaps the problem lies in dropping files that may not be realized at the same time, and another approach should be tried. The sort results in alphabetically ordered items, as expected.

tell {"02_#.pdf", "ii_#.pdf", "i_#.pdf", "01_#.pdf"} to my quickSort(it, 1, its length) --> {"01_#.pdf", "02_#.pdf", "i_#.pdf", "ii_#.pdf"}

The dropping of files parts work fine, it is out of whack in the sorting, making the roman numeral pages first and the numeric pages next.

ideally, when the pdf files are dropped on the original code, it opens indesign and places them accordingly into a new document, but it places the numeric pages “0001_#, 0002_#, 0003_#”… first then the roman numeral “i_#, ii_#, iii_#”… at the end of the document.

i need the sorting portion of it to determine roman numeral pages are first and then numeric pages.
My dumbass cant figure out exactly what code goes where…

my next problem, when that finally works, would be to have the script rename the roman numeral pages to include “0000_0001 = i, 0000_0002 = ii etc”, where as the numeric pages are already named “0001, 0002, 0003, 0004 etc”… but that is this script under a different name, so i will need them totally separate as they are used in separate instances.

Hi kemperderson.

Could you please wrap AppleScript code in this site’s [applescript] and [/applescript] tags when posting it? They make the code appear in a box with a clickable link, as in Marc’s reply. There’s a button for them just above the text field on each posting page. I did it for you with your original posts, but you’ve replaced them with another, tagless script.

Marc was saying that the sort handler works correctly. And it gives me the correct results too. Is there any white space at the beginning of the “Roman” file names?

i apologize for that.

it does sort alphabetically, yes.
but i need it to place all of the romans first then numeric.

Aha. Your first post gave the impression that the script was doing that anyway and you wanted the Romans to come last. I’ll have a think, as no doubt will others. Presumably you want the Romans sorted numerically amongst themselves?

OK. It turns out that your Quicksort handler is a specialised one which coerces the aliases in the list to text paths as it’s sorting them. As a quick patch, I’ve further adapted it to use another handler to compare the items and decide which ones must go after which. It achieves the Roman-then-decimal ordering you want. Decimals up to “99999” are sorted in order amongst themselves, but at the moment, only Romans up to “xviii” are guaranteed to be in order. If you need to go higher, it’ll be a bit more work for tomorrow, UK time — unless someone else improves it in the meantime.

Replace the on quickSort(a, L, R) … end quickSort handler declaration in your script with these two:

-- Compare pairs of items for the Quicksort handler below.
-- Paths whose item names begin with a group of decimal digits
-- are considered "greater than" those whose names don't.
on isGreater(a, b)
	set astid to AppleScript's text item delimiters
	set AppleScript's text item delimiters to {":", "_"}
	set a to text item -2 of (a as text)
	set b to text item -2 of (b as text)
	set AppleScript's text item delimiters to astid
	considering numeric strings
		if (a < "100000") then
			if (b < "100000") then return (a > b) -- Both names begin with decimal digits.
			return true -- a is a decimal, b a Roman.
		else if (b < "100000") then
			return false -- a is a Roman, b a decimal.
		else -- Both are Romans. Special-case "ix", otherwise compare alphabetically.
			if ((a ends with "ix") and (b begins with "v")) then
				return true
			else if ((b ends with "ix") and (a begins with "v")) then
				return false
			else
				return (a > b)
			end if
		end if
	end considering
end isGreater

-- Specialised Quicksort which coerces the items in the list to text as it sorts.
-- Now further specialised to compare items using the above handler.
on quickSort(a, L, R)
	set ctr to L
	set n to R
	set aRef to item L of a as text -- Pivot value, as text.
	repeat while (ctr < n)
		repeat while ((isGreater(item n of a, aRef)) and (ctr < n))
			set n to n - 1
		end repeat
		if (n is not ctr) then
			set item ctr of a to item n of a
			set ctr to ctr + 1
		end if
		repeat while ((isGreater(aRef, item ctr of a)) and (ctr < n))
			set ctr to ctr + 1
		end repeat
		if (n is not ctr) then
			set item n of a to item ctr of a
			set n to n - 1
		end if
	end repeat
	set item n of a to aRef -- Puts the pivot value back into the list as text.
	if (L < n) then
		quickSort(a, L, n - 1)
	end if
	if (R > ctr) then
		quickSort(a, ctr + 1, R)
	end if
	return a
end quickSort

PERFECT.
You guys are the greatest.
Thank you so very much.

I do apologize for any confusion, sometimes i am unable to get across to others, what i am thinking.

That’s good news. Thanks for the feedback.

I found the idea of sorting Roman numerals interesting and experimentally expanded my isGreater() handler yesterday to cope with those up to and beginning with “m” — which is probably overkill for your purposes. :wink: If I were writing it for use with one of my own sort handlers, I’d probably split the decimal/Roman distinction and Roman sort parts into different script objects and handlers, but this is fine as a patch for your existing script:

-- Compare pairs of paths for the Quicksort handler below.
-- Paths are assumed to be in the format "path:to:container:(page number)_rest of name".
-- Path a is "greater than" path b if its page number is decimal and b's Roman
-- or if the numbers are in the same notation and a's comes after b's.
on isGreater(a, b)
	-- Extract the page numbers from these two paths.
	set astid to AppleScript's text item delimiters
	set AppleScript's text item delimiters to {":", "_"}
	set a to text item -2 of (a as text)
	set b to text item -2 of (b as text)
	set AppleScript's text item delimiters to astid
	ignoring case
		-- Compare and eliminate for equality and decimals.
		considering numeric strings
			if (a = b) then return false
			if (a < "100000") then -- a has decimal digits
				if (b < "100000") then return (a > b) -- So does b.
				return true -- a is decimal, b Roman.
			end if
			if (b < "100000") then return false -- a is Roman, b decimal.
		end considering
		-- Both numbers are Romans.
		-- Test for either beginning with the other.
		if (a begins with b) then return true
		if (b begins with a) then return false
		-- Otherwise strip any leading substrings they have in common.
		repeat while (a begins with character 1 of b)
			set a to text 2 thru -1 of a
			set b to text 2 thru -1 of b
		end repeat
		-- Knowing that the remaining stubs begin with different letters,
		-- work down through the possibilities.
		if (a begins with "m") then return true
		if (b begins with "m") then return false
		if (a begins with "d") then return (b does not start with "cm")
		if (b begins with "d") then return (a begins with "cm")
		if (a begins with "c") then return true
		if (b begins with "c") then return false
		if (a begins with "l") then return (b does not start with "xc")
		if (b begins with "l") then return (a begins with "xc")
		if ((a is "ix") and (b begins with "v")) then return true
		if ((b is "ix") and (a begins with "v")) then return false
		-- Whatever's left can be compared alphabetically.
		return (a > b)
	end ignoring
end isGreater

If the file name was to have additional information, i assume the delimited portion would change?

0001_01_32_100Percent_9781338718133
0002_01_32_100Percent_9781338718133
0003_01_32_100Percent_9781338718133
0004_01_32_100Percent_9781338718133
0005_01_32_100Percent_9781338718133
i_01_32_100Percent_9781338718133
ii_01_32_100Percent_9781338718133
iii_01_32_100Percent_9781338718133
iv_01_32_100Percent_9781338718133
v_01_32_100Percent_9781338718133

Could you show me how it would correct this issue?


In my very limited experience, I can only think to 

		set filename to "0032_01_32_100Percent_9781338718133.pdf"
		set filenameDelim to kDelimitItems(filename, "_")
		set folio to item 1 of filenameDelim as integer
		set isbn to last item of filenameDelim
		set isbn to kReplaceOccurrencesOfString(isbn, ".pdf", "")

Which really messes with the entire structure of what you guys have given me...


Here you go:

-- Compare pairs of paths for the Quicksort handler below.
-- Paths are assumed to be in the format "path:to:container:(page number)_rest of name".
-- Path a is "greater than" path b if its page number is decimal and b's Roman
-- or if the numbers are in the same notation and a's comes after b's.
on isGreater(a, b)
	-- Some of the "paths" are still file or alias specifiers while they're being sorted.
	set a to a as text
	set b to b as text
	-- Extract the file or bundle names from these two paths.
	set astid to AppleScript's text item delimiters
	set AppleScript's text item delimiters to ":"
	if (a ends with ":") then
		set a to text item -2 of a
	else
		set a to text item -1 of a
	end if
	if (b ends with ":") then
		set b to text item -2 of b
	else
		set b to text item -1 of b
	end if
	-- Extract the page numbers from the names.
	set AppleScript's text item delimiters to "_"
	set a to text item 1 of a
	set b to text item 1 of b
	set AppleScript's text item delimiters to astid
	ignoring case
		-- Compare and eliminate for equality and decimals.
		considering numeric strings
			if (a = b) then return false
			if (a < "100000") then -- a has decimal digits
				if (b < "100000") then return (a > b) -- So does b.
				return true -- a is decimal, b Roman.
			end if
			if (b < "100000") then return false -- a is Roman, b decimal.
		end considering
		-- Both numbers are Romans.
		-- Test for either beginning with the other.
		if (a begins with b) then return true
		if (b begins with a) then return false
		-- Otherwise strip any leading substrings they have in common.
		repeat while (a begins with character 1 of b)
			set a to text 2 thru -1 of a
			set b to text 2 thru -1 of b
		end repeat
		-- Knowing that the remaining stubs begin with different letters,
		-- work down through the possibilities.
		if (a begins with "m") then return true
		if (b begins with "m") then return false
		if (a begins with "d") then return (b does not start with "cm")
		if (b begins with "d") then return (a begins with "cm")
		if (a begins with "c") then return true
		if (b begins with "c") then return false
		if (a begins with "l") then return (b does not start with "xc")
		if (b begins with "l") then return (a begins with "xc")
		if ((a is "ix") and (b begins with "v")) then return true
		if ((b is "ix") and (a begins with "v")) then return false
		-- Whatever's left can be compared alphabetically.
		return (a > b)
	end ignoring
end isGreater

Wow,
You make it look so easy.
I see the changes, and they actually make sense.
Thank you much.