如何在 iText7 中重用一个 pdf 中的另一个 pdf 字体?

How to reuse font from one pdf in another in iText7?

我正在尝试在 iText7 中打开 PDF 文件,在其中写入一些新文本,将原始 PDF 中的字体应用到其中并将其保存在另一个 PDF 文档中。我正在使用 Java 1.8

因此,我需要一组原始 pdf 中使用的字体名称,用户可以从中选择一个,并将其应用于新段落。 而且我还需要以某种方式应用这种字体。

现在我有这段代码,我从 here:

public static void main(String[] args) throws IOException {
        PdfDocument pdf = new PdfDocument(new PdfReader("example.pdf"));
        Set<PdfName> fonts = listAllUsedFonts(pdf);
        fonts.stream().forEach(System.out::println);
}

public static Set<PdfName> listAllUsedFonts(PdfDocument pdfDoc) throws IOException {
        PdfDictionary acroForm = pdfDoc.getCatalog().getPdfObject().getAsDictionary(PdfName.AcroForm);
        if (acroForm == null) {
            return null;
        }
        PdfDictionary dr = acroForm.getAsDictionary(PdfName.DR);
        if (dr == null) {
            return null;
        }
        PdfDictionary font = dr.getAsDictionary(PdfName.Font);
        if (font == null) {
            return null;
        }
        return font.keySet();
    }

它returns这个输出:

/Helv
/ZaDb

但是,example.pdf 唯一的字体是 Verdana(这是 Adob​​e Acrobat Pro 中的文档属性所说的)。此外,还有两种实现方式的 Verdana:Bold 和 normal。

所以,我有这些问题:

  1. 为什么这个函数 returns 两种字体而不是一种 (Verdana)。
  2. 我怎样才能生成正常的好读的字体名称来显示它们 给用户(例如 Helvetica 而不是 Helv)?
  3. 如何将从原始文档中获取的字体应用到 新段落?

提前致谢!

您不应在另一个 PDF 中重复使用一个 PDF 中的字体,原因如下:字体几乎不会完全嵌入到 PDF 文档中。例如:您使用字体 Verdana regular (238 KB) 和字体 Verdana bold (207 KB),但是当您创建一个简单的 PDF 文档时,用常规和粗体显示 "Hello World",文件大小将比238 + 207 KB。为什么是这样?因为 PDF 仅包含 Verdana regular 字体的 子集 和 Verdana bold 字体的 子集

You may have noticed that I am talking of the font Verdana regular and the font Verdana bold. Those are two different fonts from the same font family. Reading your question, I notice that you don't make that distinction. You talk about the font Verdana with two implementations bold and normal. This is incorrect. You should talk about the font family Verdana and two fonts Verdana bold and Verdana regular.

PDF 通常包含不同字体的子集。它甚至可以包含 相同字体 的两个不同子集。另见 What are the extra characters in the font name of my PDF?

您的目标是获取一个 PDF 的字体并使用另一个 PDF 的字体。但是,假设您的原始 PDF 仅包含写入 "Hello World" 所需的子集,并且您想创建一个新的 PDF 说 "Hello Universe." 那将永远行不通,因为该子集不包含字形渲染字母 Univrs.

Also take into account that fonts are usually licensed. Many fonts have a license that states that you can use to font to create a document and embed that font in that document. However, there is often a clause that says that other people are not allowed to extract to font to use it in a different context. For instance: you paid for the font when you purchased a copy of MS Windows, but someone who receives a PDF containing that font may not have a license to use that font. See

鉴于与您的问题相关的技术和法律问题,我认为处理代码示例没有意义。你的设计有缺陷。您应该使用获得许可的字体程序,而不是尝试从现有 PDF 中提取字体。这回答了问题 3:如何将从原始文档中获取的字体应用到新段落中?你不能:这是法律禁止的(参见下面的额外信息),如果子集不包含你需要的所有字符,这在技术上是不可能的!

此外,您在官方 iText 网站上找到的示例会查找 form 中定义的字体。 /HelvZaDb 指的是 Helvetica 和 Zapfdingbats。这些是一组 14 种字体中的两种字体,称为 Standard Type 1 字体。这些字体从未嵌入 文档中,因为每个查看者都应该知道如何呈现它们。如果你想使用这些字体,你不需要一个完整的字体程序;字体指标就足够了。例如:iText 附带 14 个 AFM 文件(AFM = Adob​​e Font Metrics),其中包含字体规格。

您想知道为什么找不到 Verdana,因为 Verdana 用作文档中文本的字体,但您看错了地方。您要求 iText 提供用于表单的字体,而不是文本中使用的字体。本回答问题 1:为什么这个函数 returns 两种字体而不是一种 (Verdana)。

关于你的问题2:你正在查看字体的内部名称内部名称可以是任何东西(甚至 /F1/F2、...)。字体的后记名称存储在字体字典中。这就是你需要的名字。

额外信息:

我检查了 Verdana 许可证:

Microsoft supplied font. You may use this font to create, display, and print content as permitted by the license terms or terms of use, of the Microsoft product, service, or content in which this font was included. You may only (i) embed this font in content as permitted by the embedding restrictions included in this font; and (ii) temporarily download this font to a printer or other output device to help print content. Any other use is prohibited.

禁止使用您要使用的字体。如果您拥有 Verdana 许可证,则可以将字体嵌入 PDF 中。但是,不允许提取该字体并将其用于其他目的。需要使用原版字体程序

如果您只想显示正在使用的字体的名称(这是法律允许的),您可以使用以下代码:

public void go() throws IOException {

    final Set<String> usedFontNames = new HashSet<>();
    IEventListener fontNameExtractionStrategy = new IEventListener() {
        @Override
        public void eventOccurred(IEventData iEventData, EventType eventType) {
            if(iEventData instanceof TextRenderInfo)
            {
                TextRenderInfo tri = (TextRenderInfo) iEventData;
                String fontName = tri.getFont().getFontProgram().getFontNames().getFontName();
                usedFontNames.add(fontName);
            }
        }
        @Override
        public Set<EventType> getSupportedEvents() {
            return null;
        }
    };

    PdfCanvasProcessor parser = new PdfCanvasProcessor(fontNameExtractionStrategy);

    File inputFile = new File("YOUR_INPUT_FILE_HERE.pdf");
    PdfDocument pdfDocument = new PdfDocument(new PdfReader(inputFile));
    for(int i=1;i<=pdfDocument.getNumberOfPages();i++)
    {
        parser.processPageContent(pdfDocument.getPage(i));
    }
    pdfDocument.close();

    for(String fontName : usedFontNames)
    {
        System.out.println(fontName);
    }
}