Watermarking PDFs In Lucee 5.3.7 Without using CFPDF
Recently, I’ve needed to add a watermark to PDFs but struggled with Lucee’s current implementation in 5.3.x.. Watermarking PDFs In Lucee 5.3.7 without using CFPDF is usually pretty straightforward, via
<code> <cfpdf action="addwatermark" source="#source#" image="#image#" destination="#destination#" overwrite="yes"> </code>
However, there appear to be a fair few issues with cfpdf()
at the time of writing:
- https://luceeserver.atlassian.net/browse/LDEV-1519
- https://luceeserver.atlassian.net/browse/LDEV-1385
Thankfully, there is a workaround by directly calling the iText Java libs which are already in Lucee 5.x. You can use the function below as a bit of a hacky fix in the meantime! Note, this is only tested in Lucee 5.3.7 so your mileage may vary.
<cfscript> /** * Add a image as a watermark to a PDF using iText in Lucee 5.3 * * @source string, i.e Full path to Input File: expandPath("myFile.pdf") * @destination string i.e Full path to Output File: expandPath("myFile_Watermarked.pdf") * @image string i.e Full path to Watermark Image: expandPath("watermark.png") * @x Watermark image x offset, defaults to 0 * @y Watermark image y offset, defaults to 0 * @foreground Whether to add watermark on top or behind content */ function watermarkPDF( required string source, required string destination, required string image, numeric x = 0, numeric y = 0, boolean foreground = true ){ // Create PDF reader local.pdfReader = createObject("java", "com.lowagie.text.pdf.PdfReader").init(arguments.source); // Fill form fields local.outputStream = createObject("java", "java.io.FileOutputStream").init(arguments.destination); local.pdfStamper = createObject("java", "com.lowagie.text.pdf.PdfStamper").init(local.pdfReader, local.outputStream); // Create image object for watermark image local.imageObj = createobject("java", "com.lowagie.text.Image"); // Read watermark image local.img = local.imageObj.getInstance(arguments.image); // Add watermark image to every page local.i = 0; // Loop PDF pages while (local.i LT local.pdfReader.getNumberOfPages()) { local.i = local.i + 1; local.pdfContentByte = arguments.foreground? local.pdfStamper.getOverContent( javacast("int", local.i) ) : local.pdfStamper.getUnderContent( javacast("int", local.i) ); // Set position of image local.img.setAbsolutePosition(javacast("float", arguments.x),javacast("float", arguments.y)); // Add watermark image to looped page local.pdfContentByte.addImage(local.img); } // flattern form local.pdfStamper.setFormFlattening(true); // Close and create destination pdf file local.pdfStamper.close(); local.outputStream.close(); } </cfscript> ```
Example Usage:
``` <cfscript> watermarkPDF( expandPath("/files/A1_MyDocument.pdf"), expandPath("/files/A1_MyDocument_Watermarked.pdf"), expandPath("/files/watermark.png") ); </cfscript> ```
Bear in mind, watermarking PDFs won’t stop a user from easily removing the watermark in Acrobat Pro unless you add an owner password to restrict editing (and, arguably, even then there are ‘unethical’ ways around that).
Thankfully that functionality currently works in Lucee 5.3.7, so we use cfpdf to restrict access:
``` <cfpdf action="protect" encrypt="AES_128" source="#FullPathToOutputFile#" newOwnerPassword="myAwesomePassword" permissions="none"> ```