0

I'm starting to work with some content controls (using the Developer Tools menu) in a MS Word .dotx template. What I would like to do is add some text programatically using Java and the docx4j library. Does anyone know where I can find code examples of this?

Below I have added the XML representation of the content control that I would like to work with. In this case I would like to replace the text "Klicken Sie hier, um Text einzugeben." wih my own text.

-<w:sdt>
-<w:sdtPr>
-<w:rPr>
<w:rStyle w:val="CAPITALS"/>
</w:rPr>
<w:alias w:val="Zeugnisart"/>
<w:tag w:val="Zeugnisart"/>
<w:id w:val="-1662376684"/>
-<w:placeholder>
<w:docPart w:val="DefaultPlaceholder_1082065158"/>
</w:placeholder>
<w:showingPlcHdr/>
<w:text/>
</w:sdtPr>
-<w:sdtEndPr>
-<w:rPr>
<w:rStyle w:val="Absatz-Standardschriftart"/>
<w:rFonts w:hAnsiTheme="minorHAnsi" w:asciiTheme="minorHAnsi"/>
<w:b w:val="0"/>
<w:bCs w:val="0"/>
<w:smallCaps w:val="0"/>
<w:spacing w:val="0"/>
<w:sz w:val="22"/>
</w:rPr>
</w:sdtEndPr>
<w:sdtContent>
<w:p w:rsidP="00D144D4" w:rsidRDefault="006D40B2" w:rsidR="00D144D4">
-<w:r w:rsidRPr="00372E7E">
-<w:rPr>
<w:rStyle w:val="Platzhaltertext"/>
</w:rPr>
<w:t>Klicken Sie hier, um Text einzugeben.</w:t>
</w:r>
</w:p>
</w:sdtContent>
</w:sdt>

I have attempted following code but without success:

private void replacePlaceholder(WordprocessingMLPackage template, String name, String placeholder ) throws Exception{       

    MainDocumentPart documentPart = template.getMainDocumentPart();
    HashMap<String, String> mappings = new HashMap<String, String>();
    mappings.put("Zeugnisart", "a new value");
    documentPart.variableReplace(mappings);
}

private void writeDocxToStream(WordprocessingMLPackage template, String target) throws IOException, Docx4JException {
    File f = new File(target);
    template.save(f);
}

Any tip on where I am going wrong?

  • Do you want to use content control data binding (bound to XML data via XPath) to replace that text, or some cruder method (eg variable replacement)? – JasonPlutext Jan 09 '15 at 18:51
  • I have the StdBlock and want to replace its' content with a String. –  Jan 12 '15 at 12:06

2 Answers2

1

From your clarification, it seems like you just want VariableReplace.

If you wanted instead to bind its content to an XML part (via XPath), see ContentControlsMergeXML. Of course you'd need to set up the databinding first (via a w:dataBinding element in the SdtPr).

JasonPlutext
  • 15,352
  • 4
  • 44
  • 84
  • I have attempted this code which I have added above. –  Jan 12 '15 at 14:17
  • Your variable Zeugnisart is probably split across runs. Update the XML in your question with what you have now? – JasonPlutext Jan 12 '15 at 19:17
  • I don't understand what you mean. The variable Zeugnisart is shown in the XML above. –  Jan 13 '15 at 10:31
  • For variable replacement, you'd need ${Zeugnisart} as the sdtContent, not what you have in w:tag. Of course you could write code to process what you have, but why wouldn't you just use content control data binding? Otherwise you have the joys of split runs to look forward to. – JasonPlutext Jan 13 '15 at 10:43
  • I'm trying to use content control data binding but I only manage to add a new tag rather than replace the content of the existing one. Any sample code would be greatly appreciated. –  Jan 13 '15 at 16:21
  • Given this is StackOverflow, you should explain what you've tried. Perhaps in a new question. – JasonPlutext Jan 13 '15 at 22:00
  • I've added my solution above. –  Jan 14 '15 at 15:56
1

I have managed to solve this issue using the following code:

private static List<Object> getAllElementFromObject(Object obj, Class<?> toSearch) {

    List<Object> result = new ArrayList<Object>();
    if (obj instanceof JAXBElement) obj = ((JAXBElement<?>) obj).getValue();

    if (obj.getClass().equals(toSearch))
        result.add(obj); 
    else if (obj instanceof ContentAccessor) {
        List<?> children = ((ContentAccessor) obj).getContent();
        for (Object child : children) {
            result.addAll(getAllElementFromObject(child, toSearch));
        }
    }
    return result; 
}


private void replaceTextValue(WordprocessingMLPackage template, String name, String placeholder ) throws Exception{     

    List<Object> texts = getAllElementFromObject(template.getMainDocumentPart(), SdtBlock.class);

    for (Object text : texts) {         

        SdtBlock textElement = (SdtBlock) text;
        List<Object> cList = textElement.getSdtContent().getContent();

        SdtPr pr = textElement.getSdtPr();
        List<Object> al = pr.getRPrOrAliasOrLock();

        for (Object alias : al) {   // go through all SdtPr objects

            if ( alias.getClass().toString().contains("org.docx4j.wml.Tag")) {

                String CTagVal = ((org.docx4j.wml.Tag) alias).getVal(); 

                if (CTagVal.equalsIgnoreCase(placeholder))  {   

                ClassFinder finder = new ClassFinder(Text.class);
                new TraversalUtil(cList, finder);

                for (Object o : finder.results) {
                    Object o2 = XmlUtils.unwrap(o);
                    if (o2 instanceof org.docx4j.wml.Text) {
                    org.docx4j.wml.Text txt = (org.docx4j.wml.Text)o2;
                    txt.setValue(name);
                    } else {
                    System.out.println( XmlUtils.marshaltoString(o, true, true));
                    }
                    }

                }

        }           

    }
}
}

I call the replaceTextValue method to change the text inside the "Klicken Sie hier, um Text einzugeben." text inside the tags. There may be a more efficient way to do this but this is what I have found to work.