添加了无法为Acrobat加载正确的pdfbox的字体

2022-07-18 00:00:00 java pdfbox

我正在尝试使用以下代码嵌入字体, 基于Stackoverflow和PDFBOX-2661:

作为Helvetica的替代字体嵌入的字体为DejaVuSans。

// given: PDDocument document, PDAcroForm acroForm

InputStream font_file = ClassLoader.getSystemResourceAsStream("DejaVuSans.ttf");
font = PDType0Font.load(document, font_file);
if (font_file != null) {
    font_file.close();
}
System.err.println("Embedded font 'DejaVuSans.ttf' loaded.");

PDResources resources = acroForm.getDefaultResources();
if (resources == null) {
    resources = new PDResources();
}

resources.put(COSName.getPDFName("Helv"), font);
resources.put(COSName.getPDFName("Helvetica"), font);
// Also use "DejaVuSans.ttf" for "HeBo", "HelveticaBold" and "Helvetica-Bold" in a similar way, but this is left out to keep this short.

acroForm.setDefaultResources(resources);

// let pdfbox handle refreshing the values, now that all the fonts should be there.
acroForm.refreshAppearances();
然而,在acroForm.refreshAppearances()中,它导致了大量的Using fallback font LiberationSans for CID-keyed TrueType font DejaVuSans。稍加调试,在org.apache.pdfbox.pdmodel.font.PDCIDFontType2findFontOrSubstitute中,它试图再次从文件系统加载字体文件&DejaVuSans";,而不是使用提供的资源。由于它是在JAR文件中提供的,而不是从正常的文件系统(系统的字体)中找到的,因此将使用备用字体。

如何使其正确识别并加载字体?

我已经尝试的内容:

我尝试扩展字体加载机制,但由于所有内容都是私有的和/或最终的,我不得不在复制了大约10个未更改的原始代码文件后停止,以便能够访问它们;这必须以不同的方式实现。

直接写入ContentStream似乎使用不同的方式(contentStream.setFont(pdfFont, fontSize)),因此不受影响。


解决方案

PDFBox中当前的AcroForm表单域刷新机制不能真正与尚未设置子集的字体结合使用。

原因是,每当使用字体刷新外观时,都会从某些资源词典中检索到该字体。然而,在这些资源词典中,没有您的原始PDType0Font,而只有支持您的PDType0Font的PDF对象的初步版本。但是这些PDF对象不知道它们支持的是最终将被子集化的字体,所以检索该字体会生成一个新的、不同的PDType0Font对象,该对象声称是非嵌入的。因此,它也不会被告知最终要嵌入的字形。

这也是为什么您使用的PDType0Font.load方法会用提示来记录(JavaDoc注释)如果您要为AcroForm加载字体,请改用3参数构造函数:

/**
 * Loads a TTF to be embedded and subset into a document as a Type 0 font. If you are loading a
 * font for AcroForm, then use the 3-parameter constructor instead.
 *
 * @param doc The PDF document that will hold the embedded font.
 * @param input An input stream of a TrueType font. It will be closed before returning.
 * @return A Type0 font with a CIDFontType2 descendant.
 * @throws IOException If there is an error reading the font stream.
 */
public static PDType0Font load(PDDocument doc, InputStream input) throws IOException

其文档中的3参数构造函数告诉您在AcroForm使用时不要使用字体的子集:

/**
 * Loads a TTF to be embedded into a document as a Type 0 font.
 *
 * @param doc The PDF document that will hold the embedded font.
 * @param input An input stream of a TrueType font. It will be closed before returning.
 * @param embedSubset True if the font will be subset before embedding. Set this to false when
 * creating a font for AcroForm.
 * @return A Type0 font with a CIDFontType2 descendant.
 * @throws IOException If there is an error reading the font stream.
 */
public static PDType0Font load(PDDocument doc, InputStream input, boolean embedSubset)
        throws IOException
但是,即使在设置为时使用3个参数构造函数也不会呈现良好结果。乍一看,渲染的字段看起来还不错:

但你一点击它们,就会发生一些奇怪的事情:

@Tilman,这里可能仍有需要解决的问题。


子集嵌入字体的潜在问题也可能出现在其他上下文中,例如:

try (   PDDocument pdDocument = new PDDocument();
        InputStream font_file = [...]    ) {
    PDType0Font font = PDType0Font.load(pdDocument, font_file);

    PDResources pdResources = new PDResources();
    COSName name = pdResources.add(font);
    PDPage pdPage = new PDPage();
    pdPage.setResources(pdResources);
    pdDocument.addPage(pdPage);

    try (   PDPageContentStream canvas = new PDPageContentStream(pdDocument, pdPage)    ) {
        canvas.setFont(pdResources.getFont(name), 12);
        canvas.beginText();
        canvas.newLineAtOffset(30, 700);
        canvas.showText("Some test text.");
        canvas.endText();
    }

    pdDocument.save("sampleOfType0Issue.pdf");
}

(RefreshAppearances测试testIllustrateType0Issue)

相关文章