gpt4 book ai didi

java - 用于注释的pdfbox嵌入子集字体

转载 作者:行者123 更新时间:2023-12-04 07:47:47 27 4
gpt4 key购买 nike

我正在尝试使用 Apache PDFBOX v2.0.21 来修改现有的 PDF 文档,添加签名和注释。这意味着我正在积极使用增量保存模式。我还嵌入了 LiberationSans 字体以容纳一些 Unicode 字符。对我来说使用 PDF 嵌入字体的子集功能是有意义的,因为完全嵌入 LiberationSans 使 PDF 文件的边长约 200+ KB。
经过多次试验和错误,我终于设法使一些工作 - 除了字体子集之外。我这样做的方法是使用一次初始化 PDFont 对象

  try (InputStream fs = PDFService.class.getResourceAsStream("/static/fonts/LiberationSans-Regular.ttf")) {
_font = PDType0Font.load(pddoc, fs, true);
}
然后使用自定义外观流来显示文本。
   private void addAnnotation(String name, PDDocument doc, PDPage page, float x, float y, String text) throws IOException {

List<PDAnnotation> annotations = page.getAnnotations();

PDAnnotationRubberStamp t = new PDAnnotationRubberStamp();

t.setAnnotationName(name); // might play important role
t.setPrinted(true); // always visible
t.setReadOnly(true); // does not interact with user
t.setContents(text);

PDRectangle rect = ....;
t.setRectangle(rect);

PDAppearanceDictionary ap = new PDAppearanceDictionary();
ap.setNormalAppearance(createAppearanceStream(doc, t));
ap.getCOSObject().setNeedToBeUpdated(true);
t.setAppearance(ap);

annotations.add(t);
page.setAnnotations(annotations);

t.getCOSObject().setNeedToBeUpdated(true);
page.getResources().getCOSObject().setNeedToBeUpdated(true);
page.getCOSObject().setNeedToBeUpdated(true);
doc.getDocumentCatalog().getPages().getCOSObject().setNeedToBeUpdated(true);
doc.getDocumentCatalog().getCOSObject().setNeedToBeUpdated(true);
}

private PDAppearanceStream createAppearanceStream(final PDDocument document, PDAnnotation ann) throws IOException
{
PDAppearanceStream aps = new PDAppearanceStream(document);
PDRectangle rect = ann.getRectangle();
rect = new PDRectangle(0, 0, rect.getWidth(), rect.getHeight());
aps.setBBox(rect); // set bounding box to the dimensions of the annotation itself

// embed our unicode font (NB: yes, this needs to be done otherwise aps.getResources() == null which will cause NPE later during setFont)
PDResources res = new PDResources();
_fontName = res.add(_font).getName();
aps.setResources(res);

PDAppearanceContentStream apsContent = null;

try {
// draw directly on the XObject's content stream
apsContent = new PDAppearanceContentStream(aps);

apsContent.beginText();
apsContent.setFont(_font, _fontSize);
apsContent.showText(ann.getContents());
apsContent.endText();
}
finally {
if (apsContent != null) {
try { apsContent.close(); } catch (Exception ex) { log.error(ex.getMessage(), ex); }
}
}

aps.getResources().getCOSObject().setNeedToBeUpdated(true);
aps.getCOSObject().setNeedToBeUpdated(true);
return aps;
}
这段代码运行,但创建了一个带有点而不是实际字符的 PDF,我猜这意味着字体子集尚未嵌入。此外,我收到以下警告:

2021-04-17 12:33:31.326 WARN 20820 --- [ main]o.a.p.pdmodel.PDAbstractContentStream : attempting to use subsetfont LiberationSans without proper context


在查看源代码后,我得到并且我想我在创建外观流时搞砸了一些东西 - 不知何故它没有与 PDDocument 连接,并且子集不能正常继续。请注意,当字体完全嵌入时,上面的代码运行良好(即,如果我调用 PDType0Font.load 并将最后一个参数设置为 false)
谁能想到一些提示给我?谢谢!

最佳答案

我不知道——我幸运吗?编程中的幸运常常指向完全错误或误导的事情。不管怎样,如果还有人能指点一下,我的耳朵就大开了……
同样,在查看代码后,我在 PDDocument.save() 中看到了以下内容:

// subset designated fonts
for (PDFont font : fontsToSubset)
{
font.subset();
}
这在我使用的 PDDocument.saveIncremental() 中没有发生。只是为了弄乱代码,我在对我的文档调用 saveIncremental() 之前执行了以下操作:
 _font.subset(); // you can see in the beginning of the question how _font is created
_font.getCOSObject().setNeedToBeUpdated(true);
pddoc.saveIncremental(baos);
信不信由你,但文档已正确保存 - 至少它在 Acrobat Reader DC 和 Chrome & Firefox PDF 查看器中显示正确。请注意,在外观内容流上的 showText() 期间,Unicode 代码点被添加到字体的子集中。
2021 年 4 月 18 日更新 :正如我在评论中提到的,我收到用户的报告,当他们打开修改后的 PDF 文件时,他们开始看到诸如“无法从...中提取嵌入字体 XXXXXX+LiberationSans-Regular”之类的消息。奇怪的是,我在测试期间没有看到这些消息。事实证明,我的 Acrobat Reader DC 副本比他们的新,特别是在连续发布版本 2021.001.20149 中没有显示错误,而在连续发布版本 2020.012.20043 中显示了上述消息。
经过调查,事实证明问题出在我嵌入字体的方式上。我不知道是否存在任何其他方式,而且我对 PDF 规范不太熟悉,不知道其他方式。从上面的代码可以看出,我所做的是为文档加载一次字体,然后在每个注释的外观流的资源字典中自由使用它。因此,注释内容流的所有资源字典都引用了使用 SAME/BaseFont 名称定义的 F1 字体。 PDF 引用,第 3 版。在 p.323 上特别指出:

"... the PostScript name of the font - ... - begins with a tagfollowed by a plus sign (+). The tag consists of exactly six uppercaseletters; the choice of letters is arbitrary, but different subsets inthe same PDF file must have different tags..."


一旦我开始为我的每个注释调用 PDType0Font.load 并在为每个注释创建外观流后调用 subset()(当然还有 setNeedToBeUpdated),我看到 BaseName 属性开始看起来确实不同 - 事实上,更旧的2020 版 Acrobat Reader DC 停止提示。
请注意,除了使用 iText RUPS 检查 PDF 内容外,还可以使用 Foxit PDF 查看器至少确保子集字体名称不同。 Acrobat Reader DC 和 PDF-xChange 在 Properties -> Fonts 中只显示初始字体名称,如 LiberationSans,而不显示 6 个字母的唯一前缀。
2021 年 4 月 19 日更新 我仍在解决这个问题 - 因为我仍然收到有关臭名昭著的“无法提取嵌入字体”消息的报告。该消息的原始原因很可能不是(或不仅)不同子集具有相同 BaseFont 名称的事实。我观察到的一件事是,在某些计算机上,我使用的图章注释会导致 Acrobat Reader DC 自动打开所谓的“评论 Pane ” - 有选项可以关闭此自动功能(首选项 -> 评论 ->打开带有注释的 PDF 时显示注释 Pane )。当此 Pane 手动或自动打开时,会出现错误消息(我很想知道为什么相同版本的 Acrobat Reader DC 对不同机器的行为不同)。我认为 Acrobat Reader 尝试提取字体的完整版本并失败了,因为它只是一个子集。但是,我想,这与文档的语义内容无关 - 文档仍然通过“qpdf --check”。我目前正在尝试寻找是否可以限制图章以不允许评论 - 即某种方法来禁用 Acrobat Reader DC 中的评论 Pane ,尽管我希望渺茫。
2021 年 4 月 20 日更新 开了一个新问题 here

关于java - 用于注释的pdfbox嵌入子集字体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67137356/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com