Web Trenches

Alternative for CFPDF thumbnails in Lucee



Creating PDF thumbnails with Lucee has been broken since version 5, and possibly longer.

https://dev.lucee.org/t/cfpdf-action-thumbnail-not-working-on-lucee-5/1389
https://luceeserver.atlassian.net/browse/LDEV-967

Here is a function that should help you if you really need to get it working.

UPDATE: Please see Brad Wood's new solution in the Comments section, which works with new versions of Lucee.
<cfscript>
	// Creates a jpg thumbnail from a PDF file
    // thumbnailFromPDF()
    // returns the file name of the JPG that it creates.
    // arguments
    // - pdfFile (required), This is the physical file path to the PDF
    // - destination (optional), Location to save the thumbbail, defaults to same location as original.
    // - width (optional), width of the resulting image, defaults to 200px
    // - page (optional), page number to use for the thumbnail, defaults to page 1.
	function thumbnailFromPDF(required string pdfFile,string destination = '',string width = '200', string page = 1) {
		// Java libraries needed
		var BufferedImage = createObject("java","java.awt.image.BufferedImage");
		var ImageWriter = createObject("java","org.apache.pdfbox.util.PDFImageWriter");	
		
		// load the pdf into PDFBox
		var document = createObject("java","org.apache.pdfbox.pdmodel.PDDocument").load("#arguments.pdffile#");	

		// get the base paths file names needed
		var basefile = listlast(arguments.pdffile,server.separator.file);
		var basefilename = replacenocase(basefile,".pdf","","ALL");
		var basepath = replacenocase(arguments.pdfFile,basefile,"");
		if (arguments.destination IS '') {
			arguments.destination = basepath;
		} 
		if (right(arguments.destination,1) Neq server.separator.file) {
			arguments.destination = arguments.destination & server.separator.file;
		}
		var returnfileprefix = "#arguments.destination##basefilename#_"; //includes dest path
		var returnfilename = "#basefilename#_#arguments.page#.jpg";

		//check file extension
		if (right(arguments.pdfFile,4) Neq ".pdf") {
			cfthrow(message="File does not appear to be a PDF file.");
		}

		//write the image of page 1. This writes a file called 1.jpg. There is prefix argument and in this case we use the file name as the prefix.
		// arguments... writeImage(document,image type,start page, end page, prefix to <page number>.jpg, java color profile, resolution)
		imageWriter.writeImage(document, JavaCast("string","jpg"), JavaCast("string",""), arguments.page, arguments.page, JavaCast("string",returnfileprefix), BufferedImage.TYPE_INT_RGB, 200);
		// close document object, no longer needed
		document.close();

		// resize the image
		cfimage(action="resize",source="#arguments.destination##returnfilename#",destination="#arguments.destination##returnfilename#",overwrite="true",width=arguments.width);

		return returnfilename;
	};
</cfscript>

Example of how to use this…

<cfscript>
pdfFile = "c:\path\to\my\file\name.pdf";
// make a thumbnail of page 1 in the images folder, with a width of 50
thumbnail = thumbnailFromPDF(pdffile,expandpath('/images'),50,1);
</cfscript>
<cfoutput><img src="/images/#thumbnail#" /></cfoutput>

Hopefully the Lucee team will fix cfpdf and this won’t be necessary in the future.

7 Replies to “Alternative for CFPDF thumbnails in Lucee”

  1. Great stuff. Just a few suggestions.

    1) Add PDDocument.close(); and document.close(); after imageWriter.writeImage. (You can workaround the first one by skipping var PDDocument… and using var document = createObject(“java”,”org.apache.pdfbox.pdmodel.PDDocument”).load(“#arguments.pdffile#”); instead.)
    2) Use server.separator.file instead of “\” so that it works on Windows and Unix-based systems.

  2. This is a great start, thank you for putting this together. Unfortunately, it does not seem to work when there are embedded fonts in the PDF. The text will be missing when rendering the image.

    I was looking for a workaround by possibly using `org.apache.pdfbox.rendering.PDFRenderer`, but it does not seem to be included anymore.

  3. When I wrote this, it was intended to be a stopgap while Lucee fixed the issue. It is a limited solution, and you are correct that it will not work with embedded fonts. Unfortunately, almost five years later the Lucee bug is not resolved.

  4. Here is a new version that takes into account the current PDFBox version in Lucee:

    /** Creates a jpg thumbnail from a PDF file
    *
    * @pdfFile This is the physical file path to the PDF or a byte array (binary) representing a loaded PDF
    * @destination Location to save the thumbbail, defaults to same location as original.
    * @width width of the resulting image, defaults to 200px
    * @page page number to use for the thumbnail, defaults to page 1.
    * @imagePrefix A string to prefix to the image name
    *
    * @returns The file name of the JPG that it creates.
    */
    function thumbnailFromPDF( required pdfFile, string destination=”, string width=’200′, string page=1, string imagePrefix=”) {

    // get the base paths file names needed
    if( isBinary( pdffile ) ) {
    var basefile = ‘thumbnail’;
    var loadSource = pdffile;
    } else {

    if (right(arguments.pdfFile,4) Neq “.pdf”) {
    cfthrow(message=”File does not appear to be a PDF file.”);
    }
    if (arguments.destination IS ”) {
    arguments.destination = getDirectoryFromPath( arguments.pdfFile );
    }
    var basefile = listlast(arguments.pdffile,server.separator.file);
    var loadSource = createObject( ‘java’, ‘java.io.File’ ).init( pdffile );
    }

    // load the pdf into PDFBox
    var document = createObject(“java”,”org.apache.pdfbox.Loader”).loadPDF( loadSource );
    try {
    var ImageWriter = createObject(“java”,”org.apache.pdfbox.rendering.PDFRenderer”).init( document );
    var basefilename = replacenocase(basefile,”.pdf”,””,”ALL”);
    if (right(arguments.destination,1) Neq server.separator.file) {
    arguments.destination = arguments.destination & server.separator.file;
    }
    var returnfileprefix = “#arguments.destination##imagePrefix##basefilename#_page_”; //includes dest path
    var returnfilename = “#imagePrefix##basefilename#_page_#arguments.page#.jpg”;

    //write the image of page 1. This writes a file called 1.jpg. There is prefix argument and in this case we use the file name as the prefix.
    // arguments… writeImage(document,image type,start page, end page, prefix to .jpg, java color profile, resolution)
    var BufferedImage = imageWriter.renderImage(arguments.page-1);
    } finally {
    // close document object, no longer needed
    document.close();
    }

    // resize the image
    cfimage(action=”resize”,source=BufferedImage,destination=”#arguments.destination##returnfilename#”,overwrite=”true”,width=arguments.width);

    return returnfilename;
    }

Leave a Reply

Your email address will not be published. Required fields are marked *