Я пытаюсь получить список всех объектов SDF / COS в документе PDF, используя PDFNet 7.0.4
и netcoreapp3.1
. Используя другой анализатор PDF, я знаю, что в этом документе содержится 570 объектов COS, включая 3 изображения.
Изначально я использовал PDFDoc
для загрузки документа и перебирал страницы, просто ища Element
объекты типа e_image
или e_inline_image
, но это дало только 2 из 3 изображений. В большом документе это было еще хуже; 0 из ~ 2600 изображений.
Теперь я отступил и пытаюсь выполнить поиск более низкого уровня с помощью 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 из документа?
edit
Вот оригинал 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();
}
}
Этот вид работал так, что возвращал некоторые изображения, но не все. Для некоторых образцов документов он вернул все, для некоторых он вернул подмножество, а для некоторых он вообще ничего не возвратил.
edit
Только для дальнейшего использования Благодаря ответу Райана, приведенному ниже, мы получили пару хороших чистых методов расширения:
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);