【发布时间】:2020-02-13 00:49:30
【问题描述】:
我正在尝试使用PDFNet 7.0.4 和netcoreapp3.1 获取PDF 文档中所有SDF/COS 对象的列表。使用不同的 PDF 解析器,我知道该文档共有 570 个 COS 对象,其中包括 3 个图像。
最初我使用PDFDoc 来加载文档,并遍历页面只是寻找e_image 或e_inline_image 类型的Element 对象,但这只产生了三分之二的图像。在更大的文档中,它的表现更差;约 2600 张图片中的 0 张。
现在,我退后一步,尝试通过SDFDoc 进行较低级别的搜索。我可以得到一个预告片对象,然后遍历它,递归任何e_dict 或e_stream 对象,并返回任何看起来像真实对象的东西(即任何实际具有对象编号和代号的东西)。
IEnumerable<Obj> Recurse(Obj root)
{
var idHash = new HashSet<PdfIdentifier>();
return Recurse(root, idHash);
static IEnumerable<Obj> Recurse(Obj obj, HashSet<PdfIdentifier> idHash)
{
var id = obj.ToPdfIdentifier();
if (!idHash.Contains(id))
{
if (id != nullIdentifier)
{
idHash.Add(id);
yield return obj;
}
if (obj.GetType().OneOf(Obj.ObjType.e_dict, Obj.ObjType.e_stream))
{
for (var iter = obj.GetDictIterator(); iter.HasNext(); iter.Next())
{
foreach (var child in Recurse(iter.Value(), idHash))
{
yield return child;
}
}
}
}
}
}
static PdfIdentifier nullIdentifier = new PdfIdentifier() { Generation = 0, ObjectNum = 0 };
ToPdfIdentifier是一个简单的获取对象编号和世代的扩展方法:
public static PdfIdentifier ToPdfIdentifier(this pdftron.SDF.Obj obj) => new PdfIdentifier { ObjectNum = obj.GetObjNum(), Generation = obj.GetGenNum() };
这运行正常,但只返回 45 个对象,其中没有一个是我真正感兴趣的图像。
如何简单地从文档中获取所有 COS 对象?
编辑
这是我们尝试获取所有图像的原始PDFDoc 代码:
private IEnumerable<(PdfIdentifier id, Element el)> GetImages(Stream stream)
{
var doc = new PDFDoc(stream);
var reader = new ElementReader();
for (var iter = doc.GetPageIterator(); iter.HasNext(); iter.Next())
{
reader.Begin(iter.Current());
var el = reader.Next();
while (el != null)
{
var type = el.GetType();
if (el.GetType().OneOf(Element.Type.e_image, Element.Type.e_inline_image))
{
var obj = el.GetXObject();
var id = el.GetXObject().ToPdfIdentifier();
yield return (id, el);
}
el = reader.Next();
}
reader.End();
}
}
这种方法有效,因为它返回了一些图像,但不是全部。对于某些示例文档,它返回全部,对于一些它返回一个子集,对于一些它根本没有返回。
编辑
仅供参考,感谢 Ryan 的以下回答,我们最终得到了一对不错的干净扩展方法:
public static IEnumerable<SDF.Obj> GetAllObj(this SDF.SDFDoc sdfDoc)
{
var xrefTableSize = sdfDoc.XRefSize();
for (int objNum = 0; objNum < xrefTableSize; objNum++)
{
var obj = sdfDoc.GetObj(objNum);
if (obj.IsFree())
{
continue;
}
else
{
yield return obj;
}
}
}
和
public static string Subtype(this SDF.Obj obj) => obj.FindObj("Subtype") switch
{
null => null,
var s when s.IsName() => s.GetName(),
var s when s.IsString() => s.GetAsPDFText(),
_ => throw new Exception("COS object has an invalid Subtype entry")
};
现在我们可以像sdfDoc.GetAllObj().Where(o => o.IsStream() && o.Subtype() == "Image"); 一样简单地获取图像,甚至可以使用Linq:
from o in sdfDoc.GetAllObj()
where o.IsStream() && o.Subtype() == "Image"
select new Image(o);
【问题讨论】:
-
您的代码会跳过任何数组,也会跳过任何间接对象。 “我怎样才能简单地从一个文档中获取所有 COS 对象?”仅供参考 根据定义,内联图像不是 COS 对象,它们完全定义在内容流本身内。
-
您的总体目标是什么?获取 PDF 中的所有图像?页面中实际使用的所有图像?如果注释或附件中有图像怎么办?我可以提供代码,但我认为知道你想要完成什么更重要。希望您能详细说明为什么获取此信息会对您有所帮助。
-
是的,总体目标是获取文档中的所有图像,无论位置如何。我们的旧解析器将解析预告片和外部参照并直接或其他方式返回文档中的所有对象,然后我们只需过滤到
/Subtype=Image并完成。正如我所说,我们用PDFDoc尝试过这个,但失败得很惨。我将编辑问题以添加该代码。 -
您的第一个代码 sn-p 错过了数组。您的第二个代码 sn-p 跳过 Form XObjects,其中通常包含图像。 “我们的旧解析器将解析预告片和外部参照并直接或以其他方式返回文档中的所有对象,然后我们只需过滤到 /Subtype=Image 并完成。”该逻辑会错过任何内联图像。无论如何,我会尽快为您提供应该为您提供相同输出的代码。同时,如果您能提供一个示例 PDF 文件,那就太好了,所以我们在同一页上。
-
我们所有的工作样本都受版权保护,所以我不能发布这些。我会环顾四周,看看是否能找到一些有用的公共领域。
标签: c# .net-core pdftron pdfnet