By default combined documents which contain page numbering fields will automatically have the page numbering continued throughout the joined document. For instance, the sample documents when joined together will have continuous page numbering. The page number fields (PAGENUM) will display {1-4} across the pages and total page fields (NUMPAGES) will display {4}.
A section contains the option to restart page numbering. In Microsoft Word this can be specified in the Page Numbering options.
To restart the page numbering at the start of section the PageSetup.RestartPageNumbering property must be set to true. The number which this is restarted to is defined by the PageSetup.PageStartingNumber property. This property is set to “1” in Microsoft Word and Aspose.Words by default. In this example we will restart the page numbering at the start of the source document.
Example
Shows how to append a document to another document with page numbering restarted.
[Java]
Document dstDoc = new Document(gDataDir + "TestFile.Destination.doc");
Document srcDoc = new Document(gDataDir + "TestFile.Source.doc");
// Set the appended document to appear on the next page.
srcDoc.getFirstSection().getPageSetup().setSectionStart(SectionStart.NEW_PAGE);
// Restart the page numbering for the document to be appended.
srcDoc.getFirstSection().getPageSetup().setRestartPageNumbering(true);
dstDoc.appendDocument(srcDoc, ImportFormatMode.KEEP_SOURCE_FORMATTING);
dstDoc.save(gDataDir + "TestFile.RestartPageNumbering Out.doc");
The output shows the page numbering has been restarted where the source document was appended.
Often when documents containing NUMPAGES fields are appended the desired behavior is for that field type to continue to display the total page count only for only those newly appended pages, just like how they appeared in the original document. However the actual behavior is the opposite and the NUMPAGES field will instead by design display the total number of pages across the entire document.
In this context we will refer to each appended document with the page numbering in the first section restarted as a “subdocument”. Since each subdocument has its numbering restarted and therefore its own page numbering scheme, it should have the total pages field numbering reflecting this by only having pages belonging to the sub document being counted by this field.
For example using the code below, the source document will have its numbering restarted. This will result in the joined document having the page numbers {1, 2, 1, 2}. However the total pages field (the NUMPAGES field) found next to it does not follow this scheme and will display {4} across all pages.
This issue is demonstrated in the joint document below. The total pages for the content of the destination document and source document are both desired to be “2”. However the NUMPAGES field across all pages still displays “4”.
The only possible solution to this issue is a work around. This is because this type of behavior is impossible to implement in Microsoft Word as there is no direct support for multiple schemed numbering. Since this is not possible in Microsoft Word there is no method to directly implement this using Aspose.Words as well.
The closest functionality which emulates the desired behavior is the SECTIONPAGES field. This will display the total number of pages in the section. However this could only be used a partial solution. Either of the destination and source document could possibly have many sections all of which share the same page numbering scheme. This solution will only provide the total page numbering for the current section which is not the correct output.
The source document is demonstrates this as between the two pages is a section break which divides the content of the pages into different sections.
The solution which provides the correct behavior involves replacing the NUMPAGES field with PAGEREF fields which refers to a bookmark positioned at the end of the sub document. This solution is the optimal choice as the PAGEREF field will display the desired page numbers while still retaining the other properties and behavior of a “page” field. Furthermore as long as the bookmarks are not removed further content and sections can be added and removed and the page numbering will reflect these changes correctly.
The implementation below provides an implementation and example which will automatically convert all NUMPAGE fields in the combined document to PAGEREF fields using the described technique above.
Example
Shows how to change the NUMPAGE fields in a document to display the number of pages only within a sub document.
[Java]
public static void appendDocument_ConvertNumPageFields() throws Exception
{
Document dstDoc = new Document(gDataDir + "TestFile.Destination.doc");
Document srcDoc = new Document(gDataDir + "TestFile.Source.doc");
// Restart the page numbering on the start of the source document.
srcDoc.getFirstSection().getPageSetup().setRestartPageNumbering(true);
srcDoc.getFirstSection().getPageSetup().setPageStartingNumber(1);
// Append the source document to the end of the destination document.
dstDoc.appendDocument(srcDoc, ImportFormatMode.KEEP_SOURCE_FORMATTING);
// After joining the documents the NUMPAGE fields will now display the total number of pages which
// is undesired behaviour. Call this method to fix them by replacing them with PAGEREF fields.
convertNumPageFieldsToPageRef(dstDoc);
// This needs to be called in order to update the new fields with page numbers.
dstDoc.updatePageLayout();
dstDoc.save(gDataDir + "TestFile.ConvertNumPageFields Out.doc");
}
/**
* Replaces all NUMPAGES fields in the document with PAGEREF fields. The replacement field displays the total number
* of pages in the sub document instead of the total pages in the document.
*
* @param doc The combined document to process.
*/
public static void convertNumPageFieldsToPageRef(Document doc) throws Exception
{
// This is the prefix for each bookmark which signals where page numbering restarts.
// The underscore "_" at the start inserts this bookmark as hidden in MS Word.
final String BOOKMARK_PREFIX = "_SubDocumentEnd";
// Field name of the NUMPAGES field.
final String NUM_PAGES_FIELD_NAME = "NUMPAGES";
// Field name of the PAGEREF field.
final String PAGE_REF_FIELD_NAME = "PAGEREF";
// Create a new DocumentBuilder which is used to insert the bookmarks and replacement fields.
DocumentBuilder builder = new DocumentBuilder(doc);
// Defines the number of page restarts that have been encountered and therefore the number of "sub" documents
// found within this document.
int subDocumentCount = 0;
// Iterate through all sections in the document.
for (Section section : doc.getSections())
{
// This section has it's page numbering restarted so we will treat this as the start of a sub document.
// Any PAGENUM fields in this inner document must be converted to special PAGEREF fields to correct numbering.
if (section.getPageSetup().getRestartPageNumbering())
{
// Don't do anything if this is the first section in the document. This part of the code will insert the bookmark marking
// the end of the previous sub document so therefore it is not applicable for first section in the document.
if (!section.equals(doc.getFirstSection()))
{
// Get the previous section and the last node within the body of that section.
Section prevSection = (Section)section.getPreviousSibling();
Node lastNode = prevSection.getBody().getLastChild();
// Use the DocumentBuilder to move to this node and insert the bookmark there.
// This bookmark represents the end of the sub document.
builder.moveTo(lastNode);
builder.startBookmark(BOOKMARK_PREFIX + subDocumentCount);
builder.endBookmark(BOOKMARK_PREFIX + subDocumentCount);
// Increase the subdocument count to insert the correct bookmarks.
subDocumentCount++;
}
}
// The last section simply needs the ending bookmark to signal that it is the end of the current sub document.
if (section.equals(doc.getLastSection()))
{
// Insert the bookmark at the end of the body of the last section.
// Don't increase the count this time as we are just marking the end of the document.
Node lastNode = doc.getLastSection().getBody().getLastChild();
builder.moveTo(lastNode);
builder.startBookmark(BOOKMARK_PREFIX + subDocumentCount);
builder.endBookmark(BOOKMARK_PREFIX + subDocumentCount);
}
// Iterate through each NUMPAGES field in the section and replace the field with a PAGEREF field referring to the bookmark of the current subdocument
// This bookmark is positioned at the end of the sub document but does not exist yet. It is inserted when a section with restart page numbering or the last
// section is encountered.
for (Node node : section.getChildNodes(NodeType.FIELD_START, true).toArray())
{
FieldStart fieldStart = (FieldStart)node;
if (fieldStart.getFieldType() == FieldType.FIELD_NUM_PAGES)
{
// Get the field code.
String fieldCode = getFieldCode(fieldStart);
// Since the NUMPAGES field does not take any additional parameters we can assume the remaining part of the field
// code after the fieldname are the switches. We will use these to help recreate the NUMPAGES field as a PAGEREF field.
String fieldSwitches = fieldCode.replace(NUM_PAGES_FIELD_NAME, "").trim();
// Inserting the new field directly at the FieldStart node of the original field will cause the new field to
// not pick up the formatting of the original field. To counter this insert the field just before the original field
Node previousNode = fieldStart.getPreviousSibling();
// If a previous run cannot be found then we are forced to use the FieldStart node.
if (previousNode == null)
previousNode = fieldStart;
// Insert a PAGEREF field at the same position as the field.
builder.moveTo(previousNode);
// This will insert a new field with a code like " PAGEREF _SubDocumentEnd0 *\MERGEFORMAT ".
Field newField = builder.insertField(MessageFormat.format(" {0} {1}{2} {3} ", PAGE_REF_FIELD_NAME, BOOKMARK_PREFIX, subDocumentCount, fieldSwitches));
// The field will be inserted before the referenced node. Move the node before the field instead.
previousNode.getParentNode().insertBefore(previousNode, newField.getStart());
// Remove the original NUMPAGES field from the document.
removeField(fieldStart);
}
}
}
}
The above method also uses a few functions internally. They are provided below.
Example
Provides some helper functions by the methods above
[Java]
/**
* Retrieves the field code from a field.
*
* @param fieldStart The field start of the field which to gather the field code from.
*/
private static String getFieldCode(FieldStart fieldStart) throws Exception
{
StringBuilder builder = new StringBuilder();
for (Node node = fieldStart; node != null && node.getNodeType() != NodeType.FIELD_SEPARATOR &&
node.getNodeType() != NodeType.FIELD_END; node = node.nextPreOrder(node.getDocument()))
{
// Use text only of Run nodes to avoid duplication.
if (node.getNodeType() == NodeType.RUN)
builder.append(node.getText());
}
return builder.toString();
}
/**
* Removes the Field from the document.
*
* @param fieldStart The field start node of the field to remove.
*/
private static void removeField(FieldStart fieldStart) throws Exception
{
Node currentNode = fieldStart;
boolean isRemoving = true;
while (currentNode != null && isRemoving)
{
if (currentNode.getNodeType() == NodeType.FIELD_END)
isRemoving = false;
Node nextNode = currentNode.nextPreOrder(currentNode.getDocument());
currentNode.remove();
currentNode = nextNode;
}
}
The algorithm works by inserting a bookmark at the end of the section each time it finds a section with restart page numbering. The PAGEREF fields that replace the NUMPAGE fields reference these bookmarks. These fields display the total pages within this subdocument (in between document start, end and sections with page numbering restarts). This will change the page numbering to reference the correct number pages even if the subdocument consists of many sections.
It is good to note that this code will still work even if a document has no page numbering restarts. In this case it will simply change any NUMPAGE field to a PAGEREF field with reference to a bookmark found at the end of the document. This will display the same page numbering as what the NUMPAGES field would but using PAGEREF instead.
After this code is executed the output of this document now appears correctly. The total page numbering now appears as desired.
A closer look on the last page shows that the numbering is still correct even when the content is in a different section.
By pressing ALT+F9 we can toggle field codes to show the field information. The algorithm above has changed the NUMPAGES field into a PAFEREF field in order to display the desired page numbering.