package org.businessmanager.service.docgen;
import java.io.File;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import org.docx4j.TraversalUtil;
import org.docx4j.XmlUtils;
import org.docx4j.convert.out.pdf.viaXSLFO.PdfSettings;
import org.docx4j.fonts.IdentityPlusMapper;
import org.docx4j.model.structure.HeaderFooterPolicy;
import org.docx4j.model.structure.SectionWrapper;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.wml.Body;
import org.docx4j.wml.CTBookmark;
import org.docx4j.wml.P;
import org.docx4j.wml.R;
import org.docx4j.wml.Tbl;
import org.docx4j.wml.Text;
import org.docx4j.wml.Tr;
public class DocumentGenerator {
private static String INVOICE_POS_MARKER = "rgpos";
private Map<String, Object> replacements;
private File templateFile;
public DocumentGenerator(Map<String, Object> replacements, File templateFile) {
this.replacements = replacements;
this.templateFile = templateFile;
}
// this function is not adapted completely yet - not ready for use!
public void createDocument() {
try {
WordprocessingMLPackage template = WordprocessingMLPackage
.load(templateFile);
handleMainDocumentPart(template);
handleHeaderFooter(template);
// save result as docx
template.save(new File("output.docx"));
// save result as pdf
template.setFontMapper(new IdentityPlusMapper());
org.docx4j.convert.out.pdf.PdfConversion c = new org.docx4j.convert.out.pdf.viaXSLFO.Conversion(
template);
OutputStream os = new java.io.FileOutputStream("output.pdf");
c.output(os, new PdfSettings());
System.out.println("Done");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("Errors");
}
}
private void handleMainDocumentPart(WordprocessingMLPackage template)
throws JAXBException {
MainDocumentPart mainDocumentPart = template.getMainDocumentPart();
org.docx4j.wml.Document wmlDocumentEl = (org.docx4j.wml.Document) mainDocumentPart
.getJaxbElement();
Body body = wmlDocumentEl.getBody();
new TraversalUtil(body, new WmlPackageFixer());
handleInvoicePosTable(mainDocumentPart);
substituteFields(mainDocumentPart, replacements);
}
private void handleHeaderFooter(WordprocessingMLPackage template) {
List<SectionWrapper> sectionWrappers = template.getDocumentModel()
.getSections();
for (SectionWrapper sw : sectionWrappers) {
HeaderFooterPolicy hfp = sw.getHeaderFooterPolicy();
substituteFields(hfp.getFirstHeader(), replacements);
substituteFields(hfp.getDefaultHeader(), replacements);
substituteFields(hfp.getEvenHeader(), replacements);
substituteFields(hfp.getFirstFooter(), replacements);
substituteFields(hfp.getDefaultFooter(), replacements);
substituteFields(hfp.getEvenFooter(), replacements);
}
}
@SuppressWarnings("unchecked")
private void handleInvoicePosTable(MainDocumentPart mainDocumentPart)
throws JAXBException {
final String XPATH_TO_BOOKMARK_NODES = "//w:bookmarkStart";
Object ivposRpl = this.replacements.get("ivpos");
if (!(ivposRpl instanceof List)) {
return;
}
List<Object> ivpos = (List<Object>) ivposRpl;
List<?> bookmarks = mainDocumentPart.getJAXBNodesViaXPath(
XPATH_TO_BOOKMARK_NODES, true);
for (Object obj : bookmarks) {
CTBookmark bookmark = getBookmark((JAXBElement<?>) obj);
if (bookmark != null
&& INVOICE_POS_MARKER.equals(bookmark.getName())) {
Tr templateRow = Docx4jUtil.getParentOfType(bookmark, Tr.class);
Tbl table = Docx4jUtil.getParentOfType(templateRow, Tbl.class);
int pos = table.getContent().indexOf(templateRow);
for (Object posObj : ivpos) {
if (!(posObj instanceof Map)) {
continue;
}
Map<String, Object> posValues = (Map<String, Object>) posObj;
Tr trMoved = XmlUtils.deepCopy(templateRow);
table.getContent().add(pos, trMoved);
substituteFields(trMoved, posValues);
pos++;
}
table.getContent().remove(templateRow);
}
}
}
private CTBookmark getBookmark(JAXBElement<?> jaxbObj) {
if (!(jaxbObj.getValue() instanceof CTBookmark)) {
return null;
}
CTBookmark bookmark = (org.docx4j.wml.CTBookmark) jaxbObj.getValue();
return bookmark;
}
private void substituteFields(Object object,
Map<String, Object> replacements) {
if (object == null) {
return;
}
List<Object> children = Docx4jUtil.getChildren(object);
if (children != null) {
List<Object> childrenCopy = new ArrayList<Object>(children);
for (int i = 0; i < childrenCopy.size(); i++) {
Object child = XmlUtils.unwrap(childrenCopy.get(i));
if (child instanceof Text) {
new VariableSubstitution((Text) child, object, replacements)
.execute();
} else if (child instanceof P) {
handleParagraphVariables((P) child, replacements);
} else {
substituteFields(child, replacements);
}
}
}
}
private void handleParagraphVariables(P paragraph,
Map<String, Object> replacements) {
List<Object> content = paragraph.getContent();
List<Object> newContent = new ArrayList<Object>();
List<R> candidates = new ArrayList<R>();
String paragraphText = "";
boolean candidateActive = false;
for (Object item : content) {
String tmpTxt = paragraphText;
if (!(item instanceof R)) {
continue;
}
R run = (R) item;
substituteFields(run, replacements);
String runText = Docx4jUtil.extractText(run);
tmpTxt += runText;
boolean alreadyAdded = false;
if (containsVariables(tmpTxt)) {
candidates.add(run);
List<R> newRun = combineRuns(candidates, tmpTxt);
substituteFields(newRun.get(0), replacements);
newContent.add(newRun.get(0));
candidates.clear();
candidateActive = false;
if (newRun.size() > 1) {
runText = Docx4jUtil.extractText(newRun.get(1));
run = newRun.get(1);
paragraphText = runText;
} else {
paragraphText = "";
continue;
}
} else {
paragraphText = tmpTxt;
}
if (runText != null
&& (runText.contains("$") || runText.contains("{"))) {
candidates.add(run);
candidateActive = true;
} else if (candidateActive) {
candidates.add(run);
} else if (!alreadyAdded) {
newContent.add(run);
paragraphText = "";
}
}
newContent.addAll(candidates);
paragraph.getContent().clear();
paragraph.getContent().addAll(newContent);
}
private List<R> combineRuns(List<R> candidates, String text) {
if (candidates == null || candidates.size() < 1) {
return null;
}
if (candidates.size() < 2) {
return candidates;
}
int indexOf = text.indexOf("}");
String textIncludingVariable = text.substring(0, indexOf + 1);
List<R> result = new ArrayList<R>();
R r = candidates.get(0);
Docx4jUtil.substituteText(r, textIncludingVariable);
result.add(r);
String textAfterVariable = text.substring(indexOf + 1);
if (!"".equals(textAfterVariable)) {
R r2 = candidates.get(candidates.size() - 1);
Docx4jUtil.substituteText(r2, textAfterVariable);
result.add(r2);
}
return result;
}
private boolean containsVariables(String text) {
Pattern pattern = Pattern.compile("\\$\\{(.+?)\\}");
Matcher matcher = pattern.matcher(text);
return matcher.find();
}
}