@mcouture11,
Regarding WORDSNET-21581, please check the following analysis details:
There is a different document layout in the ‘Template’ and inserted document. Margins are almost the same (but still not the same), but header size is different - 50,8 pt and 34 pt. It would be difficult to have the same number of pages when you inserts one document into another unless documents are absolutely identical. We suggest you to use page breaks to control page split (count).
For this particular case, please do the following changes:
Remove that condition at all:
if (run.getAncestor(NodeType.CELL) != null)
// Let's not anchor image to text inside Cell
continue;
And change Shape insertShape method like this
image.isLayoutInCell(false);
image.setWidth(222);
image.setHeight(58);
Complete Java code is as follows:
String templateDocument = "C:\\Temp\\Documents_part1\\Template.odt";
String contentsToInsert = "C:\\Temp\\Documents_part1\\DocumentToInsert.doc";
String signatureImage = "C:\\Temp\\Documents_part1\\Signature.gif";
String finalDocument = "C:\\Temp\\Documents_part1\\awjava-21.1.doc";
Document docFinal = new Document(templateDocument);
DocumentBuilder builderFinal = new DocumentBuilder(docFinal);
// Replace %Signature% tag by a bookmark in the template document
FindReplaceOptions optsBookmark = new FindReplaceOptions();
optsBookmark.setDirection(FindReplaceDirection.BACKWARD);
optsBookmark.setReplacingCallback(new ReplacingCallbackImpl());
docFinal.getRange().replace(Pattern.compile("%Signature%"), "", optsBookmark);
// Get the (x, y) coordinates and remove Bookmark
LayoutCollector layoutCollector = new LayoutCollector(docFinal);
LayoutEnumerator layoutEnumerator = new LayoutEnumerator(docFinal);
layoutEnumerator.setCurrent(layoutCollector.getEntity(
docFinal.getRange().getBookmarks().get("Signature").getBookmarkStart()));
double left = layoutEnumerator.getRectangle().getX();
double top = layoutEnumerator.getRectangle().getY();
docFinal.getRange().getBookmarks().get("Signature").remove();
// Remove empty Paragraphs
for (Section section : docFinal.getSections()) {
for (Paragraph para : (Iterable<Paragraph>) section.getBody().getChildNodes(NodeType.PARAGRAPH, true)) {
if (para.toString(SaveFormat.TEXT).trim().equals(""))
para.remove();
}
}
// Replace the %contents% tag by data from an external document (doc/docx)
FindReplaceOptions opts = new FindReplaceOptions();
opts.setDirection(FindReplaceDirection.BACKWARD);
InsertDocumentAtReplaceHandler insertDocumentAtReplaceHandler = new InsertDocumentAtReplaceHandler();
insertDocumentAtReplaceHandler.setEfindingsToInsert(contentsToInsert);
opts.setReplacingCallback(insertDocumentAtReplaceHandler);
docFinal.getRange().replace(Pattern.compile("%contents%"), "", opts);
// Remove empty Pargaraph(s) from the end
docFinal.getLastSection().getBody().getLastParagraph().remove();
////////////////////////////////////////////////////////
//Case 1: Only insert signature at the last page of the document
// builderFinal.moveToDocumentEnd();
// insertShape(builderFinal, signatureImage, left, top);
////////////////////////////////////////////////////////
////////////////////////////////////////////////////////
//Case 2: Insert the signature on each page of the document
NodeCollection runNodes = docFinal.getChildNodes(NodeType.RUN, true);
LayoutCollector collector = new LayoutCollector(docFinal);
int pageIndex = 1;
for (int i = 0; i < runNodes.getCount(); i++) {
Run run = (Run) runNodes.get(i);
if (collector.getStartPageIndex(run) == pageIndex) {
builderFinal.moveTo(run);
insertShape(builderFinal, signatureImage, left, top);
pageIndex++;
}
}
////////////////////////////////////////////////////////
// Save to tempprary DOCX stream
ByteArrayOutputStream baos = new ByteArrayOutputStream();
docFinal.save(baos, SaveFormat.DOCX);
// Save to DOC format
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
Document doc = new Document(bais);
doc.save(finalDocument);
public static Shape insertShape(DocumentBuilder builderFinal, String signatureImage, double left, double top) throws Exception {
Shape image = builderFinal.insertImage(signatureImage);
image.setLeft(left);
image.setTop(top);
image.setRelativeHorizontalPosition(RelativeHorizontalPosition.PAGE);
image.setRelativeVerticalPosition(RelativeVerticalPosition.PAGE);
image.setWrapType(WrapType.NONE);
image.setZOrder(10);
image.isLayoutInCell(false);
image.setWidth(222);
image.setHeight(58);
return image;
}
private static Run splitRun(Run run, int position) throws Exception {
Run afterRun = (Run) run.deepClone(true);
afterRun.setText(run.getText().substring(position));
run.setText(run.getText().substring(0, position));
run.getParentNode().insertAfter(afterRun, run);
return afterRun;
}
static class ReplacingCallbackImpl implements IReplacingCallback {
public int replacing(ReplacingArgs e) throws Exception {
// This is a Run node that contains either the beginning or the complete match.
Node currentNode = e.getMatchNode();
// The first (and may be the only) run can contain text before the match,
// in this case it is necessary to split the run.
if (e.getMatchOffset() > 0)
currentNode = splitRun((Run) currentNode, e.getMatchOffset());
ArrayList runs = new ArrayList();
// Find all runs that contain parts of the match string.
int remainingLength = e.getMatch().group().length();
while ((remainingLength > 0) && (currentNode != null) && (currentNode.getText().length() <= remainingLength)) {
runs.add(currentNode);
remainingLength = remainingLength - currentNode.getText().length();
// Select the next Run node.
// Have to loop because there could be other nodes such as BookmarkStart etc.
do {
currentNode = currentNode.getNextSibling();
} while ((currentNode != null) && (currentNode.getNodeType() != NodeType.RUN));
}
// Split the last run that contains the match if there is any text left.
if ((currentNode != null) && (remainingLength > 0)) {
splitRun((Run) currentNode, remainingLength);
runs.add(currentNode);
}
DocumentBuilder builder = new DocumentBuilder((Document) e.getMatchNode().getDocument());
builder.moveTo((Run) runs.get(0));
String name = e.getMatch().group(0).substring(1, e.getMatch().group(0).length() - 1);
builder.startBookmark(name);
builder.endBookmark(name);
for (Run run : (Iterable<Run>) runs)
run.remove();
// Signal to the replace engine to do nothing because we have already done all what we wanted.
return ReplaceAction.SKIP;
}
}
static class InsertDocumentAtReplaceHandler implements IReplacingCallback {
public static String efindingsToInsert;
public static void setEfindingsToInsert(String efindingsToInsertTest) {
efindingsToInsert = efindingsToInsertTest;
}
public int replacing(ReplacingArgs e) throws Exception {
// This is a Run node that contains either the beginning or the complete match.
Node currentNode = e.getMatchNode();
// The first (and may be the only) run can contain text before the match,
// in this case it is necessary to split the run.
if (e.getMatchOffset() > 0)
currentNode = splitRun((Run) currentNode, e.getMatchOffset());
ArrayList runs = new ArrayList();
// Find all runs that contain parts of the match string.
int remainingLength = e.getMatch().group().length();
while ((remainingLength > 0) && (currentNode != null) && (currentNode.getText().length() <= remainingLength)) {
runs.add(currentNode);
remainingLength = remainingLength - currentNode.getText().length();
// Select the next Run node.
// Have to loop because there could be other nodes such as BookmarkStart etc.
do {
currentNode = currentNode.getNextSibling();
} while ((currentNode != null) && (currentNode.getNodeType() != NodeType.RUN));
}
// Split the last run that contains the match if there is any text left.
if ((currentNode != null) && (remainingLength > 0)) {
splitRun((Run) currentNode, remainingLength);
runs.add(currentNode);
}
DocumentBuilder builder = new DocumentBuilder((Document) e.getMatchNode().getDocument());
builder.moveTo((Run) runs.get(0));
builder.insertDocument(new Document(efindingsToInsert), ImportFormatMode.KEEP_SOURCE_FORMATTING);
for (Run run : (Iterable<Run>) runs)
run.remove();
// Signal to the replace engine to do nothing because we have already done all what we wanted.
return ReplaceAction.SKIP;
}
}