001/*
002 * (C) Copyright 2013 Nuxeo SA (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 *     Vladimir Pasquier <vpasquier@nuxeo.com>
018 */
019package org.nuxeo.ecm.automation.core.operations.blob;
020
021import java.io.File;
022import java.io.IOException;
023
024import org.apache.pdfbox.multipdf.PDFMergerUtility;
025import org.nuxeo.ecm.automation.OperationContext;
026import org.nuxeo.ecm.automation.OperationException;
027import org.nuxeo.ecm.automation.core.Constants;
028import org.nuxeo.ecm.automation.core.annotations.Context;
029import org.nuxeo.ecm.automation.core.annotations.Operation;
030import org.nuxeo.ecm.automation.core.annotations.OperationMethod;
031import org.nuxeo.ecm.automation.core.annotations.Param;
032import org.nuxeo.ecm.automation.core.util.BlobList;
033import org.nuxeo.ecm.core.api.Blob;
034import org.nuxeo.ecm.core.api.Blobs;
035import org.nuxeo.runtime.api.Framework;
036
037/**
038 * Given a File document holding a pdf on the file:content property and 2 pdfs on the files:files property, the
039 * following operation will provide a pdf that is the result of the merge of all the pdfs, with the content of the one
040 * in file:content property first.
041 *
042 * @since 5.8
043 */
044@Operation(id = ConcatenatePDFs.ID, category = Constants.CAT_CONVERSION, label = "Concatenate PDFs", description = "Given a File document holding a pdf on the file:content property and 2 pdfs on the files:files property, the following operation will provide a pdf that is the result of the merge of all the pdfs, with the content of the one in file:content property first.")
045public class ConcatenatePDFs {
046
047    public static final String ID = "Blob.ConcatenatePDFs";
048
049    @Context
050    protected OperationContext ctx;
051
052    @Param(name = "blob_to_append", required = false, description = "Optional blob reference in context to append in first place.")
053    protected String xpathBlobToAppend = "";
054
055    @Param(name = "filename", required = true, description = "The merge pdf result filename.")
056    protected String filename;
057
058    @OperationMethod
059    public Blob run(Blob blob) throws OperationException, IOException {
060        PDFMergerUtility ut = new PDFMergerUtility();
061        checkPdf(blob);
062        if (xpathBlobToAppend.isEmpty()) {
063            return blob;
064        }
065        handleBlobToAppend(ut);
066        ut.addSource(blob.getStream());
067        return appendPDFs(ut);
068    }
069
070    @OperationMethod
071    public Blob run(BlobList blobs) throws IOException, OperationException {
072        PDFMergerUtility ut = new PDFMergerUtility();
073        if (!xpathBlobToAppend.isEmpty()) {
074            handleBlobToAppend(ut);
075        }
076        for (Blob blob : blobs) {
077            checkPdf(blob);
078            ut.addSource(blob.getStream());
079        }
080        return appendPDFs(ut);
081    }
082
083    protected Blob appendPDFs(PDFMergerUtility ut) throws IOException {
084        File tempFile = Framework.createTempFile(filename, ".pdf");
085        ut.setDestinationFileName(tempFile.getAbsolutePath());
086        ut.mergeDocuments();
087        Blob fb = Blobs.createBlob(tempFile);
088        Framework.trackFile(tempFile, fb);
089        fb.setFilename(filename);
090        return fb;
091    }
092
093    /**
094     * Check if blob to append is a PDF blob.
095     */
096    protected void handleBlobToAppend(PDFMergerUtility ut) throws IOException, OperationException {
097        try {
098            Blob blobToAppend = (Blob) ctx.get(xpathBlobToAppend);
099            if (blobToAppend == null) {
100                throw new OperationException("The blob to append from variable context: '" + xpathBlobToAppend
101                        + "' is null.");
102            }
103            checkPdf(blobToAppend);
104            ut.addSource(blobToAppend.getStream());
105        } catch (ClassCastException e) {
106            throw new OperationException("The blob to append from variable context: '" + xpathBlobToAppend
107                    + "' is not a blob.", e);
108        }
109    }
110
111    /**
112     * Check if blob is a pdf.
113     */
114    protected void checkPdf(Blob blob) throws OperationException {
115        if (!"application/pdf".equals(blob.getMimeType())) {
116            throw new OperationException("Blob " + blob.getFilename() + " is not a PDF.");
117        }
118    }
119}