/**
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at the
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Initial code contributed and copyrighted by<br>
* frentix GmbH, http://www.frentix.com
* <p>
*/
package org.olat.core.util.openxml;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.commons.io.IOUtils;
import org.cyberneko.html.parsers.SAXParser;
import org.olat.core.commons.services.image.ImageUtils;
import org.olat.core.commons.services.image.Size;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.util.StringHelper;
import org.olat.core.util.io.ShieldInputStream;
import org.olat.core.util.vfs.LocalFileImpl;
import org.olat.core.util.vfs.VFSContainer;
import org.olat.core.util.vfs.VFSItem;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import fmath.conversion.ConvertFromLatexToMathML;
import fmath.conversion.ConvertFromMathMLToWord;
/**
* The page are A4 format, with 2.54cm margins on top, bottom, left and right.
*
*
* Initial date: 04.09.2013<br>
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*
*/
public class OpenXMLDocument {
private static final OLog log = Tracing.createLoggerFor(OpenXMLDocument.class);
private final int DPI = 72;
private final Document document;
private final Element rootElement;
private final Element bodyElement;
private final OpenXMLStyles styles;
private int currentId = 4;
private int currentNumberingId = 0;
private String documentHeader;
private Set<String> imageFilenames = new HashSet<>();
private Map<File, DocReference> fileToImagesMap = new HashMap<File, DocReference>();
private List<Node> cursorStack = new ArrayList<>();
private List<ListParagraph> numbering = new ArrayList<>();
private List<HeaderReference> headers = new ArrayList<>();
private VFSContainer mediaContainer;
public OpenXMLDocument() {
document = OpenXMLUtils.createDocument();
rootElement = createRootElement(document);
bodyElement = createBodyElement(rootElement, document);
styles = new OpenXMLStyles();
cursorStack.add(bodyElement);
}
public String getDocumentHeader() {
return documentHeader;
}
public void setDocumentHeader(String header) {
documentHeader = header;
if(StringHelper.containsNonWhitespace(documentHeader)) {
documentHeader = documentHeader.replace("&", "&");
}
}
public VFSContainer getMediaContainer() {
return mediaContainer;
}
public void setMediaContainer(VFSContainer mediaContainer) {
this.mediaContainer = mediaContainer;
}
public Document getDocument() {
return document;
}
public OpenXMLStyles getStyles() {
return styles;
}
public Collection<DocReference> getImages() {
return fileToImagesMap.values();
}
public Collection<HeaderReference> getHeaders() {
return headers;
}
public Collection<ListParagraph> getNumbering() {
return numbering;
}
public Node getCursor() {
return cursorStack.get(cursorStack.size() - 1);
}
public void pushCursor(Node el) {
cursorStack.add(el);
}
public void popCursor(Node el) {
int index = cursorStack.indexOf(el);
if(index > 1) {
for(int i=cursorStack.size(); i-->index; ) {
cursorStack.remove(i);
}
}
}
public void resetCursor() {
for(int i=cursorStack.size(); i-->1; ) {
cursorStack.remove(i);
}
}
public void appendTitle(String text) {
appendHeading(text, PredefinedStyle.title, null);
}
public void appendHeading1(String text, String additionalText) {
appendHeading(text, PredefinedStyle.heading1, additionalText);
}
public Element createHeading1(String text, String additionalText) {
return createHeading(text, PredefinedStyle.heading1, additionalText);
}
public void appendHeading2(String text, String additionalText) {
appendHeading(text, PredefinedStyle.heading2, additionalText);
}
public Element createHeading2(String text, String additionalText) {
return createHeading(text, PredefinedStyle.heading2, additionalText);
}
private void appendHeading(String text, PredefinedStyle style, String additionalText) {
if(!StringHelper.containsNonWhitespace(text)) return;
Element paragraphEl = createHeading(text, style, additionalText);
getCursor().appendChild(paragraphEl);
}
private Element createHeading(String text, PredefinedStyle style, String additionalText) {
Element textEl = createTextEl(text);
List<Element> runsEl = new ArrayList<Element>(2);
Element runEl = createRunEl(Collections.singletonList(textEl));
runsEl.add(runEl);
Element styleEl = createParagraphStyle(style);
if(StringHelper.containsNonWhitespace(additionalText)) {
//add an "insecable" blank between the title and the additional text
Element blankRunEl = document.createElement("w:r");
blankRunEl.appendChild(createPreserveSpaceEl());
runsEl.add(blankRunEl);
//add additional text
Element addRunEl = document.createElement("w:r");
Node addRunPrefsEl = addRunEl.appendChild(document.createElement("w:rPr"));
Element bEl = (Element)addRunPrefsEl.appendChild(document.createElement("w:b"));
bEl.setAttribute("w:val", "0");
Element colorEl = (Element)addRunPrefsEl.appendChild(document.createElement("w:color"));
colorEl.setAttribute("w:val", "auto");
Element szEl = (Element)addRunPrefsEl.appendChild(document.createElement("w:sz"));
szEl.setAttribute("w:val", "24");
Element szCsEl = (Element)addRunPrefsEl.appendChild(document.createElement("w:szCs"));
szCsEl.setAttribute("w:val", "24");
addRunEl.appendChild(createTextEl(additionalText));
runsEl.add(addRunEl);
}
Element paragraphEl = createParagraphEl(styleEl, runsEl);
return paragraphEl;
}
public void appendSubtitle(String text) {
Element textEl = createTextEl(text);
List<Element> runsEl = new ArrayList<Element>(2);
Element runEl = createRunEl(Collections.singletonList(textEl), PredefinedStyle.subSubtleEmphasis);
runsEl.add(runEl);
Element styleEl = createParagraphStyle(PredefinedStyle.subSubtleEmphasis);
Element paragraphEl = createParagraphEl(styleEl, runsEl);
getCursor().appendChild(paragraphEl);
}
/*
<w:sectPr w:rsidR="00F528BA" w:rsidRPr="00DF16C8" w:rsidSect="007347AA">
<w:pgSz w:w="11900" w:h="16840" />
<w:pgMar w:top="1417" w:right="1417" w:bottom="1134" w:left="1417" w:header="708" w:footer="708" w:gutter="0" />
<w:cols w:space="708" />
<w:docGrid w:linePitch="360" />
</w:sectPr>
*/
/**
* Must be done at the end of the document. The unit for the size of the
* page and the margin is in twips or twentieths of a point.
*/
public void appendPageSettings() {
Node lastChild = bodyElement.getLastChild();
if(lastChild != null && "w:sectPr".equals(lastChild.getLocalName())) {
return;//nothing to do, already set
}
Node sectionPrefs = bodyElement.appendChild(document.createElement("w:sectPr"));
//A4
Element pageSize = (Element)sectionPrefs.appendChild(document.createElement("w:pgSz"));
pageSize.setAttribute("w:w", "11900");
pageSize.setAttribute("w:h", "16840");
Element margins = (Element)sectionPrefs.appendChild(document.createElement("w:pgMar"));
margins.setAttribute("w:top", "1440");
margins.setAttribute("w:right", "1440");
margins.setAttribute("w:bottom", "1440");
margins.setAttribute("w:left", "1440");
margins.setAttribute("w:header", "708");
margins.setAttribute("w:footer", "708");
margins.setAttribute("w:gutter", "0");
if(StringHelper.containsNonWhitespace(documentHeader)) {
try(InputStream headerIn = OpenXMLDocument.class.getResourceAsStream("_resources/header.xml")) {
String headerTemplate = IOUtils.toString(headerIn);
String header = headerTemplate.replace("[oodocumentitlte]", documentHeader);
String headerId = generateId();
Element headerRefEl = (Element)sectionPrefs.appendChild(document.createElement("w:headerReference"));
headerRefEl.setAttribute("w:type", "default");
headerRefEl.setAttribute("r:id", headerId);
HeaderReference headerRef = new HeaderReference(headerId, header);
headers.add(headerRef);
} catch (DOMException e) {
log.error("", e);
} catch (IOException e) {
log.error("", e);
}
}
}
public void appendFillInBlanck(int length, boolean newParagraph) {
Element paragraphEl = getParagraphToAppendTo(newParagraph);
Node runEl = paragraphEl.appendChild(createRunEl(null));
runEl.appendChild(createRunPrefsEl(Style.underline));
int tabLength = length / 5;
for(int i=tabLength; i-->0; ) {
runEl.appendChild(document.createElement("w:tab"));
}
getCursor().appendChild(paragraphEl);
}
public Element createFillInBlanck(int length) {
Element runEl = createRunEl(null);
runEl.appendChild(createRunPrefsEl(Style.underline));
int tabLength = length / 5;
for(int i=tabLength; i-->0; ) {
runEl.appendChild(document.createElement("w:tab"));
}
return runEl;
}
/*
<w:p w:rsidR="00F528BA" w:rsidRPr="00245F75" w:rsidRDefault="00F528BA" w:rsidP="00245F75">
<w:pPr>
<w:pBdr>
<w:bottom w:val="single" w:sz="4" w:space="1" w:color="auto" />
</w:pBdr>
</w:pPr>
</w:p>
*/
public void appendFillInBlanckWholeLine(int rows) {
for(int i=rows+1; i-->0; ) {
Element paragraphEl = createFillInBlanckWholeLine();
getCursor().appendChild(paragraphEl);
}
}
public Element createFillInBlanckWholeLine() {
Element paragraphEl = createParagraphEl();
Node pargraphPrefs = paragraphEl.appendChild(document.createElement("w:pPr"));
Node pargraphBottomPrefs = pargraphPrefs.appendChild(document.createElement("w:pBdr"));
Element bottomEl = (Element)pargraphBottomPrefs.appendChild(document.createElement("w:between"));
bottomEl.setAttribute("w:val", "single");
bottomEl.setAttribute("w:sz", "4");
bottomEl.setAttribute("w:space", "1");
bottomEl.setAttribute("w:color", "auto");
return paragraphEl;
}
public void appendText(String text, boolean newParagraph, Style... textStyles) {
if(!StringHelper.containsNonWhitespace(text)) return;
List<Element> textEls = new ArrayList<Element>();
for(StringTokenizer tokenizer = new StringTokenizer(text, "\n\r"); tokenizer.hasMoreTokens(); ) {
String token = tokenizer.nextToken();
Element textEl = createTextEl(token);
textEls.add(textEl);
if(tokenizer.hasMoreTokens()) {
textEls.add(createBreakEl());
}
}
if(textEls.size() > 0) {
Element paragraphEl = getParagraphToAppendTo(newParagraph);
Element runEl = document.createElement("w:r");
if(textStyles != null && textStyles.length > 0) {
runEl.appendChild(createRunPrefsEl(textStyles));
}
for(Element textEl:textEls) {
runEl.appendChild(textEl);
}
paragraphEl.appendChild(runEl);
getCursor().appendChild(paragraphEl);
}
}
/**
* Get a paragraph, if @param newParagraph is false, try to get the
* last paragraph of the cursor. If @param newParagraph is true, create
* always a new paragraph.
* @param newParagraph
* @return
*/
private Element getParagraphToAppendTo(boolean newParagraph) {
Element paragraphEl = null;
if(!newParagraph) {
paragraphEl = getCurrentParagraph();
//add a blank between
if(paragraphEl != null) {
Element runEl = document.createElement("w:r");
runEl.appendChild(createPreserveSpaceEl());
paragraphEl.appendChild(runEl);
}
}
if(paragraphEl == null) {
paragraphEl = createParagraphEl();
}
return paragraphEl;
}
/**
* Return the paragraph if and only if it's the last element. Return
* null if not found.
* @return
*/
private Element getCurrentParagraph() {
Element paragraphEl = null;
Node currentNode = getCursor();
if(currentNode != null && currentNode.getLastChild() != null
&& "w:p".equals(currentNode.getLastChild().getNodeName())) {
paragraphEl = (Element)currentNode.getLastChild();
}
return paragraphEl;
}
public void appendPageBreak() {
getCursor().appendChild(createPageBreakEl());
}
public void appendBreak(boolean newParagraph) {
Element breakEl = createBreakEl();
Element paragraphEl = getParagraphToAppendTo(newParagraph);
paragraphEl.appendChild(createRunEl(Collections.singletonList(breakEl)));
getCursor().appendChild(paragraphEl);
}
public void appendHtmlText(String html, Spacing spacing) {
if(!StringHelper.containsNonWhitespace(html)) return;
try {
html = cleanUpHTML(html);
SAXParser parser = new SAXParser();
parser.setContentHandler(new HTMLToOpenXMLHandler(this, spacing));
parser.parse(new InputSource(new StringReader(html)));
} catch (SAXException e) {
log.error("", e);
} catch (IOException e) {
log.error("", e);
}
}
public void appendHtmlText(String html, boolean newParagraph) {
if(!StringHelper.containsNonWhitespace(html)) return;
try {
html = cleanUpHTML(html);
SAXParser parser = new SAXParser();
Element paragraphEl = getParagraphToAppendTo(newParagraph);
parser.setContentHandler(new HTMLToOpenXMLHandler(this, paragraphEl, true));
parser.parse(new InputSource(new StringReader(html)));
} catch (SAXException e) {
log.error("", e);
} catch (IOException e) {
log.error("", e);
}
}
public void appendHtmlText(String html, boolean newParagraph, HTMLToOpenXMLHandler handler) {
if(!StringHelper.containsNonWhitespace(html)) return;
try {
html = cleanUpHTML(html);
SAXParser parser = new SAXParser();
Element paragraphEl = getParagraphToAppendTo(newParagraph);
handler.setInitialParagraph(paragraphEl);
parser.setContentHandler(handler);
parser.parse(new InputSource(new StringReader(html)));
} catch (SAXException e) {
log.error("", e);
} catch (IOException e) {
log.error("", e);
}
}
/**
* The Neko HTMl parser has some issues with <p/>.
*
* @param html The HTML to clean up
* @return HTML code which Neko understands
*/
private String cleanUpHTML(String html) {
return html.replace("<p/>", "<p></p>");
}
public Node appendTable(Integer... width) {
Element tableEl = createTable(width);
return getCursor().appendChild(tableEl);
}
/*
<w:pPr>
<w:pStyle w:val="berschrift1" />
<w:rPr>
<w:rStyle w:val="SchwacheHervorhebung" />
</w:rPr>
</w:pPr>
*/
public Element createParagraphStyle(PredefinedStyle styleId) {
Element paragraphEl = document.createElement("w:pPr");
if(styleId != null && styleId.paragraphStyleId() != null) {
Element styleEl = (Element)paragraphEl.appendChild(document.createElement("w:pStyle"));
styleEl.setAttribute("w:val", styleId.paragraphStyleId());
}
if(styleId != null && styleId.runStyleId() != null) {
Element runPrefsEl = (Element)paragraphEl.appendChild(document.createElement("w:rPr"));
Element rStyleEl = (Element)runPrefsEl.appendChild(document.createElement("w:rStyle"));
rStyleEl.setAttribute("w:val", styleId.runStyleId());
}
return paragraphEl;
}
/*
<w:p w:rsidR="003231EA" w:rsidRDefault="00A53C12">
<w:r>
<w:t>Hello word</w:t>
</w:r>
</w:p>
*/
public Element createParagraphEl(Element styleEl, Collection<Element> runEls) {
Element paragraphEl = document.createElement("w:p");
if(styleEl != null) {
paragraphEl.appendChild(styleEl);
}
for(Element runEl:runEls) {
paragraphEl.appendChild(runEl);
}
return paragraphEl;
}
public Element createParagraphEl() {
Element paragraphEl = document.createElement("w:p");
return paragraphEl;
}
/*
<w:pPr>
<w:spacing w:before="120" w:after="120" w:beforeAutospacing="0" w:afterAutospacing="0"/>
</w:pPr>
*/
public Element createParagraphEl(Indent indent, Border leftBorder, Spacing spacing, PredefinedStyle predefinedStyle) {
Element paragraphEl = document.createElement("w:p");
Element paragraphPrefsEl = (Element)paragraphEl.appendChild(document.createElement("w:pPr"));
if(indent != null) {
//<w:ind w:left="1440" w:right="1440" w:hanging="1080" />
Element indEl = (Element)paragraphPrefsEl.appendChild(document.createElement("w:ind"));
if(indent.getLeft() > 0) {
indEl.setAttribute("w:left", Integer.toString(indent.getLeft()));
}
}
if(predefinedStyle != null && predefinedStyle.paragraphStyleId() != null) {
Element styleEl = (Element)paragraphPrefsEl.appendChild(document.createElement("w:pStyle"));
styleEl.setAttribute("w:val", predefinedStyle.paragraphStyleId());
}
if(leftBorder != null) {
//<w:pBdr>
// <w:left w:val="single" w:sz="24" w:space="4" w:color="B97034" w:themeColor="accent6" w:themeShade="BF" />
Element borderEl = (Element)paragraphPrefsEl.appendChild(document.createElement("w:pBdr"));
Element leftEl = (Element)borderEl.appendChild(document.createElement("w:left"));
leftEl.setAttribute("w:val", leftBorder.getVal());
leftEl.setAttribute("w:sz", Integer.toString(leftBorder.getSize()));
leftEl.setAttribute("w:space", Integer.toString(leftBorder.getSpace()));
leftEl.setAttribute("w:color", leftBorder.getColor());
}
if(spacing != null) {
Element spacingEl = (Element)paragraphPrefsEl.appendChild(document.createElement("w:spacing"));
spacingEl.setAttribute("w:before", Integer.toString(spacing.getBefore()));
spacingEl.setAttribute("w:after", Integer.toString(spacing.getAfter()));
spacingEl.setAttribute("w:beforeAutospacing", "0");
spacingEl.setAttribute("w:afterAutospacing", "0");
}
return paragraphEl;
}
public Element createRunEl() {
return createRunEl(null, null);
}
public Element createRunEl(Collection<? extends Node> textEls) {
return createRunEl(textEls, null);
}
public Element createRunEl(Collection<? extends Node> textEls, PredefinedStyle style) {
Element runEl = document.createElement("w:r");
if(style != null && style.runStyleId() != null) {
Element runPrefsEl = (Element)runEl.appendChild(document.createElement("w:rPr"));
Element rStyleEl = (Element)runPrefsEl.appendChild(document.createElement("w:rStyle"));
rStyleEl.setAttribute("w:val", style.runStyleId());
}
if(textEls != null && textEls.size() > 0) {
for(Node textEl:textEls) {
runEl.appendChild(textEl);
}
}
return runEl;
}
public Node createRunPrefsEl(Style... runStyles) {
Element runPrefsEl = document.createElement("w:rPr");
return createRunPrefsEl(runPrefsEl, runStyles);
}
public Node createRunPrefsEl(Node runPrefsEl, Style... prefsStyles) {
if(prefsStyles != null && prefsStyles.length > 0) {
for(Style style:prefsStyles) {
if(style != null) {
switch(style) {
case underline: {
Element underlinePrefs = (Element)runPrefsEl.appendChild(document.createElement("w:u"));
underlinePrefs.setAttribute("w:val", "single");
break;
}
case italic: runPrefsEl.appendChild(document.createElement("w:i")); break;
case bold: runPrefsEl.appendChild(document.createElement("w:b")); break;
case strike: runPrefsEl.appendChild(document.createElement("w:strike")); break;
}
}
}
}
return runPrefsEl;
}
public Node createRunReversePrefsEl(Node runPrefsEl, Style... runStyles) {
if(runStyles != null && runStyles.length > 0) {
for(Style style:runStyles) {
if(style != null) {
switch(style) {
case underline:
Element underlinePrefs = (Element)runPrefsEl.appendChild(document.createElement("w:u"));
underlinePrefs.setAttribute("w:val", "none");
break;
case italic:
Element italicPrefs = (Element)runPrefsEl.appendChild(document.createElement("w:i"));
italicPrefs.setAttribute("w:val", "0");
break;
case bold:
Element boldPrefs = (Element)runPrefsEl.appendChild(document.createElement("w:b"));
boldPrefs.setAttribute("w:val", "0");
break;
case strike:
Element strikePrefs = (Element)runPrefsEl.appendChild(document.createElement("w:strike"));
strikePrefs.setAttribute("w:val", "0");
break;
}
}
}
}
return runPrefsEl;
}
/**
* Return a text element w:t
* @param text
* @return
*/
public Element createTextEl(String text) {
Element textEl = document.createElement("w:t");
textEl.appendChild(document.createTextNode(text));
return textEl;
}
public Element createPreserveSpaceEl() {
Element textEl = document.createElement("w:t");
textEl.setAttribute("xml:space", "preserve");
textEl.appendChild(document.createTextNode(" "));
return textEl;
}
public Element createParagraphEl(String text) {
Element paragraphEl = createParagraphEl();
Node runEl = paragraphEl.appendChild(document.createElement("w:r"));
for(StringTokenizer tokenizer = new StringTokenizer(text, "\n\r"); tokenizer.hasMoreTokens(); ) {
String token = tokenizer.nextToken();
Element textEl = createTextEl(token);
runEl.appendChild(textEl);
if(tokenizer.hasMoreTokens()) {
runEl.appendChild(createBreakEl());
}
}
return paragraphEl;
}
public Node createCheckbox(boolean checked) {
return createCheckbox(checked, true);
}
public Node createCheckbox(boolean checked, boolean border) {
try {
String name;
if(border) {
name = checked ? "image1.png" : "image2.png";
} else {
name = checked ? "image1_noborder.png" : "image2_noborder.png";
}
URL imgUrl = OpenXMLDocument.class.getResource("_resources/" + name);
File imgFile = new File(imgUrl.toURI());
return createImageEl(imgFile);
} catch (URISyntaxException e) {
log.error("", e);
return null;
}
}
public Element createBreakEl() {
return document.createElement("w:br");
}
public Element createPageBreakEl() {
Element paragraphEl = document.createElement("w:p");
Element runEl = (Element)paragraphEl.appendChild(document.createElement("w:r"));
Element breakEl = (Element)runEl.appendChild(document.createElement("w:br"));
breakEl.setAttribute("w:type", "page");
return paragraphEl;
}
public Element createTable() {
Element tableEl = document.createElement("w:tbl");
//preferences table
Element tablePrEl = (Element)tableEl.appendChild(document.createElement("w:tblPr"));
createWidthEl("w:tblW", 5000, Unit.pct, tablePrEl);
createWidthEl("w:tblCellSpacing", 22, Unit.dxa, tablePrEl);
Node tableCellMarEl = tablePrEl.appendChild(document.createElement("w:tblCellMar"));
createWidthEl("w:top", 45, Unit.dxa, tableCellMarEl);
createWidthEl("w:left", 45, Unit.dxa, tableCellMarEl);
createWidthEl("w:bottom", 45, Unit.dxa, tableCellMarEl);
createWidthEl("w:right", 45, Unit.dxa, tableCellMarEl);
Element tableLookEl = (Element)tablePrEl.appendChild(document.createElement("w:tblLook"));
tableLookEl.setAttribute("w:val", "0000");
tableLookEl.setAttribute("w:firstRow", "0");
tableLookEl.setAttribute("w:lastRow", "0");
tableLookEl.setAttribute("w:firstColumn", "0");
tableLookEl.setAttribute("w:lastColumn", "0");
tableLookEl.setAttribute("w:noHBand", "0");
tableLookEl.setAttribute("w:noVBand", "0");
//grid preferences
tableEl.appendChild(document.createElement("w:tblGrid"));
return tableEl;
}
/*
<w:tbl>
<w:tblPr>
<w:tblW w:w="5000" w:type="pct" />
<w:tblCellSpacing w:w="22" w:type="dxa" />
<w:tblBorders>
<w:top w:val="nil" /><w:bottom w:val="nil" /><w:insideH w:val="nil" /><w:insideV w:val="nil" />
</w:tblBorders>
<w:tblCellMar>
<w:top w:w="45" w:type="dxa" /><w:left w:w="45" w:type="dxa" /><w:bottom w:w="45" w:type="dxa" /><w:right w:w="45" w:type="dxa" />
</w:tblCellMar>
<w:tblLook w:val="0000" w:firstRow="0" w:lastRow="0" w:firstColumn="0" w:lastColumn="0" w:noHBand="0" w:noVBand="0" />
</w:tblPr>
<w:tblGrid>
<w:gridCol w:w="10178" /><w:gridCol w:w="1116" />
</w:tblGrid>
*/
public Element createTable(Integer... width) {
Element tableEl = createTable();
NodeList gridPrefs = tableEl.getElementsByTagName("w:tblGrid");
Element tableGridEl = (Element)gridPrefs.item(0);
//table grid
for(Integer w:width) {
createGridCol(w, tableGridEl);
}
return tableEl;
}
/*
<w:tr>
<w:trPr>
<w:tblCellSpacing w:w="22" w:type="dxa" />
</w:trPr>
*/
public Element createTableRow() {
Element rowEl = document.createElement("w:tr");
//trPr
return rowEl;
}
/*
<w:tc>
<w:tcPr>
<w:tcW w:w="0" w:type="auto" />
<w:tcBorders>
<w:top w:val="single" w:sz="6" w:space="0" w:color="E9EAF2" /><w:left w:val="single" w:sz="6" w:space="0" w:color="E9EAF2" /><w:bottom w:val="single" w:sz="6" w:space="0" w:color="E9EAF2" /><w:right w:val="single" w:sz="6" w:space="0" w:color="E9EAF2" />
</w:tcBorders>
<w:shd w:val="solid" w:color="E9EAF2" w:fill="auto" />
</w:tcPr>
*/
public Element createTableCell(String background, Integer width, Unit unit) {
Element cellEl = document.createElement("w:tc");
Node prefEl = null;
if(unit != null) {
prefEl = cellEl.appendChild(document.createElement("w:tcPr"));
createWidthEl("w:tcW", width, unit, cellEl);
}
if(StringHelper.containsNonWhitespace(background)) {
if(prefEl == null) {
prefEl = cellEl.appendChild(document.createElement("w:tcPr"));
}
Node borderEl = prefEl.appendChild(document.createElement("w:tcBorders"));
createBorder("w:top", background, borderEl);
createBorder("w:left", background, borderEl);
createBorder("w:bottom", background, borderEl);
createBorder("w:right", background, borderEl);
createShadow(background, prefEl);
}
return cellEl;
}
public ListParagraph createListParagraph() {
int abstractNumberingId = currentNumberingId++;
int numberingId = currentNumberingId++;
ListParagraph lp = new ListParagraph(abstractNumberingId, numberingId);
numbering.add(lp);
return lp;
}
/*
<w:p>
<w:pPr>
<w:pStyle w:val="ListParagraph"/>
<w:numPr>
<w:ilvl w:val="0"/>
<w:numId w:val="1"/>
</w:numPr>
</w:pPr>
<w:r>
<w:t>One</w:t>
</w:r>
</w:p>
*/
public Element createListParagraph(ListParagraph def) {
Element paragraphEl = document.createElement("w:p");
Element listEl = (Element)paragraphEl.appendChild(document.createElement("w:pPr"));
Element pStyleEl = (Element)listEl.appendChild(document.createElement("w:pStyle"));
pStyleEl.setAttribute("w:val", "ListParagraph");
Element numberingEl = (Element)listEl.appendChild(document.createElement("w:numPr"));
Element ilvlEl = (Element)numberingEl.appendChild(document.createElement("w:ilvl"));
ilvlEl.setAttribute("w:val", "0");
Element numIdEl = (Element)numberingEl.appendChild(document.createElement("w:numId"));
numIdEl.setAttribute("w:val", Integer.toString(def.getNumId()));
return paragraphEl;
}
/*
<w:abstractNum w:abstractNumId="0">
<w:lvl w:ilvl="0">
<w:start w:val="1"/>
<w:numFmt w:val="bullet"/>
<w:lvlText w:val="o"/>
<w:lvlJc w:val="left"/>
<w:pPr>
<w:ind w:left="720"
w:hanging="360"/>
</w:pPr>
<w:rPr>
<w:rFonts w:ascii="Symbol"
w:hAnsi="Symbol"
w:hint="default"/>
</w:rPr>
</w:lvl>
*/
public Element createAbstractNumbering(ListParagraph def, Document doc) {
Element numEl = doc.createElement("w:abstractNum");
numEl.setAttribute("w:abstractNumId", Integer.toString(def.getAbstractNumId()));
numEl.appendChild(createNumberingLevel(doc));
return numEl;
}
private Element createNumberingLevel(Document numberingDoc) {
Element levelEl = numberingDoc.createElement("w:lvl");
levelEl.setAttribute("w:ilvl", "0");
Element startEl = (Element)levelEl.appendChild(numberingDoc.createElement("w:start"));
startEl.setAttribute("w:val", "1");
Element numFmtEl = (Element)levelEl.appendChild(numberingDoc.createElement("w:numFmt"));
numFmtEl.setAttribute("w:val", "bullet");
Element lvlTextEl = (Element)levelEl.appendChild(numberingDoc.createElement("w:lvlText"));
lvlTextEl.setAttribute("w:val", Character.toString((char)0xB7));//bullet
Element lvlJcEl = (Element)levelEl.appendChild(numberingDoc.createElement("w:lvlJc"));
lvlJcEl.setAttribute("w:val", "left");
//pPr
Element pPrEl = (Element)levelEl.appendChild(numberingDoc.createElement("w:pPr"));
Element indEl = (Element)pPrEl.appendChild(numberingDoc.createElement("w:ind"));
indEl.setAttribute("w:left", "720");
indEl.setAttribute("w:hanging", "360");
//rPr
Element rPrEl = (Element)levelEl.appendChild(numberingDoc.createElement("w:rPr"));
Element rFontsEl = (Element)rPrEl.appendChild(numberingDoc.createElement("w:rFonts"));
rFontsEl.setAttribute("w:ascii", "Symbol");
rFontsEl.setAttribute("w:hAnsi", "Symbol");
rFontsEl.setAttribute("w:hint", "default");
return levelEl;
}
/*
<w:num w:numId="1">
<w:abstractNumId w:val="0"/>
</w:num>
*/
public Element createNumbering(ListParagraph def, Document numberingDoc) {
Element numEl = numberingDoc.createElement("w:num");
numEl.setAttribute("w:numId", Integer.toString(def.getNumId()));
Element abstractNumEl = (Element)numEl.appendChild(numberingDoc.createElement("w:abstractNumId"));
abstractNumEl.setAttribute("w:val", Integer.toString(def.getAbstractNumId()));
return numEl;
}
/*
<w:shd w:val="solid" w:color="E9EAF2" w:fill="auto" />
*/
private Element createShadow(String color, Node parent) {
Element borderEl = (Element)parent.appendChild(document.createElement("w:shd"));
borderEl.setAttribute("w:val", "solid");
borderEl.setAttribute("w:fill", "auto");
borderEl.setAttribute("w:color", color);
return borderEl;
}
/*
<w:top w:val="single" w:sz="6" w:space="0" w:color="E9EAF2" />
*/
private Element createBorder(String name, String color, Node parent) {
Element borderEl = (Element)parent.appendChild(document.createElement(name));
borderEl.setAttribute("w:val", "single");
borderEl.setAttribute("w:sz", "6");
borderEl.setAttribute("w:space", "0");
borderEl.setAttribute("w:color", color);
return borderEl;
}
private Element createGridCol(Integer width, Node parent) {
Element colEl = (Element)parent.appendChild(document.createElement("w:gridCol"));
colEl.setAttribute("w:w", width.toString());
return colEl;
}
/*
<w:tblCellSpacing w:w="22" w:type="dxa" />
*/
private Element createWidthEl(String name, Integer width, Unit unit, Node parent) {
Element widthEl = (Element)parent.appendChild(document.createElement(name));
if(unit == Unit.auto) {
widthEl.setAttribute("w:w", "0");
widthEl.setAttribute("w:type", "auto");
} else {
widthEl.setAttribute("w:w", width.toString());
widthEl.setAttribute("w:type", unit.unit());
}
return widthEl;
}
public Element wrapInParagraph(Node el) {
Element runEl = createRunEl(Collections.singletonList(el));
return createParagraphEl(null, Collections.singletonList(runEl));
}
public List<Node> convertLaTeX(String latex) {
List<Node> mathEls = new ArrayList<Node>();
try {
//convert latex -> mathml
String mathml = ConvertFromLatexToMathML.convertToMathML(latex);
//convert mathml to word docx
ByteArrayOutputStream out = new ByteArrayOutputStream(20000);
ConvertFromMathMLToWord.writeWordDocStreamFromMathML(out, mathml);
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
out = null;
//extract docx
ZipInputStream zip = new ZipInputStream(in);
ZipEntry entry = zip.getNextEntry();
while (entry != null) {
String name = entry.getName();
if(name.endsWith("word/document.xml")) {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false);
factory.setNamespaceAware(false);
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new ShieldInputStream(zip));
NodeList bodyList = doc.getElementsByTagName("w:body");
if(bodyList.getLength() == 1) {
Node body = bodyList.item(0);
for(Node node=body.getFirstChild(); node!=null; node=node.getNextSibling()) {
Node importedNode = document.importNode(node, true);
mathEls.add(importedNode);
}
}
}
entry = zip.getNextEntry();
}
} catch (Exception e) {
log.error("", e);
}
return mathEls;
}
public void appendImage(File file) {
Element imgEl = createImageEl(file);
if(imgEl != null) {
Element runEl = createRunEl(Collections.singletonList(imgEl));
Element paragraphEl = getParagraphToAppendTo(true);
paragraphEl.appendChild(runEl);
getCursor().appendChild(paragraphEl);
}
}
public Element createImageEl(String path) {
if(mediaContainer == null) return null;
VFSItem media = mediaContainer.resolve(path);
if(media instanceof LocalFileImpl) {
LocalFileImpl file = (LocalFileImpl)media;
return createImageEl(file.getBasefile());
}
return null;
}
/*
<w:drawing>
<wp:inline distT="0" distB="0" distL="0" distR="0"
wp14:anchorId="152D4A51" wp14:editId="0588CC29">
<wp:extent cx="2730500" cy="2730500" />
<wp:effectExtent l="0" t="0" r="12700" b="12700" />
<wp:docPr id="2" name="Bild 2" />
<wp:cNvGraphicFramePr>
<a:graphicFrameLocks
xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
noChangeAspect="1" />
</wp:cNvGraphicFramePr>
<a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
<a:graphicData
uri="http://schemas.openxmlformats.org/drawingml/2006/picture">
<pic:pic
xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture">
<pic:nvPicPr>
<pic:cNvPr id="0" name="aoi.jpg" />
<pic:cNvPicPr />
</pic:nvPicPr>
<pic:blipFill>
<a:blip r:embed="rId6">
<a:extLst>
<a:ext uri="{28A0092B-C50C-407E-A947-70E740481C1C}">
<a14:useLocalDpi xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main" val="0" />
</a:ext>
</a:extLst>
</a:blip>
<a:stretch>
<a:fillRect />
</a:stretch>
</pic:blipFill>
<pic:spPr>
<a:xfrm>
<a:off x="0" y="0" />
<a:ext cx="2730500" cy="2730500" />
</a:xfrm>
<a:prstGeom prst="rect">
<a:avLst />
</a:prstGeom>
<a:noFill />
<a:ln>
<a:noFill />
</a:ln>
</pic:spPr>
</pic:pic>
</a:graphicData>
</a:graphic>
</wp:inline>
</w:drawing>
*/
/**
* <a:blip r:embed="rId6">
* @param image
* @return
*/
public Element createImageEl(File image) {
DocReference ref = registerImage(image);
String id = ref.getId();
OpenXMLSize emuSize = ref.getEmuSize();
String filename = ref.getFilename();
Element drawingEl = document.createElement("w:drawing");
Element inlineEl = (Element)drawingEl.appendChild(document.createElement("wp:inline"));
inlineEl.setAttribute("distT", "0");
inlineEl.setAttribute("distB", "0");
inlineEl.setAttribute("distL", "0");
inlineEl.setAttribute("distR", "0");
//wp14:anchorId="152D4A51" wp14:editId="0588CC29"
Element extentEl = (Element)inlineEl.appendChild(document.createElement("wp:extent"));
extentEl.setAttribute("cx", Integer.toString(emuSize.getWidthEmu()));
extentEl.setAttribute("cy", Integer.toString(emuSize.getHeightEmu()));
Element effectExtentEl = (Element)inlineEl.appendChild(document.createElement("wp:effectExtent"));
effectExtentEl.setAttribute("l", "0");
effectExtentEl.setAttribute("t", "0");
effectExtentEl.setAttribute("r", "12700");
effectExtentEl.setAttribute("b", "0");
Element docPrEl = (Element)inlineEl.appendChild(document.createElement("wp:docPr"));
docPrEl.setAttribute("id", Integer.toString(currentId - 1));
docPrEl.setAttribute("name", filename);
Element cNvGraphicFramePrEl = (Element)inlineEl.appendChild(document.createElement("wp:cNvGraphicFramePr"));
Element graphicFrameLocksEl = (Element)cNvGraphicFramePrEl.appendChild(document.createElement("a:graphicFrameLocks"));
graphicFrameLocksEl.setAttribute("xmlns:a", "http://schemas.openxmlformats.org/drawingml/2006/main");
graphicFrameLocksEl.setAttribute("noChangeAspect", "1");
//big bloc graphic
Element graphicEl = (Element)inlineEl.appendChild(document.createElement("a:graphic"));
graphicEl.setAttribute("xmlns:a", "http://schemas.openxmlformats.org/drawingml/2006/main");
Element graphicDataEl = (Element)graphicEl.appendChild(document.createElement("a:graphicData"));
graphicDataEl.setAttribute("uri", "http://schemas.openxmlformats.org/drawingml/2006/picture");
//pic
Element picEl = (Element)graphicDataEl.appendChild(document.createElement("pic:pic"));
picEl.setAttribute("xmlns:pic", "http://schemas.openxmlformats.org/drawingml/2006/picture");
//picture information
Node nvPicPrEl = picEl.appendChild(document.createElement("pic:nvPicPr"));
Element cNvPrEl = (Element)nvPicPrEl.appendChild(document.createElement("pic:cNvPr"));
cNvPrEl.setAttribute("id", generateSimpleId());
cNvPrEl.setAttribute("name", filename);
Node cNvPicPrEl = nvPicPrEl.appendChild(document.createElement("pic:cNvPicPr"));
Element picLocksEl = (Element)cNvPicPrEl.appendChild(document.createElement("a:picLocks"));
picLocksEl.setAttribute("noChangeAspect", "1");
picLocksEl.setAttribute("noChangeArrowheads", "1");
//picture blip
Node blipFillEl = picEl.appendChild(document.createElement("pic:blipFill"));
Element blipEl = (Element)blipFillEl.appendChild(document.createElement("a:blip"));
blipEl.setAttribute("r:embed", id);
//extLst
Node extLstEl = blipEl.appendChild(document.createElement("a:extLst"));
Element extEl = (Element)extLstEl.appendChild(document.createElement("a:ext"));
extEl.setAttribute("uri", "{" + UUID.randomUUID().toString() + "}");
Element useLocalDpiEl = (Element)extEl.appendChild(document.createElement("a14:useLocalDpi"));
useLocalDpiEl.setAttribute("xmlns:a14", "http://schemas.microsoft.com/office/drawing/2010/main");
useLocalDpiEl.setAttribute("val", "0");
//srcRect
blipFillEl.appendChild(document.createElement("a:srcRect"));
//fill
Node strechEl = blipFillEl.appendChild(document.createElement("a:stretch"));
strechEl.appendChild(document.createElement("a:fillRect"));
//pic -> spPr
Element spPrEl = (Element)picEl.appendChild(document.createElement("pic:spPr"));
spPrEl.setAttribute("bwMode", "auto");
//pic -> spPr -> xfrm
Node xfrmEl = spPrEl.appendChild(document.createElement("a:xfrm"));
Element xfrmOffEl = (Element)xfrmEl.appendChild(document.createElement("a:off"));
xfrmOffEl.setAttribute("x", "0");
xfrmOffEl.setAttribute("y", "0");
Element xfrmExtEl = (Element)xfrmEl.appendChild(document.createElement("a:ext"));
xfrmExtEl.setAttribute("cx", Integer.toString(emuSize.getWidthEmu()));
xfrmExtEl.setAttribute("cy", Integer.toString(emuSize.getHeightEmu()));
//pic -> spPr -> prstGeom
Element prstGeomEl = (Element)spPrEl.appendChild(document.createElement("a:prstGeom"));
prstGeomEl.setAttribute("prst","rect");
prstGeomEl.appendChild(document.createElement("a:avLst"));
spPrEl.appendChild(document.createElement("a:noFill"));
Node lnEl = spPrEl.appendChild(document.createElement("a:ln"));
lnEl.appendChild(document.createElement("a:noFill"));
return drawingEl;
}
private DocReference registerImage(File image) {
DocReference ref;
if(fileToImagesMap.containsKey(image)) {
ref = fileToImagesMap.get(image);
} else {
String id = generateId();
Size size = ImageUtils.getImageSize(image);
OpenXMLSize emuSize = OpenXMLUtils.convertPixelToEMUs(size, DPI, 15.9/* cm */);
String filename = getUniqueFilename(image);
ref = new DocReference(id, filename, emuSize, image);
fileToImagesMap.put(image, ref);
}
return ref;
}
private void appendPicture(Element parentEl, DocReference ref) {
String id = ref.getId();
String filename = ref.getFilename();
OpenXMLSize emuSize = ref.getEmuSize();
//pic
Element picEl = (Element)parentEl.appendChild(document.createElement("pic:pic"));
picEl.setAttribute("xmlns:pic", "http://schemas.openxmlformats.org/drawingml/2006/picture");
//picture information
Node nvPicPrEl = picEl.appendChild(document.createElement("pic:nvPicPr"));
Element cNvPrEl = (Element)nvPicPrEl.appendChild(document.createElement("pic:cNvPr"));
cNvPrEl.setAttribute("id", generateSimpleId());
cNvPrEl.setAttribute("name", filename);
Node cNvPicPrEl = nvPicPrEl.appendChild(document.createElement("pic:cNvPicPr"));
Element picLocksEl = (Element)cNvPicPrEl.appendChild(document.createElement("a:picLocks"));
picLocksEl.setAttribute("noChangeAspect", "1");
picLocksEl.setAttribute("noChangeArrowheads", "1");
//picture blip
Node blipFillEl = picEl.appendChild(document.createElement("pic:blipFill"));
Element blipEl = (Element)blipFillEl.appendChild(document.createElement("a:blip"));
blipEl.setAttribute("r:embed", id);
//extLst
Node extLstEl = blipEl.appendChild(document.createElement("a:extLst"));
Element extEl = (Element)extLstEl.appendChild(document.createElement("a:ext"));
extEl.setAttribute("uri", "{" + UUID.randomUUID().toString() + "}");
Element useLocalDpiEl = (Element)extEl.appendChild(document.createElement("a14:useLocalDpi"));
useLocalDpiEl.setAttribute("xmlns:a14", "http://schemas.microsoft.com/office/drawing/2010/main");
useLocalDpiEl.setAttribute("val", "0");
//srcRect
blipFillEl.appendChild(document.createElement("a:srcRect"));
//fill
Node strechEl = blipFillEl.appendChild(document.createElement("a:stretch"));
strechEl.appendChild(document.createElement("a:fillRect"));
//pic -> spPr
Element spPrEl = (Element)picEl.appendChild(document.createElement("pic:spPr"));
spPrEl.setAttribute("bwMode", "auto");
//pic -> spPr -> xfrm
Node xfrmEl = spPrEl.appendChild(document.createElement("a:xfrm"));
Element xfrmOffEl = (Element)xfrmEl.appendChild(document.createElement("a:off"));
xfrmOffEl.setAttribute("x", "0");
xfrmOffEl.setAttribute("y", "0");
Element xfrmExtEl = (Element)xfrmEl.appendChild(document.createElement("a:ext"));
xfrmExtEl.setAttribute("cx", Integer.toString(emuSize.getWidthEmu()));
xfrmExtEl.setAttribute("cy", Integer.toString(emuSize.getHeightEmu()));
//pic -> spPr -> prstGeom
Element prstGeomEl = (Element)spPrEl.appendChild(document.createElement("a:prstGeom"));
prstGeomEl.setAttribute("prst","rect");
prstGeomEl.appendChild(document.createElement("a:avLst"));
spPrEl.appendChild(document.createElement("a:noFill"));
Node lnEl = spPrEl.appendChild(document.createElement("a:ln"));
lnEl.appendChild(document.createElement("a:noFill"));
}
/*
<w:drawing>
<wp:anchor distT="0" distB="0" distL="114300" distR="114300"
simplePos="0" relativeHeight="251663360" behindDoc="0" locked="0"
layoutInCell="1" allowOverlap="1" wp14:anchorId="0DC40B5E"
wp14:editId="2CD7359E">
<wp:simplePos x="0" y="0" />
<wp:positionH relativeFrom="column">
<wp:posOffset>0</wp:posOffset>
</wp:positionH>
<wp:positionV relativeFrom="paragraph">
<wp:posOffset>179070</wp:posOffset>
</wp:positionV>
<wp:extent cx="5756910" cy="2282190" />
<wp:effectExtent l="0" t="0" r="8890" b="3810" />
<wp:wrapThrough wrapText="bothSides">
<wp:wrapPolygon edited="0">
<wp:start x="0" y="0" />
<wp:lineTo x="0" y="21396" />
<wp:lineTo x="21538" y="21396" />
<wp:lineTo x="21538" y="0" />
<wp:lineTo x="0" y="0" />
</wp:wrapPolygon>
</wp:wrapThrough>
<wp:docPr id="5" name="Gruppierung 5" />
<wp:cNvGraphicFramePr />
<a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
<a:graphicData
uri="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup">
<wpg:wgp>
<wpg:cNvGrpSpPr />
<wpg:grpSpPr>
<a:xfrm>
<a:off x="0" y="0" />
<a:ext cx="5756910" cy="2282190" />
<a:chOff x="0" y="0" />
<a:chExt cx="5756910" cy="2282190" />
</a:xfrm>
</wpg:grpSpPr>
</wpg:wgp>
</a:graphicData>
</a:graphic>
</wp:anchor>
</w:drawing>
*/
public Element createGraphicEl(File backgroundImage, List<OpenXMLGraphic> elements) {
DocReference backgroundImageRef = registerImage(backgroundImage);
OpenXMLSize emuSize = backgroundImageRef.getEmuSize();
Element alternateContentEl = document.createElement("mc:AlternateContent");
Element choiceEl = (Element)alternateContentEl.appendChild(document.createElement("mc:Choice"));
choiceEl.setAttribute("Requires", "wpg");
Element drawingEl = (Element)choiceEl.appendChild(document.createElement("w:drawing"));
//anchor
Element anchorEl = (Element)drawingEl.appendChild(document.createElement("wp:anchor"));
anchorEl.setAttribute("distT", "0");
anchorEl.setAttribute("distB", "0");
anchorEl.setAttribute("distL", "0");
anchorEl.setAttribute("distR", "0");
anchorEl.setAttribute("simplePos", "0");
anchorEl.setAttribute("relativeHeight", "251663360");//TODO
anchorEl.setAttribute("behindDoc", "0");
anchorEl.setAttribute("locked", "0");
anchorEl.setAttribute("layoutInCell", "1");
anchorEl.setAttribute("allowOverlap", "1");
anchorEl.setAttribute("locked", "0");
//simple pos
Element simplePosEl = (Element)anchorEl.appendChild(document.createElement("wp:simplePos"));
simplePosEl.setAttribute("x", "0");
simplePosEl.setAttribute("y", "0");
/*<wp:positionH relativeFrom="column">
<wp:posOffset>0</wp:posOffset>
</wp:positionH>*/
Element positionHEl = (Element)anchorEl.appendChild(document.createElement("wp:positionH"));
positionHEl.setAttribute("relativeFrom", "column");
Element positionHPosOffsetEl = (Element)positionHEl.appendChild(document.createElement("wp:posOffset"));
positionHPosOffsetEl.appendChild(document.createTextNode("0"));
/*<wp:positionV relativeFrom="paragraph">
<wp:posOffset>179070</wp:posOffset>
</wp:positionV>*/
Element positionVEl = (Element)anchorEl.appendChild(document.createElement("wp:positionV"));
positionVEl.setAttribute("relativeFrom", "paragraph");
Element positionVposOffsetEl = (Element)positionVEl.appendChild(document.createElement("wp:posOffset"));
positionVposOffsetEl.appendChild(document.createTextNode("179070"));
String width = Integer.toString(emuSize.getWidthEmu());//"5756910";
String height = Integer.toString(emuSize.getHeightEmu());// "2282190";
//extent
Element extentEl = (Element)anchorEl.appendChild(document.createElement("wp:extent"));
extentEl.setAttribute("cx", width);
extentEl.setAttribute("cy", height);
//effectExtent
Element effectExtentEl = (Element)anchorEl.appendChild(document.createElement("wp:effectExtent"));
effectExtentEl.setAttribute("l", "0");
effectExtentEl.setAttribute("t", "0");
effectExtentEl.setAttribute("r", "8890");
effectExtentEl.setAttribute("b", "3810");
/*<wp:wrapThrough wrapText="bothSides">
<wp:wrapPolygon edited="0">
<wp:start x="0" y="0" />
<wp:lineTo x="0" y="21396" />
<wp:lineTo x="21538" y="21396" />
<wp:lineTo x="21538" y="0" />
<wp:lineTo x="0" y="0" />
</wp:wrapPolygon>
</wp:wrapThrough>*/
Element wrapThroughEl = (Element)anchorEl.appendChild(document.createElement("wp:wrapThrough"));
wrapThroughEl.setAttribute("wrapText", "bothSides");
Element wrapPolygonEl = (Element)wrapThroughEl.appendChild(document.createElement("wp:wrapPolygon"));
wrapPolygonEl.setAttribute("edited", "0");
Element wrapPolygonStartEl = (Element)wrapPolygonEl.appendChild(document.createElement("wp:start"));
wrapPolygonStartEl.setAttribute("x", "0");
wrapPolygonStartEl.setAttribute("y", "0");
appendLineTo(wrapPolygonEl, "0", "21396");//TODO
appendLineTo(wrapPolygonEl, "21538", "21396");
appendLineTo(wrapPolygonEl, "21538", "0");
appendLineTo(wrapPolygonEl, "0", "0");
//<wp:docPr id="5" name="Gruppierung 5" />
Element docPrEl = (Element)anchorEl.appendChild(document.createElement("wp:docPr"));
String groupId = generateSimpleId();
docPrEl.setAttribute("id", groupId);
docPrEl.setAttribute("name", "Gruppierung " + groupId);
//<wp:cNvGraphicFramePr />
anchorEl.appendChild(document.createElement("wp:cNvGraphicFramePr"));
//<a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
Element graphicEl = (Element)anchorEl.appendChild(document.createElement("a:graphic"));
graphicEl.setAttribute("xmlns:a", "http://schemas.openxmlformats.org/drawingml/2006/main");
//<a:graphicData uri="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup">
Element graphicDataEl = (Element)graphicEl.appendChild(document.createElement("a:graphicData"));
graphicDataEl.setAttribute("uri", "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup");
//groups
Element wpgEl = (Element)graphicDataEl.appendChild(document.createElement("wpg:wgp"));
//<wpg:cNvGrpSpPr />
wpgEl.appendChild(document.createElement("wpg:cNvGrpSpPr"));
Element wpgGrpSpPrEl = (Element)wpgEl.appendChild(document.createElement("wpg:grpSpPr"));
appendAXfrm_ch(wpgGrpSpPrEl, width, height);
// list of elements <wpg:grpSp>
Element grpSpEl = (Element)wpgEl.appendChild(document.createElement("wpg:grpSp"));
Element cNvPrEl = (Element)grpSpEl.appendChild(document.createElement("wpg:cNvPr"));
String subGroupId = generateSimpleId();
cNvPrEl.setAttribute("id", subGroupId);
cNvPrEl.setAttribute("name", "Gruppierung " + subGroupId);
grpSpEl.appendChild(document.createElement("wpg:cNvGrpSpPr"));
Element grpSpPrEl = (Element)grpSpEl.appendChild(document.createElement("wpg:grpSpPr"));
appendAXfrm_ch(grpSpPrEl, width, height);
appendPicture(grpSpEl, backgroundImageRef);
for(OpenXMLGraphic element:elements) {
appendGraphicElementEl(grpSpEl, emuSize, element);
}
return alternateContentEl;
}
/*
<wps:wsp>
<wps:cNvPr id="2" name="Rechteck 2" />
<wps:cNvSpPr />
<wps:spPr>
<a:xfrm>
<a:off x="2028190" y="581660" />
<a:ext cx="822960" cy="822960" />
</a:xfrm>
<a:prstGeom prst="rect">
<a:avLst />
</a:prstGeom>
<a:noFill />
<a:ln w="38100" />
<a:effectLst />
<a:extLst>
<a:ext uri="{FAA26D3D-D897-4be2-8F04-BA451C77F1D7}">
<ma14:placeholderFlag
xmlns:ma14="http://schemas.microsoft.com/office/mac/drawingml/2011/main" />
</a:ext>
<a:ext uri="{C572A759-6A51-4108-AA02-DFA0A04FC94B}">
<ma14:wrappingTextBoxFlag
xmlns:ma14="http://schemas.microsoft.com/office/mac/drawingml/2011/main" />
</a:ext>
</a:extLst>
</wps:spPr>
<wps:style>
<a:lnRef idx="1">
<a:schemeClr val="accent1" />
</a:lnRef>
<a:fillRef idx="3">
<a:schemeClr val="accent1" />
</a:fillRef>
<a:effectRef idx="2">
<a:schemeClr val="accent1" />
</a:effectRef>
<a:fontRef idx="minor">
<a:schemeClr val="lt1" />
</a:fontRef>
</wps:style>
<wps:bodyPr />
</wps:wsp>
*/
private void appendGraphicElementEl(Element parentEl, OpenXMLSize backgroundSize, OpenXMLGraphic element) {
Element wspEl = (Element)parentEl.appendChild(document.createElement("wps:wsp"));
String formId = generateSimpleId();
//<wps:cNvPr id="2" name="Rechteck 2" />
Element cNvPrEl = (Element)wspEl.appendChild(document.createElement("wps:cNvPr"));
cNvPrEl.setAttribute("id", formId);
cNvPrEl.setAttribute("name", "Form " + formId);
//<wps:cNvSpPr />
wspEl.appendChild(document.createElement("wps:cNvSpPr"));
Element spPrEl = (Element)wspEl.appendChild(document.createElement("wps:spPr"));
if(element.type() == OpenXMLGraphic.Type.rectangle) {
appendGraphicRectangle(spPrEl, backgroundSize, element);
} else if(element.type() == OpenXMLGraphic.Type.circle) {
appendGraphicEllipse(spPrEl, backgroundSize, element);
}
appendGraphicSolidFill_transparent(spPrEl, element.getStyle());
//spPrEl.appendChild(document.createElement("a:noFill"));
Element lnEl = (Element)spPrEl.appendChild(document.createElement("a:ln"));
lnEl.setAttribute("w", "38100");
spPrEl.appendChild(document.createElement("a:effectLst"));
//styles
Element styleEl = (Element)wspEl.appendChild(document.createElement("wps:style"));
appendAStyle(styleEl, "a:lnRef", "1", element.getStyle().name());
appendAStyle(styleEl, "a:fillRef", "3", element.getStyle().name());
appendAStyle(styleEl, "a:effectRef", "2", element.getStyle().name());
appendAStyle(styleEl, "a:fontRef", "minor", "lt1");
wspEl.appendChild(document.createElement("wps:bodyPr"));
}
/*
<a:solidFill>
<a:schemeClr val="accent3">
<a:alpha val="63000" />
</a:schemeClr>
</a:solidFill>
*/
private void appendGraphicSolidFill_transparent(Element parentEl, OpenXMLGraphic.Style style) {
Element solidFillEl = (Element)parentEl.appendChild(document.createElement("a:solidFill"));
Element schemeClrEl = (Element)solidFillEl.appendChild(document.createElement("a:schemeClr"));
schemeClrEl.setAttribute("val", style.name());
Element alphaEl = (Element)schemeClrEl.appendChild(document.createElement("a:alpha"));
alphaEl.setAttribute("val", "50000");
}
private void appendGraphicRectangle(Element spPrEl, OpenXMLSize backgroundSize, OpenXMLGraphic element) {
/*
<a:xfrm>
<a:off x="2028190" y="581660" />
<a:ext cx="822960" cy="822960" />
</a:xfrm>
*/
Element aXfrmEl = (Element)spPrEl.appendChild(document.createElement("a:xfrm"));
Element aOffEl = (Element)aXfrmEl.appendChild(document.createElement("a:off"));
List<Integer> coords = element.getCoords();
int leftx = coords.get(0);
int topy = coords.get(1);
int leftxEmu = OpenXMLUtils.convertPixelToEMUs(leftx, DPI, backgroundSize.getResizeRatio());
int topyEmu = OpenXMLUtils.convertPixelToEMUs(topy, DPI, backgroundSize.getResizeRatio());
aOffEl.setAttribute("x", Integer.toString(leftxEmu));
aOffEl.setAttribute("y", Integer.toString(topyEmu));
Element aExtEl = (Element)aXfrmEl.appendChild(document.createElement("a:ext"));
int rightx = coords.get(2);
int bottomy = coords.get(3);
int width = rightx -leftx;
int cx = OpenXMLUtils.convertPixelToEMUs(width, DPI, backgroundSize.getResizeRatio());
int height = bottomy - topy;
int cy = OpenXMLUtils.convertPixelToEMUs(height, DPI, backgroundSize.getResizeRatio());
aExtEl.setAttribute("cx", Integer.toString(cx));
aExtEl.setAttribute("cy", Integer.toString(cy));
/*
<a:prstGeom prst="rect">
<a:avLst />
</a:prstGeom>
*/
Element prstGeomEl = (Element)spPrEl.appendChild(document.createElement("a:prstGeom"));
prstGeomEl.setAttribute("prst", "rect");
prstGeomEl.appendChild(document.createElement("a:avLst"));
}
private void appendGraphicEllipse(Element spPrEl, OpenXMLSize backgroundSize, OpenXMLGraphic element) {
/*
<a:xfrm>
<a:off x="2028190" y="581660" />
<a:ext cx="822960" cy="822960" />
</a:xfrm>
*/
Element aXfrmEl = (Element)spPrEl.appendChild(document.createElement("a:xfrm"));
Element aOffEl = (Element)aXfrmEl.appendChild(document.createElement("a:off"));
List<Integer> coords = element.getCoords();
int centerx = coords.get(0);
int centery = coords.get(1);
int radius = coords.get(2);
int topx = centerx - radius;
int lefty = centery - radius;
int topxEmu = OpenXMLUtils.convertPixelToEMUs(topx, DPI, backgroundSize.getResizeRatio());
int leftyEmu = OpenXMLUtils.convertPixelToEMUs(lefty, DPI, backgroundSize.getResizeRatio());
aOffEl.setAttribute("x", Integer.toString(topxEmu));
aOffEl.setAttribute("y", Integer.toString(leftyEmu));
Element aExtEl = (Element)aXfrmEl.appendChild(document.createElement("a:ext"));
int width = (radius * 2);
int widthEmu = OpenXMLUtils.convertPixelToEMUs(width, DPI, backgroundSize.getResizeRatio());
aExtEl.setAttribute("cx", Integer.toString(widthEmu));
aExtEl.setAttribute("cy", Integer.toString(widthEmu));
/*
<a:prstGeom prst="rect">
<a:avLst />
</a:prstGeom>
*/
Element prstGeomEl = (Element)spPrEl.appendChild(document.createElement("a:prstGeom"));
prstGeomEl.setAttribute("prst", "ellipse");
prstGeomEl.appendChild(document.createElement("a:avLst"));
}
private void appendAStyle(Element styleEl, String name, String idx, String schemeClrVal) {
Element aStyleEl = (Element)styleEl.appendChild(document.createElement(name));
aStyleEl.setAttribute("idx", idx);
Element schemeClrEl = (Element)aStyleEl.appendChild(document.createElement("a:schemeClr"));
schemeClrEl.setAttribute("val", schemeClrVal);
}
// <wp:lineTo x="0" y="21396" />
private void appendLineTo(Element parentEl, String x, String y) {
Element lineToEl = (Element)parentEl.appendChild(document.createElement("wp:lineTo"));
lineToEl.setAttribute("x", x);
lineToEl.setAttribute("y", y);
}
/*
<a:xfrm>
<a:off x="0" y="0" />
<a:ext cx="5756910" cy="2282190" />
<a:chOff x="0" y="0" />
<a:chExt cx="5756910" cy="2282190" />
</a:xfrm>
*/
private void appendAXfrm_ch(Element parentEl, String cx, String cy) {
Element aXfrmEl = (Element)parentEl.appendChild(document.createElement("a:xfrm"));
//<a:off x="0" y="0" />
Element aOffEl = (Element)aXfrmEl.appendChild(document.createElement("a:off"));
aOffEl.setAttribute("x", "0");
aOffEl.setAttribute("y", "0");
//<a:ext cx="5756910" cy="2282190" />
Element aExtEl = (Element)aXfrmEl.appendChild(document.createElement("a:ext"));
aExtEl.setAttribute("cx", cx);
aExtEl.setAttribute("cy", cy);
//<a:chOff x="0" y="0" />
Element chOffEl = (Element)aXfrmEl.appendChild(document.createElement("a:chOff"));
chOffEl.setAttribute("x", "0");
chOffEl.setAttribute("y", "0");
//<a:chExt cx="5756910" cy="2282190" />
Element chExtEl = (Element)aXfrmEl.appendChild(document.createElement("a:chExt"));
chExtEl.setAttribute("cx", cx);
chExtEl.setAttribute("cy", cy);
}
private String getUniqueFilename(File image) {
String filename = image.getName().toLowerCase();
int extensionIndex = filename.lastIndexOf('.');
if(extensionIndex > 0) {
String name = filename.substring(0, extensionIndex);
String extension = filename.substring(extensionIndex);
filename = StringHelper.transformDisplayNameToFileSystemName(name) + extension;
} else {
filename = StringHelper.transformDisplayNameToFileSystemName(filename);
}
if(imageFilenames.contains(filename)) {
for(int i=1; i<1000; i++) {
String nextFilename = i +"_" + filename;
if(!imageFilenames.contains(nextFilename)) {
filename = nextFilename;
imageFilenames.add(filename);
break;
}
}
} else {
imageFilenames.add(filename);
}
return filename;
}
/**
* Generate an identifier in the form of rId7. The number
* is unique.
*
* @return
*/
protected String generateId() {
return "rId" + (++currentId);
}
/**
* Generate an identifier which is a number. The number is
* unique.
*
* @return
*/
protected String generateSimpleId() {
return Integer.toString(++currentId);
}
private final Element createRootElement(Document doc) {
Element docEl = (Element)doc.appendChild(doc.createElement("w:document"));
docEl.setAttribute("xmlns:wpc","http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas");
docEl.setAttribute("xmlns:mo","http://schemas.microsoft.com/office/mac/office/2008/main");
docEl.setAttribute("xmlns:mc","http://schemas.openxmlformats.org/markup-compatibility/2006");
docEl.setAttribute("xmlns:mv","urn:schemas-microsoft-com:mac:vml");
docEl.setAttribute("xmlns:o","urn:schemas-microsoft-com:office:office");
docEl.setAttribute("xmlns:r","http://schemas.openxmlformats.org/officeDocument/2006/relationships");
docEl.setAttribute("xmlns:m","http://schemas.openxmlformats.org/officeDocument/2006/math");
docEl.setAttribute("xmlns:v","urn:schemas-microsoft-com:vml");
docEl.setAttribute("xmlns:wp14","http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing");
docEl.setAttribute("xmlns:wp","http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing");
docEl.setAttribute("xmlns:w10","urn:schemas-microsoft-com:office:word");
docEl.setAttribute("xmlns:w","http://schemas.openxmlformats.org/wordprocessingml/2006/main");
docEl.setAttribute("xmlns:w14","http://schemas.microsoft.com/office/word/2010/wordml");
docEl.setAttribute("xmlns:wpg","http://schemas.microsoft.com/office/word/2010/wordprocessingGroup");
docEl.setAttribute("xmlns:wpi","http://schemas.microsoft.com/office/word/2010/wordprocessingInk");
docEl.setAttribute("xmlns:wne","http://schemas.microsoft.com/office/word/2006/wordml");
docEl.setAttribute("xmlns:wps","http://schemas.microsoft.com/office/word/2010/wordprocessingShape");
docEl.setAttribute("mc:Ignorable","w14 wp14");
return docEl;
}
private final Element createBodyElement(Element rootEl, Document doc) {
return (Element)rootEl.appendChild(doc.createElement("w:body"));
}
public enum Style {
underline,
italic,
bold,
strike
}
public enum Unit {
dxa("dxa"),
pct("pct"),
auto("auto");
private final String unit;
private Unit(String unit) {
this.unit = unit;
}
public String unit() {
return unit;
}
}
public enum PredefinedStyle {
title("ooTitle", null),
heading1("ooHeading1", null),
heading2("ooHeading2", null),
subTitle("ooUntertitel", "ooUntertitelZeichen"),
subSubtleEmphasis("ooSubtleEmphasis", "ooSubtleEmphasisZeichen"),
quote("ooQuote", "ooQuoteZeichen");
private final String paragraphStyleId;
private final String runStyleId;
private PredefinedStyle(String paragraphStyleId, String runStyleId) {
this.paragraphStyleId = paragraphStyleId;
this.runStyleId = runStyleId;
}
public String paragraphStyleId() {
return paragraphStyleId;
}
public String runStyleId() {
return runStyleId;
}
}
public static class HeaderReference {
private final String id;
private final String header;
public HeaderReference(String id, String header) {
this.id = id;
this.header = header;
}
public String getId() {
return id;
}
public String getFilename() {
return "header" + id + ".xml";
}
public String getHeader() {
return header;
}
}
public static class Spacing {
private final int before;
private final int after;
public Spacing(int before, int after) {
this.before = before;
this.after = after;
}
public int getBefore() {
return before;
}
public int getAfter() {
return after;
}
}
public static class Border {
private final int space;
private final int size;
private final String val;
private final String color;
public Border(int space, int size, String color) {
this.space = space;
this.size = size;
this.color = color;
val = "single";
}
public Border(Border border, String val) {
this.space = border.space;
this.size = border.size;
this.color = border.color;
this.val = val;
}
public int getSpace() {
return space;
}
public int getSize() {
return size;
}
public String getColor() {
return color;
}
public String getVal() {
return val;
}
public boolean same(Border border) {
return color.equals(border.color) && size == border.size && space == border.space;
}
public Border cloneAndStack(Border border) {
String stackedVal = border.val;
switch(border.val) {
case "single": stackedVal = "double"; break;
case "double": stackedVal = "triple"; break;
}
return new Border(this, stackedVal);
}
}
public static class Indent {
private final int left;
public Indent(int left) {
this.left = left;
}
public int getLeft() {
return left;
}
}
public static class ListParagraph {
private final int abstractNumId;
private final int numId;
public ListParagraph(int abstractNumId, int numId) {
this.abstractNumId = abstractNumId;
this.numId = numId;
}
public int getAbstractNumId() {
return abstractNumId;
}
public int getNumId() {
return numId;
}
}
}