package org.docx4j.model.fields; import org.docx4j.TraversalUtil; import org.docx4j.XmlUtils; import org.docx4j.jaxb.Context; import org.docx4j.model.fields.docproperty.DocPropertyResolver; import org.docx4j.openpackaging.exceptions.Docx4JException; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.openpackaging.parts.JaxbXmlPart; import org.docx4j.openpackaging.parts.relationships.Namespaces; import org.docx4j.openpackaging.parts.relationships.RelationshipsPart; import org.docx4j.relationships.Relationship; import org.docx4j.wml.CTSimpleField; import org.docx4j.wml.ContentAccessor; import org.docx4j.wml.P; import org.docx4j.wml.R; import org.docx4j.wml.Text; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.xml.transform.TransformerException; import java.util.ArrayList; import java.util.List; /** * Refreshes the values of certain fields in the * docx (currently DOCPROPERTY only). * * Do this whether they are simple or complex. * * This updates the docx. If you don't want to do * that, apply it to a clone instead. * * @author jharrop * */ public class FieldUpdater { private static Logger log = LoggerFactory.getLogger(FieldUpdater.class); WordprocessingMLPackage wordMLPackage; DocPropertyResolver docPropertyResolver; StringBuilder report = null; public FieldUpdater(WordprocessingMLPackage wordMLPackage) { this.wordMLPackage = wordMLPackage; // docPropsCustomPart = wordMLPackage.getDocPropsCustomPart(); docPropertyResolver = new DocPropertyResolver(wordMLPackage); } public void update(boolean processHeadersAndFooters) throws Docx4JException { report = new StringBuilder(); updatePart(wordMLPackage.getMainDocumentPart() ); if (processHeadersAndFooters) { RelationshipsPart rp = wordMLPackage.getMainDocumentPart().getRelationshipsPart(); for ( Relationship r : rp.getJaxbElement().getRelationship() ) { if (r.getType().equals(Namespaces.HEADER) || r.getType().equals(Namespaces.FOOTER)) { JaxbXmlPart part = (JaxbXmlPart)rp.getPart(r); report.append("\n" + part.getPartName() + "\n"); log.debug("\n" + part.getPartName() + "\n"); updatePart(part ); // performOnInstance( // ((ContentAccessor)part).getContent() ); } } } log.info(report.toString()); } public void updatePart(JaxbXmlPart part) throws Docx4JException { updateSimple(part); updateComplex(part); } public void updateSimple(JaxbXmlPart part) throws Docx4JException { FldSimpleModel fsm = new FldSimpleModel(); //gets reused List contentList = ((ContentAccessor)part).getContent(); WordprocessingMLPackage wmlPackage = (WordprocessingMLPackage)part.getPackage(); // find fields SimpleFieldLocator fl = new SimpleFieldLocator(); new TraversalUtil(contentList, fl); report.append("\n\nSimple Fields in " + part.getPartName() + "\n"); report.append("============= \n"); report.append("Found " + fl.simpleFields.size() + " simple fields \n "); for( CTSimpleField simpleField : fl.simpleFields ) { //System.out.println(XmlUtils.marshaltoString(simpleField, true, true)); // System.out.println(simpleField.getInstr()); if ("DOCPROPERTY".equals(FormattingSwitchHelper.getFldSimpleName(simpleField.getInstr()))) { //only parse those fields that get processed try { fsm.build(simpleField.getInstr()); } catch (TransformerException e) { e.printStackTrace(); } String key = fsm.getFldParameters().get(0); String val; try { val = (String) docPropertyResolver.getValue(key); } catch (FieldValueException e) { report.append( simpleField.getInstr() + "\n"); report.append( key + " -> NOT FOUND! \n"); continue; } if (val==null) { report.append( simpleField.getInstr() + "\n"); report.append( key + " -> NOT FOUND! \n"); } else { //docPropsCustomPart.getProperty(key); // System.out.println(val); val = FormattingSwitchHelper.applyFormattingSwitch(wmlPackage, fsm, val); // System.out.println("--> " + val); report.append( simpleField.getInstr() + "\n"); report.append( "--> " + val + "\n"); R r=null; if (simpleField.getInstr().toUpperCase().contains("MERGEFORMAT")) { // find the first run and use the formatting of that r = getFirstRun(simpleField.getContent()); } if (r==null) { r = Context.getWmlObjectFactory().createR(); } else { r.getContent().clear(); } simpleField.getContent().clear(); simpleField.getContent().add(r); Text t = Context.getWmlObjectFactory().createText(); t.setValue(val); // t.setSpace(value) //TODO r.getContent().add(t); // System.out.println(XmlUtils.marshaltoString(simpleField, true, true)); } } else { report.append("Ignoring " + simpleField.getInstr() + "\n"); } } } private R getFirstRun(List<Object> content) { for (Object o : content) { if (o instanceof R) return (R)o; } return null; } public void updateComplex(JaxbXmlPart part) throws Docx4JException { FldSimpleModel fsm = new FldSimpleModel(); //gets reused List contentList = ((ContentAccessor)part).getContent(); WordprocessingMLPackage wmlPackage = (WordprocessingMLPackage)part.getPackage(); ComplexFieldLocator fl = new ComplexFieldLocator(); new TraversalUtil(contentList, fl); report.append("\n Complex Fields in "+ part.getPartName() + "\n"); report.append("============== \n"); report.append("Found " + fl.getStarts().size() + " fields \n"); // canonicalise and setup fieldRefs List<FieldRef> fieldRefs = new ArrayList<FieldRef>(); for( P p : fl.getStarts() ) { int index; if (p.getParent() instanceof ContentAccessor) { index = ((ContentAccessor)p.getParent()).getContent().indexOf(p); P newP = FieldsPreprocessor.canonicalise(p, fieldRefs); // log.debug("NewP length: " + newP.getContent().size() ); ((ContentAccessor)p.getParent()).getContent().set(index, newP); } else if (p.getParent() instanceof java.util.List) { // This does happen! index = ((java.util.List)p.getParent()).indexOf(p); P newP = FieldsPreprocessor.canonicalise(p, fieldRefs); // log.debug("NewP length: " + newP.getContent().size() ); ((java.util.List)p.getParent()).set(index, newP); } else { throw new Docx4JException ("Unexpected parent: " + p.getParent().getClass().getName() ); } } // Populate for (FieldRef fr : fieldRefs) { if ("DOCPROPERTY".equals(fr.getFldName())) { String instr = extractInstr(fr.getInstructions()); try { fsm.build(instr); } catch (TransformerException e) { e.printStackTrace(); } String key = fsm.getFldParameters().get(0); String val = (String) docPropertyResolver.getValue(key); try { val = (String) docPropertyResolver.getValue(key); } catch (FieldValueException e) { report.append( instr + "\n"); report.append( key + " -> NOT FOUND! \n"); continue; } if (val==null) { report.append( instr + "\n"); report.append( key + " -> NOT FOUND! \n"); } else { // System.out.println(val); val = FormattingSwitchHelper.applyFormattingSwitch(wmlPackage, fsm, val); // System.out.println("--> " + val); report.append( instr + "\n"); report.append( "--> " + val + "\n"); fr.setResult(val); // // If doing an actual mail merge, the begin-separate run is removed, as is the end run // fr.getParent().getContent().remove(fr.getBeginRun()); // fr.getParent().getContent().remove(fr.getEndRun()); // System.out.println(XmlUtils.marshaltoString( // fr.getParent(), true, true)); } } else { report.append("Ignoring " + fr.getFldName() + "\n"); } } } private String extractInstr(List<Object> instructions) { // For DOCPROPERTY, expect the list to contain a simple string if (instructions.size()!=1) { log.error("TODO DOCPROPERTY field contained complex instruction"); return null; } Object o = XmlUtils.unwrap(instructions.get(0)); if (o instanceof Text) { return ((Text)o).getValue(); } else { if(log.isErrorEnabled()) { log.error("TODO: extract field name from " + o.getClass().getName()); log.error(XmlUtils.marshaltoString(instructions.get(0), true, true)); } return null; } } /** * @param args * @throws Docx4JException */ public static void main(String[] args) throws Docx4JException { WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load( new java.io.File( System.getProperty("user.dir") + "/aq1.docx")); FieldUpdater fu = new FieldUpdater(wordMLPackage); fu.update(true); System.out.println(XmlUtils.marshaltoString(wordMLPackage.getMainDocumentPart().getJaxbElement(), true, true)); } }