docx4j.TextSubstitution.java Source code

Java tutorial

Introduction

Here is the source code for docx4j.TextSubstitution.java

Source

/*
 *
docx4j and docx4j-ws are licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
    
You may obtain a copy of the License at
    
    http://www.apache.org/licenses/LICENSE-2.0
    
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
    
 */

package docx4j;

import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.WebParam;

import com.lowagie.text.DocumentException;
import com.lowagie.text.Image;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfStamper;
import org.docx4j.model.structure.HeaderFooterPolicy;
import org.docx4j.model.structure.SectionWrapper;
import org.docx4j.openpackaging.parts.WordprocessingML.FooterPart;
import org.docx4j.openpackaging.parts.WordprocessingML.HeaderPart;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import java.io.*;
import java.util.List;
import java.util.Properties;
import org.apache.commons.io.*;

import org.docx4j.fonts.IdentityPlusMapper;
import org.docx4j.model.datastorage.BindingHandler;
import org.docx4j.model.datastorage.CustomXmlDataStorage;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.CustomXmlDataStoragePart;

/**
 * This is a WORK IN PROGRESS. I have just recently started this project
 * so not everything is working yet. What is this project? This project uses
 * sample code from the docx4j project and uses it in another context, as a web
 * service that takes a docx file and an XML file and replaces the customXML
 * data part in the docx file with the provided XML file.
 */

@WebService
public final class TextSubstitution extends ws.PdfProcessor {

    private String lastError;

    @WebMethod
    public SubstitutionResult getPdfFileFromName(
            @WebParam(name = "fileName", partName = "fileName") String fileName) {
        SubstitutionResult result = new SubstitutionResult();
        String filePath = this.getFolderPath() + fileName;

        try {

            // get the file as a base64 string
            BASE64Encoder encoder = new BASE64Encoder();
            FileInputStream fileStream = new FileInputStream(filePath);

            result.pdf = encoder.encode(IOUtils.toByteArray(fileStream));
            result.error = "";

        } catch (FileNotFoundException e) {
            result.error = "lost";
        } catch (IOException e) {
            result.error = "unreadable";
        }

        return result;
    }

    @WebMethod
    public SubstitutionResult getSubstitutionFile(
            @WebParam(name = "templateFile", partName = "templateFile") String templateFile,
            @WebParam(name = "xmlData", partName = "xmlData") String xmlData,
            @WebParam(name = "addWatermark", partName = "addWatermark") int addWatermark) {
        SubstitutionResult result = new SubstitutionResult();
        String originalFileName;
        String watermarkedFileName;
        String error;
        int pages = 0;

        try {

            PreparationResult inputs = this.getInputStreamsFromWebServiceValues(templateFile, xmlData);
            while (this.setFilePath(this.getFolderPath()) == false) {
                System.out.println("UUID collision for file name on " + this.fileName);
            }

            if (!this.substituteUsingDocx4j(inputs.template, inputs.xmlData)) {
                error = this.lastError;

            } else {
                originalFileName = this.fileName;
                watermarkedFileName = "wm" + originalFileName;
                pages = this.getPageCount();

                /**
                 * At this point result.pdf points to the PDF file (by name) and is not an actual PDF file.
                 * We need to make it a base64 encoded file. We either make a watermarked version or use the original.
                 * In either case result.pdf will change from a string that is the file name to a string that is a
                 * base64 encoded PDF that we need to return to the client. Since we read the PDF file from disk
                 * and encoded it to return to the client. We also need to remove the files that are still on disk.
                 */

                // if a watermark version is needed
                if (addWatermark > 0) {
                    // create it
                    error = this.addWaterMark();
                    if (error == null) {
                        // set the result to the watermarked version of the file
                        result = getPdfFileFromName(watermarkedFileName);
                        error = result.error;
                        // delete the original file because the new with the watermark file is being passed as a result
                        error += this.deleteFileFromName(originalFileName);
                        // delete the watermark version as well, after we set the result
                        error += this.deleteFileFromName(watermarkedFileName);
                    }

                    // if the original version is needed
                } else {
                    result = this.getPdfFileFromName(originalFileName);
                    error = result.error;
                }
            }

            // catch-all for any processes above which may have set "error"
            if (error.length() > 0) {
                result.error = error;
                result.pages = 0;
                result.pdf = "";
            } else {
                result.pages = pages;
                result.error = "";
            }

            // catch for exceptional problems
        } catch (Exception e) {
            result.error = e.getMessage();
            result.pages = 0;
            result.pdf = "";
        }

        return result;
    }

    //region Privates

    private String deleteFileFromName(String fileName) {
        String result = "";
        String filePath = this.getFolderPath() + fileName;

        try {
            if (!(new File(filePath).delete())) {
                result = "Could not delete " + filePath;
            }
        } catch (Exception e) {
            result = e.getMessage();
        }
        return result;
    }

    private String getFolderPath() {
        String result;
        try {
            Properties props = new Properties();
            props.load(getClass().getResourceAsStream("/resources/config.properties"));

            result = (props.getProperty("folderpath"));

            if (result == null) {
                result = "/var/opt/docx4j/docs/";
            }

        } catch (IOException e) {
            result = "/var/opt/docx4j/docs/";
        }

        return result;
    }

    private PreparationResult getInputStreamsFromWebServiceValues(String templateFile, String xmlData)
            throws IOException {

        PreparationResult result = new PreparationResult();
        BASE64Decoder decoder = new BASE64Decoder();
        xmlData = xmlData.trim();

        result.template = new ByteArrayInputStream(decoder.decodeBuffer(templateFile));
        xmlData = xmlData.substring(9, xmlData.length() - 3); // strip CDATA tags from start and end
        result.xmlData = new ByteArrayInputStream(xmlData.getBytes());

        return result;
    }

    private boolean substituteUsingDocx4j(InputStream docxFile, InputStream xmlData) {
        boolean result = false;
        HeaderPart header = null;
        FooterPart footer = null;

        try {
            WordprocessingMLPackage wordMLPackage;
            wordMLPackage = WordprocessingMLPackage.load(docxFile);
            if (wordMLPackage.getCustomXmlDataStorageParts().isEmpty()) {
                this.lastError = "No custom XML data storage parts were found";
                return false;
            }
            // grab the first custom XML data storage part
            CustomXmlDataStoragePart customXmlDataStoragePart = (CustomXmlDataStoragePart) wordMLPackage
                    .getCustomXmlDataStorageParts().values().toArray()[0];
            // get a reference to the data in it
            CustomXmlDataStorage customXmlDataStorage = customXmlDataStoragePart.getData();
            // replace it's data with the new XML data
            customXmlDataStorage.setDocument(xmlData);
            // get references to the section wrappers to get references to the header and footer
            List<SectionWrapper> sectionWrappers = wordMLPackage.getDocumentModel().getSections();
            // get references to the header and footer
            for (SectionWrapper sw : sectionWrappers) {
                HeaderFooterPolicy hfp = sw.getHeaderFooterPolicy();
                if (hfp.getDefaultHeader() != null) {
                    header = hfp.getDefaultHeader();
                    footer = hfp.getDefaultFooter();
                }
            }
            // bindings must be applied before the DOCX can be exported to HTML / PDF
            BindingHandler.applyBindings(wordMLPackage.getMainDocumentPart());
            if (header != null) {
                BindingHandler.applyBindings(header);
            }
            if (footer != null) {
                BindingHandler.applyBindings(footer);
            }
            // save as PDF
            wordMLPackage.setFontMapper(new IdentityPlusMapper());
            org.docx4j.convert.out.pdf.PdfConversion conversion = new org.docx4j.convert.out.pdf.viaXSLFO.Conversion(
                    wordMLPackage);
            OutputStream stream = new FileOutputStream(this.filePath);
            org.docx4j.convert.out.pdf.viaXSLFO.PdfSettings pdfSettings = new org.docx4j.convert.out.pdf.viaXSLFO.PdfSettings();
            conversion.output(stream, pdfSettings);
            // save as new DOCX
            //wordMLPackage.save(new java.io.File(folderPath + "/document.docx"));

            result = true;
        } catch (Exception exception) {
            this.lastError = exception.getMessage();
            if (exception.getCause() != null) {
                this.lastError += "; " + exception.getCause().getMessage();
            }
        }
        return result;
    }

    private int getPageCount() throws IOException {
        int result;
        // get the number of pages in the file
        PdfReader reader = new PdfReader(this.filePath);
        result = reader.getNumberOfPages();
        reader.close();
        return result;
    }

    private String addWaterMark() throws IOException, DocumentException {
        String result = null;
        String newFileName = this.getFolderPath() + "wm" + this.fileName;

        try {
            // try to delete the file if it already exists so that we refresh the watermark
            // if it fails then at least we have the existing version which can be deleted manually
            File target = new File(newFileName);
            if (target.exists()) {
                if (!target.delete()) {
                    return "existing";
                }
            }

            // add the watermark
            PdfReader reader = new PdfReader(this.filePath);
            PdfStamper stamp = new PdfStamper(reader, new FileOutputStream(newFileName));
            Image watermark_image = Image.getInstance(this.getFolderPath() + "doc-watermark.png");
            watermark_image.setAbsolutePosition(5, 5);
            int i = 0;
            PdfContentByte add_watermark;

            // loop the pages adding the watermark
            while (i < reader.getNumberOfPages()) {
                i++;
                add_watermark = stamp.getUnderContent(i);
                add_watermark.addImage(watermark_image);
            }
            stamp.close();
            reader.close();
        } catch (Exception e) {
            result = e.getMessage();
        }
        return result;
    }

    //endregion
}