首页 > 解决方案 > 为什么pdf只包含一个字段大约是500Kb

问题描述

在这里你可以下载带有一个 acroform 字段的 pdf,他的大小正好是 427Kb

如果我删除这个独特的字段,文件只有 3Kb,请问为什么会发生这种情况?我尝试使用 PDF Debugger 进行分析,但对我来说没有什么奇怪的。

在此处输入图像描述

标签: javapdfbox

解决方案


在 acroform 默认资源中嵌入了“Arial”字体,请参阅Root/AcroForm/DR/Font/Arial/FontDescriptor/FontFile2.

您或创建 pdf 的人无缘无故地添加了它。未使用/引用该字体。对于 acroform 默认资源,您可以检查每个字段的 /DA 条目(默认外观)是否包含字体名称。

当您以某种方式删除该字段时,您也从 acroForm 默认资源中删除了字体。(你没有写你是如何删除它的)

这里有一些代码可以做到这一点(空检查大多缺失):

    PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm();
    PDResources defaultResources = acroForm.getDefaultResources();
    COSDictionary fontDict = (COSDictionary) defaultResources.getCOSObject().getDictionaryObject(COSName.FONT);
    List<String> defaultAppearances = new ArrayList<>();
    List<COSName> fontDeletionList = new ArrayList<>();
    for (PDField field : acroForm.getFieldTree())
    {
        if (field instanceof PDVariableText)
        {
            PDVariableText vtField = (PDVariableText) field;
            defaultAppearances.add(vtField.getDefaultAppearance());
        }
    }
    for (COSName fontName : defaultResources.getFontNames())
    {
        if (COSName.HELV.equals(fontName) || COSName.ZA_DB.equals(fontName))
        {
            // Adobe default, always keep
            continue;
        }
        boolean found = false;
        for (String da : defaultAppearances)
        {
            if (da != null && da.contains("/" + fontName.getName()))
            {
                found = true;
                break;
            }
        }
        System.out.println(fontName + ": " + found);
        if (!found)
        {
            fontDeletionList.add(fontName);
        }
    }
    System.out.println("deletion list: " + fontDeletionList);
    for (COSName fontName : fontDeletionList)
    {
        fontDict.removeItem(fontName);
    }

生成的文件现在有 5KB 大小。

我没有检查注释。其中一些还有 /DA 字符串,但在重建丢失的外观流时是否要使用 acroform 默认资源字体尚不清楚。

更新:这是用 Helv 替换 Arial 的一些附加代码:

for (PDField field : acroForm.getFieldTree())
{
    if (field instanceof PDVariableText)
    {
        PDVariableText vtField = (PDVariableText) field;
        String defaultAppearance = vtField.getDefaultAppearance();
        if (defaultAppearance.startsWith("/Arial"))
        {
            vtField.setDefaultAppearance("/Helv " + defaultAppearance.substring(7));
            vtField.getWidgets().get(0).setAppearance(null); // this removes the font usage
            vtField.setValue(vtField.getValueAsString());
        }
        defaultAppearances.add(vtField.getDefaultAppearance());
    }
}

请注意,这可能不是一个好主意,因为标准的 14 种字体只有有限的字符。尝试

vtField.setValue("Ayşe");

你会得到一个例外。

可以在此答案中找到替换字体的更通用代码。


推荐阅读