【问题标题】:How to reuse font from one pdf in another in iText7?如何在 iText7 中重用一个 pdf 中的字体?
【发布时间】:2018-01-11 03:26:32
【问题描述】:

我正在尝试在 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();
    }

它返回这个输出:

/Helv
/ZaDb

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

所以,我有这些问题:

  1. 为什么此函数返回两种字体而不是一种 (Verdana)。
  2. 如何生成正常可读的字体名称来显示它们 给用户(例如 Helvetica 而不是 Helv)?
  3. 如何将原始文档中的字体应用到 新段落?

提前感谢您!

【问题讨论】:

    标签: java pdf itext7


    【解决方案1】:

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

    你可能已经注意到我说的是 字体 Verdana 常规 字体 Verdana 粗体。这些是两种不同的字体 相同的字体系列。阅读您的问题,我注意到您没有做出这种区分。你谈论 font Verdana 两个实现粗体和正常。这是不正确的。你应该 谈谈 字体系列 Verdana 和两个 字体 Verdana 粗体和 Verdana 常规。

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

    您的目标是采用一个 PDF 的字体并使用另一个 PDF 的字体。但是,假设您的原始 PDF 仅包含编写“Hello World”所需的子集,并且您想创建一个新的 PDF,说“Hello Universe”。这永远不会奏效,因为子集将不包含用于呈现字母 Univrs 的字形。

    还要考虑到字体通常是经过许可的。许多字体 有一个许可证,说明您可以使用字体来创建一个 文档并将该字体嵌入到该文档中。然而,有 通常是一个条款,说不允许其他人 提取 到字体以在不同的上下文中使用它。例如:您在购买 MS Windows 副本时为字体付费,但有人 收到包含该字体的 PDF 的人可能没有使用许可 那个字体。见Does one need to have a license for fonts if we are using ttf files in itext?

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

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

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

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

    额外信息:

    我检查了 Verdana 许可证:

    Microsoft 提供的字体。在许可条款或使用条款允许的情况下,您可以使用此字体来创建、显示和打印包含此字体的 Microsoft 产品、服务或内容的内容。您只能 (i) 在此字体中包含的嵌入限制允许的情况下将此字体嵌入到内容中; (ii) 暂时将此字体下载到打印机或其他输出设备以帮助打印内容。禁止任何其他用途。

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

    【讨论】:

    • 非常感谢您一贯准确的回答。你在理解字体使用的法律问题和字体术语方面帮助了我很多。我将重新设计我的代码,使其具有法律上的适用性和技术上的可执行性。
    【解决方案2】:

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

    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);
        }
    }
    

    【讨论】:

    • 非常感谢您的回答。我现在可以显示用户使用的字体名称。
    猜你喜欢
    • 2017-07-13
    • 2018-09-05
    • 2020-07-10
    • 1970-01-01
    • 2020-06-05
    • 1970-01-01
    • 2016-11-30
    • 2022-01-05
    • 1970-01-01
    相关资源
    最近更新 更多