Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NullPointerException when using PDF/A conformance #517

Closed
ThorbenKuck opened this issue Jul 23, 2020 · 11 comments
Closed

NullPointerException when using PDF/A conformance #517

ThorbenKuck opened this issue Jul 23, 2020 · 11 comments

Comments

@ThorbenKuck
Copy link

ThorbenKuck commented Jul 23, 2020

I am facing an issue, while creating a PDF with PDF/A conformance. Creating a normal PDF works fine, but once PDF/A conformance is enabled, the application exits with a NullPointerException.

The PDF i am trying to create has to be PDF/A conform.

My process of creating a PDF starts with an HTML template i process using Thymeleaf in Spring Boot. The application is written in Kotlin. The rendered PDF is at last returned as a ByteArray and provided by a controller as a Blob-Download. This part is working fine. The code generating the PDF looks something like this:

val model: Model = modelFactory.buildModel(id)
val context = Context(model.locale)
val outputStream = ByteArrayOutputStream()
val builder = PdfRendererBuilder()
val conformance = PdfRendererBuilder.PdfAConformance.NONE

context.setVariable("model", model)
// Other variables

val htmlContent: String = templateEngine.process("content", context)

builder.useFastMode().usePdfAConformance(conformance)
         .withHtmlContent(htmlContent, "")
         .usePdfVersion(if (conformance.part == 1) 1.4f else 1.5f)
         .toStream(outputStream)
         .useFont({ ClassPathResource("fonts/first-font.ttf").inputStream }, "first-font")
         .useFont({ ClassPathResource("fonts/second-font.ttf").inputStream }, "second-font")
         .useFont({ ClassPathResource("fonts/third-font.ttf").inputStream }, "third-font")
         .useFont({ ClassPathResource("fonts/fourth-font.ttf").inputStream }, "fourth-font")
         .run()

return outputStream.toByteArray()

The code has been slightly altered, but the logic is kept the same.

The html contains css, which includes the fonts like this:

@page {
    size: a4;

    @bottom-center {
        font-size: 8pt;
        border-top: 1px solid black;
        content: 'Generated on [[${formatters.formatDateTime(model.generatedAt)}]]';
        font-family: "first-font"; /* Font provided in code */
    }

    // ...
}
html {
    font-size: 9pt;
    font-family: "second-font"; /* Font provided in code */
}

// ...

With PdfAConformance.NONE, everything works fine and the PDF is rendered as expected.

However, once i change it to any PDF/A conformance, it no longer works. There is a very long list of logs to System.err, containing the following two lines. Repeated over and over again.

com.openhtmltopdf.exception WARNING:: Font metrics not available. Probably a bug.
com.openhtmltopdf.render WARNING:: Font is null.

And finally, the process ends with the following three info logs and NullPointerException

com.openhtmltopdf.general INFO:: Using fast-mode renderer. Prepare to fly.
com.openhtmltopdf.general INFO:: Specified fonts don't contain a space character!
com.openhtmltopdf.general INFO:: Specified fonts don't contain a space character!


java.lang.NullPointerException
	at org.apache.pdfbox.pdmodel.PDPageContentStream.setFont(PDPageContentStream.java:409)
	at com.openhtmltopdf.pdfboxout.PdfContentStreamAdapter.setFont(PdfContentStreamAdapter.java:262)
	at com.openhtmltopdf.pdfboxout.PdfBoxFastOutputDevice.drawStringFast(PdfBoxFastOutputDevice.java:462)
	at com.openhtmltopdf.pdfboxout.PdfBoxFastOutputDevice.drawString(PdfBoxFastOutputDevice.java:408)
	at com.openhtmltopdf.pdfboxout.PdfBoxTextRenderer.drawString(PdfBoxTextRenderer.java:51)
	at com.openhtmltopdf.render.AbstractOutputDevice.drawText(AbstractOutputDevice.java:107)
	at com.openhtmltopdf.render.InlineText.paint(InlineText.java:171)
	at com.openhtmltopdf.render.InlineLayoutBox.paintInline(InlineLayoutBox.java:279)
	at com.openhtmltopdf.render.simplepainter.SimplePainter.paintInlineContent(SimplePainter.java:170)
	at com.openhtmltopdf.render.simplepainter.SimplePainter.paintLayer(SimplePainter.java:72)
	at com.openhtmltopdf.render.PageBox.paintMarginAreas(PageBox.java:430)
	at com.openhtmltopdf.pdfboxout.PdfBoxRenderer.paintPageFast(PdfBoxRenderer.java:886)
	at com.openhtmltopdf.pdfboxout.PdfBoxRenderer.writePDFFast(PdfBoxRenderer.java:619)
	at com.openhtmltopdf.pdfboxout.PdfBoxRenderer.createPdfFast(PdfBoxRenderer.java:554)
	at com.openhtmltopdf.pdfboxout.PdfBoxRenderer.createPDF(PdfBoxRenderer.java:472)
	at com.openhtmltopdf.pdfboxout.PdfBoxRenderer.createPDF(PdfBoxRenderer.java:409)
	at com.openhtmltopdf.pdfboxout.PdfBoxRenderer.createPDF(PdfBoxRenderer.java:391)
	at com.openhtmltopdf.pdfboxout.PdfRendererBuilder.run(PdfRendererBuilder.java:42)
    ....

This Exception is thrown after calling run on the Builder.

I have tried a lot of possible combinations using the conformance. All 3 PDF/A conformance settings, in combination with and without the fast mode, because this post suggested it might be an issue. I made sure to [...] [use] a name that is not sans-serif, serif or monospace as this question might have suggested. Also, adding the "-fs-font-subset: complete-font", as well as the url additionally in the css entries did not help, as it apparently did in this question. If possible, i want to include the fonts in code not in css, since resolving those urls was tricky and finicky in the past.

However, those answers and all other answers to issues as well as on stack overflow did not help me in my case. I am lost. I have also tried to use different fonts, maybe they where broken, but every font i tried resulted in this exception.

Is there anything i am missing? Do i have to do something different to get PDF/A conformance? Maybe there is something special i have to bend for PDF/A conformance?

@danfickle
Copy link
Owner

Hi @ThorbenKuck,

Are those fonts being used when no PDF/A conformance? It looks like it can't load any of them.

@ThorbenKuck
Copy link
Author

I am uncertain. What exactly makes a ttf font PFD/A conform?

@danfickle
Copy link
Owner

If this little sample (taken from here) works, it is a bug in this project, otherwise, the font file may be at fault (or PDFBOX's font loading):

import java.io.IOException;
 
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
 
import org.apache.pdfbox.pdmodel.PDPageContentStream;
 
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
 
public class CreatePdf {
    public static void main(String[] args) throws IOException {
        String filename = "whatever.pdf";
        String message = "This font works.";
        
        PDDocument doc = new PDDocument();
        try {
            PDPage page = new PDPage();
            doc.addPage(page);
            
            PDFont font = PDType0Font.load(doc, yourInputStreamFont, true);
 
            PDPageContentStream contents = new PDPageContentStream(doc, page);
            contents.beginText();
            contents.setFont(font, 30);
            contents.newLineAtOffset(50, 700);
            contents.showText(message);
            contents.endText();
            contents.close();
            
            doc.save(filename);
        }
        finally {
            doc.close();
        }
    }
}

@ThorbenKuck
Copy link
Author

I adjusted the example to Kotlin code like this:

fun main() {
    val filename = "whatever.pdf"
    val message = "This font works."
    val file = "/fonts/first-font.ttf"
    val inputStream = PdfRenderer::class.java.getResourceAsStream(file) ?: throw IllegalArgumentException("Null")

    val byteArrayInputStream = ByteArrayInputStream(inputStream.readAllBytes())

    val doc = PDDocument()
    doc.use {
        val page = PDPage()
        it.addPage(page)
        val font: PDFont = PDType0Font.load(it, byteArrayInputStream, true)
        val contents = PDPageContentStream(it, page)
        contents.beginText()
        contents.setFont(font, 30f)
        contents.newLineAtOffset(50f, 700f)
        contents.showText(message)
        contents.endText()
        contents.close()
        it.save(filename)
    }
}

This example fails with this Exception upon loading the font:

Exception in thread "main" java.io.IOException: Illegal seek position: 3172466688
	at org.apache.fontbox.ttf.MemoryTTFDataStream.seek(MemoryTTFDataStream.java:164)
	at org.apache.fontbox.ttf.TrueTypeFont.readTable(TrueTypeFont.java:352)
	at org.apache.fontbox.ttf.TTFParser.parseTables(TTFParser.java:173)
	at org.apache.fontbox.ttf.TTFParser.parse(TTFParser.java:150)
	at org.apache.fontbox.ttf.TTFParser.parse(TTFParser.java:106)
	at org.apache.pdfbox.pdmodel.font.PDType0Font.load(PDType0Font.java:97)

I tried to verify this with different fonts and according to this even OpenSans is not PDF/A conform.

To make sure, that it is not an encoding issue, i added the resources filter to the pom

                    <resources>
                        <resource>
                            <directory>${project.basedir}/src/main/resources</directory>
                            <filtering>true</filtering>
                            <includes>
                                <include>**/*.ttf</include>
                            </includes>
                        </resource>

I also checked in java, where the same behaviour occurs. I must be clearly doing something wrong, right? Because i doubt that OpenSans is not PDF/A conform.

@syjer
Copy link
Contributor

syjer commented Jul 23, 2020

hi @ThorbenKuck you don't want to filter the ttf font, as they will be mangled by the maven-resources-plugin (being binary files), see #360 (comment).

You need to exclude them.

@ThorbenKuck
Copy link
Author

Sorry. You are right. I tried so many different combinations, that is also changed this before copying. Normally filtering is set to false.

@ThorbenKuck
Copy link
Author

Sometimes when running, i also get the following exception:

java.lang.IndexOutOfBoundsException: Index 0 out of bounds for length 0
	at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
	at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
	at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:248)
	at java.base/java.util.Objects.checkIndex(Objects.java:372)
	at java.base/java.util.ArrayList.get(ArrayList.java:458)
	at com.openhtmltopdf.pdfboxout.PdfBoxFastOutputDevice.drawString(PdfBoxFastOutputDevice.java:391)
	at com.openhtmltopdf.pdfboxout.PdfBoxTextRenderer.drawString(PdfBoxTextRenderer.java:51)
	at com.openhtmltopdf.render.AbstractOutputDevice.drawText(AbstractOutputDevice.java:107)
	at com.openhtmltopdf.render.InlineText.paint(InlineText.java:171)
	at com.openhtmltopdf.render.InlineLayoutBox.paintInline(InlineLayoutBox.java:279)
	at com.openhtmltopdf.render.simplepainter.SimplePainter.paintInlineContent(SimplePainter.java:170)
	at com.openhtmltopdf.render.simplepainter.SimplePainter.paintLayer(SimplePainter.java:72)
	at com.openhtmltopdf.render.PageBox.paintMarginAreas(PageBox.java:430)
	at com.openhtmltopdf.pdfboxout.PdfBoxRenderer.paintPageFast(PdfBoxRenderer.java:886)
	at com.openhtmltopdf.pdfboxout.PdfBoxRenderer.writePDFFast(PdfBoxRenderer.java:619)
	at com.openhtmltopdf.pdfboxout.PdfBoxRenderer.createPdfFast(PdfBoxRenderer.java:554)
	at com.openhtmltopdf.pdfboxout.PdfBoxRenderer.createPDF(PdfBoxRenderer.java:472)
	at com.openhtmltopdf.pdfboxout.PdfBoxRenderer.createPDF(PdfBoxRenderer.java:409)
	at com.openhtmltopdf.pdfboxout.PdfBoxRenderer.createPDF(PdfBoxRenderer.java:391)
	at com.openhtmltopdf.pdfboxout.PdfRendererBuilder.run(PdfRendererBuilder.java:42)

@syjer
Copy link
Contributor

syjer commented Jul 23, 2020

Sometimes when running, i also get the following exception.

this is most likely another issue (?).

I think it would be simpler if you could provide a repository with a minimal example that reproduce the issue. From the stack trace I'm unfortunately not able to find a culprit.

@syjer
Copy link
Contributor

syjer commented Jul 23, 2020

the last stack trace is due to:

public void drawString(String s, float x, float y, JustificationInfo info) {
        PDFont firstFont = _font.getFontDescription().get(0).getFont();

So most likely related to the font issue too.

@ThorbenKuck
Copy link
Author

I'll have a look, if i can provide some repository with reduced information, which still keeps the gist of it, in my free time.

Is this not checked prior to rendering? Because i expected that before calling .get(0) on something, there would be some sort of check, which throws a named Exception, telling what exactly is wrong. Do i have to do something, to enable those checks?

@danfickle
Copy link
Owner

The stack trace suggests there is not a valid font for one of the page margin areas such @top-center, etc. Logging and error handling is something we have been working on but still a long way to go I guess.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants