AEM 61 - Add "Save as PDF" button to the Assets Console Action bar

Goal


In DAM Assets console of Touch UI, add button Save as PDF to the Action Bar for downloading selected assets as PDF

Demo | Package Install | Source Code


Save as PDF button added to Action Bar



Downloaded PDF



Solution


1) Create a OSGI servlet apps.experienceaem.assets.CreatePDFFromAssetsServlet in CRX folder /apps/eaem-save-assets-as-pdf with the following code

package apps.experienceaem.assets;

import com.adobe.internal.io.ByteWriter;
import com.adobe.internal.io.RandomAccessFileByteWriter;
import com.adobe.internal.pdftoolkit.core.types.ASMatrix;
import com.adobe.internal.pdftoolkit.pdf.document.PDFDocument;
import com.adobe.internal.pdftoolkit.pdf.document.PDFOpenOptions;
import com.adobe.internal.pdftoolkit.pdf.document.PDFSaveFullOptions;
import com.adobe.internal.pdftoolkit.pdf.graphics.PDFExtGState;
import com.adobe.internal.pdftoolkit.pdf.graphics.PDFRectangle;
import com.adobe.internal.pdftoolkit.pdf.graphics.xobject.PDFXObjectImage;
import com.adobe.internal.pdftoolkit.pdf.page.PDFPage;
import com.adobe.internal.pdftoolkit.pdf.page.PDFPageTree;
import com.adobe.internal.pdftoolkit.services.imageconversion.ImageManager;
import com.day.cq.dam.api.Asset;
import com.day.cq.dam.api.AssetManager;
import com.day.cq.dam.commons.util.DamUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.imageio.ImageIO;
import javax.jcr.Node;
import javax.servlet.ServletException;
import java.awt.image.BufferedImage;
import java.io.*;

@Component(metatype = true, label = "Experience AEM Create PDF From Assets", description = "")
@Service
@Properties({
        @Property(name = "sling.servlet.methods", value = {"GET" }, propertyPrivate = true),
        @Property(name = "sling.servlet.paths", value = "/bin/eaem/createpdf", propertyPrivate = true)})
public class CreatePDFFromAssetsServlet extends SlingAllMethodsServlet {
    private static final Logger log = LoggerFactory.getLogger(CreatePDFFromAssetsServlet.class);

    protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response)
            throws ServletException, IOException {
        try{
            String assetPaths = request.getParameter("assetPaths");

            if(StringUtils.isEmpty(assetPaths)){
                response.getWriter().print("No asset paths provided");
            }else{
                Asset pdf = createPDF(assetPaths, request);

                if(pdf == null){
                    response.getWriter().print("Error creating pdf");
                }else{
                    writePDF(response, pdf);
                }
            }
        }catch(Exception e){
            log.error("Error creating pdf", e);
        }
    }

    private void writePDF(SlingHttpServletResponse response, Asset pdf) throws Exception{
        response.setContentType("application/pdf");
        response.addHeader("Content-Disposition", "attachment; filename=" + pdf.getName());
        response.setContentLength((int)pdf.getOriginal().getSize());

        InputStream is = null;

        try{
            is = pdf.getOriginal().getStream();
            OutputStream responseOutputStream = response.getOutputStream();
            int bytes;

            while ((bytes = is.read()) != -1) {
                responseOutputStream.write(bytes);
            }
        }finally{
            if(is != null){
                is.close();
            }
        }
    }

    private Asset createPDF(String assetPaths, SlingHttpServletRequest request) throws Exception{
        String pdfPath = null;
        File tmpFile = null;
        ByteWriter tmpFileWriter = null;

        FileInputStream tmpFileReader = null;
        Asset asset = null, pdf = null;
        ResourceResolver resolver = request.getResourceResolver();

        PDFDocument pdfDocument = null;
        InputStream assetStream = null;

        try{
            Node folder = null;

            pdfDocument = PDFDocument.newInstance(PDFOpenOptions.newInstance());
            PDFPageTree pageTree = pdfDocument.requireCatalog().getPages();

            for(String assetPath : assetPaths.split(",")){
                asset = DamUtil.resolveToAsset(resolver.getResource(assetPath));

                if(folder == null){
                    folder = asset.adaptTo(Node.class).getParent();
                }

                assetStream = asset.getOriginal().getStream();

                BufferedImage bImage = ImageIO.read(assetStream);

                PDFXObjectImage image = ImageManager.getPDFImage(bImage, pdfDocument);

                PDFPage newPage = PDFPage.newInstance(pdfDocument,
                                        PDFRectangle.newInstance(pdfDocument,0, 0, image.getWidth(), image.getHeight()));

                ImageManager.insertImageInPDF(image, newPage,
                        PDFExtGState.newInstance(pdfDocument),
                        new ASMatrix(image.getWidth(), 0, 0, image.getHeight(), 0, 0));

                if (pageTree == null){
                    pageTree = PDFPageTree.newInstance(pdfDocument, newPage);
                }else{
                    pageTree.getPage(0).prependPage(newPage);
                }

                assetStream.close();
            }

            tmpFile = File.createTempFile(folder.getName(), ".pdf");

            tmpFileWriter = getTempFileWriter(tmpFile);

            pdfDocument.save(tmpFileWriter, PDFSaveFullOptions.newInstance());

            tmpFileReader = new FileInputStream(tmpFile);

            AssetManager assetMgr = resolver.adaptTo(AssetManager.class);

            pdfPath = folder.getPath() + "/" + folder.getName() + ".pdf";

            Resource resource = resolver.getResource(pdfPath);

            if(resource != null){
                resource.adaptTo(Node.class).remove();
            }

            pdf = assetMgr.createAsset( pdfPath, tmpFileReader, "application/pdf", true);
        }catch(Exception e){
            log.warn("Error generating pdf", e);
        }finally{
            if(assetStream != null){
                assetStream.close();
            }

            if (pdfDocument != null) {
                pdfDocument.close();
            }

            if (tmpFileWriter != null) {
                tmpFileWriter.close();
            }

            if(tmpFileReader !=null ){
                tmpFileReader.close();
            }
        }

        return pdf;
    }

    private ByteWriter getTempFileWriter(File file) throws IOException {
        file.delete();

        File parent = file.getParentFile();

        if (parent != null) {
            parent.mkdirs();
        }

        file.createNewFile();

        return new RandomAccessFileByteWriter(new RandomAccessFile(file, "rw"));
    }
}


2) For compilation, Gibson libraries (com.adobe.internal.pdftoolkit) are not available on Adobe public maven repo Nexus; so create a private repo - eaemrepo and add the location in repositories section of project pom.xml (the jars can be found in CQ install eg. author\crx-quickstart\launchpad\felix\bundle446\data\install\pdfcore-3.0.568820.jar or you can get them from this sample's source code)

    <repositories>
        <repository>
            <id>eaemrepo</id>
            <name>Experience AEM Internal Repo</name>
            <url>file://${project.basedir}/../libs</url>
        </repository>
        <repository>
            <id>adobe</id>
            <name>Adobe Public Repository</name>
            <url>http://repo.adobe.com/nexus/content/groups/public/</url>
            <layout>default</layout>
        </repository>
    </repositories>
     
3) Create a clientlib (type cq:ClientLibraryFolder) /apps/eaem-save-assets-as-pdf/clientlib with categories - cq.gui.damadmin.admin

4) Create file /apps/eaem-save-assets-as-pdf/clientlib/js.txt with the following content

                                                save-as-pdf.js

5) Create file /apps/eaem-save-assets-as-pdf/clientlib/save-as-pdf.js with the following code

(function ($, $document) {
    var DOWNLOAD_ACTIVATOR = "cq-damadmin-admin-actions-download-activator",
        TEXT = "Save as PDF",
        CREATE_PDF_URL = "/bin/eaem/createpdf?assetPaths=",
        added = false;

    $document.on("foundation-mode-change", addButton);

    function addButton(e, mode){
        if(added || (mode !== "selection") ){
            return;
        }

        added = true;

        var $cFolder = $("." + DOWNLOAD_ACTIVATOR);

        if ($cFolder.length == 0) {
            return;
        }

        var $downloadPdf = $cFolder.after($($cFolder[0].outerHTML));

        $downloadPdf.attr("title", TEXT)
            .removeClass(DOWNLOAD_ACTIVATOR)
            .removeAttr("href")
            .click(downloadPDF)
            .find("span").html(TEXT);

        $document.on("foundation-selections-change", ".foundation-collection", function() {
            var $selectedItems = $(".foundation-selections-item");

            if($selectedItems.length > 0){
                $downloadPdf.removeAttr("hidden");
            }
        });

        function downloadPDF() {
            var $items = $(".foundation-selections-item"),
                assetPaths = [];

            $items.each(function () {
                assetPaths.push($(this).data("path"));
            });

            window.open(CREATE_PDF_URL + assetPaths.join(","), "_blank");
        }
    }
})(jQuery, jQuery(document));

No comments:

Post a Comment