Page 1 of 1

Bookmarks and templates advice

PostPosted: Thu Apr 14, 2011 1:24 am
by freemink
Hi,

Wonder if you have time to offer some general advice.

Currently we have an application that populates Word (2003 + compatibility pack) documents using bookmarks as placeholders and VB to insert values. We wish to move to a server-side, java-based solution.

There are two types of bookmark placeholder:
1. single valued - vb locates the bookmark and adds a value immediately after it (this is the easy case)
2. multi-valued - these bookmarks always exist in table cells. first vb locates the table (based on bookmark name), then adds the required number of rows, then, for each column (based on a bookmark name) vb inserts a value and moves down a row.

I'm new to docx4j and trying to determine the best approach. Considering case 2 (multi-valued), can this be done with the CustomXMLBinding stuff? Would it even work in Word 2003? Or should I mimic the vb and manually add rows to the table? Should I be using XPath to locate the bookmarks?

Any advice welcome.
Many thanks.

Re: Bookmarks and templates advice

PostPosted: Thu Apr 14, 2011 1:48 am
by Franke
I went with the bookmark way, but coding with some kind of binding is probably alot smoother, though I think it's only supported in word 2007 (but don't quote me on this). Anyhow, if you do make the choice to go with bookmarks, I have some code you might find useful.

documentPart is a MainDocumentPart object that you get from WordprocessingMLPackage getMainDocumentPart. This object contains most of your document.

Traversing upwards is unpractical since many elements are wrapped in JaxbElements, thus removing information of their true parent. That's the reason why I make additional xpath queries instead of just traversing upwards. The unwrap method makes sure I don't return a JAXBElement.

Given a bookmark name, these functions provides the surrounding paragraph, cell, row, or table.

The traverse method was needed to find bookmarks in the header or footer due to the fact that only MainDocumentPart has the method getJAXBNodesViaXPath.

Code: Select all
private Tr findBookmarkedRow(String name) throws JAXBException {
        final String xpath = "//w:bookmarkStart[@w:name='" + name + "']/../../..";
        List<Object> objects = documentPart.getJAXBNodesViaXPath(xpath, false);
        return (org.docx4j.wml.Tr) XmlUtils.unwrap(objects.get(0));
    }

    private Tc findBookmarkedCell(String name) throws JAXBException {
        P p = findBookmarkedParagraphInMainDocumentPart(name);
        return (Tc) p.getParent();
    }

    private P findBookmarkedParagraphInMainDocumentPart(String name) throws JAXBException {
        final String xpath = "//w:bookmarkStart[@w:name='" + name + "']/..";
        List<Object> objects = documentPart.getJAXBNodesViaXPath(xpath, false);
        return (org.docx4j.wml.P) XmlUtils.unwrap(objects.get(0));
    }

    // No xpath implementation for other parts than main document; traverse manually
    private P findBookmarkedParagraphInPart(Object parent, String bookmark) {
        P p = traversePartForBookmark(parent, bookmark);
        return p;
    }

    // Used internally by findBookmarkedParagrapghInPart().
    private P traversePartForBookmark(Object parent, String bookmark) {
        P p = null;
        List children = TraversalUtil.getChildrenImpl(parent);
        if (children != null) {
            for (Object o : children) {
                o = XmlUtils.unwrap(o);
                if (o instanceof CTBookmark) {
                    if (((CTBookmark) o).getName().toLowerCase().equals(bookmark)) {
                        return (P) parent; // If bookmark found, the surrounding P is what is interesting.
                    }
                }
                p = traversePartForBookmark(o, bookmark);
                if (p != null) {
                    break;
                }
            }
        }
        return p;
    }

Re: Bookmarks and templates advice

PostPosted: Thu Apr 14, 2011 1:58 am
by freemink
Thanks for sharing Franke. Very decent of you :)
I'll study your code and possibly steal it! If I come up with anything useful I'll post it back...

Re: Bookmarks and templates advice

PostPosted: Thu Apr 14, 2011 2:09 am
by jason
As far as the data binding approach is concerned, docx4j can do multi-valued stuff (eg multiple table rows) using the "repeat" feature of the OpenDoPE convention I devised for this purpose. see opendope.org for more info, and of course the databinding sub-forum here.

Word 2003 + compatibility pack should be able to open the resulting documents (which contain content controls), but iirc, you won't see the controls in the docx (just the contents - rather like Word 2011 for the Mac), and nor can Word 2003 resolve the databindings (again, iirc, though this shouldn't matter in that docx4j will resolve them for you. still, it does remove one of the attractions of the approach, namely the ability for edits in the Word UI to update the XML data).

Also a quick note that HeaderPart in svn does now support the XPath stuff; the same should be added to FooterPart!

If either of you guys create field related stuff that would be a useful addition for others, pls consider contributing it. Thanks! :-)