How to identify the location of a node/text from the bottom of page, and move it to next page on condition

Hi, I am working on text substitution on word document and word to pdf conversion.

  1. My requirement is to find a token (which is to be substituted with actual value), substitute it and check if its at the bottom of the page. If its at bottom, then move the text to next page.

  2. Similarly, find another token and create a table in place of that. If its coming at the bottom of the page, and breaking into next page, then move the entire table to next page.

Substitution, table creation are all done. I just want to know how can we handle this ‘moving content to next page on the basis of location’ scenario.

Thanks

@Mojo3991,

Yes, it is possible to calculate the distance of content from the bottom of Page and conditionally move content (Text, Table Images etc.) to next Page. You can build logic on the following Java code to get the desired output:

Document doc = new Document("C:\\Temp\\input.docx");
DocumentBuilder builder = new DocumentBuilder(doc);

// Find Token, Insert temporary Bookmark in its place and remove Token
FindReplaceOptions opts = new FindReplaceOptions();
opts.setDirection(FindReplaceDirection.BACKWARD);
opts.setReplacingCallback(new ReplacingHandler());
doc.getRange().replace(Pattern.compile("token"), "", opts);

Bookmark myBookmark = doc.getRange().getBookmarks().get("myBookmark");

// Define threshold - Acceptable distance from Bottom of Page
Section section = (Section) myBookmark.getBookmarkStart().getAncestor(NodeType.SECTION);
double threshold = section.getPageSetup().getPageHeight() - (2 * 72); // 2 inches from bottom

// Substitute Content (Insert new Text Table Image etc)
builder.moveToBookmark("myBookmark", true, true);
builder.writeln("new content");
builder.writeln("another new paragraph");

doc.updatePageLayout();

// Determine if content Spans across multiple pages
// or end of content is too close to Page bottom
boolean moveToNextPage = false;

LayoutCollector layoutCollector = new LayoutCollector(doc);
LayoutEnumerator layoutEnumerator = new LayoutEnumerator(doc);

if (layoutCollector.getEndPageIndex(myBookmark.getBookmarkEnd()) -
        layoutCollector.getStartPageIndex(myBookmark.getBookmarkStart()) > 0) {
    moveToNextPage = true;
} else {
    // Determine coordinates of end of new content
    layoutEnumerator.setCurrent(layoutCollector.getEntity(myBookmark.getBookmarkEnd()));
    if (threshold < layoutEnumerator.getRectangle().y) {
        moveToNextPage = true;
    }
}

if (moveToNextPage) {
    // Move entire Bookmarked content to next page
    builder.moveToBookmark("myBookmark", true, false);
    builder.insertBreak(BreakType.PAGE_BREAK);
}

// Remove bookmark
myBookmark.remove();

doc.updatePageLayout();

doc.save("C:\\Temp\\awjava-20.11.docx");
doc.save("C:\\Temp\\awjava-20.11.pdf");

static class ReplacingHandler 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));

        builder.startBookmark("myBookmark");
        builder.endBookmark("myBookmark");

        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.STOP;
    }
}

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;
}