PdfFontCacheTest.java

/*
    This file is part of the iText (R) project.
    Copyright (c) 1998-2025 Apryse Group NV
    Authors: Apryse Software.

    This program is offered under a commercial and under the AGPL license.
    For commercial licensing, contact us at https://itextpdf.com/sales.  For AGPL licensing, see below.

    AGPL licensing:
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
package com.itextpdf.kernel.pdf;

import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.io.font.constants.StandardFonts;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.font.PdfFontFactory.EmbeddingStrategy;
import com.itextpdf.kernel.font.PdfType3Font;
import com.itextpdf.kernel.font.Type3Glyph;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.kernel.utils.CompareTool;
import com.itextpdf.test.ExtendedITextTest;
import com.itextpdf.test.TestUtil;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

@Tag("IntegrationTest")
public class PdfFontCacheTest extends ExtendedITextTest {
    public static final String sourceFolder = "./src/test/resources/com/itextpdf/kernel/pdf/PdfFontCacheTest/";
    private static final String fontsFolder = "./src/test/resources/com/itextpdf/kernel/pdf/fonts/";
    public static final String destinationFolder = TestUtil.getOutputPath() + "/kernel/pdf/PdfFontCacheTest/";


    static final String pangramme = "Amazingly few discotheques provide jukeboxes " +
            "but it now while sayingly ABEFGHJKNOPQRSTUWYZ?";

    @BeforeAll
    public static void beforeClass() {
        createDestinationFolder(destinationFolder);
    }

    @AfterAll
    public static void afterClass() {
        CompareTool.cleanup(destinationFolder);
    }
    
    private static final String[] TextSetHelloWorld = new String[]{"Hello World"};
    private static final String[] TextSetWithABC = new String[]{"Hello World", "ABC", "XYZ"};
    private static final String[] TextSetInternational = new String[]{"Hello World", "������������, ������", "���������������", "������ ������"};
    private static final String[] TextSetChinese = new String[]{"Hello World", "������", "������"};

    @Test
    public void createDocumentWithKozmin() throws IOException, InterruptedException {
        String testName = "DocumentWithKozmin";
        String filename = destinationFolder + testName + ".pdf";
        String cmpFilename = sourceFolder + "cmp_" + testName + ".pdf";

        PdfDocument pdfDoc = createDocument(filename);
        addPagesWithFonts(pdfDoc, "KozMinPro-Regular", "UniJIS-UCS2-H", TextSetChinese);
        addPagesWithFonts(pdfDoc, "KozMinPro-Regular", "Adobe-Japan1-0", TextSetChinese);
        pdfDoc.close();

        Assertions.assertEquals(2, countPdfFonts(filename));
        Assertions.assertNull(new CompareTool().compareByContent(filename, cmpFilename, destinationFolder, "diff_"));
    }

    @Test
    public void createDocumentWithHelveticaMixEncodings() throws IOException, InterruptedException {
        String testName = "DocumentWithHelveticaMixEncodings";

        String filename = destinationFolder + testName + ".pdf";
        String cmpFilename = sourceFolder + "cmp_" + testName + ".pdf";
        PdfDocument pdfDoc = createDocument(filename);

        String font = StandardFonts.HELVETICA;
        String encoding = null;

        addPagesWithFonts(pdfDoc, font, encoding, TextSetWithABC);
        addPagesWithFonts(pdfDoc, font, "MacRoman", TextSetWithABC);
        pdfDoc.close();

        Assertions.assertEquals(2, countPdfFonts(filename));
        Assertions.assertNull(new CompareTool().compareByContent(filename, cmpFilename, destinationFolder, "diff_"));
    }

    @Test
    public void createDocumentWithHelvetica() throws IOException, InterruptedException {
        String testName = "DocumentWithHelvetica";

        String filename = destinationFolder + testName + ".pdf";
        String cmpFilename = sourceFolder + "cmp_" + testName + ".pdf";
        PdfDocument pdfDoc = createDocument(filename);

        String font = StandardFonts.HELVETICA;
        String encoding = null;

        addPagesWithFonts(pdfDoc, font, encoding, TextSetWithABC);
        addPagesWithFonts(pdfDoc, font, encoding, TextSetWithABC);
        addPagesWithFonts(pdfDoc, font, encoding, TextSetWithABC);
        pdfDoc.close();

        Assertions.assertEquals(1, countPdfFonts(filename));
        Assertions.assertNull(new CompareTool().compareByContent(filename, cmpFilename, destinationFolder, "diff_"));
    }

    @Test
    public void createDocumentWithHelveticaFlushed() throws IOException, InterruptedException {
        String testName = "DocumentWithHelveticaFlushed";

        String filename = destinationFolder + testName + ".pdf";
        String cmpFilename = sourceFolder + "cmp_" + testName + ".pdf";
        PdfDocument pdfDoc = createDocument(filename);

        String font = StandardFonts.HELVETICA;
        String encoding = null;

        addPagesWithFonts(pdfDoc, font, encoding, TextSetWithABC);
        pdfDoc.flushFonts();
        addPagesWithFonts(pdfDoc, font, encoding, TextSetWithABC);
        pdfDoc.flushFonts();
        addPagesWithFonts(pdfDoc, font, encoding, TextSetWithABC);
        pdfDoc.close();

        //Flushed fonts cannot be reused.
        Assertions.assertEquals(3, countPdfFonts(filename));
        Assertions.assertNull(new CompareTool().compareByContent(filename, cmpFilename, destinationFolder, "diff_"));
    }

    @Test
    public void createDocumentWithTimesAndCustomEncoding() throws IOException, InterruptedException {
        String testName = "DocumentTimesAndCustomEncoding";

        String filename = destinationFolder + testName + ".pdf";
        String cmpFilename = sourceFolder + "cmp_" + testName + ".pdf";
        PdfDocument pdfDoc = createDocument(filename);

        String font = StandardFonts.TIMES_ROMAN;
        String encoding = "# full 'A' Aring 0041 'E' Egrave 0045 32 space 0020";

        String[] AE = new String[] {"A E"};
        addPagesWithFonts(pdfDoc, font, encoding, AE);
        addPagesWithFonts(pdfDoc, font, encoding, AE);
        addPagesWithFonts(pdfDoc, font, encoding, AE);

        pdfDoc.close();

        Assertions.assertEquals(1, countPdfFonts(filename));
        Assertions.assertNull(new CompareTool().compareByContent(filename, cmpFilename, destinationFolder, "diff_"));
    }

    @Test
    public void createDocumentWithCourierAndWinAnsiEncodings() throws IOException, InterruptedException {
        String testName = "DocumentCourierAndWinAnsiEncodings";

        String filename = destinationFolder + testName + ".pdf";
        String cmpFilename = sourceFolder + "cmp_" + testName + ".pdf";
        PdfDocument pdfDoc = createDocument(filename);

        String font = StandardFonts.COURIER;
        EmbeddingStrategy embeddingStrategy = EmbeddingStrategy.PREFER_NOT_EMBEDDED;

        //All those encodings actually the same winansi.
        addPagesWithFonts(pdfDoc, font, null, embeddingStrategy, TextSetWithABC);
        addPagesWithFonts(pdfDoc, font, "", embeddingStrategy, TextSetWithABC);
        addPagesWithFonts(pdfDoc, font, "WinAnsi", embeddingStrategy, TextSetWithABC);
        addPagesWithFonts(pdfDoc, font, "WinAnsiEncoding", embeddingStrategy, TextSetWithABC);

        pdfDoc.close();

        Assertions.assertEquals(1, countPdfFonts(filename));
        Assertions.assertNull(new CompareTool().compareByContent(filename, cmpFilename, destinationFolder, "diff_"));
    }

    @Test
    public void createDocumentWithAbserifAndIdentityHEncodings() throws IOException, InterruptedException {
        String testName = "DocumentWithAbserifAndIdentityHEncodings";

        String filename = destinationFolder + testName + ".pdf";
        String cmpFilename = sourceFolder + "cmp_" + testName + ".pdf";
        PdfDocument pdfDoc = createDocument(filename);

        String font = fontsFolder + "abserif4_5.ttf";

        //All those encodings actually the same Identity-H.
        addPagesWithFonts(pdfDoc, font, null, TextSetWithABC);
        addPagesWithFonts(pdfDoc, font, "", TextSetWithABC);
        addPagesWithFonts(pdfDoc, font, PdfEncodings.IDENTITY_H, TextSetWithABC);

        pdfDoc.close();

        Assertions.assertEquals(1, countPdfFonts(filename));
        Assertions.assertNull(new CompareTool().compareByContent(filename, cmpFilename, destinationFolder, "diff_"));
    }

    @Test
    public void createDocumentWithEmbeddedAbserifFirstWinAnsiThenIdentityHEncodings() throws IOException, InterruptedException {
        String testName = "DocumentWithEmbeddedAbserifFirstWinAnsiThenIdentityHEncodings";

        String filename = destinationFolder + testName + ".pdf";
        String cmpFilename = sourceFolder + "cmp_" + testName + ".pdf";
        PdfDocument pdfDoc = createDocument(filename);

        String font = fontsFolder + "abserif4_5.ttf";

        addPagesWithFonts(pdfDoc, font, PdfEncodings.WINANSI, TextSetWithABC);
        addPagesWithFonts(pdfDoc, font, "", TextSetWithABC);
        pdfDoc.close();

        Assertions.assertEquals(2, countPdfFonts(filename));
        Assertions.assertNull(new CompareTool().compareByContent(filename, cmpFilename, destinationFolder, "diff_"));
    }

    @Test
    public void createDocumentWithEmbeddedAbserifFirstIdentityHThenWinAnsiEncodings() throws IOException, InterruptedException {
        String testName = "DocumentWithEmbeddedAbserifFirstIdentityHThenWinAnsiEncodings";

        String filename = destinationFolder + testName + ".pdf";
        String cmpFilename = sourceFolder + "cmp_" + testName + ".pdf";
        PdfDocument pdfDoc = createDocument(filename);

        String font = fontsFolder + "abserif4_5.ttf";

        addPagesWithFonts(pdfDoc, font, "", TextSetWithABC);
        addPagesWithFonts(pdfDoc, font, PdfEncodings.WINANSI, TextSetWithABC);
        pdfDoc.close();

        Assertions.assertEquals(2, countPdfFonts(filename));
        Assertions.assertNull(new CompareTool().compareByContent(filename, cmpFilename, destinationFolder, "diff_"));
    }

    @Test
    public void createDocumentWithNotEmbeddedAbserifFirstWinAnsiThenIdentityHEncodings() throws IOException, InterruptedException {
        String testName = "DocumentWithNotEmbeddedAbserifFirstWinAnsiThenIdentityHEncodings";

        String filename = destinationFolder + testName + ".pdf";
        String cmpFilename = sourceFolder + "cmp_" + testName + ".pdf";
        PdfDocument pdfDoc = createDocument(filename);

        String font = fontsFolder + "abserif4_5.ttf";
        EmbeddingStrategy embeddingStrategy = EmbeddingStrategy.PREFER_NOT_EMBEDDED;

        addPagesWithFonts(pdfDoc, font, PdfEncodings.WINANSI, embeddingStrategy, TextSetWithABC);
        addPagesWithFonts(pdfDoc, font, "", embeddingStrategy, TextSetWithABC);
        pdfDoc.close();

        Assertions.assertEquals(2, countPdfFonts(filename));
        Assertions.assertNull(new CompareTool().compareByContent(filename, cmpFilename, destinationFolder, "diff_"));
    }

    @Test
    public void createDocumentWithNotEmbeddedAbserifFirstIdentityHThenWinAnsiEncodings() throws IOException, InterruptedException {
        String testName = "DocumentWithNotEmbeddedAbserifFirstIdentityHThenWinAnsiEncodings";

        String filename = destinationFolder + testName + ".pdf";
        String cmpFilename = sourceFolder + "cmp_" + testName + ".pdf";
        PdfDocument pdfDoc = createDocument(filename);

        String font = fontsFolder + "abserif4_5.ttf";
        EmbeddingStrategy embeddingStrategy = EmbeddingStrategy.PREFER_NOT_EMBEDDED;

        addPagesWithFonts(pdfDoc, font, "", embeddingStrategy, TextSetWithABC);
        addPagesWithFonts(pdfDoc, font, PdfEncodings.WINANSI, embeddingStrategy, TextSetWithABC);
        pdfDoc.close();

        Assertions.assertEquals(2, countPdfFonts(filename));
        Assertions.assertNull(new CompareTool().compareByContent(filename, cmpFilename, destinationFolder, "diff_"));
    }

    @Test
    public void createDocumentWithTimesBoldAndMacRomanEncodings() throws IOException, InterruptedException {
        String testName = "DocumentTimesBoldAndMacRomanEncodings";

        String filename = destinationFolder + testName + ".pdf";
        String cmpFilename = sourceFolder + "cmp_" + testName + ".pdf";
        PdfDocument pdfDoc = createDocument(filename);

        String font = StandardFonts.TIMES_BOLD;

        //All those encodings actually the same MacRoman.
        addPagesWithFonts(pdfDoc, font, "MacRoman", TextSetWithABC);
        addPagesWithFonts(pdfDoc, font, "MacRomanEncoding", TextSetWithABC);

        pdfDoc.close();

        Assertions.assertEquals(1, countPdfFonts(filename));
        Assertions.assertNull(new CompareTool().compareByContent(filename, cmpFilename, destinationFolder, "diff_"));
    }

    @Test
    public void createDocumentWithTrueTypeAsType0DefaultEncoding() throws IOException, InterruptedException {
        String testName = "DocumentWithTrueTypeAsType0DefaultEncoding";

        String filename = destinationFolder + testName + ".pdf";
        String cmpFilename = sourceFolder + "cmp_" + testName + ".pdf";
        PdfDocument pdfDoc = createDocument(filename);

        String font = fontsFolder + "abserif4_5.ttf";
        String encoding = null;

        addPagesWithFonts(pdfDoc, font, encoding, TextSetWithABC);
        addPagesWithFonts(pdfDoc, font, encoding, TextSetWithABC);
        addPagesWithFonts(pdfDoc, font, encoding, TextSetWithABC);
        pdfDoc.close();

        Assertions.assertEquals(1, countPdfFonts(filename));
        Assertions.assertNull(new CompareTool().compareByContent(filename, cmpFilename, destinationFolder, "diff_"));
    }

    @Test
    public void createDocumentWithTrueTypeAsTrueType() throws IOException, InterruptedException {
        String testName = "DocumentWithTrueType";

        String filename = destinationFolder + testName + ".pdf";
        String cmpFilename = sourceFolder + "cmp_" + testName + ".pdf";
        PdfDocument pdfDoc = createDocument(filename);

        String font = fontsFolder + "abserif4_5.ttf";
        String encoding = PdfEncodings.WINANSI;

        addPagesWithFonts(pdfDoc, font, encoding, TextSetWithABC);
        addPagesWithFonts(pdfDoc, font, encoding, TextSetWithABC);
        addPagesWithFonts(pdfDoc, font, encoding, TextSetWithABC);
        pdfDoc.close();

        Assertions.assertEquals(1, countPdfFonts(filename));
        Assertions.assertNull(new CompareTool().compareByContent(filename, cmpFilename, destinationFolder, "diff_"));
    }

    @Test
    public void createDocumentWithTrueTypeFlushed() throws IOException, InterruptedException {
        String testName = "DocumentWithTrueTypeFlushed";

        String filename = destinationFolder + testName + ".pdf";
        String cmpFilename = sourceFolder + "cmp_" + testName + ".pdf";
        PdfDocument pdfDoc = createDocument(filename);

        String font = fontsFolder + "abserif4_5.ttf";
        String encoding = null;

        addPagesWithFonts(pdfDoc, font, encoding, TextSetWithABC);
        pdfDoc.flushFonts();
        addPagesWithFonts(pdfDoc, font, encoding, TextSetWithABC);
        pdfDoc.flushFonts();
        addPagesWithFonts(pdfDoc, font, encoding, TextSetWithABC);
        pdfDoc.close();

        //Flushed fonts cannot be reused.
        //For some reason Acrobat shows only one font in Properties.
        //RUPS shows 3 instances of the same font.
        Assertions.assertEquals(3, countPdfFonts(filename));
        Assertions.assertNull(new CompareTool().compareByContent(filename, cmpFilename, destinationFolder, "diff_"));
    }

    @Test
    public void createDocumentWithTrueTypeAsType0() throws IOException, InterruptedException {
        String testName = "DocumentWithTrueTypeAsType0";

        String filename = destinationFolder + testName + ".pdf";
        String cmpFilename = sourceFolder + "cmp_" + testName + ".pdf";
        PdfDocument pdfDoc = createDocument(filename);

        String font = fontsFolder + "abserif4_5.ttf";
        String encoding = "Identity-H";

        addPagesWithFonts(pdfDoc, font, encoding, TextSetWithABC);
        addPagesWithFonts(pdfDoc, font, encoding, TextSetWithABC);
        addPagesWithFonts(pdfDoc, font, encoding, TextSetWithABC);
        pdfDoc.close();

        Assertions.assertEquals(1, countPdfFonts(filename));
        Assertions.assertNull(new CompareTool().compareByContent(filename, cmpFilename, destinationFolder, "diff_"));
    }

    @Test
    public void createDocumentWithTrueTypeAsType0Flushed() throws IOException, InterruptedException {
        String testName = "DocumentWithTrueTypeAsType0Flushed";

        String filename = destinationFolder + testName + ".pdf";
        String cmpFilename = sourceFolder + "cmp_" + testName + ".pdf";
        PdfDocument pdfDoc = createDocument(filename);

        String font = fontsFolder + "abserif4_5.ttf";
        String encoding = "Identity-H";

        addPagesWithFonts(pdfDoc, font, encoding, TextSetWithABC);
        pdfDoc.flushFonts();
        addPagesWithFonts(pdfDoc, font, encoding, TextSetWithABC);
        pdfDoc.flushFonts();
        addPagesWithFonts(pdfDoc, font, encoding, TextSetWithABC);
        pdfDoc.close();

        //Flushed fonts cannot be reused.
        Assertions.assertEquals(3, countPdfFonts(filename));
        Assertions.assertNull(new CompareTool().compareByContent(filename, cmpFilename, destinationFolder, "diff_"));
    }

    @Test
    public void createDocumentWithOpenTypeAsType0() throws IOException, InterruptedException {
        String testName = "DocumentWithOpenTypeAsType0";

        String filename = destinationFolder + testName + ".pdf";
        String cmpFilename = sourceFolder + "cmp_" + testName + ".pdf";
        PdfDocument pdfDoc = createDocument(filename);

        String font = fontsFolder + "NotoSansCJKjp-Bold.otf";
        String encoding = "Identity-H";

        addPagesWithFonts(pdfDoc, font, encoding, TextSetInternational);
        addPagesWithFonts(pdfDoc, font, encoding, TextSetInternational);
        addPagesWithFonts(pdfDoc, font, encoding, TextSetInternational);
        pdfDoc.close();

        Assertions.assertEquals(1, countPdfFonts(filename));
        Assertions.assertNull(new CompareTool().compareByContent(filename, cmpFilename, destinationFolder, "diff_"));
    }

    @Test
    public void createDocumentWithOpenTypeAsType0Flushed() throws IOException, InterruptedException {
        String testName = "DocumentWithOpenTypeAsType0Flushed";

        String filename = destinationFolder + testName + ".pdf";
        String cmpFilename = sourceFolder + "cmp_" + testName + ".pdf";
        PdfDocument pdfDoc = createDocument(filename);

        String font = fontsFolder + "NotoSansCJKjp-Bold.otf";
        String encoding = "Identity-H";

        addPagesWithFonts(pdfDoc, font, encoding, TextSetInternational);
        pdfDoc.flushFonts();
        addPagesWithFonts(pdfDoc, font, encoding, TextSetInternational);
        pdfDoc.flushFonts();
        addPagesWithFonts(pdfDoc, font, encoding, TextSetInternational);
        pdfDoc.close();

        //Flushed fonts cannot be reused.
        Assertions.assertEquals(3, countPdfFonts(filename));
        Assertions.assertNull(new CompareTool().compareByContent(filename, cmpFilename, destinationFolder, "diff_"));
    }

    @Test
    public void createDocumentWithHelveticaFromDocument() throws IOException, InterruptedException {
        String testName = "DocumentWithHelveticaFromDocument";

        String input = sourceFolder + "DocumentWithHelvetica.pdf";
        String filename = destinationFolder + testName + ".pdf";
        String cmpFilename = sourceFolder + "cmp_" + testName + ".pdf";

        PdfReader reader = new PdfReader(input);
        PdfWriter writer = CompareTool.createTestPdfWriter(filename).setCompressionLevel(CompressionConstants.NO_COMPRESSION);
        PdfDocument pdfDoc = new PdfDocument(reader, writer);

        String font = StandardFonts.HELVETICA;
        String encoding = "WinAnsiEncoding";

        PdfDictionary fontDict = (PdfDictionary) pdfDoc.getPdfObject(6);
        Assertions.assertEquals(font, fontDict.getAsName(PdfName.BaseFont).getValue());
        Assertions.assertEquals(encoding, fontDict.getAsName(PdfName.Encoding).getValue());


        PdfFont documentFont = PdfFontFactory.createFont(fontDict);

        //Add it to PdfDocument#documentFonts via PdfCanvas.
        PdfCanvas canvas = new PdfCanvas(pdfDoc.addNewPage());
        canvas.beginText()
                .moveText(36, 700)
                .setFontAndSize(documentFont, 20)
                .showText(pangramme.substring(0, pangramme.length()/2))
                .endText()
                .beginText()
                .setFontAndSize(documentFont, 20)
                .moveText(36, 670)
                .showText(pangramme.substring(pangramme.length()/2))
                .endText()
                .release();

        //There is only one just loaded and used document font.
        Assertions.assertEquals(1, pdfDoc.getDocumentFonts().size());

        addPagesWithFonts(pdfDoc, font, "WinAnsi", TextSetWithABC);
        addPagesWithFonts(pdfDoc, font, null, TextSetWithABC);
        pdfDoc.close();

        //We cannot rely on font name for a document font, so we treat them as two different fonts.
        //However we're trying to detect standard fonts in this case, so it will work.
        Assertions.assertEquals(1, countPdfFonts(filename));
        Assertions.assertNull(new CompareTool().compareByContent(filename, cmpFilename, destinationFolder, "diff_"));
    }

    @Test
    public void createDocumentWithHelveticaFromDocumentWithWrongEncoding() throws IOException, InterruptedException {
        String testName = "DocumentWithHelveticaFromDocumentWithWrongEncoding";

        String input = sourceFolder + "DocumentWithHelvetica.pdf";
        String filename = destinationFolder + testName + ".pdf";
        String cmpFilename = sourceFolder + "cmp_" + testName + ".pdf";

        PdfReader reader = new PdfReader(input);
        PdfWriter writer = CompareTool.createTestPdfWriter(filename).setCompressionLevel(CompressionConstants.NO_COMPRESSION);
        PdfDocument pdfDoc = new PdfDocument(reader, writer);

        String font = StandardFonts.HELVETICA;
        String encoding = "WinAnsiEncoding";

        PdfDictionary fontDict = (PdfDictionary) pdfDoc.getPdfObject(6);
        Assertions.assertEquals(font, fontDict.getAsName(PdfName.BaseFont).getValue());
        Assertions.assertEquals(encoding, fontDict.getAsName(PdfName.Encoding).getValue());


        PdfFont documentFont = PdfFontFactory.createFont(fontDict);

        //Add it to PdfDocument#documentFonts via PdfCanvas.
        PdfCanvas canvas = new PdfCanvas(pdfDoc.addNewPage());
        canvas.beginText()
                .moveText(36, 700)
                .setFontAndSize(documentFont, 20)
                .showText(pangramme.substring(0, pangramme.length()/2))
                .endText()
                .beginText()
                .setFontAndSize(documentFont, 20)
                .moveText(36, 670)
                .showText(pangramme.substring(pangramme.length()/2))
                .endText()
                .release();

        //There is only one just loaded and used document font.
        Assertions.assertEquals(1, pdfDoc.getDocumentFonts().size());

        addPagesWithFonts(pdfDoc, font, null, TextSetWithABC);
        addPagesWithFonts(pdfDoc, font, "MacRoman", TextSetWithABC);
        pdfDoc.close();

        //Two different encodings were used -> two fonts are expected.
        Assertions.assertEquals(2, countPdfFonts(filename));
        Assertions.assertNull(new CompareTool().compareByContent(filename, cmpFilename, destinationFolder, "diff_"));
    }
    @Test
    public void createDocumentWithTrueTypeAboriginalFromDocument() throws IOException, InterruptedException {
        String testName = "DocumentWithTrueTypeAboriginalFromDocument";

        String input = sourceFolder + "DocumentWithTrueTypeAboriginalSerif.pdf";
        String filename = destinationFolder + testName + ".pdf";
        String cmpFilename = sourceFolder + "cmp_" + testName + ".pdf";

        PdfReader reader = new PdfReader(input);
        PdfWriter writer = CompareTool.createTestPdfWriter(filename).setCompressionLevel(CompressionConstants.NO_COMPRESSION);
        PdfDocument pdfDoc = new PdfDocument(reader, writer);

        String font = "AboriginalSerif";
        String encoding = "WinAnsiEncoding";

        PdfDictionary fontDict = (PdfDictionary) pdfDoc.getPdfObject(6);
        Assertions.assertEquals(font, fontDict.getAsName(PdfName.BaseFont).getValue());
        Assertions.assertEquals(encoding, fontDict.getAsName(PdfName.Encoding).getValue());

        PdfFont documentFont = PdfFontFactory.createFont(fontDict);

        //Add it to PdfDocument#documentFonts via PdfCanvas.
        PdfCanvas canvas = new PdfCanvas(pdfDoc.addNewPage());
        canvas.beginText()
                .moveText(36, 700)
                .setFontAndSize(documentFont, 20)
                .showText(pangramme.substring(0, pangramme.length()/2))
                .endText()
                .beginText()
                .setFontAndSize(documentFont, 20)
                .moveText(36, 670)
                .showText(pangramme.substring(pangramme.length()/2))
                .endText()
                .release();

        //There is only one just loaded and used document font.
        Assertions.assertEquals(1, pdfDoc.getDocumentFonts().size());

        addPagesWithFonts(pdfDoc, fontsFolder + "abserif4_5.ttf", "WinAnsi", TextSetWithABC);
        pdfDoc.close();

        //We cannot rely on font name for a document font, so we treat them as two different fonts.
        Assertions.assertEquals(2, countPdfFonts(filename));
        Assertions.assertNull(new CompareTool().compareByContent(filename, cmpFilename, destinationFolder, "diff_"));
    }

    @Test
    public void createDocumentWithType1NotoFromDocument() throws IOException, InterruptedException {
        String testName = "DocumentWithType1NotoFromDocument";

        String input = sourceFolder + "DocumentWithType1Noto.pdf";
        String filename = destinationFolder + testName + ".pdf";
        String cmpFilename = sourceFolder + "cmp_" + testName + ".pdf";

        PdfReader reader = new PdfReader(input);
        PdfWriter writer = CompareTool.createTestPdfWriter(filename).setCompressionLevel(CompressionConstants.NO_COMPRESSION);
        PdfDocument pdfDoc = new PdfDocument(reader, writer);

        String font = "NotoSansCJKjp-Bold";
        String encoding = "WinAnsiEncoding";

        PdfDictionary fontDict = (PdfDictionary) pdfDoc.getPdfObject(6);
        Assertions.assertEquals(font, fontDict.getAsName(PdfName.BaseFont).getValue());
        Assertions.assertEquals(encoding, fontDict.getAsName(PdfName.Encoding).getValue());

        PdfFont documentFont = PdfFontFactory.createFont(fontDict);

        //Add it to PdfDocument#documentFonts via PdfCanvas.
        PdfCanvas canvas = new PdfCanvas(pdfDoc.addNewPage());
        canvas.beginText()
                .moveText(36, 700)
                .setFontAndSize(documentFont, 20)
                .showText(pangramme.substring(0, pangramme.length()/2))
                .endText()
                .beginText()
                .setFontAndSize(documentFont, 20)
                .moveText(36, 670)
                .showText(pangramme.substring(pangramme.length()/2))
                .endText()
                .release();

        //There is only one just loaded and used document font.
        Assertions.assertEquals(1, pdfDoc.getDocumentFonts().size());

        addPagesWithFonts(pdfDoc, fontsFolder + "NotoSansCJKjp-Bold.otf", encoding,
                EmbeddingStrategy.FORCE_NOT_EMBEDDED, TextSetWithABC);
        pdfDoc.close();

        //We cannot rely on font name for a document font, so we treat them as two different fonts.
        Assertions.assertEquals(2, countPdfFonts(filename));
        Assertions.assertNull(new CompareTool().compareByContent(filename, cmpFilename, destinationFolder, "diff_"));
    }

    @Test
    public void createDocumentWithType0AboriginalFromDocument1() throws IOException, InterruptedException {
        String testName = "DocumentWithType0AboriginalFromDocument";

        String input = sourceFolder + "DocumentWithType0AboriginalSerif.pdf";
        String filename = destinationFolder + testName + ".pdf";
        String cmpFilename = sourceFolder + "cmp_" + testName + ".pdf";

        PdfReader reader = new PdfReader(input);
        PdfWriter writer = CompareTool.createTestPdfWriter(filename).setCompressionLevel(CompressionConstants.NO_COMPRESSION);
        PdfDocument pdfDoc = new PdfDocument(reader, writer);

        String font = "AboriginalSerif";
        String encoding = "Identity-H";

        PdfDictionary fontDict = (PdfDictionary) pdfDoc.getPdfObject(6);
        Assertions.assertEquals(font, fontDict.getAsName(PdfName.BaseFont).getValue());
        Assertions.assertEquals(encoding, fontDict.getAsName(PdfName.Encoding).getValue());

        PdfFont documentFont = PdfFontFactory.createFont(fontDict);

        //Add it to PdfDocument#documentFonts via PdfCanvas.
        PdfCanvas canvas = new PdfCanvas(pdfDoc.addNewPage());
        canvas.beginText()
                .moveText(36, 700)
                .setFontAndSize(documentFont, 20)
                .showText(pangramme.substring(0, pangramme.length()/2))
                .endText()
                .beginText()
                .setFontAndSize(documentFont, 20)
                .moveText(36, 670)
                .showText(pangramme.substring(pangramme.length()/2))
                .endText()
                .release();

        //There is only one just loaded and used document font.
        Assertions.assertEquals(1, pdfDoc.getDocumentFonts().size());

        addPagesWithFonts(pdfDoc, fontsFolder + "abserif4_5.ttf", encoding, TextSetWithABC);
        pdfDoc.close();

        //We cannot rely on font name for a document font, so we treat them as two different fonts.
        Assertions.assertEquals(2, countPdfFonts(filename));
        Assertions.assertNull(new CompareTool().compareByContent(filename, cmpFilename, destinationFolder, "diff_"));
    }

    @Test
    public void createDocumentWithType0NotoFromDocument1() throws IOException, InterruptedException {
        String testName = "DocumentWithType0NotoFromDocument";

        String input = sourceFolder + "DocumentWithType0Noto.pdf";
        String filename = destinationFolder + testName + ".pdf";
        String cmpFilename = sourceFolder + "cmp_" + testName + ".pdf";

        PdfReader reader = new PdfReader(input);
        PdfWriter writer = CompareTool.createTestPdfWriter(filename).setCompressionLevel(CompressionConstants.NO_COMPRESSION);
        PdfDocument pdfDoc = new PdfDocument(reader, writer);

        String encoding = "Identity-H";
        String font = "NotoSansCJKjp-Bold-" + encoding;

        PdfDictionary fontDict = (PdfDictionary) pdfDoc.getPdfObject(6);
        Assertions.assertEquals(font, fontDict.getAsName(PdfName.BaseFont).getValue());
        Assertions.assertEquals(encoding, fontDict.getAsName(PdfName.Encoding).getValue());

        PdfFont documentFont = PdfFontFactory.createFont(fontDict);

        //Add it to PdfDocument#documentFonts via PdfCanvas.
        PdfCanvas canvas = new PdfCanvas(pdfDoc.addNewPage());
        canvas.beginText()
                .moveText(36, 700)
                .setFontAndSize(documentFont, 20)
                .showText(pangramme.substring(0, pangramme.length()/2))
                .endText()
                .beginText()
                .setFontAndSize(documentFont, 20)
                .moveText(36, 670)
                .showText(pangramme.substring(pangramme.length()/2))
                .endText()
                .release();

        //There is only one just loaded and used document font.
        Assertions.assertEquals(1, pdfDoc.getDocumentFonts().size());

        addPagesWithFonts(pdfDoc, fontsFolder + "NotoSansCJKjp-Bold.otf", PdfEncodings.WINANSI,
                EmbeddingStrategy.PREFER_NOT_EMBEDDED, TextSetWithABC);
        pdfDoc.close();

        //We cannot rely on font name for a document font, so we treat them as two different fonts.
        Assertions.assertEquals(2, countPdfFonts(filename));
        Assertions.assertNull(new CompareTool().compareByContent(filename, cmpFilename, destinationFolder, "diff_"));
    }

    @Test
    public void createDocumentWithType3Font() throws IOException, InterruptedException {
        String testName = "DocumentWithType3Font";

        String filename = destinationFolder + testName + ".pdf";
        String cmpFilename = sourceFolder + "cmp_" + testName + ".pdf";

        PdfDocument pdfDoc = createDocument(filename);

        PdfType3Font type3Font = PdfFontFactory.createType3Font(pdfDoc, false);
        Type3Glyph type3Glyph = type3Font.addGlyph('A', 600, 0, 0, 600, 700);
        type3Glyph.setLineWidth(100);
        type3Glyph.moveTo(5, 5);
        type3Glyph.lineTo(300, 695);
        type3Glyph.lineTo(595, 5);
        type3Glyph.closePathFillStroke();

        PdfPage page = pdfDoc.addNewPage();

        PdfCanvas canvas = new PdfCanvas(page);
        canvas.saveState()
                .beginText()
                .setFontAndSize(type3Font, 36)
                .moveText(50, 700)
                .showText("AA")
                .endText();

        type3Font = PdfFontFactory.createType3Font(pdfDoc, false);
        type3Glyph = type3Font.addGlyph('A', 600, 0, 0, 600, 700);
        type3Glyph.setLineWidth(100);
        type3Glyph.moveTo(5, 5);
        type3Glyph.lineTo(300, 695);
        type3Glyph.lineTo(595, 5);
        type3Glyph.closePathFillStroke();

        canvas = new PdfCanvas(page);
        canvas.saveState()
                .beginText()
                .setFontAndSize(type3Font, 36)
                .moveText(50, 650)
                .showText("AAA")
                .endText();

        pdfDoc.close();

        //PdfType3Font comparing returns false;
        Assertions.assertEquals(2, countPdfFonts(filename));

        // reading and comparing text
        Assertions.assertNull(new CompareTool().compareByContent(filename, cmpFilename, destinationFolder, "diff_"));
    }

    private void addPagesWithFonts(PdfDocument pdfDoc, String fontProgram, String fontEncoding, String[] text) throws IOException {
        addPagesWithFonts(pdfDoc, fontProgram, fontEncoding, EmbeddingStrategy.PREFER_EMBEDDED, text);
    }

    private void addPagesWithFonts(PdfDocument pdfDoc, String fontProgram, String fontEncoding,
            EmbeddingStrategy embeddingStrategy, String[] text) throws IOException {
        final int top = 700;
        for (String t : text) {
            PdfPage page = pdfDoc.addNewPage();
            PdfCanvas canvas = new PdfCanvas(page);
            canvas.saveState()
                    .beginText()
                    .moveText(36, top)
                    .setFontAndSize(
                            PdfFontFactory.createFont(fontProgram, fontEncoding, embeddingStrategy, pdfDoc), 72)
                    .showText(t)
                    .endText()
                    .restoreState();

            canvas.release();
            page.flush();
        }
    }

    private int countPdfFonts(String filename) throws IOException {
        PdfReader reader = CompareTool.createOutputReader(filename);
        PdfDocument pdfDoc = new PdfDocument(reader);
        Set<PdfIndirectReference> fonts = new HashSet<>();
        for (int i = 1; i <= pdfDoc.getNumberOfPages(); i++) {
            PdfPage page = pdfDoc.getPage(i);
            for (PdfObject value : page.getResources().getResource(PdfName.Font).values()) {
                fonts.add(value.getIndirectReference());
            }
        }
        return fonts.size();
    }

    private PdfDocument createDocument(String filename) throws IOException {
        PdfWriter writer = CompareTool.createTestPdfWriter(filename).setCompressionLevel(CompressionConstants.NO_COMPRESSION);
        return new PdfDocument(writer);
    }
}