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>
jars added in eaemrepo
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));