There are two types of cross-references in FrameMaker: spot cross-references and paragraph cross-references. (There is a third type, element cross-references, available only in FrameMaker+SGML. This tutorial will not discuss element cross-references). Both paragraph and spot cross-references are similar, in that you have to insert an Xref object that points to a Cross-Ref Marker. The Xref object has an XRefSrcText property that must exactly match the marker text of the Cross-Ref Marker. Let's start with spot cross-references, since they are a little simpler to work with.
Spot cross-references
As an example, here is a table that contains the names of maintenance procedures that occur in a document. We want to use FrameScript to make cross-references from the entries in the table to the actual procedures in the document. Each procedure title uses a Heading2 paragraph format.
Adding cross reference markers
One principle we like to add here when solving problems with FrameScript, is to break down the problem into small tasks. Our first task is to put cross-reference markers in each procedure heading paragraph. Since we know that procedures start with Heading2 paragraphs, we will only put markers in those paragraphs. The marker text will be set to the text of the paragraph. It is important that each cross-reference marker in the document contains unique text. We will assume in our example that each Heading2 paragraph contains text that is unique from all other Heading2 paragraphs in the document.
// Test for an active document.
If ActiveDoc = 0
MsgBox 'No active document. ';
LeaveSub;
Else
Set vCurrentDoc = ActiveDoc;
EndIf
Loop ForEach(Pgf) In(vCurrentDoc) LoopVar(vPgf)
If vPgf.Name = 'Heading2'
New Marker NewVar(vMarker) MarkerName('Cross-Ref')
TextLoc(vPgf);
// Set the marker text to the paragraph text.
Set vMarker.MarkerText = vPgf.Text;
EndIf
EndLoop
Each Heading2 paragraph will have a cross-reference marker at the beginning of it with the text of the paragraph as the marker text. Here is one of the Heading2 paragraphs with the marker window showing the marker text.
Because we are creating markers with FrameScript instead of the FrameMaker interface, let's take a look at the marker properties. You can run the following code with the marker anchor selected to see the marker's properties. Notice that the MarkerText property is the text that shows in the Marker dialog.
Get TextList InRange(TextSelection) MarkerAnchor
NewVar(vTextList);
If vTextList.Count > 0
Get Member Number(1) From(vTextList) NewVar(vMarker);
Set vMarker = vMarker.TextData;
Display vMarker.Properties;
Else
MsgBox 'There is no marker selected. ';
EndIf
Adding the cross-references
Now we can go to the table and have a script add cross-references to the markers ("spots") we inserted earlier. We will assume that the cursor is in the table when the following code is run.
// Test for an active document.
If ActiveDoc = 0
MsgBox 'There is no active document. ';
LeaveSub;
Else
Set vCurrentDoc = ActiveDoc;
EndIf
// Set a variable for the current table.
Set vTbl = vCurrentDoc.SelectedTbl;
// Make sure a the cursor is in a table.
If vTbl.ObjectName not= 'Tbl'
MsgBox 'There is no selected table. ';
LeaveSub; // Exit the script.
EndIf
// Find the first body row in the table.
Set vRow = vTbl.FirstRowInTbl;
Loop While(vRow.RowType = RowHeading)
Set vRow = vRow.NextRowInTbl;
EndLoop
// Go to the first cell in the first body row.
Set vCell = vRow.FirstCellInRow;
Now that we are in the first cell, we can begin a loop down through the cells. For each cell, we need to "select" the text so that the new cross-reference can replace it. The XRefSrcText property of the XRef (cross-reference) will be set to the selected text since this will match the marker text of the corresponding cross-reference marker that was inserted earlier. To select the text, we will make a text range, delete the text, and then insert the XRef object in its place.
// Begin the loop.
Loop While(vCell)
// Select the text by making a TextRange.
New TextRange NewVar(vTextRange) Object(vCell.FirstPgf)
Offset(0) Offset(ObjEndOffset-1);
// Set a variable for the TextRange text.
Set vXRefSrcText = vTextRange.Text;
// Delete the text.
Delete Text TextRange(vTextRange);
// Insert the cross-reference.
New XRef Format('Heading & Page') TextLoc(vCell.FirstPgf)
NewVar(vXRef);
Set vXRef.XRefSrcText = vXRefSrcText;
// Go to the next cell and repeat the loop.
Set vCell = vCell.CellBelowInCol;
EndLoop
// Update the cross-references.
Update DocObject(vCurrentDoc) XRefs Everything;
The table now shows the original text replaced with the cross-references. If you double-click on one of the cross-references, it will open the Cross-Reference dialog box as shown below.
The key thing to remember is that the XRef.XRefSrcText property must exactly match the XRef.MarkerText property of the corresponding marker otherwise, you will have an unresolved cross-reference. To see the cross-reference properties, highlight one of the cross-references and run the following code. Notice the XRefSrcText property.
Get TextList InRange(TextSelection) XRefBegin
NewVar(vTextList);
If vTextList.Count > 0
Get Member Number(1) From(vTextList) NewVar(XRefBegin);
Set XRefBegin = XRefBegin.TextData;
Display XRefBegin.Properties;
Else
MsgBox 'There is no cross-reference selected. ';
EndIf
Cross-references to other documents
One other important property to note is the XRefFile property. For the above cross-reference it is a NULL string because it is an internal cross-reference. If we were cross-referencing another file, then the XRefFile property would contain the absolute path to the other file.
Paragraph cross-references
Before discussing the insertion of paragraph cross-references with FrameScript, let's review how they differ from spot cross-references in the FrameMaker interface. When you insert a spot cross-reference with FrameMaker, you have to insert a cross-reference marker before you insert the cross-reference. You have to make a "spot" that will appear in the Cross-Reference dialog box.
In contrast, paragraph cross-references don’t require the insertion of a cross-reference marker ahead of time. Instead, you point to the desired paragraph in the Cross-Reference dialog box, click Insert, and FrameMaker inserts the cross-reference and the cross-reference marker in the appropriate places.
Once inserted, spot and paragraph cross-references don't differ in their basic underlying properties. All cross-references are accessed as XRef objects by FrameScript. There is a difference, however, in the way they are presented in the FrameMaker Cross-Reference dialog box. When you double-click on a spot cross-reference, the dialog shows Cross-Ref markers under the Source Type and marker text in the right-hand list. Double-clicking a paragraph cross-reference shows the Paragraph Tag of the source paragraph under Source Type and the paragraph text each of the selected Paragraph Tag paragraphs in the right-hand list. Below is the Cross-Reference dialog box showing a paragraph cross-reference.
The difference between the cross-reference types comes from a difference in the syntax of the cross-reference markers. To see this, insert a paragraph cross-reference, Control-Alt-Click on the cross-reference to go to the source, and open the marker window. You will see the syntax of the marker that FrameMaker inserts with a paragraph cross-reference. Below is a sample marker window followed by the XRef properties.
Like the spot cross-reference markers we inserted earlier, you can see the text of the source paragraph in the marker window and the XRefSrcText property. In addition, the paragraph text is preceded by a five-digit number (followed by a colon and space), and the name of the paragraph format of the source paragraph (followed by a colonm and space). It is these two additional components in the XRefSrcText property of the XRef that causes FrameMaker to "see" this as a paragraph cross-reference.
Keeping cross-ref markers unique
There is another reason for the different syntax between paragraph and spot cross-references. It is essential that Cross-Ref markers within a document are unique. While you can have more than one cross-reference pointing to a single Cross-Ref marker, you can't have a single cross-reference trying to point to more than one marker with the same marker text. When you insert spot cross-references, it is up to you to make sure that each Cross-Ref marker is unique. When you insert paragraph cross-references, FrameMaker makes sure the markers it inserts are unique by using the special syntax, particularly the five-digit serial number on the front.
The cross-ref marker text should never be changed after the markers are inserted. You could have several cross-references pointing to the same marker; if you change the marker text, the cross-references would become unresolved. Likewise, changing the text of the source paragraph will not change the marker text.
Before writing some code to insert paragraph cross-references, here are the important points to remember about both paragraph and spot cross-references.
Adding paragraph cross-references
We will use the same example we used with spot cross-references, except this time we will insert the cross-references and its corresponding marker at the same time. In our previous example, we inserted all of the markers first and then the cross-references. We assumed that for every entry in the table, there would be an exactly matching Heading2 paragraph in the document. This might not always be a safe assumption and could result in unresolved cross-references.
We will start with our loop through the table. The entire code listing is below.
// Test for an active document.
If ActiveDoc = 0
MsgBox 'There is no active document. ';
LeaveSub;
Else
Set vCurrentDoc = ActiveDoc;
EndIf
// Set a variable for the current table.
Set vTbl = vCurrentDoc.SelectedTbl;
// Make sure a the cursor is in a table.
If vTbl.ObjectName not= 'Tbl'
MsgBox 'There is no selected table. ';
LeaveSub; // Exit the script.
EndIf
// Make a property list for the color red.
Get Object Type(Color) Name('Red') DocObject(vCurrentDoc)
NewVar(vColor);
New PropertyList NewVar(vProps) Color(vColor);
// Find the first body row in the table.
Set vRow = vTbl.FirstRowInTbl;
Loop While(vRow.RowType = RowHeading)
Set vRow = vRow.NextRowInTbl;
EndLoop
// Go to the first cell in the first body row.
Set vCell = vRow.FirstCellInRow;
// Begin the loop.
Loop While(vCell)
// Select the text by making a TextRange.
New TextRange NewVar(vTextRange) Object(vCell.FirstPgf)
Offset(0) Offset(ObjEndOffset-1);
// Set a variable for the TextRange text.
Set vXRefSrcText = vTextRange.Text;
// Run a subroutine to find the corresponding
heading.
Set vHeadingFound = 0;
Run FindSourceHeading Returns vMarkerText(vXRefSrcText);
If vHeadingFound = 1
// Delete the text.
Delete Text TextRange(vTextRange);
// Insert the cross-reference.
New XRef Format('Heading & Page') TextLoc(vCell.FirstPgf)
NewVar(vXRef);
Set vXRef.XRefSrcText = vXRefSrcText;
Else
// If the corresponding heading can’t be found, color the
// text red so it stands out.
Apply TextProperties TextRange(vTextRange)
Properties(vProps);
EndIf
// Go to the next cell and repeat the loop.
Set vCell = vCell.CellBelowInCol;
EndLoop
// Update the cross-references.
Update DocObject(vCurrentDoc) XRefs Everything;
For each entry in the table, we are going to run a subroutine called FindSourceHeading that attempts to locate the corresponding heading in the document. If it doesn't find a corresponding head with the same text, it will apply the color red to the text in the table cell so you can easily know that there is a problem. Here is the code listing for the subroutine.
Sub FindSourceHeading
//
Loop ForEach(Pgf) In(vCurrentDoc) LoopVar(vPgf)
If vPgf.Name = 'Heading2'
// See if the paragraph text is the same as the table
// cell text.
If vPgf.Text = vXRefSrcText
// Make marker text with paragraph cross-ref syntax.
// Get the unique Id of the paragraph and convert it to
// a string.
New String NewVar(vMarkerText) Value(vPgf.Unique);
// Drop the first character so we end up with 5 digits.
Get String FromString(vMarkerText) NewVar(vMarkerText)
StartPos(vMarkerText.Size - 4);
// Add the paragraph tag and text.
Set vMarkerText = vMarkerText + ': ' + vPgf.Name + ': ' +
vPgf.Text;
// Add the cross-reference marker.
New Marker NewVar(vMarker) MarkerName('Cross-Ref')
TextLoc(vPgf);
// Set the marker text to the paragraph text.
Set vMarker.MarkerText = vMarkerText;
// Set the vHeadingFound variable to 1.
Set vHeadingFound = 1;
// Leave the subroutine.
LeaveSub;
EndIf
EndIf
EndLoop
//
EndSub
Notice that this code is similar to the earlier script that inserted the spot cross reference markers. It is a simple loop through the document's paragraphs looking for Heading2 paragraphs. It has an addition test to see if the paragraph's text matches the text of the table cell.