All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.vladsch.flexmark.docx.converter.util.DocxHelper Maven / Gradle / Ivy

There is a newer version: 0.64.8
Show newest version
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