CFDocument Intelligently Reuses Repeated Image Objects In Lucee CFML 5.3.4.80
The other day, I experimented with saving InVision prototypes as interactive PDFs in Lucee CFML. And, while very few people will have an interest in such a technique, it's gotten me thinking a lot about how I might use PDFs more effectively. One thing I started to wonder about is how CFDocument
handles repeated Image URLs. The answer to this would certainly influence how I would implement different types of PDF content: whether I crop images in a pre-processing step, generating unique image URLs per cropping; or, whether I should just use overflow:hidden
in order to simulate cropping on a repeated image URL. To explore this, I created a simple demo in which I can dynamically repeat an image in a CFDocument
tag using Lucee CFML 5.3.4.80.
This demo is super simple. All I'm doing is passing in a URL parameter, pageCount
, and then using CFLoop
to generate N-number of pages all with the same img
tag and src
attribute:
<!--- We can use the URL to drive the number of pages generated in the PDF. --->
<cfparam name="url.pageCount" type="numeric" default="1" />
<cfdocument
format="pdf"
filename="./images.pdf"
overwrite="true"
localurl="true"
orientation="portrait"
pagetype="a4"
margintop="0.5"
marginright="0.5"
marginbottom="0.5"
marginleft="0.5">
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<style type="text/css">
p img {
border: 3px solid #ff3366 ;
width: 600px ;
}
</style>
</head>
<body>
<cfoutput>
<cfloop index="i" from="1" to="#url.pageCount#" step="1">
<p>
<!---
This IMAGE file is going to be used on N-number of pages. We can
look at the size (bytes on disk) of the generated file to see if
the repeated image is "reused" intelligently.
--->
<img src="file:///#expandPath( "./goose-duck.jpg" )#" />
</p>
<cfdocumentitem type="pagebreak" />
</cfloop>
</cfoutput>
</body>
</html>
</cfdocument>
<!--- Now that the file has been generated, let's look at the file-size. --->
<cfoutput>
Page Count: #url.pageCount#
<br />
File Size: #numberFormat( getFileInfo( "./images.pdf" ).size )#
</cfoutput>
As you can see, there's almost nothing going on in this ColdFusion code. Just a repeated image and set of page-breaks. Then, once the CFDocument
tag is done generating the PDF, I output the number of pages and the resultant file-size (in bytes).
If we run this with one-page as our Control, we get the following output:
Ok, so 1-page, 1-image is only about 95kb on disk. Now, let's try to increase the page-count / image-count by one oreder-of-magnitude:
Despite the 10x increase in image count, the file-size of the PDF has only gone up a mere 5kb. Clearly, the number of times that an image is repeated is not directly proportional to the size of the PDF.
And, let's try increasing it by one more order-of-magnitude:
This time, we get a slightly larger jump in file-size; however, even at two orders-of-magnitude in page-count and image-count, we're still only 40% larger in file-size than the 1-page version. Clearly, the CFDocument
tag is being very intelligent in how it embeds and then reuses images objects within a single PDF document.
This is important to understand, especially for InVision where we repeat the same image multiple times in a PDF when outputting comments. Instead of attempting to externally crop each conversation image prior to embedding it, we should use the same image whenever possible and then "mimic" cropping by using CSS.
Epilogue On External Image URLs
In this demo, I'm using the localUrl
attribute for CFDocument
. But, I should point out that the same repeated-image efficiency exists for external, HTTP-URL images. That is, as long as the URL for the image doesn't change, it will be embedded and reused intelligently. However, if the URL is different for each image, then the CFDocument
tag will view each instance of the image as a unique binary.
Want to use code from this post? Check out the license.
Reader Comments