package org.docx4j.model.datastorage.migration;
import org.docx4j.TraversalUtil;
import org.docx4j.XmlUtils;
import org.docx4j.model.fields.ComplexFieldLocator;
import org.docx4j.model.fields.FieldRef;
import org.docx4j.model.fields.FieldsPreprocessor;
import org.docx4j.openpackaging.io.SaveToZipFile;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.wml.ContentAccessor;
import org.docx4j.wml.P;
import org.docx4j.wml.Text;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
/**
* This class will help you to migrate
* from MERGEFIELDs
* to use of content control data bindings.
*
* After migrating, you'll be able to
* use the OpenDoPE authoring tool to
* add repeats, conditionals, and other
* OpenDoPE features, if you need them.
*
* Limitations: this first version
* operates only on the main document part
* (ie it won't process variables in
* headers/footers, footnotes/endnotes,
* or comments)
*
* @author jharrop
* @since 3.0.0
*/
public class FromMergeFields extends AbstractMigrator {
private static Logger log = LoggerFactory.getLogger(FromMergeFields.class);
public WordprocessingMLPackage migrate(WordprocessingMLPackage pkgIn) throws Exception {
// TODO - test that OpenDoPE parts aren't already present
// or if they are, that this docx is using our answer format
// (since only that format is supported here)
// Clone it
WordprocessingMLPackage pkgOut = (WordprocessingMLPackage)pkgIn.clone();
// Create the CustomXML parts
createParts(pkgOut);
FieldsPreprocessor.complexifyFields(pkgOut.getMainDocumentPart() );
if(log.isDebugEnabled()) {
log.debug("complexified: "
+ XmlUtils.marshaltoString(pkgOut.getMainDocumentPart().getJaxbElement(), true));
}
// find fields
ComplexFieldLocator fl = new ComplexFieldLocator();
new TraversalUtil(pkgOut.getMainDocumentPart().getContent(), fl);
log.info("Found " + fl.getStarts().size() + " fields ");
// canonicalise and setup fieldRefs
List<FieldRef> fieldRefs = new ArrayList<FieldRef>();
for( P p : fl.getStarts() ) {
int index = ((ContentAccessor)p.getParent()).getContent().indexOf(p);
P newP = FieldsPreprocessor.canonicalise(p, fieldRefs);
((ContentAccessor)p.getParent()).getContent().set(index, newP);
/*
* <w:p xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:ns23="http://schemas.openxmlformats.org/schemaLibrary/2006/main" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math">
<w:r>
<w:t xml:space="preserve">Hallo, lower </w:t>
</w:r>
<w:r>
<w:rPr>
<w:noProof/>
</w:rPr>
<w:fldChar w:fldCharType="begin"/>
<w:instrText xml:space="preserve"> MERGEFIELD kundenname \* MERGEFORMAT </w:instrText>
<w:fldChar w:fldCharType="separate"/>
</w:r>
<w:r>
<w:rPr>
<w:noProof/>
</w:rPr>
<w:t>«Kundenname»</w:t>
</w:r>
<w:r/>
<w:r>
<w:fldChar w:fldCharType="end"/>
</w:r>
</w:p>
*/
}
// Populate
for (FieldRef fr : fieldRefs) {
if ( fr.getFldName().equals("MERGEFIELD") ) {
String instr = extractInstr(fr.getInstructions() );
// eg <w:instrText xml:space="preserve"> MERGEFIELD Kundenstrasse \* MERGEFORMAT </w:instrText>
// or <w:instrText xml:space="preserve"> MERGEFIELD Kundenstrasse</w:instrText>
String tmp = instr.substring( instr.indexOf("MERGEFIELD") + 10);
tmp = tmp.trim();
String key = tmp.indexOf(" ") >-1 ? tmp.substring(0, tmp.indexOf(" ")) : tmp ;
log.info("Key: '" + key + "'");
// Remove the field related runs
int end = fr.getParent().getContent().indexOf(fr.getEndRun());
int begin = fr.getParent().getContent().indexOf(fr.getBeginRun());
for (int i = end; i>=begin; i--) {
fr.getParent().getContent().remove(i);
}
// Now add a content control
List<Object> replacementContent = new ArrayList<Object>();
createContentControl(null, replacementContent, key);
fr.getParent().getContent().addAll(begin, replacementContent);
// System.out.println(XmlUtils.marshaltoString(
// fr.getParent(), true, true));
}
}
return pkgOut;
}
private static String extractInstr(List<Object> instructions) {
// For MERGEFIELD, expect the list to contain a simple string
if (instructions.size()!=1) {
log.error("TODO MERGEFIELD 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;
}
}
// public static boolean isMergeField(String type) {
//
// if (type.contains("MERGEFIELD")) {
// return true;
// } else {
// return false;
// }
// }
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
String inputfilepath = System.getProperty("user.dir")
+ "/TEST1.docx";
String outputfilepath = System.getProperty("user.dir")
+ "/OUT_TEST1.docx";
WordprocessingMLPackage pkgIn = WordprocessingMLPackage.load(new java.io.File(inputfilepath));
FromMergeFields migrator = new FromMergeFields();
WordprocessingMLPackage pkgOut = migrator.migrate(pkgIn);
SaveToZipFile saver = new SaveToZipFile(pkgOut);
saver.save(outputfilepath);
}
}