com.vladsch.flexmark.docx.converter.util.DocxHelper Maven / Gradle / Ivy
Show all versions of flexmark-docx-converter Show documentation
package com.vladsch.flexmark.docx.converter.util;
import com.vladsch.flexmark.docx.converter.DocxRendererOptions;
import org.docx4j.model.PropertyResolver;
import org.docx4j.model.styles.StyleUtil;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.openpackaging.parts.WordprocessingML.NumberingDefinitionsPart;
import org.docx4j.wml.*;
import javax.xml.datatype.XMLGregorianCalendar;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import static java.math.BigInteger.ZERO;
public class DocxHelper {
protected final WordprocessingMLPackage myPackage;
protected final MainDocumentPart myDocumentPart;
protected final ObjectFactory myFactory;
protected PropertyResolver myResolver;
protected final HashMap myNumPrColorMap;
protected final DocxRendererOptions myOptions;
public DocxHelper(WordprocessingMLPackage mlPackage, ObjectFactory factory, DocxRendererOptions options) {
myPackage = mlPackage;
myFactory = factory;
myOptions = options.isResolved ? options : new DocxRendererOptions(options, mlPackage);
myDocumentPart = mlPackage.getMainDocumentPart();
myNumPrColorMap = new HashMap<>();
}
public Style getStyle(String styleId) {
return myDocumentPart.getStyleDefinitionsPart().getStyleById(styleId);
}
public PropertyResolver getResolver() {
if (myResolver == null) {
try {
myResolver = new PropertyResolver(myPackage);
} catch (Docx4JException e) {
e.printStackTrace();
}
}
return myResolver;
}
public static BigInteger safeBigInt(BigInteger value) {
return value == null ? ZERO : value;
}
public static BigInteger safeBigInt(BigInteger value, long nullValue) {
return value == null ? BigInteger.valueOf(nullValue) : value;
}
public ObjectFactory getFactory() {
return myFactory;
}
public enum CombineBigInt {
NONE, // pass through
ADD, // add copy and orig
MAX, // max of copy and orig
MIN, // min of copy and orig
ADD_OTHER,
MAX_OTHER,
MIN_OTHER,
RANGE,
;
BigInteger combine(BigInteger orig, BigInteger copy, BigInteger other) {
if (this == ADD) {
return copy == null && orig == null ? null : safeBigInt(copy).add(safeBigInt(orig));
} else if (this == MAX) {
return copy == null && orig == null ? null : safeBigInt(copy).max(safeBigInt(orig));
} else if (this == MIN) {
return copy == null && orig == null ? null : safeBigInt(copy).max(safeBigInt(orig));
} else if (this == ADD_OTHER) {
return copy == null && orig == null && other == null ? null : safeBigInt(copy).add(safeBigInt(orig).add(safeBigInt(other)));
} else if (this == MAX_OTHER) {
return copy == null && orig == null && other == null ? null : safeBigInt(copy).max(safeBigInt(orig).max(safeBigInt(other)));
} else if (this == MIN_OTHER) {
return copy == null && orig == null && other == null ? null : safeBigInt(copy).min(safeBigInt(orig).min(safeBigInt(other)));
} else if (this == RANGE) {
return copy == null && orig == null && other == null ? null : safeBigInt(copy).max(safeBigInt(orig).min(safeBigInt(other)));
} else {
return copy;
}
}
}
public void combine(PPrBase.Ind child, PPrBase.Ind parent, CombineBigInt left, CombineBigInt right) {
if (parent != null) {
child.setLeft(left.combine(child.getLeft(), parent.getLeft(), parent.getHanging()));
child.setRight(right.combine(child.getRight(), parent.getRight(), parent.getHanging()));
}
}
public void inheritInd(PPrBase child, PPrBase parent, CombineBigInt left, CombineBigInt right) {
combine(child.getInd(), parent.getInd(), left, right);
}
public boolean has(Object value) {
return value != null;
}
public BigInteger safeIndLeft(PPrBase pPrBase) {
return pPrBase == null ? ZERO : safeIndLeft(pPrBase.getInd());
}
public BigInteger safeIndLeft(PPrBase pPrBase, long nullValue) {
return pPrBase == null ? BigInteger.valueOf(nullValue) : safeIndLeft(pPrBase.getInd());
}
public BigInteger safeIndLeft(PPrBase.Ind ind) {
return ind == null || ind.getLeft() == null ? ZERO : ind.getLeft();
}
public BigInteger safeIndLeft(PPrBase.Ind ind, long nullValue) {
return ind == null || ind.getLeft() == null ? BigInteger.valueOf(nullValue) : ind.getLeft();
}
public BigInteger safeIndRight(PPrBase pPrBase) {
return pPrBase == null ? ZERO : safeIndRight(pPrBase.getInd());
}
public BigInteger safeIndRight(PPrBase pPrBase, long nullValue) {
return pPrBase == null ? BigInteger.valueOf(nullValue) : safeIndRight(pPrBase.getInd());
}
public BigInteger safeIndRight(PPrBase.Ind ind) {
return ind == null || ind.getRight() == null ? ZERO : ind.getRight();
}
public BigInteger safeIndRight(PPrBase.Ind ind, long nullValue) {
return ind == null || ind.getRight() == null ? BigInteger.valueOf(nullValue) : ind.getRight();
}
public BigInteger safeIndHanging(PPrBase pPrBase) {
return pPrBase == null ? ZERO : safeIndHanging(pPrBase.getInd());
}
public BigInteger safeIndHanging(PPrBase pPrBase, long nullValue) {
return pPrBase == null ? BigInteger.valueOf(nullValue) : safeIndHanging(pPrBase.getInd());
}
public BigInteger safeIndHanging(PPrBase.Ind ind) {
return ind == null || ind.getHanging() == null ? ZERO : ind.getHanging();
}
public BigInteger safeIndHanging(PPrBase.Ind ind, long nullValue) {
return ind == null || ind.getHanging() == null ? BigInteger.valueOf(nullValue) : ind.getHanging();
}
/*
Spacing access
*/
public BigInteger safeSpacingBefore(PPr pPr) {
return pPr == null ? ZERO : safeSpacingBefore(pPr.getSpacing());
}
public BigInteger safeSpacingBefore(PPr pPr, long nullValue) {
return pPr == null ? BigInteger.valueOf(nullValue) : safeSpacingBefore(pPr.getSpacing());
}
public BigInteger safeSpacingBefore(PPrBase.Spacing spacing) {
return spacing == null || spacing.getBefore() == null ? ZERO : spacing.getBefore();
}
public BigInteger safeSpacingBefore(PPrBase.Spacing spacing, long nullValue) {
return spacing == null || spacing.getBefore() == null ? BigInteger.valueOf(nullValue) : spacing.getBefore();
}
public BigInteger safeSpacingAfter(PPr pPr) {
return pPr == null ? ZERO : safeSpacingAfter(pPr.getSpacing());
}
public BigInteger safeSpacingAfter(PPr pPr, long nullValue) {
return pPr == null ? BigInteger.valueOf(nullValue) : safeSpacingAfter(pPr.getSpacing());
}
public BigInteger safeSpacingAfter(PPrBase.Spacing spacing) {
return spacing == null || spacing.getAfter() == null ? ZERO : spacing.getAfter();
}
public BigInteger safeSpacingAfter(PPrBase.Spacing spacing, long nullValue) {
return spacing == null || spacing.getAfter() == null ? BigInteger.valueOf(nullValue) : spacing.getAfter();
}
public BigInteger safeSpacingLine(PPr pPr) {
return pPr == null ? ZERO : safeSpacingLine(pPr.getSpacing());
}
public BigInteger safeSpacingLine(PPr pPr, long nullValue) {
return pPr == null ? BigInteger.valueOf(nullValue) : safeSpacingLine(pPr.getSpacing());
}
public BigInteger safeSpacingLine(PPrBase.Spacing spacing) {
return spacing == null || spacing.getLine() == null ? ZERO : spacing.getLine();
}
public BigInteger safeSpacingLine(PPrBase.Spacing spacing, long nullValue) {
return spacing == null || spacing.getLine() == null ? BigInteger.valueOf(nullValue) : spacing.getLine();
}
public PPrBase.PBdr ensurePBdr(PPrBase pPrBase) {
if (pPrBase.getPBdr() == null) {
PPrBase.PBdr pBdr = myFactory.createPPrBasePBdr();
pPrBase.setPBdr(pBdr);
}
return pPrBase.getPBdr();
}
public PPrBase.Ind ensureInd(PPrBase pPrBase) {
if (pPrBase.getInd() == null) {
PPrBase.Ind ind = myFactory.createPPrBaseInd();
pPrBase.setInd(ind);
}
return pPrBase.getInd();
}
public PPrBase.Spacing ensureSpacing(PPrBase pPrBase) {
if (pPrBase.getSpacing() == null) {
PPrBase.Spacing spacing = myFactory.createPPrBaseSpacing();
pPrBase.setSpacing(spacing);
}
return pPrBase.getSpacing();
}
/**
* Try to keep the border of the parent and offset the left border by the difference in indentation between the two so the border stays
* aligned with the parent. Max offset for border is 31pt or 620tw
*
* if the child has its own left border then nothing is done
*
* other borders are not affected
*
* Must be called after the child indent is set
*
* @param child child ppr
* @param parent parent ppr
*/
public void inheritPBdr(PPr child, PPr parent) {
parent = getResolver().getEffectivePPr(parent);
PPr styledChild = getResolver().getEffectivePPr(child);
if (has(parent.getPBdr()) && (!has(styledChild.getPBdr()) || !has(styledChild.getPBdr().getLeft()) && has(parent.getPBdr().getLeft()))) {
PPrBase.Ind cInd = getCopy(styledChild.getInd(), true);
PPrBase.Ind pInd = getCopy(parent.getInd(), false);
CTBorder leftBorder = getCopy(parent.getPBdr().getLeft(), true);
PPrBase.NumPr numPr = styledChild.getNumPr();
if (numPr != null) {
// need to check that too, it may have settings we don't have
NumberingDefinitionsPart ndp = myDocumentPart.getNumberingDefinitionsPart();
if (ndp != null) {
PPrBase.Ind ndpInd = ndp.getInd(numPr);
if (ndpInd != null) {
if (cInd.getLeft() == null && ndpInd.getLeft() != null) {
cInd.setLeft(ndpInd.getLeft());
}
if (cInd.getRight() == null && ndpInd.getRight() != null) {
cInd.setRight(ndpInd.getRight());
}
if (cInd.getHanging() == null && ndpInd.getHanging() != null) {
cInd.setHanging(ndpInd.getHanging());
}
}
}
}
// now add difference between the left indents
BigInteger indentDiff = safeIndLeft(cInd).subtract(safeIndHanging(cInd)).subtract(safeIndLeft(pInd));
if (indentDiff.compareTo(ZERO) > 0) {
BigInteger[] divideAndRemainder = indentDiff.divideAndRemainder(BigInteger.valueOf(20));
// convert to points and add to space
BigInteger space = safeBigInt(leftBorder.getSpace()).add(divideAndRemainder[0]).min(BigInteger.valueOf(31));
leftBorder.setSpace(space);
int remainder = divideAndRemainder[1].intValue();
if (remainder > 0) {
// need to adjust indent since we could not adjust space exactly or it will appear off to the eye
ensureInd(child);
child.getInd().setLeft(ZERO.max(safeIndLeft(cInd).subtract(BigInteger.valueOf(remainder))));
}
}
ensurePBdr(child).setLeft(leftBorder);
}
}
/**
* @param child PPr of child element to inherit indent
* @param parent PPr of parent element, must be explicit or effective ppr
*/
public void inheritInd(PPr child, PPr parent) {
if (parent != null && has(parent.getInd())) {
PPr styledChild = getResolver().getEffectivePPr(child);
PPrBase.Ind cInd = getCopy(styledChild == null ? null : styledChild.getInd(), true);
PPrBase.Ind pInd = parent.getInd();
PPrBase.NumPr numPr = styledChild == null ? null : styledChild.getNumPr();
if (numPr != null) {
// need to check that too, it may have settings we don't have
NumberingDefinitionsPart ndp = myDocumentPart.getNumberingDefinitionsPart();
if (ndp != null) {
PPrBase.Ind ndpInd = ndp.getInd(numPr);
if (ndpInd != null) {
if (cInd.getLeft() == null && ndpInd.getLeft() != null) {
cInd.setLeft(ndpInd.getLeft());
}
if (cInd.getRight() == null && ndpInd.getRight() != null) {
cInd.setRight(ndpInd.getRight());
}
if (cInd.getHanging() == null && ndpInd.getHanging() != null) {
cInd.setHanging(ndpInd.getHanging());
}
}
}
}
combine(cInd, pInd, CombineBigInt.ADD, CombineBigInt.NONE);
cInd = keepDiff(cInd, styledChild == null ? null : styledChild.getInd());
child.setInd(cInd);
}
}
// get a num pr for a list of given color (other than default)
//
public BigInteger getNumPrFor(BigInteger baseNumID, Color color) {
RPr rPr = getResolver().getEffectiveRPr(myOptions.PREFORMATTED_TEXT_STYLE);
if (rPr != null && rPr.getColor() != null) {
if (keepDiff(rPr.getColor(), color) == null) {
return baseNumID;
}
}
String colorID = String.format("%s:%s", baseNumID.toString(), color.getVal());
BigInteger numID = myNumPrColorMap.get(colorID);
if (numID != null) {
return numID;
}
// we create a copy of the baseNubPr and add the changed color property
NumberingDefinitionsPart ndp = myDocumentPart.getNumberingDefinitionsPart();
try {
Numbering numbering = ndp.getContents();
List num = numbering.getNum();
List abstractNumList = numbering.getAbstractNum();
for (Numbering.AbstractNum abstractNum : abstractNumList) {
if (abstractNum.getAbstractNumId().compareTo(baseNumID) == 0) {
// we have our list to copy
// TODO: create a copy and set the color it the list's rpr.
}
}
return null;
} catch (Docx4JException e) {
e.printStackTrace();
}
return null;
}
public PPr getExplicitPPr(PPr pPr) {
return getResolver().getEffectivePPr(pPr);
}
public RPr getExplicitRPr(RPrAbstract rPr, PPr pPr) {
RPr copyRPr = myFactory.createRPr();
setRPr(copyRPr, rPr, false);
return getResolver().getEffectiveRPr(copyRPr, pPr);
}
/**
* Only gets character style properties and basedOn style tree
*
* does not use doc defaults or ppr properties.
*
* Use this for rPr cleaning
*
* @param rPr rpr to resolve fully
* @return fully resolved rpr properties
*/
public RPr getExplicitRPr(RPr rPr) {
RPr styledRPr = myFactory.createRPr();
ArrayList