001/*
002 * (C) Copyright 2016-2018 Nuxeo (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Thibaud Arguillere
018 *     Miguel Nixo
019 */
020package org.nuxeo.ecm.platform.pdf;
021
022import java.io.File;
023import java.io.IOException;
024
025import org.apache.commons.lang3.StringUtils;
026import org.apache.pdfbox.exceptions.COSVisitorException;
027import org.apache.pdfbox.exceptions.CryptographyException;
028import org.apache.pdfbox.pdmodel.PDDocument;
029import org.apache.pdfbox.pdmodel.PDDocumentInformation;
030import org.apache.pdfbox.pdmodel.encryption.BadSecurityHandlerException;
031import org.apache.pdfbox.pdmodel.encryption.StandardDecryptionMaterial;
032import org.nuxeo.ecm.core.api.Blob;
033import org.nuxeo.ecm.core.api.Blobs;
034import org.nuxeo.ecm.core.api.NuxeoException;
035import org.nuxeo.ecm.core.api.impl.blob.FileBlob;
036
037/**
038 * Grouping miscellaneous utilities in this class.
039 *
040 * @since 8.10
041 */
042public class PDFUtils {
043
044    public static final String DEFAULT_BLOB_XPATH = "file:content";
045
046    public static int[] hex255ToRGB(String inHex) {
047        int[] result = { 0, 0, 0 };
048        if (inHex != null) {
049            inHex = inHex.toLowerCase().replace("#", "").replace("0x", "");
050            if (inHex.length() >= 6) {
051                for (int i = 0; i < 3; i++) {
052                    result[i] = Integer.parseInt(inHex.substring(i * 2, i * 2 + 2), 16);
053                }
054            }
055        }
056        return result;
057    }
058
059    /**
060     * This is just a shortcut. We often load() and openProtection().
061     *
062     * @param inBlob Input Blob.
063     * @param inPwd Input password.
064     * @throws NuxeoException
065     */
066    public static PDDocument load(Blob inBlob, String inPwd) throws NuxeoException {
067        PDDocument pdfDoc;
068        try {
069            pdfDoc = PDDocument.load(inBlob.getStream());
070            if (pdfDoc.isEncrypted()) {
071                pdfDoc.openProtection(new StandardDecryptionMaterial(inPwd));
072            }
073        } catch (IOException e) {
074            throw new NuxeoException("Failed to load the PDF", e);
075        } catch (BadSecurityHandlerException | CryptographyException e) {
076            throw new NuxeoException("Failed to decrypt the PDF", e);
077        }
078        return pdfDoc;
079    }
080
081    /**
082     * Create a temporary PDF file and return a FileBlob built from this file.
083     * <p>
084     * Mainly a utility used just by this plug-in actually.
085     *
086     * @param inPdfDoc Input PDF document.
087     * @return FileBlob
088     * @throws IOException
089     * @throws COSVisitorException
090     */
091    public static FileBlob saveInTempFile(PDDocument inPdfDoc) throws IOException, COSVisitorException {
092        return saveInTempFile(inPdfDoc, null);
093    }
094
095    public static FileBlob saveInTempFile(PDDocument inPdfDoc, String inFileName) throws IOException,
096        COSVisitorException {
097        Blob result = Blobs.createBlobWithExtension(".pdf");
098        File resultFile = result.getFile();
099        inPdfDoc.save(result.getFile());
100        result.setMimeType("application/pdf");
101        if (StringUtils.isNotBlank(inFileName)) {
102            result.setFilename(inFileName);
103        }
104        FileBlob fb = new FileBlob(resultFile);
105        fb.setMimeType("application/pdf");
106        return fb;
107    }
108
109    /**
110     * Convenience method: If a parameter is null or "", it is not modified.
111     *
112     * @param inPdfDoc Input PDF document.
113     * @param inTitle Title of the PDF document.
114     * @param inSubject Subject of the PDF document.
115     * @param inAuthor Author of the PDF document.
116     */
117    public static void setInfos(PDDocument inPdfDoc, String inTitle, String inSubject, String inAuthor) {
118        if (inTitle != null && inTitle.isEmpty()) {
119            inTitle = null;
120        }
121        if (inSubject != null && inSubject.isEmpty()) {
122            inSubject = null;
123        }
124        if (inAuthor != null && inAuthor.isEmpty()) {
125            inAuthor = null;
126        }
127        if (inTitle != null || inAuthor != null || inSubject != null) {
128            PDDocumentInformation docInfo = inPdfDoc.getDocumentInformation();
129            if (inTitle != null) {
130                docInfo.setTitle(inTitle);
131            }
132            if (inSubject != null) {
133                docInfo.setSubject(inSubject);
134            }
135            if (inAuthor != null) {
136                docInfo.setAuthor(inAuthor);
137            }
138            inPdfDoc.setDocumentInformation(docInfo);
139        }
140    }
141
142    public static String checkXPath(String inXPath) {
143        if (StringUtils.isBlank(inXPath)) {
144            inXPath = DEFAULT_BLOB_XPATH;
145        }
146        return inXPath;
147    }
148
149    public static void closeSilently(PDDocument... inPdfDocs) {
150        for (PDDocument doc : inPdfDocs) {
151            if (doc != null) {
152                try {
153                    doc.close();
154                } catch (IOException e) {
155                    // Ignore
156                }
157            }
158        }
159    }
160
161}