javapdfboxemoji

Rendering Emojis using PDFBox Does Not Work


I attempted to render emojis using the emoji-supported font Noto Color Emoji (https://fonts.google.com/noto/specimen/Noto+Color+Emoji) with the PDFBox 3.0.3 Java library. However, the font.hasGlyph(codePoint) method consistently returns false for all codepoint availability checks. Despite this, the emojis in question are clearly supported by the specified Google font. I have included the code below. Could someone kindly guide me in identifying the cause of this issue?

import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDType0Font;

import java.io.File;
import java.io.IOException;

public class EmojiPdfExample {
    public static void main(String[] args) {
        try (PDDocument doc = new PDDocument()) {

            File fontFile = new File("NotoColorEmoji-Regular.ttf");
            var font = PDType0Font.load(doc, fontFile);

            // Add a page
            PDPage page = new PDPage();
            doc.addPage(page);

            try (PDPageContentStream cs = new PDPageContentStream(doc, page)) {
                cs.beginText();
                cs.setFont(font, 20);
                cs.newLineAtOffset(50, 700);

                // Emoji string
                String text = "\uD83D\uDE00 \uD83D\uDE09";

                for (int i = 0; i < text.length(); ) {
                    int codePoint = Character.codePointAt(text, i);
                    i += Character.charCount(codePoint);

                    String glyph;
                    try {
                        glyph = new String(Character.toChars(codePoint));
                    } catch (IllegalArgumentException e) {
                        System.err.println("Invalid code point: U+" + Integer.toHexString(codePoint));
                        continue; // Skip invalid code points
                    }

                    try {
                        if (font.hasGlyph(codePoint)) {
                            cs.showText(glyph);
                        } else {
                            // Handle unsupported glyphs 
                            System.out.println("Unsupported glyph," + glyph);
                        }
                    } catch (IOException e) {
                        System.err.println("IOException while showing glyph: U+" + Integer.toHexString(codePoint));
                    }
                }
                cs.endText();
            }

            doc.save("emojis.pdf");
            System.out.println("PDF created: emojis.pdf");

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Solution

  • tl;dr: use a different font and simpler javacode.

    I tried your code but failed initially... emojis usually don't work. Then I tried this:

    String text = new String(Character.toChars(0x1f600));
    

    because \uD83D\uDE00 is really unicode U+1f600. However this didn't work either. A look at the font with DTL OTMaster 3.7 light shows that the code in the PDF (2386) is correct because that one does have unicode 1f600, however no glyph.

    Glyph 2386 for emoji 1f600

    Same when doing

    text = "😀 😉";
    

    I then tried a different font that I found while researching, "Symbola", and I got this:

    Emojis in the PDF

    It works both with using the emojis directly or using your original line with "\uD83D\uDE00 \uD83D\uDE09". However the long java segment where you're breaking this into codepoints doesn't work, probably because font.hasGlyph() doesn't work the way you expect. The javadoc mentions that the "code" parameter isn't unicode but that's what you're passing. It's better to catch the IllegalArgumentException like done in the EmbeddedMultipleFonts example.

    Oh, if you were expecting this to appear in color like in your browser: that isn't supported. That's not part of the original truetype specification.