Search code examples
spring-bootjava-native-interfacenativepdfboxjava-2d

Is it possible to render image from a PDF using PDFBox in a Spring Boot Native application?


I am trying to figure out how to use PDFBox (2.0.30) to render image from a PDF document in a native application based on Spring Boot (3.2.1).

PDF generation works fine, but image rendering fails. when I try to convert a PDocument into an image using the PDFRenderer#renderImage(int, float), there is the following error:

java.lang.NoSuchFieldError: sun.java2d.pipe.ShapeSpanIterator.pData
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.jni.functions.JNIFunctions$Support.getFieldID(JNIFunctions.java:1357) ~[na:na]
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.jni.functions.JNIFunctions.GetFieldID(JNIFunctions.java:449) ~[na:na]
        at [email protected]/sun.java2d.pipe.ShapeSpanIterator.initIDs(Native Method) ~[na:na]
        at [email protected]/sun.java2d.pipe.ShapeSpanIterator.<clinit>(ShapeSpanIterator.java:71) ~[na:na]
        at [email protected]/sun.java2d.pipe.LoopPipe.getFillSSI(LoopPipe.java:228) ~[na:na]
        at [email protected]/sun.java2d.SunGraphics2D.validateCompClip(SunGraphics2D.java:1911) ~[na:na]
        at [email protected]/sun.java2d.SunGraphics2D.setClip(SunGraphics2D.java:2046) ~[na:na]
        at org.apache.pdfbox.rendering.PageDrawer.transferClip(PageDrawer.java:440) ~[na:na]
        at org.apache.pdfbox.rendering.PageDrawer.setClip(PageDrawer.java:411) ~[na:na]
        at org.apache.pdfbox.rendering.PageDrawer.beginText(PageDrawer.java:447) ~[na:na]
        at org.apache.pdfbox.contentstream.operator.text.BeginText.process(BeginText.java:41) ~[na:na]
        at org.apache.pdfbox.contentstream.PDFStreamEngine.processOperator(PDFStreamEngine.java:958) ~[awt-native-test:2.0.30]
        at org.apache.pdfbox.contentstream.PDFStreamEngine.processStreamOperators(PDFStreamEngine.java:531) ~[awt-native-test:2.0.30]
        at org.apache.pdfbox.contentstream.PDFStreamEngine.processStream(PDFStreamEngine.java:506) ~[awt-native-test:2.0.30]
        at org.apache.pdfbox.contentstream.PDFStreamEngine.processPage(PDFStreamEngine.java:150) ~[awt-native-test:2.0.30]
        at org.apache.pdfbox.rendering.PageDrawer.drawPage(PageDrawer.java:288) ~[na:na]
        at org.apache.pdfbox.rendering.PDFRenderer.renderImage(PDFRenderer.java:355) ~[na:na]
        at org.apache.pdfbox.rendering.PDFRenderer.renderImage(PDFRenderer.java:272) ~[na:na]
        at org.apache.pdfbox.rendering.PDFRenderer.renderImage(PDFRenderer.java:232) ~[na:na]
        at com.vialink.awtnativetest.PDFService.toImage(PDFService.java:37) ~[awt-native-test:na]
        ...

The Spring Webflux Application (awt-native-test) is a native executable built with the Native Maven Plugin:

mvn native:compose -Pnative

To run it:

./target/awt-native-test

Java Version:

openjdk version "21.0.1" 2023-10-17 LTS
OpenJDK Runtime Environment Liberica-NIK-23.1.1-1 (build 21.0.1+12-LTS)
OpenJDK 64-Bit Server VM Liberica-NIK-23.1.1-1 (build 21.0.1+12-LTS, mixed mode, sharing)

I guess I have to declare several runtime hints for Java2D to use PDFBox (something like hints.jni().registerType(ShapeSpanIterator.class, MemberCategory.values());), however I do not known how to do it as sun.java2D.pipe code is not exported by the module.

What am I missing ? Is image rendering from PDFBox native-ready ?

Code to reproduce my problem is here: https://github.com/nicolasjanet/awt-native-test


Solution

  • I ran my app using the tracing agent to get the missing hints, and I end up adding these hints using TypeReference in my RuntimeHintsRegistrar implementation:

    // Java2D
    hints.jni().registerType(TypeReference.of("sun.java2d.pipe.ShapeSpanIterator"), MemberCategory.values());
    
    // JPEG
    hints.jni().registerType(TypeReference.of("sun.awt.image.ByteComponentRaster"), MemberCategory.values());
    hints.jni().registerType(TypeReference.of("com.sun.imageio.plugins.jpeg.JPEGImageWriter"), MemberCategory.values());
    hints.jni().registerType(JPEGQTable.class, MemberCategory.values());
    hints.jni().registerType(JPEGHuffmanTable.class, MemberCategory.values());