c# - 从 PDF 中获取所有 SDF/COS 对象
问题描述
我正在尝试使用PDFNet 7.0.4
和获取 PDF 文档中所有 SDF/COS 对象的列表netcoreapp3.1
。使用不同的 PDF 解析器,我知道该文档中总共有 570 个 COS 对象,包括 3 个图像。
最初,我用来PDFDoc
加载文档,并遍历页面只是寻找Element
类型为e_image
or的对象e_inline_image
,但这仅产生了 3 个图像中的 2 个。在更大的文档中,它的表现更差;约 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);
解决方案
如果您想获取 PDF 页面上实际使用的图像(以防 PDF 中碰巧有未使用的图像),那么您将使用此示例代码。此代码将具有包含内联图像的额外好处。 https://www.pdftron.com/documentation/samples/dotnetcore/cs/ImageExtractTest
虽然上述可能很慢,但如果文档有数百或数千页,那么图形上会很复杂。
如您所述,另一种方法是迭代 COS 对象。以下 C# 代码查找所有图像流。请注意,PDF 标准明确规定 Streams 必须是间接对象。所以我认为你可以放心地省略阅读所有直接对象。
using (PDFDoc doc = new PDFDoc("2002.04610.pdf"))
{
doc.InitSecurityHandler();
int xrefSz = doc.GetSDFDoc().XRefSize();
for (int xrefCounter = 0; xrefCounter < xrefSz; ++xrefCounter)
{
Obj o = doc.GetSDFDoc().GetObj(xrefCounter);
if (o.IsFree())
{
continue;
}
if(o.IsStream())
{
Obj subtypeObj = o.FindObj("Subtype");
if (subtypeObj != null)
{
string subtype = "";
if(subtypeObj.IsName()) subtype = subtypeObj.GetName();
if(subtypeObj.IsString()) subtype = subtypeObj.GetAsPDFText(); // Subtype should be a Name, but just in case
if (subtype.CompareTo("Image") == 0)
{
Console.WriteLine("Indirect object {0} is an Image Stream", o.GetObjNum());
}
}
}
}
}
推荐阅读
- angular - NullInjectorError: StaticInjectorError(AppModule)[GoogleChartComponent -> DataTableService
- node.js - 如何修复角度 11 中的最大调用堆栈大小超出错误?
- reactjs - 获取电子邮件:Zandesk requests api 上不能为空
- macos - 没有这样的文件或目录错误mac终端
- reactjs - 如何通过状态更改来处理事件以避免渲染
- amazon-web-services - 启用 S3 网页以使用查询参数进行重定向
- java - 如何从孩子设置默认父属性?
- stackdriver - 在 Google Cloud Monitoring 中获取随时间变化的比率
- javascript - React : list.map 不是带有数组的函数
- wordpress - Klaviyo + Woocommerce 的已下订单指标中的订单项目为空白