java - 为什么pdf只包含一个字段大约是500Kb
问题描述
在这里你可以下载带有一个 acroform 字段的 pdf,他的大小正好是 427Kb
如果我删除这个独特的字段,文件只有 3Kb,请问为什么会发生这种情况?我尝试使用 PDF Debugger 进行分析,但对我来说没有什么奇怪的。
解决方案
在 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");
你会得到一个例外。
可以在此答案中找到替换字体的更通用代码。
推荐阅读
- laravel - 即使 vue 中有数据,长度也为 0
- sql - 使用 SQL 将数据从行转换为列
- algorithm - 这是什么类型的排序算法
- python - 发生异常:InvalidArgumentError 您必须使用 dtype 字符串和形状 [1] 为占位符张量“Placeholder”提供一个值
- ruby - docker-compose build failed - http://security.debian.org/dists/jessie/updates/InRelease 的发布文件已过期
- ios - 如何在 UITableViewCell 中添加值
- node.js - 如何在 Node.js 中保存流?
- android - 如何在颤动中返回计划的后台任务列表
- reactjs - 使用 apollo-link 获取任何突变的查询
- mysql - 无法加载插件缓存_sha2_password:/mariadb19/plugin/caching_sha2_password.so:无法打开共享对象文件