javafontsjsouppdfboxopenhtmltopdf

Embed a custom font into PDF created out of HTML


I create a PDF from HTML with jsoup and OpenHTMLToPDF. I have to use a different font in my PDF to have non-latin glyphs covered (see here). How can I embed my font correctly?

Simplified program reproducing the issue:

src/main/resources/test.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>Font Test</title>
        <style>
            @font-face {
                font-family: 'source-sans';
                font-style: normal;
                font-weight: 400;
                src: url(fonts/SourceSansPro-Regular.ttf);
            }
        </style>
    </head>
    <body>    
        <p style="font-family: 'source-sans',serif">Latin Script</p>
        <p style="font-family: 'source-sans',serif">Είμαι ελληνικό κείμενο.</p>
    </body>
</html>

This file needs to be converted to PDF.

In a browser it looks correctly and uses the Source Sans font.

src/main/java/main.java:

import com.openhtmltopdf.extend.FSSupplier;
import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;
import org.jsoup.Jsoup;
import org.jsoup.helper.W3CDom;
import org.w3c.dom.Document;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

public class main {

    public static void main(String[] args) {
        System.out.println("Starting");

        try {
            final W3CDom w3cDom = new W3CDom();
            final Document w3cDoc = w3cDom.fromJsoup(Jsoup.parse(readFile()));
            final OutputStream outStream = new FileOutputStream("test.pdf");

            final PdfRendererBuilder pdfBuilder = new PdfRendererBuilder();
            pdfBuilder.useFastMode();
            pdfBuilder.withW3cDocument(w3cDoc, "/");
            pdfBuilder.useFont(new File(main.class.getClassLoader().getResource("fonts/SourceSansPro-Regular.ttf").getFile()), "source-sans");
            pdfBuilder.toStream(outStream);

            pdfBuilder.run();
            outStream.close();
        } catch (Exception e) {
            System.out.println("PDF could not be created: " + e.getMessage());
        }

        System.out.println("Finish.");
    }

    private static String readFile() throws IOException {
        final ClassLoader classLoader = main.class.getClassLoader();
        final InputStream inputStream = classLoader.getResourceAsStream("test.html");
        final StringBuilder sb = new StringBuilder();
        final Reader r = new InputStreamReader(Objects.requireNonNull(inputStream), StandardCharsets.UTF_8);
        char[] buf = new char[1024];
        int amt = r.read(buf);
        while(amt > 0) {
            sb.append(buf, 0, amt);
            amt = r.read(buf);
        }
        return sb.toString();
    }
}

Don't bother about the second function, it just reads the HTML file and is only included here, to have a complete program.

src/main/resources/fonts/SourceSansPro-regular.ttf

Download here: https://www.fontsquirrel.com/fonts/source-sans-pro

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>paf</groupId>
    <artifactId>test</artifactId>
    <version>1.0-SNAPSHOT</version>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>7</source>
                    <target>7</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>com.openhtmltopdf</groupId>
            <artifactId>openhtmltopdf-pdfbox</artifactId>
            <version>0.0.1-RC18</version>
        </dependency>
        <dependency>
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
            <version>1.11.2</version>
        </dependency>
    </dependencies>
</project>

Program output:

Starting
com.openhtmltopdf.load INFO:: TIME: parse stylesheets  148ms
com.openhtmltopdf.match INFO:: media = print
com.openhtmltopdf.match INFO:: Matcher created with 147 selectors
com.openhtmltopdf.load INFO:: Loading font(source-sans) from InputStream supplier now.
com.openhtmltopdf.exception WARNING:: bad URL given: /fonts/SourceSansPro-Regular.ttf
com.openhtmltopdf.exception WARNING:: Could not load @font-face font: /fonts/SourceSansPro-Regular.ttf
com.openhtmltopdf.exception WARNING:: Font metrics not available. Probably a bug.
com.openhtmltopdf.exception WARNING:: Font metrics not available. Probably a bug.
com.openhtmltopdf.render WARNING:: Font is null.
com.openhtmltopdf.render WARNING:: Font is null.
com.openhtmltopdf.render WARNING:: Font is null.
com.openhtmltopdf.render WARNING:: Font is null.
com.openhtmltopdf.render WARNING:: Font is null.
com.openhtmltopdf.render WARNING:: Font is null.
com.openhtmltopdf.render WARNING:: Font is null.
Finish.

Resulting PDF

Latin Script
##### ######## #######.

In Serif-Font.


Solution

  • Okay. Thanks to the comments of @Tilman Hausherr I asked in the GitHub-Issue Tracker of openhtmltopdf and got some help.

    These changes made it work, if someone landing here is interested:

    src/main/java/main.java (changed part only, see rest above):

    public static void main(String[] args) {
        System.out.println("Starting");
    
        try {
            final W3CDom w3cDom = new W3CDom();
            final Document w3cDoc = w3cDom.fromJsoup(Jsoup.parse(readFile()));
            final OutputStream outStream = new FileOutputStream("test.pdf");
    
            final PdfRendererBuilder pdfBuilder = new PdfRendererBuilder();
            pdfBuilder.useFastMode();
            pdfBuilder.withW3cDocument(w3cDoc, "/");
            pdfBuilder.useFont(new File(main.class.getClassLoader().getResource("fonts/SourceSansPro-Regular.ttf").getFile()), "source-sans");
            pdfBuilder.toStream(outStream);
    
            pdfBuilder.run();
            outStream.close();
        } catch (Exception e) {
            System.out.println("PDF could not be created: " + e.getMessage());
        }
    
        System.out.println("Finish.");
    }
    

    src/main/resources/fonts/SourceSansPro-regular.ttf

    Downloaded newer version here: https://github.com/adobe-fonts/source-sans-pro/releases

    from src/main/resources/test.html (changed part only, see rest above)

    @font-face {
        font-family: 'source-sans';
        font-style: normal;
        font-weight: 400;
        src: url(fonts/SourceSansPro-Regular.ttf);
        -fs-font-subset: complete-font;
    }