The document describes the synchronization of documents using the priint:comet Plugins.

The starting point is a so-called master document and any number of variants of the sample document. With the help of synchronization, subsequent changes to the master file can then be transferred to the variants without changes in the variants being overwritten.

The following changes in the master document can be synchronized:

The implementation of the synchronization was done on the basis of a general description of masters and variants and the workflow with them given by the customer. No test documents or use cases were available at implementation time. We have therefore done the implementation "to the best of our knowledge and belief" as the requirement suggests. We tried to solve conflict situations that only became apparent during implementation in the best possible way. Please refer to the following descriptions for details.

The implementation does not include requests for partial synchronizations. Unfortunately, we were not able to filter out any resilient requirements from the existing descriptions.

We deliberately did not implement the adoption of clipping paths and other picture-relevant options of picture frames. We assume here that masters and variants usually contain different images, where it would certainly do more harm than good to transfer image-related data. Should this option nevertheless be desired, it can also be adapted directly in the supplied sync script by third-party providers with the help of cScript functions. Unfortunately, a plugin-side targeted registration of local changes to image settings and clipping paths (as with document frames) is not possible.

Please note that the following changes to the master file are not taken over by the synchronization, with the exception of the creation of new frames:

New frames of the master document are completely transferred to the variants with all set attributes and contents.

Simultaneous changes to images and text in master and variants can, as is easily imaginable, lead to conflicts during synchronisation that cannot be resolved (or at least not automatically). In the interest of automatic synchronisation, changes to images and texts will therefore not be implemented by WORK II, even in possible extension stages of synchronisation. Therefore, the priint:comet placeholders should always be used for the automatic maintenance of texts and images!

The targeted adoption of changes to frame designs is possible in principle, but requires a considerable amount of effort on the part of WERK II and can therefore only be implemented after separate (and chargeable) commissioning.

For more information about the automatic resolution of conflicts that occur, see the descriptions of the individual synchronization steps.

Please also note that during synchronization, the following changes are overwritten in the variant if there is a corresponding object in the master file at the same time. Objects that only exist in the variant remain unchanged (and are not deleted!).

The above-mentioned changes in the variants are overwritten even if they have not been changed at all in the sample update! The background to this is that different variants can have been synchronized at different times and with different frequencies, which makes it unclear what is and is not a change from the point of view of the variant in question.

Due to the hardly foreseeable effort, WERK II does not consider changing the general and complete transfer of the above-mentioned data from the master document.

For more information about the automatic resolution of conflicts that occur, see the descriptions of the individual synchronization steps.

Any InDesign® document can be used as a master document. In order for it to be recognized later as a master document, only two elements have to be created in the root element of the XML structure of the document:

Here's a screenshot

To open the XML document structure, choose View -> Structure -> Show Structure

Variants of a master document have the same documentID as the sample document and the value variant in the element priint_syncDocType. Here is a screenshot of a variant:

To check whether a document is a variant of a master document, the script function document::sync::possible can be used.

In the pages of the variants, the UIDs of the original pages are stored in the master document. Directly after creating a variant, the UID of a page of the variant and the original UID are the same. However, due to the different "consumption" of UIDs, these UIDs may differ considerably from each other later. If a new page is created in a variant during synchronization, the UID of the original page must be entered in the new page of the variant.

Pages created locally in variants are marked with the original UID 0. The 0 marking is done automatically by the priint:comet plugins and is also done when duplicating pages or spreads.

The original page of a page is shown together with the page template at the top left of the document page:

To show the page templates in the document, activate the menu View -> Show page templates.

The following functions can be used to set and query the original page:

In the frames of the variants, the UIDs of the original frames are stored in the master document in the script tag prrint_syncOrigin (ATTENTION: Wrong spelling).

Manual changes to the original UID can lead to errors during synchronization or even to the crash of InDesign®!

Immediately after creating a variant, the UID of the variant frame and the original UID are the same. However, due to the different "consumption" of UIDs, these UIDs can deviate considerably from each other later. If a new frame is created in a variant during synchronization, the UID of the original frame must be entered in the new frame of the variant.

Frames created locally in variants are marked with the original UID 0. The 0 marking is done automatically by the priint:comet plugins and is also done when duplicating frames and on all frames of duplicated pages and layers.

If the frame numbers are shown in the document, the original UID is displayed directly beside the frame UID. For frames without or without an empty original UID, the second UID is not displayed:

How to turn on showing the frame UIDs :

    - Context menue Nails & Magnets -> Show
    - Context menu Nalis & Magnest -> Show Frame UIDs

Attention : You have to activate both menus!

The following functions can be used to set and query the original page:

To create a variant, use the script function document::sync::create_variant. The function expects the master document and a path for the variant as input. If the target path is empty or 0, the variant is placed with the extension _variant next to the master file. All assignments to the originals for later synchronizations described above are inserted.

The function document::sync::create_variant is a shortcut only for the convenient creation of variants. You can also easily program this functionality yourself using the above descriptions.

Synchronization is done in several steps:

Swatches must have unique names throughout the document. This also applies to swatches that are grouped in color groups. The names of the swatches are therefore used for synchronization.

  1. Create all swatches that are defined in the master document but are missing in the variant.
  2. Adoption of all (possibly specific) settings of the swatches defined in master and variant. Local changes in the variant are overwritten!
  3. Restoring the color groups in the variant
  4. Transfer additional color groups from the master. If a color is in different color groups in master and variant, the color group of the variant is used.

Synchronization can change the sort order of the color fields.

Color fields that are only defined in the variant are retained (not deleted!).

The script function document::sync::swatches can be used to synchronize the colors.

Layers must have unique names throughout the document. The layer names are therefore used to synchronize the layers.

Please note: No content is transferred during layer synchronization. Only the names, positions and properties (color, visibility, locking, printability, text flow, guide line visibility, guide line locking) are updated. The contents of the layers are transferred when the frames are synchronized.

The synchronization is done in several steps:

  1. All layers of the master file are created or updated in sequence in the variant and moved to the same position as in the master.
  2. All other layers are moved one after the other so that they are behind the same layer as before step 1.
  3. New layers (which did not have a predecessor in the variant) are added to the end of the list.

The table shows the procedure using an example in which the master file has the levels A, B and C and the variant the levels C, B, 1, 2, 3 and A .

Master Variant Step 1 Steps 2 and 3
A C A A A A
B B B B B B
C 1 C 1 1 1
2 1 C 2 2
3 2 2 C 3
A 3 3 3 C

The fact that layers that are only defined in the variant are not deleted also means that layers that were deleted in the master document are no longer deleted in the variants!

The script function document::sync::layers can be used to synchronize the layers.

All style definitions of the master document are completely copied to the variants. Local changes to the style definitions of the variants are overwritten.

All classes of styles support by InDesign® are synchronized:

Defints used by other styles are transferred first.

Due to the possible dependencies between style definitions, it is not possible to transfer only individual style definitions.

The transfer of individual style properties, e.g. only the font size of a character style, is not supported. WERK II does not consider this because the effort involved is almost impossible to estimate.

The script function document::sync::style can be used to synchronize the style definitions.

All master spreads of the master document are completely transferred to the variants. Local changes to the master spreads in the variants are overwritten. Master spreads that are used by other master spreads are transferred first.

Due to the possible dependencies between master spreads, it is not possible to transfer only individual master spreads.

The individual transfer of frames or settings of master spreads (such as a changed page size) is not supported. Due to the almost unpredictable effort involved, WERK II does not consider this at all.

The script function document::sync::masterspreads can be used to synchronize the master pages.

The assignment of pages of the variants to pages of the master document takes place via an additional original UID of the page stored in the pages. Here you will find a complete description including all necessary script functions.

Please note: No content is transferred when the pages are synchronized. Only the page properties (size, binding, guides, position, sample page, ...) are updated. The contents of the pages are transferred when the frames are synchronized.

Using the original UIDs of the pages of the variant, we can synchronize the pages as follows:

  1. All pages of the master file are created or updated in sequence in the variant and moved to the same position as in the master.
  2. All other pages are moved one after the other so that they are behind the same page as before step 1.
  3. New pages (which did not yet have a predecessor in the variant) are added to the beginning of the document.
  4. Pages deleted in the master are deleted in the variant too. The pages are deleted even if they contain locally created or changed frames!

The table shows the procedure using an example in which the master file has pages A, B and C and the variant has pages 4, C, B, 1, 2, 3 and A:

Master Variant Step 1 Steps 2 and 3
A 4 A A A A 4
B C B B B B A
C B C 1 1 1 B
1 4 C 2 2 1
2 1 4 C 3 2
3 2 2 4 C 3
A 3 3 3 4 C

The script functions can be used to synchronize the pages.document::sync::pages

All frames of the master document are copied to the variant or updated there. The following frame characteristics can be updated:

After changes to the frame geometry or the page, the layout rules "After loading" and "After geometry changes" of the frame are executed!

If a frame in one of the above properties has been changed in the variant, changes to the same property in the master document are ignored. Please note that after changing a geometry property (e.g. the position), all other geometry changes (e.g. size changes) to the frame in the master document are also ignored!

Once a change mark has been set, it is not reset - not even if the original value is reset!

Please note the following information!

Text frames can be set so that their size and position automatically adapt to the content (Text Frame Options -> Auto Resize). This setting is in direct contradiction to the desire to transfer geometry changes from the sample file 1:1 into the variants. However, we still transfer changes to the frame geometry in the master to the variants and InDesign will then correct the frame size and position again according to the current settings on the frame!

A further problem with text frames with automatic resizing is that we cannot recognize who triggers the geometry change and the automatic resizing would lead to local geometry changes in the frame every time. To avoid this, frames with automatic resizing generally ignore geometry changes. If you still want to register a local geometry change, you must hold the CMD key (CTRL for Windows).

The Z-order of frames cannot be restored and under no circumstances the Z-order of master and variants can be synchronized. Please use layers to realize frame overlaps.

When resizing frames, it is not possible to determine at which corner or side the resize was made. In case of image frames, this can result in different image parts shown in master and variants. To keep the problem as small as possible, image positions are automatically adjusted if all of the following conditions are met:

Please note that the automatic position adjustment of identical images does not allow you to change only the image position in the variant. This change will be reset during the next synchronization.

Local changes to frames of a variant are automatically registered by the priint:comet plugins and stored in the priint_syncChanged script tag of the frame. The automatic registration only takes place in documents that are marked as variants.

To make the Frame Labels panel easier to use, it is planned to make the value fields of the panel type-dependent. The value of priint_syncChanged could then be changed by activating a simple checkbox in the list only. This feature is not part of the current synchronization assignment.

In addition, frames can generally be excluded from synchronization. Set the script tag priint_syncDisabled to the value 1. This value is changed manually! Alternatively you can create a script and change the value using the script function frame::set_script_tag.

Script tags are displayed in the panel Windows -> Comet Admin -> Frame Labels and can also be changed there manually. The following values are possible:

Key Value Icon Description
priint_syncDisabled 0 | 1

1 : The frame is completely excluded from synchronization and will not be deleted even if the original was deleted.

0 : The frame is synchronized, with any local changes made being limited.

priint_syncChanged geometry

Geometry changes:

  • Position
  • Size
  • Scale
  • Rotation
  • Skew
page

The frame has been moved to another page..

layer

The frame has been moved to another layer..

style

The object style of the frame has been changed.

Please note: The printability of a frame is part of the object styles. If the object style is changed, the print mark is removed and only set again if the printability is explicitly changed again.

lock

The frame lock has been changed.

visibility

The visibility of the frame has been changed.

print

The printability of the frame has been changed.

Please note: The printability of a frame is part of the object styles. If the object style is changed, the print mark is removed and only set again if the printability is explicitly changed again.

The values can be manually changed or removed in the panel Frame Labels.

Please note that the value of print_syncOrigin must never be changed manually!

The value of print_syncPagenum is used internally. It does not always reflect the current page number of the frame!

For visible frame UIDs, local changes in the variants are displayed right behind the original frame UID:

How to turn on showing the frame UIDs :

    - Context menue Nails & Magnets -> Show
    - Context menu Nalis & Magnest -> Show Frame UIDs

Attention : You have to activate both menus!

A list showing all manually changed or locked frames of the document or current page was explicitly not part of the current synchronization request and was therefore not implemented. But we could imagine (after a separate order) to extend the panel priint:adjust Frame List for it.

The InDesign® standard behavior for copying frames from text chains is that frames with continuation only get the currently visible text part. The last frame of the chain, on the other hand, also contains the entire text of the overset.

Here's an example of a text chain containing the text "A B C D" with in overset:"D"

Duplicating the blue frame will create a blue frame with the text "A" (and no longer). If you duplicate the green frame, the text "B" will be used. The red frame, on the other hand, will contain the text "C D" (and have an overset again).

This behaviour certainly corresponds to what one expects. But it is fatal for synchronization, as the following two cases show:

  1. Reslove overset in master document
    1. Create another frame behind the red frame in which the text "D" from the overset can be displayed.
    2. During the next synchronization, this frame (as a new frame in the master) will be transferred to the variant - with its text content "D".
    3. Then, the new frame is added to the text chain in the variant that already contains the text "A B C D".
    4. The text chain now contains the text "A B C DD".
  2. Resolce overset in varaint:
    1. Remove the red frame
    2. Enlarge the green frame until the overset is resolved.
    3. At the next synchronization the red frame will be created again. As the last frame in the chain it contains the text "C D".
    4. Now the red frame is linked again to the text chain with the text "A B C D" .
    5. The text chain now contains the text "A B C DC D".

The cScript provided for the synchronization solves these problems and text chains are restored according to the text chain in the master.

Additional text links created in the variant are appended to the end of the chain taken from the master document.

For all frames of the variant that have an original frame in the master document, the system checks whether this frame still exists. If the frame cannot (or no longer) be found in the master document, it is also deleted in the variant. If the frame is part of a text chain, the chain is reconnected if necessary.

Even frames with manual changes are deleted!

Frames that are excluded from synchronization (see here) are not deleted!

In this step, the frame groups are restored:

  1. In the variant, all mastered frames are removed from their possible groups
  2. Restore all groups and subgroups as in the master

Groups of the master document therefore have priority over groups of the variant!

All locally created pages without content will be removed from the variant. A page is empty if it:

  1. not contains frames created locally in the variant nor those from the master document.
  2. not contains any localized master spread item

On the other hand, pages may of course show non-localized master page frames (otherwise no page with master page frames could be deleted).

All layers without content and not defined in master too will be removed from the variant. A layer is empty if it:

  1. not contains frames created locally in the variant nor those from the master document.
  2. not contains any localized master spread item

On the other hand, layers may of course show non-localized master page frames (otherwise no page with master page frames could be deleted).

Here is a complete script for synchronization.

#pragma plain

// Author			: Paul Seidel
// Date				: 28.09.2019
// Coypright (c)	: WERK II GmbH, 2019
//

//***************************************************************************

#include "internal/types.h"

//***************************************************************************

String 	stErrorMessage	= 0;
int 	stResult	= 1;			

//***************************************************************************

// Get the page index that a frame of the master must have in the variant.
//
int get_pnum (ItemRef master, ItemRef fm, IDTypeList origns, int * pnum_v)
{
    int 	pnum_m 	= page::get (fm, 1);				// page number in master
    int 	puid 	= page::get_uid (master, pnum_m);	// UID of page in master

    if (!puid)	
    {
    	// page not found in master
    	string::append (stErrorMessage, "I see that frame %d is on page %d of the master, but I can't find this page.\n\n", item::getint (fm), pnum_m);
        return 1;	    
    }

    *pnum_v	= idtypelist::find_id2 (origns, puid);
    if (*pnum_v < 1)	
    {
    	// page not found in variant
    	string::append (stErrorMessage, "The page %d of the master cannot be found in the variant.\n\n", pnum_m);
    	return 2;
    }

    return 0;
}

//***************************************************************************

// Move a frame to the center of the page. 
// We do this to prevent frames from falling off the pasteboard 
// during geomtrie changes. The following size changes are then
//always carried out with the frame center as reference point.
//
int center_frame_in_page (ItemRef fr)
{
    int 		pnum	= page::get (fr);
    ItemRef		docRef	= item::alloc ();
    float 		pl, pt, pr, pb;
    float 		l, t, r, b;
    
    item::define (docRef, fr, 1);  
    page::get_size (pnum, &pl, &pt, &pr, &pb, 0, docRef);
    frame::bbox (fr, &l, &t, &r, &b);
            
    frame::moveto (fr, ((pr-pl) - (r-l))/2.0, ((pb-pt) - (b-t))/2.0);

    return 0;
}

//***************************************************************************

// Helper function for determining the frame center in one call
//    direction 1 : x
//    direction 2 : y
// 
float get_center (ItemRef fr, int direction)
{
    float 		l, t, r, b;

    frame::bbox (fr, &l, &t, &r, &b);
    if (direction == 1)	return (r-l)/2.0;
    else 		return (b-t)/2.0;
    
}

//***************************************************************************

// The string #changes# consists of a list of local changes:
//
// 	page
// 	geometry
// 	layer
// 	style
// 	lock
// 	visibility
// 	print
//
// if one change is NOT included, the corresponding value(s)
// must be taken from the master.
//
int must_update (String changes, char * what)
{
    if (string::find (changes, what) == -1) return 1;
    return 0;
}

//***************************************************************************

// Update an existing frame in the variant
// 
// Which local changes we are not allowed to change 
// can be found in the ScriptTag priint_syncChanged.
//
// pnum is the expected 1-based page number in variant!
//
int update_frame (ItemRef fv, ItemRef fm, int pnum)
{
    int  		result 		= 0;
    String 		changes		= string::alloc ();
    String 		disabled	= string::alloc ();
    float 		lm, tm, rm, bm;
    float 		lv, tv, rv, bv;
    float 		w, h, sx, sy, skew, rota;
    int 		applyRules	= 0;
    int 		pgv;
    String 		sm			= string::alloc ();
    String 		sv			= string::alloc ();
    int 		im, iv;
    ItemRef 	group 		= item::alloc ();
    ItemList 	groupFrames = 0;
    int 		lockm, lockv;
    
    while (1)
    {
    	string::set (disabled, frame::get_script_tag (fv, "priint_syncDisabled"));
    	if (string::compare (disabled, "1") == 0)
    	{
            break;
    	}
        
    	// Which local changes we are not allowed to change 
    	// can be found in the ScriptTag priint_syncChanged
    	//
    	string::set (changes, frame::get_script_tag (fv, "priint_syncChanged"));

    	// Remember my current xy position
    	//
    	frame::bbox (fm, &lm, &tm, &rm, &bm);
    	frame::bbox (fv, &lv, &tv, &rv, &bv);
    
    	if (must_update (changes, "geometry"))
    	{
            // Get the current frame settings of fm
            //
            frame::get_size (fm, &w, &h);
            frame::get_scale (fm, &sx, &sy);
            skew = frame::get_skew (fm);
            rota = frame::get_rotate (fm);
    
            // To be sure that the frame does not fall from the pasteboard, 
            // we first move it to the middle of the page.
            //
            center_frame_in_page (fv);
    
            result = frame::resize 	(fv, w, h, 0, kRefPointCenter, 0.0, 0.0, applyRules);			if (result) break;
   
            result = frame::rotate 	(fv, rota, 0.0, 0.0, applyRules, kRefPointCenter);			if (result) break;
            result = frame::scale 	(fv, sx, sy, get_center (fv, 1), get_center (fv, 2), applyRules);	if (result) break;
            result = frame::skew 	(fv, skew, get_center (fv, 1), get_center (fv, 2), applyRules);		if (result) break;
    
            // Move back to the old position
            frame::moveto (fv, lm, tm, -1, 0, applyRules);
            frame::apply_layout_rules (fv, 10);
    	}
    	else
    	{
            frame::moveto (fv, lv, tv, -1, 0, applyRules);
    	}
    	if (result) 
    	{
            string::append (stErrorMessage, "The geometry setting of frame %d of the master could not be applied to the corresponding frame %d of the variant (Error %d).\n\n", item::getint (fm), item::getint (fv), result);
            break;
    	}

    	// The position is okay now
    	// Fix the page if needed
    	//
    	if (must_update (changes, "page"))
    	{
            pgv = page::get (fv);
            if (pgv != pnum)
            {
                // If fv belongs to an InDesign group, we have to release
                // this group and re-group it without fv then if possible.
                //
                item::define (group, fv, 0);
                frame::get_group (fv, group);
                if (item::getint (group))
                {
                    itemlist::alloc (groupFrames);
                    frame::ungroup (group, groupFrames);
            
                    itemlist::remove (groupFrames, fv);
                    if (itemlist::length (groupFrames) > 1)
                    {
                    	frame::group (groupFrames);
                    }
                    itemlist::release (groupFrames);
                }
    	
                frame::bbox (fv, &lv, &tv, &rv, &bv);
                result 	= frame::moveto (fv, lv, tv, pnum, 0, applyRules);
                frame::apply_layout_rules (fv, 10);
            }
    	}		
    	if (result) 
    	{
            string::append (stErrorMessage, "The frame %d could not be moved to the page %d (Error %d).\n\n", item::getint (fv), pnum, result);
            break;
    	}

    	// Fix the layer
    	//		
    	if (must_update (changes, "layer"))
    	{
            frame::get_layer (fm, sm);
            frame::get_layer (fv, sv);
    
            if (string::compare (sm, sv) != 0)
            {
                // Neither of the two layers must be locked.
                //
                lockm = layer::get_lock (sm);
                lockv = layer::get_lock (sv);
                if (lockm) layer::unlock (sm);
                if (lockv) layer::unlock (sv);
    	
                // Move the frame to the desired layer
                //
                result = frame::move_to_layer (fv, sm, 1);
    	
                // Lock layers again
                //
                if (lockm) layer::lock (sm);
                if (lockv) layer::lock (sv);
            }
    
    	}		
    	if (result) 
    	{
            string::append (stErrorMessage, "The frame %d could not be moved to the layer '%s' (Error %d).\n\n", item::getint (fv), sm, result);
            break;
    	}

    	// Fix the style
    	//
    	if (must_update (changes, "style"))
    	{
            string::set (sm, frame::get_objectstyle (fm));
            string::set (sv, frame::get_objectstyle (fv));
    
            if (string::compare (sm, sv) != 0)
            {
                result = frame::set_objectstyle (fv, sm, 0, 1);
            }
    	}
    	if (result) 
    	{
            string::append (stErrorMessage, "Could not apply object style '%s to frame %d (Error %d).\n\n", sm, item::getint (fv), result);
            break;
    	}

    	// Fix lock state
    	//
    	if (must_update (changes, "lock"))
    	{
            im = frame::is_locked (fm);
            iv = frame::is_locked (fv);
    
            if (im != iv)
            {
                if (im)	result = frame::lock (fv);
                else	result = frame::unlock (fv);
            }
    	}
    	if (result) 
    	{
            if (im)	string::append (stErrorMessage, "Could not lock frame %d (Error %d).\n\n", sm, item::getint (fv), result);
            else 	string::append (stErrorMessage, "Could not unlock frame %d (Error %d).\n\n", sm, item::getint (fv), result);
            break;
    	}

    	// Fix visibilty state
    	//
    	if (must_update (changes, "visibility"))
    	{
            im = frame::is_hidden (fm);
            iv = frame::is_hidden (fv);
    
            if (im != iv)
            {
                if (im)	result = frame::hide (fv);
                else	result = frame::show (fv);
            }

    	}
    	if (result) 
    	{
            if (im)	string::append (stErrorMessage, "Could not hide frame %d (Error %d).\n\n", sm, item::getint (fv), result);
            else 	string::append (stErrorMessage, "Could not show frame %d (Error %d).\n\n", sm, item::getint (fv), result);
            break;
    	}

    	// Fix print state
    	//
    	if (must_update (changes, "print"))
    	{
            im = frame::get_printable (fm);
            iv = frame::get_printable (fv);
    
            if (im != iv)
            {
                result = frame::set_printable (fv, im);
            }
    	}
    	if (result) 
    	{
            string::append (stErrorMessage, "Could not change printablitiy of frame %d (Error %d).\n\n", sm, item::getint (fv), result);
            break;
    	}
    	
    	break;
    }
    
    string::release (changes);
    string::release (sm);
    string::release (sv);
    item::release (group);
    
    return result;
}

//***************************************************************************

// Add the minimum sync information to all frames on 
// new pages of the master.
// pp2
//
int prepare_master (ItemRef variant, ItemRef master)
{
    IDTypeList 		originsPages	= idtypelist::alloc ();
    int				i, j, id, puid;						
    ItemRef 		fv				= item::alloc ();
    ItemList		fvl;
    char 			istr [256];
    
    // Get the list of all pages in the variant watched by an origin
    // The list contains the key:value pairs
    //
    //	id 			id2
    //	----------------------------------
    //	origin_in_master 	index_in_variant
    //
    for (i = 0; i < document::pages (variant); i++)
    {
    	page::get_info (variant, i+1, "origin", &id);
    	if (id)	
        {
        	idtypelist::append (originsPages, id, i+1, 0, "");
        }
    }
    
    // Walk through all pages of the master. If we do not
    // find it in the variant, it must be a new page.
    // In this case take all frames of the new page and
    // add the minimum sync information to these frames  
    //
    for (i = 1; i <= document::pages (master); i++)
    {
    	puid	= page::get_uid (master, i);
    	if (idtypelist::find (originsPages, puid) < 0)
    	{
            fvl = itemlist::pageframes_of_doc (master, i. 1);
            
            for (j = 0; j < itemlist::length (fvl); j++)
            {
                itemlist::get (fvl, fv, j);
                sprintf (istr, "%d", item::getint (fv));
                frame::set_script_tag (fv, "0", "priint_syncDisabled");
                frame::set_script_tag (fv, istr, "prrint_syncOrigin");
            }
    
            itemlist::release (fvl);
    	}
    }
    
    idtypelist::release (originsPages);
    item::release (fv);
    return 0;
}

//***************************************************************************

// Run through all the frames of the master and find the corresponding 
// frames in the variant.
//
// if the frame is not found in the variant, a copy of the frame is created
// in the variant. Otherwise, the settings of the frame in the master are 
// copied to the variant.
//
int sync_frames (ItemRef variant, ItemRef master)
{
    ItemList		FV 				= itemlist::allframes_of_doc (variant, 1, 0, "", 0, 1);
    ItemList		FM 				= itemlist::allframes_of_doc (master, 1, 0, "", 0, 1);
    ItemList 		chained;
    ItemRef 		fv 				= item::alloc ();
    ItemRef 		fm 				= item::alloc ();
    ItemRef 		ch 				= item::alloc ();
    ItemRef 		next 			= item::alloc ();
    IDTypeList 		origins			= idtypelist::alloc ();
    IDTypeList 		originsPages	= idtypelist::alloc ();
    int 			i, j, id, pg_v, must_relink;	
    float 			l, t, r, b;
    String 			lay				= string::alloc ();
    int				result			= 0;

    // Get a list of all frames in the variant watched by an origin
    // The list contains the key:value pairs
    //
    //	id 			id2
    //	----------------------------------
    //	origin_in_master 	uid_in_variant
    //
    for (i = 0; i < itemlist::length (FV); i++)
    {
    	fv 	= itemlist::get (FV, fv, i);
    	id 	= frame::get_origin (fv);
    	
    	if (id)	idtypelist::append (origins, id, item::getint (fv), 0, "");
    }
    
    // Get the list of all pages in the variant watched by an origin
    // The list contains the key:value pairs
    //
    //	id 			id2
    //	----------------------------------
    //	origin_in_master 	index_in_variant
    //
    for (i = 0; i < document::pages (variant); i++)
    {
    	result = page::get_info (variant, i+1, "origin", &id);
    	if (id)	
        {
        	idtypelist::append (originsPages, id, i+1, 0, "");
        }
    }
    
    // Walk through all frames of the master and create/update the frames
    //
    for (i = 0; i < itemlist::length (FM); i++)
    {
    	fm 	= itemlist::get (FM, fm, i);
    	
    	// Get the frame UID of fm in variant by searching the master UID in the origins.
    	// 
    	// 	- If we don't find the master UID in the origins, we have to duplicate the frame
    	// 	- If we find the UID in the origins, we need to update the corresponding frame of the variant
    	//
    	id	 = idtypelist::find_id2 (origins, item::getint (fm));
    	
    	if (id == -1)
    	{
            // Create frame fm in variant
            //
            result	= get_pnum (master, fm, originsPages, &pg_v); 	// expected page index in variant
            if (result) break;

            frame::bbox (fm, &l, &t, &r, &b);			// position and size
            frame::get_layer (fm, lay);				// layer

            // For frames in text chains, only the text content that is 
            // currently visible in the frame is copied during duplication.
            // This is of course very annoying when frames have changed in size.
            // we will either lose text and get some parts multiple times.
            //
            // We therefore break the chain before duplicating frames from a chain.
            // After duplicating, the chain is rebuilt.
            //
            must_relink	= 0;
            chained 	= itemlist::chained (fm);
            if (chained && itemlist::length (chained) > 1)
            {
                itemlist::get (chained, ch, 0);
                frame::link (ch, 0);				// unlink the frame
                must_relink = 1;
            }

            // Duplicate the frame
            //
            result = frame::duplicate (fv, fm, l, t, variant, pg_v, lay, 0);

            // Relink frames
            //
            if (must_relink) 
            {
                for (j = 0; j < itemlist::length (chained) - 1; j++)
                {
                    itemlist::get (chained, ch, j);
                    itemlist::get (chained, next, j+1);
                    frame::link (ch, next);
                }
            }
            itemlist::release (chained);

            if (result)	string::append (stErrorMessage, "The frame % could not be copied to the variant to pg %d and layer '%s' at posistion [%f %f], (Error %d).\n\n", item::getint (fm), pg_v, lay, l, t, result);

            frame::set_origin (fv, item::getint (fm));
    	}
    	else
    	{
            // Get the expected page index of fm in the variant.
            // To do that, we first have to query the page in the master.
            // Then we have to look into the map originsPages to
            // find the index there.
            //
            result	= get_pnum (master, fm, originsPages, &pg_v); 
            if (result) break;

            // Update frame fm in variant
            //
            item::define (fv, variant, id);
            result = update_frame (fv, fm, pg_v);
    	}
        
    	if (result) break;
    }
    
    // Clean up
    //
    itemlist::release (FV);	
    itemlist::release (FM);
    item::release (fv);
    item::release (fm);
    item::release (ch);
    item::release (next);
    idtypelist::release (origins);
    idtypelist::release (originsPages);
    string::release (lay);

    // Ready
    //
    return result;
}

//***************************************************************************

int remove_superflous_frames (ItemRef variant, ItemRef master)
{
    ItemList		FV 			= itemlist::allframes_of_doc (variant, 1, 0, "", 0, 1);
    ItemRef 		fv 			= item::alloc ();
    ItemRef 		fm 			= item::alloc ();
    String 			disabled 	= string::alloc ();
    int 			i, origin;
    
    for (i = 0; i < itemlist::length (FV); i++)
    {
    	fv 	= itemlist::get (FV, fv, i);
    	origin	= frame::get_origin (fv);

    	string::set (disabled, frame::get_script_tag (fv, "priint_syncDisabled"));
    	if (string::length (disabled) == 0 || string::compare (disabled, "0") == 0)
    	{
            if (origin)
            {        
                item::define (fm, master, origin, 0);
    	
                if (!frame::is_valid (fm))
                {        		
                    frame::remove (fv);
                }
            }
    	}
    }
    
    // Clean up
    //
    itemlist::release (FV);
    item::release (fv);
    item::release (fm);
    string::release (disabled);

    return 0;
}

//***************************************************************************

// Rebuild text chains
// The text links of the master file are restored. Further text links in the 
// variant are attached to the end of the updated chain.
// 
int rebuild_text_chains (ItemRef variant, ItemRef master)
{
    ItemList		FV 		= itemlist::allframes_of_doc (variant, 1, 0, "", 0, 1);
    ItemList		FM 		= itemlist::allframes_of_doc (master, 1, 0, "", 0, 1);
    ItemRef 		fv 		= item::alloc ();
    ItemRef 		fm 		= item::alloc ();
    IDTypeList 		origins	= idtypelist::alloc ();
    IDTypeList 		dones	= idtypelist::alloc ();
    ItemList		chain 	= 0;
    ItemRef 		ch1 	= item::alloc ();
    ItemRef 		ch2 	= item::alloc ();
    ItemRef 		de1 	= item::alloc ();
    ItemRef 		de2 	= item::alloc ();
    int				result	= 0;
    int 			i, j, p, id1, id2;

    // Get a list of all frames in the variant watched by an origin
    // The list contains the key:value pairs
    //
    //	id 			id2
    //	----------------------------------
    //	origin_in_master 	uid_in_variant
    //
    for (i = 0; i < itemlist::length (FV); i++)
    {
    	itemlist::get (FV, fv, i);
    	id1 = frame::get_origin (fv);
    	
    	if (id1)	idtypelist::append (origins, id1, item::getint (fv), 0, "");
    }
    
    // Walk through all frames of the master and relink text chains
    //
    for (i = 0; i < itemlist::length (FM); i++)
    {
    	itemlist::get (FM, fm, i);
                
    	// Get the complete chain of the current frame
    	//
    	if (chain)
    	{
            itemlist::release (chain);
            chain = 0;
    	}
    	chain = itemlist::chained (fm);
        
    	// Recreate the chain if any
    	//
    	if (chain && itemlist::length (chain) > 1)
    	{
            // Check whether we handled the chain already
            //
            itemlist::get (chain, ch1, 0);
            p = idtypelist::find (dones, item::getint (ch1));
    
            if (p == -1)
            {
                // Rebuild the chain step by step
                //
                for (j = 0; j < itemlist::length (chain) - 1; j++)
                {
                    itemlist::get (chain, ch1, j);
                    itemlist::get (chain, ch2, j+1);
            
                    id1	 	= idtypelist::find_id2 (origins, item::getint (ch1));
                    id2	 	= idtypelist::find_id2 (origins, item::getint (ch2));
            
                    if (id1 == -1)	
                    {
                    	string::append (stErrorMessage, "Relinking %d and %d of master, but cannot find first frame in variant.\n\n", item::getint (ch1), item::getint (ch2));
                    	result = 4;
                    }
                    else if (id2 == -1)	
                    {
                    	string::append (stErrorMessage, "Relinking %d and %d of master, but cannot find second frame in variant.\n\n", item::getint (ch1), item::getint (ch2));
                    	result = 4;
                    }
                    else
                    {
                    	item::define (de1, variant, id1);
                    	item::define (de2, variant, id2);
                    	result = frame::link (de1, de2);
                    	if (result) string::append (stErrorMessage, "Cannot link together frames %d and %d in variant.\n\n", item::getint (ch1), item::getint (ch2));
                    }
                    if (result) break;
                }
                if (result) break;
    	
                // All frames of chain are relinked now.
                // Insert them into the dones list.
                //
                for (j = 0; j < itemlist::length (chain); j++)
                {
                    itemlist::get (chain, ch1, 0);
                    idtypelist::append (dones, item::getint (ch1), 0, 0, "");
                }
            }
    	}
    }

    // Clean up
    //
    itemlist::release (chain);
    itemlist::release (FV);	
    itemlist::release (FM);	
    itemlist::release (ch1);
    itemlist::release (ch2);
    itemlist::release (de1);
    itemlist::release (de2);
    item::release (fv);
    item::release (fm);
    idtypelist::release (origins);

    return result;
}

//***************************************************************************

// All functions of the Sync have the same call parameters. 
// We can therefore execute them all with the same stack call.
// 
int apply (int fn, ItemRef variant, ItemRef master, int step)
{
    int 	result;
    String 	m 	= string::alloc ();

    // MAIN WORK IS DONE HERE
    //
    wlog ("", "# %s ... ", symname (fn, 1));
    result = fn (variant, master);

    // Error handlings
    //
    if (result) 
    {
    	wlog ("", "ERROR %d\n", result);
    	wlog ("", "%s", stErrorMessage);

    	string::set (m, "SYNCHRONISATION FAILED\n");

    	if (step == 1)			string::append (m, "\nUpdating swatches failed with error %d.\n", result);
    	else if (step == 2)		string::append (m, "\nUpdating layers failed with error %d.\n", result);
    	else if (step == 3)		string::append (m, "\nUpdating styles failed with error %d.\n", result);
    	else if (step == 4)		string::append (m, "\nUpdating master spreads failed with error %d.\n", result);
    	else if (step == 5)		string::append (m, "\nUpdating page settings failed with error %d.\n", result);
    	else if (step == 6)		string::append (m, "\nUpdating frames failed with error %d.\n", result);
    	else if (step == 7)		string::append (m, "\nRemoving superflous frames failed with error %d.\n", result);
    	else if (step == 8)		string::append (m, "\nRebuilding text chains failed with error %d.\n", result);
    	else if (step == 9)		string::append (m, "\nRemoving InDesign groups failed with error %d.\n", result);
    	else if (step == 10)	string::append (m, "\nRemoving superflous pages failed with error %d.\n", result);
    	else if (step == 11)	string::append (m, "\nRemoving superflous layers failed with error %d.\n", result);

    	if (string::length (stErrorMessage))
    	{
            string::append (m, "\n%s", stErrorMessage);
    	}

    	showerror (m);
    }
    else
    {
    	wlog ("", "done with no errors\n");
    }

    stResult = result;
    return result;
}

//***************************************************************************

int main ()
{
    String 		path		= string::alloc ();
    String 		lang		= string::alloc ();
    String 		country		= string::alloc ();
    String 		vlang		= string::alloc ();
    String 		vcountry	= string::alloc ();
    String 		name		= string::alloc ();
    ItemRef 	variant		= document::get_front ();
    ItemRef 	master		= 0;
    int 		wasOpen		= 0;
    
    if (gRun > 1)	return 0;
    if (system::shiftkey ())
    {
    	// Ask for language and country. We put it temporarily into the Master.
    	// This means that the new variant does not have to be opened separately.
    	// At the end the old values are reset again.
    	//
    	xml::get_document_attribute (0, "Language", lang);
    	xml::get_document_attribute (0, "Country", country);
    	string::set (vlang, lang);
    	string::set (vcountry, country);
    	
    	if (!askstring2 (vlang, "Language", 1, vcountry, "Country", 1, 0, 0, 0, 0, 0, 0, "Language Settings of Variant")) return 0;
    	
    	xml::set_document_attribute (0, "Language", vlang);
    	xml::set_document_attribute (0, "Country", vcountry);
    	
    	// The real work is done here: Create the sync next to the master.
    	// You can change this according to your requirements!
    	//
    	document::sync::create_variant (0, "");
    	
    	// Resetting the Master
    	//
    	xml::set_document_attribute (0, "Language", lang);
    	xml::set_document_attribute (0, "Country", country);
    	
    	return 0;
    }

    stErrorMessage	= string::alloc ();
    stResult		= 0;

    // Select the master, open it and set the varaint as to be the front document again
    //
    document::folder (path, 0);		// Get the folder of the variant to open the dialog there
    if (file::select_file (path, "Select Master Document") != 0) return 0;
    wasOpen	= document::is_opened (path);
    master 	= document::open (path);
    document::path (path, master);	
    if (!master || string::length (path) == 0)
    {
    	showerror ("Cannot open master document\n\n%s", master);
    	if (string::length (path) == 0) 
    	if (!wasOpen) document::close (master, 0);
    	return 0;
    }
    document::select (variant);
    
    // Check if a sync of both documents is possible
    //
    if (!document::sync::possible (variant, master))
    {
    	showerror ("Your document is not a variant or the selected document is not a master of it.\n\nBe sure to have documents with same documentID and priint_syncDocType set to 'master' resp. 'variant'.");
    	if (!wasOpen) document::close (master, 0);
    	return 0;
    }
    
    // Write some time stamped log
    //
    wtlog ("", "# STARTING SYNC:\n");
    document::path (path, master);	wlog ("", "	Master 	: '%s'\n", path);
    document::path (path, variant);	wlog ("", "	Variant	: '%s'\n\n", path);
    
    // Prepare the sync
    //
    document::sync::before (variant);
    
    // Do the several sync steps
    //
    while (1)
    {
    	if (apply (prepare_master, variant, master, 13) != 0) break;
    	
    	if (apply (document::sync::swatches, variant, master, 1) != 0) break;
    	if (apply (document::sync::layers, variant, master, 2) != 0) break;
    	
    	if (apply (document::sync::styles, variant, master, 3) != 0) break;
    	if (apply (document::sync::masterspreads, variant, master, 4) != 0) break;
    	if (apply (document::sync::pages, variant, master, 5) != 0) break;
    	
    	if (apply (sync_frames,  variant, master, 6) != 0) break;
    	if (apply (remove_superflous_frames, variant, master, 7) != 0) break;
    	if (apply (rebuild_text_chains, variant, master, 8) != 0) break;
    	if (apply (document::sync::groups, variant, master, 9) != 0) break;
    	
    	if (apply (document::sync::remove_empty_pages, variant, master, 10) != 0) break;
    	if (apply (document::sync::remove_empty_layers, variant, master, 11) != 0) break;
    	if (apply (document::sync::cleanup, variant, master, 12) != 0) break;
        
    	break;
    }
    
    //
    // Clean up
    // PLZ note : We do not save the variant here!
    //
    document::sync::after (variant);
    if (!wasOpen) document::close (master, 0);
    
    wlog ("", "\n");
    if (!stResult)	wtlog ("", "# SYNC FINISHED successfully\n\n");
    else 			wtlog ("", "# SYNC FINISHED with error %d\n\n", stResult);
    
    item::release (master);
    item::release (variant);
    string::release (lang);
    string::release (country);
    string::release (vlang);
    string::release (vcountry);
    string::release (name);
    
    return 0;
}