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 static org.apache.pdfbox.pdmodel.font.PDType1Font.COURIER;
023import static org.apache.pdfbox.pdmodel.font.PDType1Font.COURIER_BOLD;
024import static org.apache.pdfbox.pdmodel.font.PDType1Font.COURIER_BOLD_OBLIQUE;
025import static org.apache.pdfbox.pdmodel.font.PDType1Font.COURIER_OBLIQUE;
026import static org.apache.pdfbox.pdmodel.font.PDType1Font.HELVETICA;
027import static org.apache.pdfbox.pdmodel.font.PDType1Font.HELVETICA_BOLD;
028import static org.apache.pdfbox.pdmodel.font.PDType1Font.HELVETICA_BOLD_OBLIQUE;
029import static org.apache.pdfbox.pdmodel.font.PDType1Font.HELVETICA_OBLIQUE;
030import static org.apache.pdfbox.pdmodel.font.PDType1Font.SYMBOL;
031import static org.apache.pdfbox.pdmodel.font.PDType1Font.TIMES_BOLD;
032import static org.apache.pdfbox.pdmodel.font.PDType1Font.TIMES_BOLD_ITALIC;
033import static org.apache.pdfbox.pdmodel.font.PDType1Font.TIMES_ITALIC;
034import static org.apache.pdfbox.pdmodel.font.PDType1Font.TIMES_ROMAN;
035import static org.apache.pdfbox.pdmodel.font.PDType1Font.ZAPF_DINGBATS;
036
037import java.io.File;
038import java.io.IOException;
039import java.io.InputStream;
040import java.util.Collections;
041import java.util.HashMap;
042import java.util.List;
043import java.util.Map;
044
045import org.apache.commons.lang3.StringUtils;
046import org.apache.pdfbox.pdmodel.PDDocument;
047import org.apache.pdfbox.pdmodel.PDDocumentInformation;
048import org.apache.pdfbox.pdmodel.font.PDType1Font;
049import org.nuxeo.ecm.core.api.Blob;
050import org.nuxeo.ecm.core.api.Blobs;
051import org.nuxeo.ecm.core.api.NuxeoException;
052import org.nuxeo.ecm.core.api.impl.blob.FileBlob;
053
054/**
055 * Grouping miscellaneous utilities in this class.
056 *
057 * @since 8.10
058 */
059public class PDFUtils {
060
061    public static final String DEFAULT_BLOB_XPATH = "file:content";
062
063    protected static final Map<String, PDType1Font> STANDARD_14;
064    static {
065        Map<String, PDType1Font> map = new HashMap<>();
066        for (PDType1Font font : List.of( //
067                TIMES_ROMAN, TIMES_BOLD, TIMES_ITALIC, TIMES_BOLD_ITALIC, //
068                HELVETICA, HELVETICA_BOLD, HELVETICA_OBLIQUE, HELVETICA_BOLD_OBLIQUE, //
069                COURIER, COURIER_BOLD, COURIER_OBLIQUE, COURIER_BOLD_OBLIQUE, //
070                SYMBOL, ZAPF_DINGBATS)) {
071            map.put(font.getBaseFont(), font);
072        }
073        STANDARD_14 = Collections.unmodifiableMap(map);
074    }
075
076    /**
077     * Gets one of the Standard 14 Type 1 Fonts.
078     *
079     * @since 11.1
080     */
081    public static PDType1Font getStandardType1Font(String name) {
082        return STANDARD_14.get(name);
083    }
084
085    public static int[] hex255ToRGB(String inHex) {
086        int[] result = { 0, 0, 0 };
087        if (inHex != null) {
088            inHex = inHex.toLowerCase().replace("#", "").replace("0x", "");
089            if (inHex.length() >= 6) {
090                for (int i = 0; i < 3; i++) {
091                    result[i] = Integer.parseInt(inHex.substring(i * 2, i * 2 + 2), 16);
092                }
093            }
094        }
095        return result;
096    }
097
098    /**
099     * This is just a shortcut. We often load() and openProtection().
100     *
101     * @param inBlob Input Blob.
102     * @param inPwd Input password.
103     */
104    public static PDDocument load(Blob inBlob, String inPwd) throws NuxeoException {
105        PDDocument pdfDoc;
106        try {
107            pdfDoc = PDDocument.load(inBlob.getStream(), inPwd);
108        } catch (IOException e) {
109            throw new NuxeoException("Failed to load the PDF", e);
110        }
111        return pdfDoc;
112    }
113
114    /**
115     * Create a temporary PDF file and return a FileBlob built from this file.
116     * <p>
117     * Mainly a utility used just by this plug-in actually.
118     *
119     * @param inPdfDoc Input PDF document.
120     * @return FileBlob
121     */
122    public static FileBlob saveInTempFile(PDDocument inPdfDoc) throws IOException {
123        return saveInTempFile(inPdfDoc, null);
124    }
125
126    public static FileBlob saveInTempFile(PDDocument inPdfDoc, String inFileName) throws IOException {
127        Blob result = Blobs.createBlobWithExtension(".pdf");
128        File resultFile = result.getFile();
129        inPdfDoc.save(result.getFile());
130        result.setMimeType("application/pdf");
131        if (StringUtils.isNotBlank(inFileName)) {
132            result.setFilename(inFileName);
133        }
134        FileBlob fb = new FileBlob(resultFile);
135        fb.setMimeType("application/pdf");
136        return fb;
137    }
138
139    /**
140     * Convenience method: If a parameter is null or "", it is not modified.
141     *
142     * @param inPdfDoc Input PDF document.
143     * @param inTitle Title of the PDF document.
144     * @param inSubject Subject of the PDF document.
145     * @param inAuthor Author of the PDF document.
146     */
147    public static void setInfos(PDDocument inPdfDoc, String inTitle, String inSubject, String inAuthor) {
148        if (inTitle != null && inTitle.isEmpty()) {
149            inTitle = null;
150        }
151        if (inSubject != null && inSubject.isEmpty()) {
152            inSubject = null;
153        }
154        if (inAuthor != null && inAuthor.isEmpty()) {
155            inAuthor = null;
156        }
157        if (inTitle != null || inAuthor != null || inSubject != null) {
158            PDDocumentInformation docInfo = inPdfDoc.getDocumentInformation();
159            if (inTitle != null) {
160                docInfo.setTitle(inTitle);
161            }
162            if (inSubject != null) {
163                docInfo.setSubject(inSubject);
164            }
165            if (inAuthor != null) {
166                docInfo.setAuthor(inAuthor);
167            }
168            inPdfDoc.setDocumentInformation(docInfo);
169        }
170    }
171
172    public static String checkXPath(String inXPath) {
173        if (StringUtils.isBlank(inXPath)) {
174            inXPath = DEFAULT_BLOB_XPATH;
175        }
176        return inXPath;
177    }
178
179    public static void closeSilently(PDDocument... inPdfDocs) {
180        for (PDDocument doc : inPdfDocs) {
181            if (doc != null) {
182                try {
183                    doc.close();
184                } catch (IOException e) {
185                    // Ignore
186                }
187            }
188        }
189    }
190
191}