hamid
7/10/2019 - 11:05 AM

Creating Docx and Convert Docx to PDF

import com.documents4j.api.DocumentType;
import com.documents4j.api.IConverter;
import com.documents4j.job.LocalConverter;
import org.apache.commons.lang.text.StrSubstitutor;
import org.docx4j.XmlUtils;
import org.docx4j.model.datastorage.migration.VariablePrepare;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.utils.SingleTraversalUtilVisitorCallback;
import org.docx4j.wml.ContentAccessor;
import org.docx4j.wml.Tbl;
import org.docx4j.wml.Tr;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * docx4j utilization.
 * <p> for creating template-ready docx files and converting docx to pdf. </p>
 *
 * @author Hamidreza Rashid.
 * @version 0.5
 */

public class DocxUtil {

    private Map<String, String> variableMap;

    private final String TEMPLATE_FILE;

    private ByteArrayOutputStream file;

    private WordprocessingMLPackage wordMLPackage;

    private MainDocumentPart documentPart;

    private List<DocxTable> docxTables;

    private DocxUtil(DocxBuilder builder) throws DocxUtilException {

        this.TEMPLATE_FILE = builder.templatePath;
        this.variableMap = builder.variableMap;
        this.docxTables = builder.docxTables;

        try {
            loadWordMLPackage();
        } catch (Docx4JException e) {
            throw new DocxUtilException("Template File Not Found. " + e.getMessage());
        }
    }

    private void loadWordMLPackage() throws Docx4JException {
        wordMLPackage = WordprocessingMLPackage.load(new File(DocxUtil.class.getResource(TEMPLATE_FILE).getFile()));
        documentPart = wordMLPackage.getMainDocumentPart();
    }

    public byte[] getDocxFile() {
        return file.toByteArray();
    }

    public byte[] getPDF() {
        return createPDF(file.toByteArray()).toByteArray();
    }

    private void create() throws DocxUtilException {
        try {
            VariablePrepare.prepare(wordMLPackage);
            for (DocxTable docxTable : docxTables) {
                Table table = new Table(docxTable);
                table.createTable();
            }
            documentPart.variableReplace(variableMap);
            file = new ByteArrayOutputStream();
            wordMLPackage.save(file);
        } catch (Exception e) {
            throw new DocxUtilException("Cannot create file. " + e.getMessage());
        }
    }

    private Object prepareVariables(Object body) {
        new SingleTraversalUtilVisitorCallback(new VariablePrepare.TraversalUtilParagraphVisitor()).walkJAXBElements(body);
        return body;
    }

    public static class DocxBuilder {

        private Map<String, String> variableMap;
        private String templatePath;
        private List<DocxTable> docxTables;

        DocxBuilder(String templatePath) {
            this.templatePath = templatePath;
            docxTables = new ArrayList<>();
        }

        public DocxBuilder setDocxTable(DocxTable docxTable) {
            this.docxTables.add(docxTable);
            return this;
        }

        public DocxBuilder setVariableMap(Map<String, String> variableMap) {
            this.variableMap = variableMap;
            return this;
        }

        public DocxUtil build() throws DocxUtilException {
            DocxUtil docxUtil;
            try {
                docxUtil = new DocxUtil(this);
                docxUtil.create();
                return docxUtil;
            } catch (DocxUtilException e) {
                throw new DocxUtilException(e.getMessage());
            }
        }
    }

    public static class DocxUtilException extends Exception {

        DocxUtilException(String message) {
            super(message);
        }

    }

    private class Table {

        private final Tbl table;
        private final String headerRowAsString;
        private final String dataRow;
        private final DocxTable docxTable;

        public Table(DocxTable docxTable) {

            this.docxTable = docxTable;

            this.table = XmlUtils.deepCopy(((Tbl) getElements(documentPart, Tbl.class).get(0)));

            List<Object> rows = getAllElementFromObject(this.table, Tr.class);

            headerRowAsString = XmlUtils.marshaltoString(prepareVariables(rows.get(0)));

            dataRow = XmlUtils.marshaltoString(prepareVariables(rows.get(rows.size() - 1)));

        }

        private void createTable() throws Exception {
            removeTableByIndex(wordMLPackage, docxTable.getTableComponentIndex());
            documentPart.getContent().add(fillWithData());
        }


        Tbl fillWithData() throws Exception {
            Tbl localTable = XmlUtils.deepCopy(table);
            localTable.getContent().clear();
            localTable.getContent().add(getFilledHeaderRow());
            for (String[] data : docxTable.getTableValue()) {
                localTable.getContent().add(getFilledDataRow(data));
            }
            return localTable;
        }


        private Object getFilledDataRow(String[] data) throws JAXBException {
            int index = 0;
            Map<String, String> variableMap = new HashMap<>();
            for (Map.Entry<String, String> stringStringEntry : docxTable.getHeaderTitles().entrySet()) {
                if (docxTable.getTableRowIndexVariableName() != null && stringStringEntry.getKey().equals(docxTable.getTableRowIndexVariableName())) {
                    variableMap.put(stringStringEntry.getKey(), String.valueOf(index + 1));
                } else {
                    variableMap.put(stringStringEntry.getKey(), data[index++]);
                }
            }
            StrSubstitutor strSubstitutor = new StrSubstitutor(variableMap);
            return XmlUtils.unmarshalString(strSubstitutor.replace(dataRow));
        }


        private Object getFilledHeaderRow() throws JAXBException {
            StrSubstitutor strSubstitutor = new StrSubstitutor(docxTable.getHeaderTitles());
            return XmlUtils.unmarshalString(strSubstitutor.replace(headerRowAsString));
        }


        private boolean removeTableByIndex(WordprocessingMLPackage wordMLPackage, int index) {
            boolean flag = false;
            if (index < 0) {
                return flag;
            }

            List<Object> objList = wordMLPackage.getMainDocumentPart().getContent();

            if (objList == null) {
                return flag;
            }

            int k = -1;
            for (int i = 0, len = objList.size(); i < len; i++) {
                Object obj = XmlUtils.unwrap(objList.get(i));
                if (obj instanceof Tbl) {
                    k++;
                    if (k == index) {
                        wordMLPackage.getMainDocumentPart().getContent().remove(i);
                        flag = true;
                        break;
                    }
                }
            }
            return flag;
        }


        /**
         * get elements from docx file based on type
         *
         * @param documentPart
         * @param type         class of type
         * @return list of founded elements
         */
        private List<Object> getElements(MainDocumentPart documentPart, Class<?> type) {
            return getAllElementFromObject(documentPart, type);
        }

        private List<Object> getAllElementFromObject(Object obj, Class<?> toSearch) {
            List<Object> result = new ArrayList<>();
            if (obj instanceof JAXBElement) obj = ((JAXBElement<?>) obj).getValue();
            if (obj.getClass().equals(toSearch)) {
                result.add(obj);
            } else if (obj instanceof ContentAccessor) {
                List<?> children = ((ContentAccessor) obj).getContent();
                for (Object child : children) {
                    result.addAll(getAllElementFromObject(child, toSearch));
                }
            }
            return result;
        }
    }

    public static ByteArrayOutputStream convertToPDF(File docxFile){
        return createPDF(docxFile);
    }

    private static ByteArrayOutputStream createPDF(File docxFile){
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        IConverter converter = LocalConverter.builder().build();
        converter.convert(docxFile).as(DocumentType.DOCX).to(outputStream).as(DocumentType.PDF).execute();
        return outputStream;
    }

    private static ByteArrayOutputStream createPDF(byte[] docxFile){
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        IConverter converter = LocalConverter.builder().build();
        converter.convert(new ByteArrayInputStream(docxFile)).as(DocumentType.DOCX).to(outputStream).as(DocumentType.PDF).execute();
        return outputStream;
    }
     <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-text</artifactId>
            <version>1.7</version>
        </dependency>
        
        <dependency>
            <groupId>org.docx4j</groupId>
            <artifactId>docx4j-JAXB-ReferenceImpl</artifactId>
            <version>8.1.0</version>
        </dependency>

        <dependency>
            <groupId>org.docx4j</groupId>
            <artifactId>docx4j-JAXB-Internal</artifactId>
            <version>8.0.0</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.docx4j</groupId>
            <artifactId>docx4j-core</artifactId>
            <version>8.0.0</version>
        </dependency>

        <!-- docoument4j -->
        <dependency>
            <groupId>com.documents4j</groupId>
            <artifactId>documents4j-local</artifactId>
            <version>1.0.3</version>
        </dependency>

        <dependency>
            <groupId>com.documents4j</groupId>
            <artifactId>documents4j-transformer-msoffice-word</artifactId>
            <version>1.0.3</version>
        </dependency>
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.util.List;
import java.util.Map;

@Data
@AllArgsConstructor
@EqualsAndHashCode
public class DocxTable implements Serializable {

    private int tableComponentIndex;

    private String tableRowIndexVariableName;

    private List<String[]> tableValue;

    private Map<String,String> headerTitles;
}