OS: Windows Server 2012 Java Version: 11.0.6 Tomcat Version: Apache Tomcat/9.0.31 Lucee Version: Lucee 5.3.8.3-SNAPSHOT
Hey,
I’m currently trying to make a thumbnail of a PDF file. I noticed that the cfpdf tag doesn’t work well, and thus did not generate a image file. Now I’m using code I found here: link but I’m getting an error:
No matching Method for writeImage(org.apache.pdfbox.pdmodel.PDDocument, string, string, numeric, numeric, string, numeric, numeric) found for org.apache.pdfbox.util.PDFImageWriter
this is strange because when I writedump the function itself I get:
or without the last two arguments. Am I doing something wrong? I have the feeling the parameters are not correct but I tried a lot of things and still the same error. Some help would be appreciated!
I’m not sure what the issue is but it could be to do with the version of PDF Box you’re using, which I assume is the one Lucee loads? i.e. you’re not loading it separately?
For my own thumbnail workaround I’m using pdfbox-app-2.0.19 and the PDFRenderer class. This isn’t exactly how I’m doing it, but should give you an idea:
If you are using PDFBox 2.x.x, that example code will no longer work as the PDFImageWriter class was removed with that release. This is noted here: Apache PDFBox | PDFBox 2.0.0 Migration Guide under the PDF Rendering section.
<cffunction name="PDFpageToImage" output="false">
<cfargument name="filebytes" required="true">
<cfargument name="pageNo" default="0">
<cfargument name="dpi" default="72">
<cfscript>
/* creates a 72dpi base64 png image of pdf page input as bytes (could also pass in filename)
for byteArray return, do return after baos.close() and doc.close()
'org.apache.pdfbox.app' is in bundles directory :)
*/
var PDDocument = CreateObject("java", "org.apache.pdfbox.pdmodel.PDDocument", "org.apache.pdfbox.app", "2.0.18");
var PDFRenderer = CreateObject("java", "org.apache.pdfbox.rendering.PDFRenderer", "org.apache.pdfbox.app", "2.0.18");
var ImageIO = CreateObject("java", "javax.imageio.ImageIO");
var doc = PDDocument.init().load(arguments.filebytes);
//var doc = PDDocument.init().load(arguments.filename);
var renderer = PDFRenderer.init(doc);
var image = renderer.renderImageWithDPI(arguments.pageNo, arguments.dpi);
var baos = createObject("java","java.io.ByteArrayOutputStream").init();
ImageIO.write(image, "PNG", baos);
baos.flush();
var imageInBytes = baos.toByteArray();
baos.close();
doc.close();
var Base64 = CreateObject("java", "java.util.Base64");
var b64 = Base64.getEncoder().encodeToString(imageInBytes);
return b64;
//output: 'res' is the returned base64 (b64)
//dataurl = "data:image/png;base64,#res#"; see pagetoimage.cfm
</cfscript>
</cffunction>
That’s why the bundles directory is awesome! You can have multiple versions of a jar file and they are all separate. (I mention this in case anyone has not tried the bundles directory).
When I run this script I get OSGi Bundle org.apache.pdfbox.app is not available…
I look in Lucee admin under Bundles and it is there but says not loaded. I have searched every where and cannot find how to load it? Any help is appreciated.
What type of data is arguments.filebytes? Is it binary data (byte array) that represents a file? Based on the error message you shared, it seems to think you’re passing a string value. The load method can take a byte array, Java File, or Java InputStream.
This may not be your situation, but Lucee just upped their PDFBox version to the v3 release candidate, which no longer uses the .load method. It uses loaders. This post and the example he gave helped me figure it out: java - Apache PDFBox cannot find class 'Loader'. Why? - Stack Overflow
var PDFRenderer = CreateObject(“java”, “org.apache.pdfbox.rendering.PDFRenderer”);
var ImageIO = CreateObject(“java”, “javax.imageio.ImageIO”);
var Loader = CreateObject(“java”, “org.apache.pdfbox.Loader”);
//load PDF
var file = CreateObject( “java”, “java.io.File” ).init( JavaCast( “string”, arguments.filename ) );
var doc = Loader.LoadPDF(file);
// render pdf
var renderer = PDFRenderer.init(doc);
Built-in image generation via ‘thumbnail’ is great, but the max resolution is not enough for all use cases. Here’s a working code snippet to squeeze out max quality available via the thumbnail generation route for anyone who stumbles here:—
I could be mistaken (depending on your settings) - I believe there may be a couple unscoped vars and a duplicate renderer in the snippet.
Below are proposed fixes (where unexpected results may occur under load).
Good thinking! Here is a new version improved by AI:—
<cffunction name="convertPdfPageToImage" output="false" returntype="string" hint="Converts a specified PDF page to a base64-encoded PNG image.">
<cfargument name="filename" type="string" required="true" hint="The path to the PDF file.">
<cfargument name="pageNo" type="numeric" default="0" hint="The page number to convert.">
<cfargument name="dpi" type="numeric" default="72" hint="The DPI (dots per inch) of the output image.">
<cfscript>
var PDFRenderer = createObject("java", "org.apache.pdfbox.rendering.PDFRenderer");
var ImageIO = createObject("java", "javax.imageio.ImageIO");
var Loader = createObject("java", "org.apache.pdfbox.Loader");
var file = createObject("java", "java.io.File").init(expandPath(arguments.filename));
if (!file.exists()) {
throw("The specified file does not exist.");
}
var doc = Loader.LoadPDF(file);
if (arguments.pageNo lt 0 or arguments.pageNo gte doc.getNumberOfPages()) {
throw("The specified page number is out of range.");
}
var renderer = PDFRenderer.init(doc);
var image = renderer.renderImageWithDPI(arguments.pageNo, arguments.dpi);
var baos = createObject("java", "java.io.ByteArrayOutputStream").init();
ImageIO.write(image, "PNG", baos);
baos.flush();
var imageInBytes = baos.toByteArray();
baos.close();
doc.close();
var b64 = binaryEncode(imageInBytes, "base64");
return b64;
</cfscript>
</cffunction>