Sorting Pages in QuarkXPress: A Constrained Approach

AI Thread Summary
The discussion revolves around a sorting challenge in QuarkXPress, where the user is constrained to using only the provided functions for moving pages. The main issue arises from the need to resort pages after a renumbering operation, which complicates the index management since any insert operation alters subsequent indexes. The user seeks a more efficient algorithm that avoids the need for recomputing the entire array after each insertion. Suggestions include utilizing sorting algorithms like quicksort or mergesort, which could potentially minimize the need for index recalculations. Ultimately, the user realizes that sorting and renumbering could be handled in separate passes to simplify the process.
ktoz
Messages
170
Reaction score
12
Hi

I have a sorting problem I haven't been able to solve and was hoping someone here could lend some insights.

The basic problem is resorting pages in a page layout application (QuarkXPress) where I'm constrained to only the functions Quark supplies for moving pages around in a document. Basically all I can do is place a page before or after another page. I have no access to the internal data structure so can't just grab the page array and swap pointers.

The difficulty arises when I perform an insert before/after a page, the indexes for everything after the insert changes which requires recomputing the entire array (at least in my very inefficient efforts thus far) and I'd like to come up with something a bit more elegant.

Here is the problem space

- Quark native page array with no access to it's elements or structure. The only known about this array is that pages are accessed by one based index using an accessor function.
- An unordered associative array (an Object-C http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSDictionary_Class/Reference/Reference.html") where array keys are the unique page id stored in a database. The database and NSDictionary are mine and are not part of Quark. I use them to provide direct access to a page without having to search the entire array in a loop.
- The only Quark manipulation functions available are insert before/insert after.
- I would like any solution to use only one of the insert before/insert after functions so as to keep it simple and automatic.

With those in mind, here is a concrete example:

Say there is an array of 4 pages (1 thru 4) sorted correctly and the user renumbers page 2 to page 10

Code:
initial page sort: 
[1, 2, 3, 4]

initial associative array (NSDictionary) : 
{ 
	754: {
			id: 754, 
			number: 1,
			ads: {}, 
			images: {}, 
			stories: {}
		}, 
	755: {
			id: 755, 
			number: 2,
			ads: {}, 
			images: {}, 
			stories: {}
		}, 
	756: {
			id: 756, 
			number: 3,
			ads: {}, 
			images: {}, 
			stories: {}
		}, 
	757: {
			id: 757, 
			number: 4,
			ads: {}, 
			images: {}, 
			stories: {}
		}
}

User renumbers page 2
Code:
Quark order before resort: [1, 10, 3, 4]
NSDictionary after renumbering: 
{ 
	754: {
			id: 754, 
			number: 1,
			ads: {}, 
			images: {}, 
			stories: {}
		}, 
	755: {
			id: 755, 
			number: 10,
			ads: {}, 
			images: {}, 
			stories: {}
		}, 
	756: {
			id: 756, 
			number: 3,
			ads: {}, 
			images: {}, 
			stories: {}
		}, 
	757: {
			id: 757, 
			number: 4,
			ads: {}, 
			images: {}, 
			stories: {}
		}
}

Sorted key array:
[754, 756, 757, 755]

Since the NSDictionary is mine, I'm free to do key sorts using standard sorting functions and can use this array as reference for sorting the Quark pages. Keep in mind that the actual NSDictionary itself is not resorted though. I can create arrays of sorted keys, but for efficiency sake, the dictionary preserves it's initial order.

Now using only "insert before" or "insert after" (but not both) resort the page array to match the order found in the key array.

This may actually be relatively easy but I've developed a brain block and haven't been able to figure it out. Any help greatly appreciated
 
Last edited by a moderator:
Technology news on Phys.org
I'm confused, somewhere in the process it would seem you'd need a delete for every insert, else the pages would just get duplicated. Given this, you could then implement a "swap" function, to swap two pages in the array, which would only affect the indexes during each swap operation.
 
The first and most important question -- does this even matter? Does this sorting routine actually consume so many resources that it deserves to be optimized? (or alternatively, is this exercise intended for entertainment value?)


Anyways, I note that a quicksort moves pages around in a very predictable fashion -- you shouldn't have to recompute anything: any time you come to a page, you should just know how far it's moved from its original location. Mergesort may also share this quality.

Or, think outside the box: can you remove pages from the document? Maybe you can put them into your own handcrafted page array, and then put them back into the document. If you can't do this directly, can you move a page from one document into another? Then you could create new documents to serve that purpose.
 
Hurkyl said:
The first and most important question -- does this even matter? Does this sorting routine actually consume so many resources that it deserves to be optimized? (or alternatively, is this exercise intended for entertainment value?)

It's not an exercise. I need this for a real Quark plugin. The actual sort probably doesn't consume many resources, it's just that all my efforts have produced really strange results, pages sorted in ways that are hard to unravel.

Anyways, I note that a quicksort moves pages around in a very predictable fashion -- you shouldn't have to recompute anything: any time you come to a page, you should just know how far it's moved from its original location. Mergesort may also share this quality.

But I don't know. Quark seems to maintain it's own internal indexing list and any time you swap two pages, this internal list gets invalidated and you have to start at the beginning and reset all the indexes for each page. I may have this wrong, but as I said, I have a brain block on this one...

Or, think outside the box: can you remove pages from the document? Maybe you can put them into your own handcrafted page array, and then put them back into the document. If you can't do this directly, can you move a page from one document into another? Then you could create new documents to serve that purpose.

Not realistic. Pages can have links to other pages (Think of a newspaper where a story can jump to another page) so removing a page requires unlinking one or several stories, remembering which box on which page it jumped to and relinking etc. Not to mention that there are lots of large resources on a page (photos, graphics, ads) that can make copying the entire page very resource intensive.

As so often happens, the act of clarifying the problem for a post, has helped me see where the algorithm is messing up. It comes from using Quark's "renumber page" function. If I don't renumber the page, the following algorithm works perfectly

Code:
The actual Quark function is under NDA so here's an a fake function witrh the same arguments

QuarkNDAMovePageFunction(layoutRefNum, moveStartIndex, moveEndIndex, destIndex, insertOption);

Here's my XTLayoutView class's sort method

- (void) sortPageViews
{
	int		layoutRefNum	= [self qxLayoutRef],
			maxIndex	= [pageViews count] - 1, // pageViews is my internal list of which pages a document contains
			swapCount	= 1,
			i;
						
	XTPageView	*pg1,
			*pg2;
						
	
	while (swapCount > 0)
	{
		swapCount	= 0;
		
		for (i = 0; i < maxIndex; i++)
		{
			// page indexes in array are i, i + 1 (zero based)
			pg1	= [pageViews objectAtIndex: i];
			pg2	= [pageViews objectAtIndex: i + 1];
			
			NSLog(@"comparing %hi with: %hi", [pg1 pageNumber], [pg2 pageNumber]);
			
			if ([pg1 pageNumber] > [pg2 pageNumber])
			{
				NSLog(@"swapping: %hi", [pg1 pageNumber], [pg2 pageNumber]);
				
				// move the pg1 to it's new location
				// page indexes in document are i + 1, i + 2 (1 based)
				QuarkNDAMovePageFunction(layoutRefNum, i + 1, i + 1, i + 2, INSERT_AFTER);
				
				// update the qxPageRefs (index) for both pages
				[pg1 setQXPageRef: i + 2];
				[pg2 setQXPageRef: i + 1];
				
				// swap elements in array
				[pageViews swapObject: pg1 withObject: pg2];
				
				// increment swapcount
				swapCount++;
			}
		}
	}
}

I think if I sort the pages and then make another pass renumbering them, it will be all better. I was just trying to make the renumbering concurrent with the sort and this was what got me so confused.
 
Dear Peeps I have posted a few questions about programing on this sectio of the PF forum. I want to ask you veterans how you folks learn program in assembly and about computer architecture for the x86 family. In addition to finish learning C, I am also reading the book From bits to Gates to C and Beyond. In the book, it uses the mini LC3 assembly language. I also have books on assembly programming and computer architecture. The few famous ones i have are Computer Organization and...
What percentage of programmers have learned to touch type? Have you? Do you think it's important, not just for programming, but for more-than-casual computer users generally? ChatGPT didn't have much on it ("Research indicates that less than 20% of people can touch type fluently, with many relying on the hunt-and-peck method for typing ."). 'Hunt-and-peck method' made me smile. It added, "For programmers, touch typing is a valuable skill that can enhance speed, accuracy, and focus. While...
I had a Microsoft Technical interview this past Friday, the question I was asked was this : How do you find the middle value for a dataset that is too big to fit in RAM? I was not able to figure this out during the interview, but I have been look in this all weekend and I read something online that said it can be done at O(N) using something called the counting sort histogram algorithm ( I did not learn that in my advanced data structures and algorithms class). I have watched some youtube...
Back
Top