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">
```
