Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.docx4j.model.datastorage.BindingTraverserXSLT Maven / Gradle / Ivy
Go to download
docx4j is a library which helps you to work with the Office Open
XML file format as used in docx
documents, pptx presentations, and xlsx spreadsheets.
package org.docx4j.model.datastorage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.text.DateFormat;
import java.text.Format;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import javax.xml.bind.JAXBException;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.stream.StreamSource;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.xalan.extensions.ExpressionContext;
import org.apache.xmlgraphics.image.loader.ImageSize;
import org.docx4j.Docx4jProperties;
import org.docx4j.XmlUtils;
import org.docx4j.convert.in.xhtml.XHTMLImporter;
import org.docx4j.convert.out.html.HtmlCssHelper;
import org.docx4j.dml.wordprocessingDrawing.Inline;
import org.docx4j.jaxb.Context;
import org.docx4j.model.sdt.QueryString;
import org.docx4j.model.styles.StyleTree;
import org.docx4j.model.styles.StyleUtil;
import org.docx4j.model.styles.Tree;
import org.docx4j.model.styles.StyleTree.AugmentedStyle;
import org.docx4j.openpackaging.contenttype.ContentType;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.exceptions.InvalidFormatException;
import org.docx4j.openpackaging.io3.stores.UnzippedPartStore;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.CustomXmlDataStoragePart;
import org.docx4j.openpackaging.parts.CustomXmlPart;
import org.docx4j.openpackaging.parts.JaxbXmlPart;
import org.docx4j.openpackaging.parts.PartName;
import org.docx4j.openpackaging.parts.WordprocessingML.AltChunkType;
import org.docx4j.openpackaging.parts.WordprocessingML.AlternativeFormatInputPart;
import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage;
import org.docx4j.openpackaging.parts.opendope.XPathsPart;
import org.docx4j.openpackaging.parts.relationships.Namespaces;
import org.docx4j.openpackaging.parts.relationships.RelationshipsPart;
import org.docx4j.relationships.Relationship;
import org.docx4j.utils.ResourceUtils;
import org.docx4j.wml.BooleanDefaultTrue;
import org.docx4j.wml.CTAltChunk;
import org.docx4j.wml.CTAltChunkPr;
import org.docx4j.wml.CTSdtDate;
import org.docx4j.wml.Color;
import org.docx4j.wml.P;
import org.docx4j.wml.R;
import org.docx4j.wml.RPr;
import org.docx4j.wml.SdtPr;
import org.docx4j.wml.Style;
import org.opendope.xpaths.Xpaths.Xpath;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.traversal.NodeIterator;
public class BindingTraverserXSLT implements BindingTraverserInterface {
private static Logger log = LoggerFactory.getLogger(BindingTraverserXSLT.class);
static Templates xslt;
static {
try {
Source xsltSource = new StreamSource(
org.docx4j.utils.ResourceUtils.getResource(
"org/docx4j/model/datastorage/bind.xslt"));
xslt = XmlUtils.getTransformerTemplate(xsltSource);
} catch (IOException e) {
e.printStackTrace();
} catch (TransformerConfigurationException e) {
e.printStackTrace();
}
}
/**
* @param part
* @param pkg
* @param doc
* @param xPathsPart
* @throws Docx4JException
*/
public Object traverseToBind(JaxbXmlPart part,
org.docx4j.openpackaging.packages.OpcPackage pkg,
XPathsPart xPathsPart)
throws Docx4JException {
org.w3c.dom.Document doc = XmlUtils.marshaltoW3CDomDocument(
part.getJaxbElement() );
try {
// We used to use a JAXBResult, which
// but its better to use DOMResult
// so we can use part.unmarshal, which should create a binder where possible
DOMResult result = new DOMResult();
Map transformParameters = new HashMap();
transformParameters.put("customXmlDataStorageParts",
part.getPackage().getCustomXmlDataStorageParts());
transformParameters.put("wmlPackage", (WordprocessingMLPackage)pkg);
transformParameters.put("sourcePart", part);
transformParameters.put("xPathsPart", xPathsPart);
org.docx4j.XmlUtils.transform(doc, xslt, transformParameters, result);
if (log.isDebugEnabled()) {
org.w3c.dom.Document docResult = ((org.w3c.dom.Document)result.getNode());
log.debug(XmlUtils.w3CDomNodeToString(docResult));
return XmlUtils.unmarshal(docResult);
} else {
//part.unmarshal( ((org.w3c.dom.Document)result.getNode()).getDocumentElement() );
return XmlUtils.unmarshal(((org.w3c.dom.Document)result.getNode()) );
}
} catch (Exception e) {
throw new Docx4JException("Problems applying bindings", e);
}
}
public static void log(ExpressionContext expressionContext, String message ) {
//log.info( com.sun.org.apache.xalan.internal.lib.NodeInfo.lineNumber(expressionContext ) + " " + message);
// com.sun.org.apache hell
// but that only gives line number of input XML anyway, whereas more useful is
// currently executing line number of XSLT. ErrorListener seems to know this? Explore some time...
log.info( "[String] " + message);
}
/**
* @param nodeIterator
* @deprecated
*/
public static void log(NodeIterator nodeIterator ) {
Node n = nodeIterator.nextNode();
log.info(XmlUtils.w3CDomNodeToString(n));
}
public static void logXml(NodeIterator nodeIterator ) {
// Has different method, to prevent Xalan preferring the String log method
log(nodeIterator);
}
//<html><body> <p>hello </p> </body></html>
private static DocumentFragment placeholderFragment = null;
private static byte[] placeholderBytes = null;
private static final String placeholderResourceFallback = "org/docx4j/model/datastorage/placeholder.xml";
private static final String placeholderResource = "OpenDoPE/placeholder.xml";
private static DocumentFragment createPlaceholder(RPr rPr, String contentParent) throws Exception {
// One time
if (placeholderFragment==null) {
createPlaceholderFragment();
}
if (placeholderBytes==null) {
createPlaceholderBytes();
}
if (contentParent.equals("p")) {
// Will always be invoked with this, for xpathGenerateRuns
if (rPr==null) {
// Usual case, just reuse the fragment
return placeholderFragment;
} else {
// Specific formatting
R run = (R)XmlUtils.unmarshal(new ByteArrayInputStream(placeholderBytes));
run.setRPr(rPr);
Document tmpDoc = XmlUtils.marshaltoW3CDomDocument(run);
DocumentFragment docfrag = tmpDoc.createDocumentFragment();
XmlUtils.treeCopy(tmpDoc.getDocumentElement(), docfrag);
return docfrag;
}
} else {
R run = (R)XmlUtils.unmarshal(new ByteArrayInputStream(placeholderBytes));
run.setRPr(rPr);
Document tmpDoc = XmlUtils.marshaltoW3CDomDocument(run);
DocumentFragment docfrag = tmpDoc.createDocumentFragment();
if (contentParent.equals("tbl")) {
org.w3c.dom.Element wtr = tmpDoc.createElementNS(Namespaces.NS_WORD12, "tr");
docfrag.appendChild(wtr);
org.w3c.dom.Element wtc = tmpDoc.createElementNS(Namespaces.NS_WORD12, "tc");
wtr.appendChild(wtc);
org.w3c.dom.Element wp = tmpDoc.createElementNS(Namespaces.NS_WORD12, "p");
wtc.appendChild(wp);
wp.appendChild(tmpDoc.getDocumentElement());
return docfrag;
} else if (contentParent.equals("tr")) {
org.w3c.dom.Element wtc = tmpDoc.createElementNS(Namespaces.NS_WORD12, "tc");
docfrag.appendChild(wtc);
org.w3c.dom.Element wp = tmpDoc.createElementNS(Namespaces.NS_WORD12, "p");
wtc.appendChild(wp);
wp.appendChild(tmpDoc.getDocumentElement());
return docfrag;
} else if (contentParent.equals("tc")
|| contentParent.equals("body")) {
org.w3c.dom.Element wp = tmpDoc.createElementNS(Namespaces.NS_WORD12, "p");
docfrag.appendChild(wp);
wp.appendChild(tmpDoc.getDocumentElement());
return docfrag;
} else {
// can't happen
return null;
}
}
}
private static void createPlaceholderFragment() throws Exception {
// create it - one time operation
InputStream is;
try {
is = ResourceUtils.getResource(placeholderResource);
} catch (IOException e) {
log.info("No resource on classpath at OpenDoPE/placeholder.xml; falling back to using org/docx4j/model/datastorage/placeholder.xml");
is = ResourceUtils.getResource(placeholderResourceFallback);
}
DocumentBuilderFactory newInstance = DocumentBuilderFactory.newInstance();
newInstance.setNamespaceAware(true);
Document tmpDoc = newInstance.newDocumentBuilder().parse(is);
placeholderFragment = tmpDoc.createDocumentFragment();
XmlUtils.treeCopy(tmpDoc.getDocumentElement(), placeholderFragment);
}
private static void createPlaceholderBytes() throws Exception {
// Only want to do this once
InputStream is;
try {
is = ResourceUtils.getResource(placeholderResource);
} catch (IOException e) {
log.info("No resource on classpath at OpenDoPE/placeholder.xml; falling back to using org/docx4j/model/datastorage/placeholder.xml");
is = ResourceUtils.getResource(placeholderResourceFallback);
}
placeholderBytes = IOUtils.toByteArray(is);
}
/**
* Convert the input XHTML into a WordML w3c DocumentFragment, which Xalan
* can insert into XSLT output.
*
* Note that the input XHTML must be suitable for the context
* ie you can't insert block level stuff (eg p) into a run level sdt.
*
* This method requires docx4j-XHTMLImport.jar (LGPL) and its dependencies
* in order to function.
*/
public static DocumentFragment convertXHTML(
WordprocessingMLPackage pkg,
JaxbXmlPart sourcePart,
Map customXmlDataStorageParts,
//String storeItemId, String xpath, String prefixMappings,
XPathsPart xPathsPart,
String sdtParent,
String contentChild,
NodeIterator rPrNodeIt,
String tag) {
log.debug("convertXHTML extension function for: " + sdtParent + "/w:sdt/w:sdtContent/" + contentChild);
org.w3c.dom.Document docContainer = XmlUtils.neww3cDomDocument();
DocumentFragment docfrag = docContainer.createDocumentFragment();
XHTMLImporter xHTMLImporter= null;
try {
Class> xhtmlImporterClass = Class.forName("org.docx4j.convert.in.xhtml.XHTMLImporterImpl");
Constructor> ctor = xhtmlImporterClass.getConstructor(WordprocessingMLPackage.class);
xHTMLImporter = (XHTMLImporter) ctor.newInstance(pkg);
} catch (Exception e) {
log.error("docx4j-XHTMLImport jar not found. Please add this to your classpath.");
log.error(e.getMessage(), e);
return xhtmlError(sdtParent, docContainer, docfrag, "Missing XHTML Handler!");
}
QueryString qs = new QueryString();
HashMap map = qs.parseQueryString(tag, true);
String xpathId = map.get(OpenDoPEHandler.BINDING_ROLE_XPATH);
log.info("Looking for xpath by id: " + xpathId);
Xpath xpath = xPathsPart.getXPathById(xPathsPart.getJaxbElement(), xpathId);
if (xpath==null) {
log.warn("Couldn't find xpath with id: " + xpathId);
return null;
}
String storeItemId = xpath.getDataBinding().getStoreItemID();
String xpathExp = xpath.getDataBinding().getXpath();
String prefixMappings = xpath.getDataBinding().getPrefixMappings();
String r = BindingHandler.xpathGetString(pkg, customXmlDataStorageParts, storeItemId, xpathExp, prefixMappings);
try {
RPr rPrSDT = null;
Node rPrNode = rPrNodeIt.nextNode();
if (rPrNode!=null) {
rPrSDT = (RPr)XmlUtils.unmarshal(rPrNode);
}
if (r==null || r.trim().equals("")) {
return createPlaceholder(rPrSDT, sdtParent);
}
r = r.trim();
// log.debug(r);
//String unescaped = StringEscapeUtils.unescapeHtml(r);
//log.info("Unescaped: " + unescaped);
// It comes to us unescaped, so the above is unnecessary.
if (r.startsWith(" cTree = styleTree.getCharacterStylesTree();
org.docx4j.model.styles.Node asn = cTree.get(rStyleVal);
if (asn==null) {
log.warn("No style node for: " + rStyleVal);
} else {
classVal = StyleTree.getHtmlClassAttributeValue(cTree, asn);
}
String css = null;
if ( rPrSDT!=null) {
StringBuilder result = new StringBuilder();
HtmlCssHelper.createCss(pkg, rPrSDT, result);
css = result.toString();
if (css.equals("")) {
css =null;
}
}
if (css==null && classVal==null) {
// Do nothing
} else if (classVal==null) {
// just @style
r = "" + r + " ";
} else if (css==null) {
// just @class
r = "" + r + " ";
} else {
r = "" + r + " ";
}
log.debug("\nenhanced with css: \n" + r);
} else if (Docx4jProperties.getProperty("docx4j.model.datastorage.BindingTraverser.XHTML.Block.rStyle.Adopt", false)) {
log.debug("Block.rStyle.Adopt..");
// its block level, and we're instructed to apply the paragraph style
// linked to w:sdtPr/w:rPr/w:rStyle (if any)
String rStyleVal=null;
if ( rPrSDT!=null && rPrSDT.getRStyle()!=null) {
rStyleVal = rPrSDT.getRStyle().getVal();
log.debug(".." + rStyleVal);
}
if (rStyleVal==null) {
log.debug("No rStyle specified ");
} else {
Style pStyle = pkg.getMainDocumentPart().getStyleDefinitionsPart(false).getLinkedStyle(rStyleVal);
if (pStyle==null) {
log.warn("No linked style for " + rStyleVal);
} else {
// Got the pStyle .. now apply it in the XHTML
StyleTree styleTree = pkg.getMainDocumentPart().getStyleTree();
String pStyleVal = pStyle.getStyleId();
log.debug(".." + pStyleVal);
// Set @class
String classVal =null;
Tree pTree = styleTree.getParagraphStylesTree();
org.docx4j.model.styles.Node asn = pTree.get(pStyleVal);
if (asn==null) {
log.warn("No style node for: " + pStyleVal);
} else {
classVal = StyleTree.getHtmlClassAttributeValue(pTree, asn);
}
String css = null;
if ( rPrSDT!=null) {
StringBuilder result = new StringBuilder();
HtmlCssHelper.createCss(pkg, rPrSDT, result);
css = result.toString();
if (css.equals("")) {
css =null;
}
}
// Recurse the XHTML, adding @class and @style
r = XHTMLAttrInjector.injectAttrs(r, classVal, css);
log.debug(".." + r);
}
}
}
xHTMLImporter.setHyperlinkStyle(BindingHandler.getHyperlinkResolver().getHyperlinkStyleId());
// Method setHyperlinkStyleMethod = xhtmlImporterClass.getMethod("setHyperlinkStyle", String.class);
// setHyperlinkStyleMethod.invoke(null,
// BindingHandler.getHyperlinkResolver().getHyperlinkStyleId());
String baseUrl = null;
List results = null;
try {
results = xHTMLImporter.convert(r, baseUrl );
// Method convertMethod = xhtmlImporterClass.getMethod("convert", String.class, String.class, WordprocessingMLPackage.class );
// results = (List)convertMethod.invoke(null, r, baseUrl, pkg);
} catch (Exception e) {
if (e instanceof NullPointerException) {
((NullPointerException)e).printStackTrace();
}
log.error("with XHTML: " + r, e);
//throw new Docx4JException("Problem converting XHTML", e);
String errMsg = e.getMessage() + " with XHTML from " + xpathExp + " : " + r;
return xhtmlError(sdtParent, docContainer, docfrag, errMsg);
}
if (results==null) {
log.error("Couldn't convert " + r);
return docfrag;
}
log.info("Got results: " + results.size() );
if (results.size()>0
&& results.get(0) instanceof P
&& sdtParent.equals("p")) {
// Importer class always returns run-level content wrapped in a w:p
// so extract contents
if (results.size()>1) {
log.warn("In paragraph context, so extra block-level content is being discarded!");
}
for (Object o : ((P)results.get(0)).getContent() ) {
// if (o instanceof R) {
//
// // Start with rPrSDT,
// // and then superimpose on top anything which comes
// // from the CSS.
//
// if (rPrSDT==null) {
// // Leave the CSS rPr as it is
// } else {
// RPr cssRPR = ((R)o).getRPr();
// if (cssRPR==null) {
// ((R)o).setRPr(rPrSDT);
// } else {
// log.debug("CSS rPR: " + XmlUtils.marshaltoString(cssRPR, true, true));
// RPr baseRPR = XmlUtils.deepCopy(rPrSDT);
//
// // We want to apply
// // real CSS settings, but not the defaults eg those in
// // src/main/resources/XhtmlNamespaceHandler.css
// // CSS defaults are:
// //
// //
// // We want to ignore those.
// if (rPrSDT.getColor()!=null
// && cssRPR.getColor()!=null
// && cssRPR.getColor().getVal().equals("000000")) {
// cssRPR.setColor(null);
// }
// if (rPrSDT.getSz()!=null
// && cssRPR.getSz()!=null
// && cssRPR.getSz().getVal().toString().equals("22")) {
// cssRPR.setSz(null);
// }
//
// StyleUtil.apply(cssRPR, baseRPR);
// ((R)o).setRPr(baseRPR);
// }
// }
// }
Document tmpDoc = XmlUtils.marshaltoW3CDomDocument(o);
XmlUtils.treeCopy(tmpDoc.getDocumentElement(), docfrag);
}
} else {
for(Object o : results) {
String debug = XmlUtils.marshaltoString(o, true);
log.debug("Conversion result: " + debug);
Document tmpDoc = XmlUtils.marshaltoW3CDomDocument(o);
XmlUtils.treeCopy(tmpDoc.getDocumentElement(), docfrag);
}
}
return docfrag;
} catch (Exception e) {
log.error(e.getMessage(), e);
return null;
}
}
/**
* @param sdtParent
* @param docContainer
* @param docfrag
* @param errMsg
* @return
*/
private static DocumentFragment xhtmlError(String sdtParent,
org.w3c.dom.Document docContainer, DocumentFragment docfrag,
String errMsg) {
org.w3c.dom.Element wr = docContainer.createElementNS(Namespaces.NS_WORD12, "r");
org.w3c.dom.Element wt = docContainer.createElementNS(Namespaces.NS_WORD12, "t");
wt.setTextContent(errMsg);
wr.appendChild(wt);
if (sdtParent.equals("p")) {
docfrag.appendChild(wr);
return docfrag;
} else if (sdtParent.equals("tbl")) {
org.w3c.dom.Element wtr = docContainer.createElementNS(Namespaces.NS_WORD12, "tr");
docfrag.appendChild(wtr);
org.w3c.dom.Element wtc = docContainer.createElementNS(Namespaces.NS_WORD12, "tc");
wtr.appendChild(wtc);
org.w3c.dom.Element wp = docContainer.createElementNS(Namespaces.NS_WORD12, "p");
wtc.appendChild(wp);
wp.appendChild(wr);
return docfrag;
} else if (sdtParent.equals("tr")) {
org.w3c.dom.Element wtc = docContainer.createElementNS(Namespaces.NS_WORD12, "tc");
docfrag.appendChild(wtc);
org.w3c.dom.Element wp = docContainer.createElementNS(Namespaces.NS_WORD12, "p");
wtc.appendChild(wp);
wp.appendChild(wr);
return docfrag;
} else if (sdtParent.equals("tc")) {
org.w3c.dom.Element wp = docContainer.createElementNS(Namespaces.NS_WORD12, "p");
docfrag.appendChild(wp);
wp.appendChild(wr);
return docfrag;
} else {
// eg body
org.w3c.dom.Element wp = docContainer.createElementNS(Namespaces.NS_WORD12, "p");
docfrag.appendChild(wp);
wp.appendChild(wr);
return docfrag;
}
}
/**
* bind.xslt calls this, for case where 'od:xpath' is present
*/
public static DocumentFragment xpathGenerateRuns(
WordprocessingMLPackage pkg,
JaxbXmlPart sourcePart,
Map customXmlDataStorageParts,
XPathsPart xPathsPart,
NodeIterator sdtPrNodeIt,
String sdtParent,
String contentChild,
boolean multiLine) {
SdtPr sdtPr = null;
Node sdtPrNode = sdtPrNodeIt.nextNode();
try {
sdtPr = (SdtPr)XmlUtils.unmarshal(sdtPrNode);
} catch (JAXBException e) {
log.error(e.getMessage(), e);
}
String odTag = sdtPr.getTag().getVal();
QueryString qs = new QueryString();
HashMap map = qs.parseQueryString(odTag, true);
String xpathId = map.get(OpenDoPEHandler.BINDING_ROLE_XPATH);
log.debug("Looking for xpath with id: " + xpathId + " referenced from part " + sourcePart.getPartName().getName() + " at " + odTag);
Xpath xpath = null;
try {
xpath = xPathsPart.getXPathById(xPathsPart.getJaxbElement(), xpathId);
} catch (InputIntegrityException iie) {
log.error("Couldn't find xpath with id: " + xpathId + " referenced from part " + sourcePart.getPartName().getName() + " at " + odTag);
throw iie;
// Could fallback to trying to use the databinding sdtPr, but would need to pass that in
}
String storeItemId = xpath.getDataBinding().getStoreItemID();
String xpathExp = xpath.getDataBinding().getXpath();
String prefixMappings = xpath.getDataBinding().getPrefixMappings();
return xpathGenerateRuns(
pkg,
sourcePart,
customXmlDataStorageParts,
storeItemId, xpathExp, prefixMappings,
sdtPr, sdtParent, contentChild,
multiLine);
}
/**
* bind.xslt calls this, for case where 'od:xpath' is not present
*/
public static DocumentFragment xpathGenerateRuns(
WordprocessingMLPackage pkg,
JaxbXmlPart sourcePart,
Map customXmlDataStorageParts,
String storeItemId, String xpath, String prefixMappings,
NodeIterator sdtPrNodeIt,
String sdtParent,
String contentChild,
boolean multiLine) {
SdtPr sdtPr = null;
Node sdtPrNode = sdtPrNodeIt.nextNode();
try {
sdtPr = (SdtPr)XmlUtils.unmarshal(sdtPrNode);
} catch (JAXBException e) {
log.error(e.getMessage(), e);
}
return xpathGenerateRuns(
pkg,
sourcePart,
customXmlDataStorageParts,
storeItemId, xpath, prefixMappings,
sdtPr,
sdtParent,
contentChild,
multiLine);
}
public static DocumentFragment xpathGenerateRuns(
WordprocessingMLPackage pkg,
JaxbXmlPart sourcePart,
Map customXmlDataStorageParts,
String storeItemId, String xpath, String prefixMappings,
SdtPr sdtPr,
String sdtParent,
String contentChild,
boolean multiLine) {
/**
* TODO test cases:
*
* - multiline data, including cases which start/end with empty token
* - multiline data with w:multiLine absent or set to 0 ie false
* - cases with and without rPr
* - inline and block level sdt
*/
String r = BindingHandler.xpathGetString(pkg, customXmlDataStorageParts, storeItemId, xpath, prefixMappings);
try {
log.info(xpath + " yielded result '" + r + "'");
RPr rPr = null;
for (Object o : sdtPr.getRPrOrAliasOrLock() ) {
o = XmlUtils.unwrap(o); // Sun/Oracle JAXB (recent versions?) wraps RPR in JAXBElement
if (o instanceof RPr) {
rPr = (RPr)o;
break;
}
}
if (r==null || r.equals("")) {
return createPlaceholder(rPr, "p");
}
org.w3c.dom.Document docContainer = XmlUtils.neww3cDomDocument();
DocumentFragment docfrag = docContainer.createDocumentFragment();
StringTokenizer st = new StringTokenizer(r, "\n\r\f"); // tokenize on the newline character, the carriage-return character, and the form-feed character
if (multiLine) {
// our docfrag may contain several runs
boolean firsttoken = true;
while (st.hasMoreTokens()) {
String line = (String) st.nextToken();
if (firsttoken) {
firsttoken = false;
} else {
addBrRunToDocFrag(docfrag, rPr);
}
processString(sourcePart, docfrag, line, rPr);
}
} else {
// not multiline, so remove any CRLF in data;
// our docfrag wil contain a single run
StringBuilder sb = new StringBuilder();
while (st.hasMoreTokens()) {
sb.append( st.nextToken() );
}
processString(sourcePart, docfrag, sb.toString(), rPr);
}
return docfrag;
} catch (Exception e) {
log.error(e.getMessage(), e);
return null;
}
}
private static void addBrRunToDocFrag(DocumentFragment docfrag, RPr rPr) throws JAXBException {
// Not sure whether there is ever anything of interest in the rPr,
// but add it anyway
org.docx4j.wml.R run = Context.getWmlObjectFactory().createR();
if (rPr!=null) {
run.setRPr(rPr);
}
run.getRunContent().add(Context.getWmlObjectFactory().createBr());
Document tmpDoc = XmlUtils.marshaltoW3CDomDocument(run);
XmlUtils.treeCopy(tmpDoc.getDocumentElement(), docfrag);
}
private static void processString(JaxbXmlPart sourcePart, DocumentFragment docfrag, String text, RPr rPr) throws JAXBException {
int pos = BindingHandler.getHyperlinkResolver().getIndexOfURL(text);
if (pos==-1 || BindingHandler.getHyperlinkStyleId() == null) {
addRunToDocFrag(sourcePart, docfrag, text, rPr);
return;
}
// There is a hyperlink to deal with
// We'll need to remove:
//
//
// or Word can't open the resulting docx, but we can't do it here,
// since sdtPr is in effect read only. So it is done in bind.xslt
if (pos==0) {
int spacePos = text.indexOf(" ");
if (spacePos==-1) {
addHyperlinkToDocFrag(sourcePart, docfrag, text);
return;
}
// Could contain more than one hyperlink, so process recursively
String first = text.substring(0, spacePos);
String rest = text.substring(spacePos);
addHyperlinkToDocFrag( sourcePart, docfrag, first);
// .. now the recursive bit ..
processString(sourcePart, docfrag, rest, rPr);
return;
}
String first = text.substring(0, pos);
String rest = text.substring(pos);
addRunToDocFrag( sourcePart, docfrag, first, rPr);
// .. now the recursive bit ..
processString(sourcePart, docfrag, rest, rPr);
}
private static void addRunToDocFrag(JaxbXmlPart sourcePart, DocumentFragment docfrag, String string, RPr rPr) {
org.docx4j.wml.R run = Context.getWmlObjectFactory().createR();
if (rPr!=null) {
run.setRPr(rPr);
}
org.docx4j.wml.Text text = Context.getWmlObjectFactory().createText();
run.getRunContent().add(text);
if (string.startsWith(" ") || string.endsWith(" ") ) {
// TODO: tab character?
log.debug("setting xml:space=preserve for '" + string + "'");
text.setSpace("preserve");
}
text.setValue(string);
Document tmpDoc = XmlUtils.marshaltoW3CDomDocument(run);
// avoid WRONG_DOCUMENT_ERR: A node is used in a different document than the one that created it.
// but NOT_SUPPORTED_ERR: The implementation does not support the requested type of object or operation.
// at com.sun.org.apache.xerces.internal.dom.CoreDocumentImpl.importNode
// docfrag.appendChild(fragdoc.importNode(document, true));
// so:
XmlUtils.treeCopy(tmpDoc.getDocumentElement(), docfrag);
}
private static void addHyperlinkToDocFrag(JaxbXmlPart sourcePart, DocumentFragment docfrag, String url) throws JAXBException {
// We need to add a relationship to word/_rels/document.xml.rels
// but since its external, we don't use the
// usual wordMLPackage.getMainDocumentPart().addTargetPart
// mechanism
org.docx4j.relationships.ObjectFactory factory =
new org.docx4j.relationships.ObjectFactory();
org.docx4j.relationships.Relationship rel = factory.createRelationship();
rel.setType( Namespaces.HYPERLINK );
rel.setTarget(url);
rel.setTargetMode("External");
sourcePart.getRelationshipsPart().addRelationship(rel); // addRelationship sets the rel's @Id
Document tmpDoc = XmlUtils.marshaltoW3CDomDocument(
BindingHandler.getHyperlinkResolver().generateHyperlink(rel.getId(), url));
XmlUtils.treeCopy(tmpDoc.getDocumentElement(), docfrag);
}
public static DocumentFragment xpathInjectImage(WordprocessingMLPackage wmlPackage,
JaxbXmlPart sourcePart,
Map customXmlDataStorageParts,
String storeItemId, String xpath, String prefixMappings,
String sdtParent,
String contentChild,
String cx, String cy) {
log.info("Falling back to pre-v3 picture processing for " + xpath);
log.debug("parent: " + sdtParent);
log.debug("child: " + contentChild);
// TODO: remove any images in package which are no longer used.
// Needs to be done once after BindingHandler has been done
// for all parts for which it is to be called (eg mdp, header parts etc).
CustomXmlDataStoragePart part = customXmlDataStorageParts.get(storeItemId.toLowerCase());
if (part==null) {
log.error("Couldn't locate part by storeItemId " + storeItemId);
return null;
}
try {
String xpResult = part.getData().xpathGetString(xpath, prefixMappings);
log.debug(xpath + " yielded result length" + xpResult.length());
// Base64 decode it
byte[] bytes = Base64.decodeBase64( xpResult.getBytes("UTF8") );
// Create image part and add it
BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wmlPackage, sourcePart, bytes);
String filenameHint = null;
String altText = null;
int id1 = 0;
int id2 = 1;
Inline inline = null;
long cxl = 0;
long cyl = 0;
try {
cxl = Long.parseLong(cx);
cyl = Long.parseLong(cy);
} catch (Exception e) {}
if (cxl==0 || cyl==0) {
// Let BPAI work out size
log.debug("image size - from image");
inline = imagePart.createImageInline( filenameHint, altText,
id1, id2, false);
} else {
// Use existing size
log.debug("image size - from content control size");
// Respect aspect ratio of injected image
ImageSize size = imagePart.getImageInfo().getSize();
double ratio = (double) size.getHeightPx() / (double) size.getWidthPx();
log.debug("fit ratio: " + ratio);
if (ratio > 1) {
cxl = (long)((double) cyl / ratio);
} else {
cyl = (long)((double) cxl * ratio);
}
inline = imagePart.createImageInline( filenameHint, altText,
id1, id2, cxl, cyl, false);
}
// In certain circumstances, save it immediately
if (wmlPackage.getTargetPartStore()!=null
&& wmlPackage.getTargetPartStore() instanceof UnzippedPartStore) {
log.debug("incrementally saving " + imagePart.getPartName().getName());
((UnzippedPartStore)wmlPackage.getTargetPartStore()).saveBinaryPart(imagePart);
// remove it from memory
ByteBuffer bb = null;
imagePart.setBinaryData(bb);//new byte[0]);
imagePart.setImageInfo(null); // this might help as well
}
// Now add the inline in w:p/w:r/w:drawing
org.docx4j.wml.ObjectFactory factory = new org.docx4j.wml.ObjectFactory();
org.docx4j.wml.Tc tc = factory.createTc();
org.docx4j.wml.P p = factory.createP();
if (sdtParent.equals("tr")) {
tc.getContent().add(p);
}
org.docx4j.wml.R run = factory.createR();
org.docx4j.wml.Drawing drawing = factory.createDrawing();
run.getContent().add(drawing);
drawing.getAnchorOrInline().add(inline);
if (sdtParent.equals("body")
|| sdtParent.equals("tr")
|| sdtParent.equals("tc") ) {
p.getContent().add(run);
}
/* return following node
*
*
etc
*/
Document document = null;
if (sdtParent.equals("body")
|| sdtParent.equals("tc") ) {
document = XmlUtils.marshaltoW3CDomDocument(p);
log.debug(XmlUtils.marshaltoString(p, true, true));
} else if ( sdtParent.equals("tr") ) {
document = XmlUtils.marshaltoW3CDomDocument(tc);
log.debug(XmlUtils.marshaltoString(tc, true, true));
} else if ( sdtParent.equals("p") ) {
document = XmlUtils.marshaltoW3CDomDocument(run);
log.debug(XmlUtils.marshaltoString(run, true, true));
} else if ( sdtParent.equals("sdtContent") ) {
log.info("contentChild: " + contentChild);
if (contentChild.equals("p")) {
p.getContent().add(run);
document = XmlUtils.marshaltoW3CDomDocument(p);
log.debug(XmlUtils.marshaltoString(p, true, true));
} else if (contentChild.equals("r")) {
document = XmlUtils.marshaltoW3CDomDocument(run);
log.debug(XmlUtils.marshaltoString(run, true, true));
} else {
log.error("how to inject image for unexpected sdt's content: " + contentChild);
}
} else {
log.error("how to inject image for unexpected sdt's parent: " + sdtParent);
}
DocumentFragment docfrag = document.createDocumentFragment();
docfrag.appendChild(document.getDocumentElement());
return docfrag;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* Process a rich text control containing an image.
*
* @param wmlPackage
* @param sourcePart
* @param customXmlDataStorageParts
* @param xPathsPart
* @param tag
* @return
* @since 3.0.1
*/
public static String xpathInjectImageRelId(WordprocessingMLPackage wmlPackage,
JaxbXmlPart sourcePart,
Map customXmlDataStorageParts,
XPathsPart xPathsPart,
String tag) {
QueryString qs = new QueryString();
HashMap map = qs.parseQueryString(tag, true);
String xpathId = map.get(OpenDoPEHandler.BINDING_ROLE_XPATH);
log.info("Looking for xpath by id: " + xpathId);
Xpath xpath = xPathsPart.getXPathById(xPathsPart.getJaxbElement(), xpathId);
if (xpath==null) {
log.warn("Couldn't find xpath with id: " + xpathId);
return null;
}
String storeItemId = xpath.getDataBinding().getStoreItemID();
String xpathExp = xpath.getDataBinding().getXpath();
String prefixMappings = xpath.getDataBinding().getPrefixMappings();
return xpathInjectImageRelId( wmlPackage,
sourcePart,
customXmlDataStorageParts,
storeItemId, xpathExp, prefixMappings);
}
/**
* Pass back to XSLT, the value of w:blip/@r:embed, preserving everything
* else about the existing template image.
*
* @param wmlPackage
* @param sourcePart
* @param customXmlDataStorageParts
* @param storeItemId
* @param xpath
* @param prefixMappings
* @return
* @since 3.0.0
*/
public static String xpathInjectImageRelId(WordprocessingMLPackage wmlPackage,
JaxbXmlPart sourcePart,
Map customXmlDataStorageParts,
String storeItemId, String xpath, String prefixMappings) {
// TODO: remove any images in package which are no longer used.
// Needs to be done once after BindingHandler has been done
// for all parts for which it is to be called (eg mdp, header parts etc).
CustomXmlDataStoragePart part = customXmlDataStorageParts.get(storeItemId.toLowerCase());
if (part==null) {
log.error("Couldn't locate part by storeItemId " + storeItemId);
return null;
}
try {
String xpResult = part.getData().xpathGetString(xpath, prefixMappings);
log.debug(xpath + " yielded result length" + xpResult.length());
// Base64 decode it
byte[] bytes = Base64.decodeBase64( xpResult.getBytes("UTF8") );
// Create image part and add it
BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wmlPackage, sourcePart, bytes);
// In certain circumstances, save it immediately
if (wmlPackage.getTargetPartStore()!=null
&& wmlPackage.getTargetPartStore() instanceof UnzippedPartStore) {
log.debug("incrementally saving " + imagePart.getPartName().getName());
((UnzippedPartStore)wmlPackage.getTargetPartStore()).saveBinaryPart(imagePart);
// remove it from memory
ByteBuffer bb = null;
imagePart.setBinaryData(bb);//new byte[0]);
imagePart.setImageInfo(null); // this might help as well
}
return imagePart.getRelLast().getId();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static String getRepeatPositionCondition(
XPathsPart xPathsPart,
String odTag) {
QueryString qs = new QueryString();
HashMap map = qs.parseQueryString(odTag, true);
String xpathId = map.get(OpenDoPEHandler.BINDING_ROLE_RPT_POS_CON);
log.info("Looking for xpath by id: " + xpathId);
Xpath xpath = xPathsPart.getXPathById(xPathsPart.getJaxbElement(), xpathId);
String expression =xpath.getDataBinding().getXpath() ;
log.info(expression);
return expression;
}
public static DocumentFragment nullResultParagraph(String sdtParent, String message) {
try
{
org.docx4j.wml.ObjectFactory factory = new org.docx4j.wml.ObjectFactory();
org.docx4j.wml.R run = factory.createR();
org.docx4j.wml.Text text = factory.createText();
text.setValue(message);
run.getContent().add(text);
org.w3c.dom.Document docContainer = XmlUtils.neww3cDomDocument();
if (sdtParent.equals("p")) {
// Stuff it in a run
docContainer = XmlUtils.marshaltoW3CDomDocument(run);
} else {
// Stuff it in a p
org.docx4j.wml.P p = factory.createP();
p.getContent().add(run);
docContainer = XmlUtils.marshaltoW3CDomDocument(p);
}
DocumentFragment docfrag = docContainer.createDocumentFragment();
docfrag.appendChild(docContainer.getDocumentElement());
return docfrag;
} catch (Exception e) {
log.error(e.getMessage(), e);
return null;
}
}
public static DocumentFragment xpathDate(WordprocessingMLPackage wmlPackage,
JaxbXmlPart sourcePart,
Map customXmlDataStorageParts,
String storeItemId, String xpath, String prefixMappings,
String sdtParent,
String contentChild,
NodeIterator dateNodeIt) {
CustomXmlPart part = customXmlDataStorageParts.get(storeItemId.toLowerCase());
if (part==null) {
log.error("Couldn't locate part by storeItemId " + storeItemId);
return null;
}
try {
String r= part.xpathGetString(xpath, prefixMappings);
log.debug(xpath + " yielded result " + r);
if (r==null) return nullResultParagraph(sdtParent, "[missing!]");
CTSdtDate sdtDate = null;
Node dateNode = dateNodeIt.nextNode();
if (dateNode!=null) {
//sdtDate = (CTSdtDate)XmlUtils.unmarshal(dateNode);
sdtDate = (CTSdtDate)XmlUtils.unmarshal(dateNode, Context.jc, CTSdtDate.class);
}
/*
Assume our String r contains something like "2012-08-19T00:00:00Z"
We need to convert it to the given dateFormat string.
*/
// Drop the Z
if (r.indexOf("Z")>0) {
r = r.substring(0, r.indexOf("Z")-1);
log.warn("date now " + r);
}
DateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
String format = sdtDate.getDateFormat().getVal();
log.debug("Using format: " + format);
// C# dddd (eg "Monday') needs translation
// to "EEEE"
if (format.contains("dddd")) {
format = format.replace("dddd", "EEEE");
}
Format formatter = new SimpleDateFormat(format);
org.docx4j.wml.ObjectFactory factory = new org.docx4j.wml.ObjectFactory();
Date date;
RPr rPr = null;
try {
date = (Date)dateTimeFormat.parse(r);
} catch (ParseException e) {
try {
// 2012-08-28
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
date = (Date) dateFormat.parse(r);
} catch (ParseException e2) {
log.warn(e.getMessage());
date = new Date();
//
rPr = factory.createRPr();
Color colorRed = factory.createColor();
colorRed.setVal("FF0000");
rPr.setColor(colorRed);
}
}
String result = formatter.format(date);
org.docx4j.wml.R run = factory.createR();
if (rPr!=null) {
run.setRPr(rPr);
}
org.docx4j.wml.Text text = factory.createText();
text.setValue(result);
run.getContent().add(text);
org.w3c.dom.Document docContainer = XmlUtils.neww3cDomDocument();
if (sdtParent.equals("p")) {
// Stuff it in a run
docContainer = XmlUtils.marshaltoW3CDomDocument(run);
} else {
// Stuff it in a p
org.docx4j.wml.P p = factory.createP();
p.getContent().add(run);
docContainer = XmlUtils.marshaltoW3CDomDocument(p);
}
DocumentFragment docfrag = docContainer.createDocumentFragment();
docfrag.appendChild(docContainer.getDocumentElement());
return docfrag;
} catch (Exception e) {
log.error(e.getMessage(), e);
return null;
}
}
/**
* Convert the FlatOPC into an AltChunk, which Xalan
* can insert into XSLT output.
*
* @since 3.0.1
*/
public static DocumentFragment convertFlatOPC(
WordprocessingMLPackage pkg,
JaxbXmlPart sourcePart,
Map customXmlDataStorageParts,
//String storeItemId, String xpath, String prefixMappings,
XPathsPart xPathsPart,
String sdtParent,
String contentChild,
NodeIterator rPrNodeIt,
String tag) {
try {
log.debug("convertFlatOPC extension function for: " + sdtParent + "/w:sdt/w:sdtContent/" + contentChild);
QueryString qs = new QueryString();
HashMap map = qs.parseQueryString(tag, true);
String xpathId = map.get(OpenDoPEHandler.BINDING_ROLE_XPATH);
log.info("Looking for xpath by id: " + xpathId);
Xpath xpath = xPathsPart.getXPathById(xPathsPart.getJaxbElement(), xpathId);
if (xpath==null) {
log.warn("Couldn't find xpath with id: " + xpathId);
return null;
}
String storeItemId = xpath.getDataBinding().getStoreItemID();
String xpathExp = xpath.getDataBinding().getXpath();
String prefixMappings = xpath.getDataBinding().getPrefixMappings();
String r = BindingHandler.xpathGetString(pkg, customXmlDataStorageParts, storeItemId, xpathExp, prefixMappings);
if (r==null) return nullResultParagraph(sdtParent, "[missing!]");
if (!r.startsWith(" // Word can't open it without this!
// optional
*/
r = "\n" + r;
}
//System.out.println(r);
// .. create the part
AlternativeFormatInputPart afiPart = new AlternativeFormatInputPart(
getNewPartName("/chunk", ".xml", sourcePart.getRelationshipsPart()));
afiPart.setBinaryData(r.getBytes("UTF-8"));
afiPart.setAltChunkType(AltChunkType.Xml); // Flat OPC XML
Relationship altChunkRel =sourcePart.addTargetPart(afiPart);
// now that its attached to the package ..
afiPart.registerInContentTypeManager();
CTAltChunk ac = Context.getWmlObjectFactory()
.createCTAltChunk();
ac.setId(altChunkRel.getId());
// This setting makes no difference in that the altChunk
// still won't use the style from the containing docx
// if it isn't in the styles part in the altChunk!
// // http://webapp.docx4java.org/OnlineDemo/ecma376/WordML/matchSrc.html
// CTAltChunkPr acPr = Context.getWmlObjectFactory()
// .createCTAltChunkPr();
// BooleanDefaultTrue bft = new BooleanDefaultTrue();
// bft.setVal(false);
// acPr.setMatchSrc(bft);
// ac.setAltChunkPr(acPr);
org.w3c.dom.Document docContainer = XmlUtils.marshaltoW3CDomDocument(ac);
DocumentFragment docfrag = docContainer.createDocumentFragment();
docfrag.appendChild(docContainer.getDocumentElement());
return docfrag;
} catch (Exception e) {
log.error(e.getMessage(), e);
return null;
}
}
// TODO - add something like this to RelationshipsPart??
private static PartName getNewPartName(String prefix, String suffix,
RelationshipsPart rp) throws InvalidFormatException {
PartName proposed = null;
int i = 1;
do {
if (i > 1) {
proposed = new PartName(prefix + i + suffix);
} else {
proposed = new PartName(prefix + suffix);
}
i++;
} while (rp.getRel(proposed) != null);
return proposed;
}
}