mirror of https://github.com/qpdf/qpdf.git
Add several methods for working with form XObjects (fixes #436)
Make some more methods in QPDFPageObjectHelper work with form XObjects, provide forEach methods to walk through nested form XObjects, possibly recursively. This should make it easier to work with form XObjects from user code.
This commit is contained in:
parent
afb48d23a9
commit
a139d2b36d
14
ChangeLog
14
ChangeLog
|
@ -1,3 +1,17 @@
|
|||
2021-01-01 Jay Berkenbilt <ejb@ql.org>
|
||||
|
||||
* Add methods to QPDFPageObjectHelper: forEachXObject,
|
||||
forEachImage, forEachFormXObject to call a function on each
|
||||
XObject (or image or form XObject) in a page or form XObject,
|
||||
possibly recursing into nested form XObjects.
|
||||
|
||||
* Add method QPDFPageObjectHelper::getFormXObjects to return a map
|
||||
of keys to form XObjects (non-recursively) from a page or form
|
||||
XObject.
|
||||
|
||||
* Add method QPDFObjectHandle::isImage to test whether an object
|
||||
is an image.
|
||||
|
||||
2020-12-31 Jay Berkenbilt <ejb@ql.org>
|
||||
|
||||
* QPDFPageObjectHelper::removeUnreferencedResources can now be
|
||||
|
|
1
TODO
1
TODO
|
@ -8,7 +8,6 @@ Candidates for upcoming release
|
|||
* #473: zsh completion with directories
|
||||
* Investigate how serverless does completion
|
||||
* Non-bugs
|
||||
* #436: parsing of document with form xobject
|
||||
* #488: allow specification of jpeg compression quality; may also
|
||||
need an option to recompress jpeg
|
||||
|
||||
|
|
|
@ -1150,6 +1150,11 @@ class QPDFObjectHandle
|
|||
QPDF_DLL
|
||||
bool isFormXObject();
|
||||
|
||||
// Indicate if this is an image. If exclude_imagemask is true,
|
||||
// don't count image masks as images.
|
||||
QPDF_DLL
|
||||
bool isImage(bool exclude_imagemask=true);
|
||||
|
||||
private:
|
||||
QPDFObjectHandle(QPDF*, int objid, int generation);
|
||||
QPDFObjectHandle(QPDFObject*);
|
||||
|
|
|
@ -72,11 +72,42 @@ class QPDFPageObjectHelper: public QPDFObjectHelper
|
|||
QPDFObjectHandle
|
||||
getMediaBox(bool copy_if_shared = false);
|
||||
|
||||
// Iterate through XObjects, possibly recursing into form
|
||||
// XObjects. This works with pages or form XObjects. Call action
|
||||
// on each XObject for which selector, if specified, returns true.
|
||||
// With no selector, calls action for every object. In addition to
|
||||
// the object being passed to action, the containing XObject
|
||||
// dictionary and key are passed in. Remember that the XObject
|
||||
// dictionary may be shared, and the object may appear in multiple
|
||||
// XObject dictionaries.
|
||||
QPDF_DLL
|
||||
void forEachXObject(
|
||||
bool recursive,
|
||||
std::function<void(QPDFObjectHandle& obj,
|
||||
QPDFObjectHandle& xobj_dict,
|
||||
std::string const& key)> action,
|
||||
std::function<bool(QPDFObjectHandle)> selector=nullptr);
|
||||
// Only call action for images
|
||||
QPDF_DLL
|
||||
void forEachImage(
|
||||
bool recursive,
|
||||
std::function<void(QPDFObjectHandle& obj,
|
||||
QPDFObjectHandle& xobj_dict,
|
||||
std::string const& key)> action);
|
||||
// Only call action for form XObjects
|
||||
QPDF_DLL
|
||||
void forEachFormXObject(
|
||||
bool recursive,
|
||||
std::function<void(QPDFObjectHandle& obj,
|
||||
QPDFObjectHandle& xobj_dict,
|
||||
std::string const& key)> action);
|
||||
|
||||
// Returns an empty map if there are no images or no resources.
|
||||
// Prior to qpdf 8.4.0, this function did not support inherited
|
||||
// resources, but it does now. Return value is a map from XObject
|
||||
// name to the image object, which is always a stream. Works with
|
||||
// form XObjects as well as pages.
|
||||
// form XObjects as well as pages. This method does not recurse
|
||||
// into nested form XObjects. For that, use forEachImage.
|
||||
QPDF_DLL
|
||||
std::map<std::string, QPDFObjectHandle> getImages();
|
||||
|
||||
|
@ -84,6 +115,14 @@ class QPDFPageObjectHelper: public QPDFObjectHelper
|
|||
QPDF_DLL
|
||||
std::map<std::string, QPDFObjectHandle> getPageImages();
|
||||
|
||||
// Returns an empty map if there are no form XObjects or no
|
||||
// resources. Otherwise, returns a map of keys to form XObjects
|
||||
// directly referenced from this page or form XObjects. This does
|
||||
// not recurse into nested form XObjects. For that, use
|
||||
// forEachFormXObject.
|
||||
QPDF_DLL
|
||||
std::map<std::string, QPDFObjectHandle> getFormXObjects();
|
||||
|
||||
// Convert each inline image to an external (normal) image if the
|
||||
// size is at least the specified number of bytes.
|
||||
QPDF_DLL
|
||||
|
|
|
@ -2948,6 +2948,21 @@ QPDFObjectHandle::isFormXObject()
|
|||
("/Form" == dict.getKey("/Subtype").getName()));
|
||||
}
|
||||
|
||||
bool
|
||||
QPDFObjectHandle::isImage(bool exclude_imagemask)
|
||||
{
|
||||
if (! this->isStream())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
QPDFObjectHandle dict = this->getDict();
|
||||
return (dict.hasKey("/Subtype") &&
|
||||
(dict.getKey("/Subtype").getName() == "/Image") &&
|
||||
((! exclude_imagemask) ||
|
||||
(! (dict.getKey("/ImageMask").isBool() &&
|
||||
dict.getKey("/ImageMask").getBoolValue()))));
|
||||
}
|
||||
|
||||
void
|
||||
QPDFObjectHandle::assertPageObject()
|
||||
{
|
||||
|
|
|
@ -386,6 +386,73 @@ QPDFPageObjectHelper::getMediaBox(bool copy_if_shared)
|
|||
return getAttribute("/MediaBox", copy_if_shared);
|
||||
}
|
||||
|
||||
void
|
||||
QPDFPageObjectHelper::forEachXObject(
|
||||
bool recursive,
|
||||
std::function<void(QPDFObjectHandle& obj,
|
||||
QPDFObjectHandle& xobj_dict,
|
||||
std::string const& key)> action,
|
||||
std::function<bool(QPDFObjectHandle)> selector)
|
||||
{
|
||||
QTC::TC("qpdf", "QPDFPageObjectHelper::forEachXObject",
|
||||
recursive
|
||||
? (this->oh.isFormXObject() ? 0 : 1)
|
||||
: (this->oh.isFormXObject() ? 2 : 3));
|
||||
std::set<QPDFObjGen> seen;
|
||||
std::list<QPDFPageObjectHelper> queue;
|
||||
queue.push_back(*this);
|
||||
while (! queue.empty())
|
||||
{
|
||||
QPDFPageObjectHelper ph = queue.front();
|
||||
queue.pop_front();
|
||||
QPDFObjGen og = ph.oh.getObjGen();
|
||||
if (seen.count(og))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
seen.insert(og);
|
||||
QPDFObjectHandle resources = ph.getAttribute("/Resources", false);
|
||||
if (resources.isDictionary() && resources.hasKey("/XObject"))
|
||||
{
|
||||
QPDFObjectHandle xobj_dict = resources.getKey("/XObject");
|
||||
for (auto const& key: xobj_dict.getKeys())
|
||||
{
|
||||
QPDFObjectHandle obj = xobj_dict.getKey(key);
|
||||
if ((! selector) || selector(obj))
|
||||
{
|
||||
action(obj, xobj_dict, key);
|
||||
}
|
||||
if (recursive && obj.isFormXObject())
|
||||
{
|
||||
queue.push_back(QPDFPageObjectHelper(obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
QPDFPageObjectHelper::forEachImage(
|
||||
bool recursive,
|
||||
std::function<void(QPDFObjectHandle& obj,
|
||||
QPDFObjectHandle& xobj_dict,
|
||||
std::string const& key)> action)
|
||||
{
|
||||
forEachXObject(recursive, action,
|
||||
[](QPDFObjectHandle obj) { return obj.isImage(); });
|
||||
}
|
||||
|
||||
void
|
||||
QPDFPageObjectHelper::forEachFormXObject(
|
||||
bool recursive,
|
||||
std::function<void(QPDFObjectHandle& obj,
|
||||
QPDFObjectHandle& xobj_dict,
|
||||
std::string const& key)> action)
|
||||
{
|
||||
forEachXObject(recursive, action,
|
||||
[](QPDFObjectHandle obj) { return obj.isFormXObject(); });
|
||||
}
|
||||
|
||||
std::map<std::string, QPDFObjectHandle>
|
||||
QPDFPageObjectHelper::getPageImages()
|
||||
{
|
||||
|
@ -396,32 +463,23 @@ std::map<std::string, QPDFObjectHandle>
|
|||
QPDFPageObjectHelper::getImages()
|
||||
{
|
||||
std::map<std::string, QPDFObjectHandle> result;
|
||||
QPDFObjectHandle resources = getAttribute("/Resources", false);
|
||||
if (resources.isDictionary())
|
||||
{
|
||||
if (resources.hasKey("/XObject"))
|
||||
{
|
||||
QPDFObjectHandle xobject = resources.getKey("/XObject");
|
||||
std::set<std::string> keys = xobject.getKeys();
|
||||
for (std::set<std::string>::iterator iter = keys.begin();
|
||||
iter != keys.end(); ++iter)
|
||||
{
|
||||
std::string key = (*iter);
|
||||
QPDFObjectHandle value = xobject.getKey(key);
|
||||
if (value.isStream())
|
||||
{
|
||||
QPDFObjectHandle dict = value.getDict();
|
||||
if (dict.hasKey("/Subtype") &&
|
||||
(dict.getKey("/Subtype").getName() == "/Image") &&
|
||||
(! dict.hasKey("/ImageMask")))
|
||||
{
|
||||
result[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
forEachImage(false, [&result](QPDFObjectHandle& obj,
|
||||
QPDFObjectHandle&,
|
||||
std::string const& key) {
|
||||
result[key] = obj;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
std::map<std::string, QPDFObjectHandle>
|
||||
QPDFPageObjectHelper::getFormXObjects()
|
||||
{
|
||||
std::map<std::string, QPDFObjectHandle> result;
|
||||
forEachFormXObject(false, [&result](QPDFObjectHandle& obj,
|
||||
QPDFObjectHandle&,
|
||||
std::string const& key) {
|
||||
result[key] = obj;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -4857,6 +4857,60 @@ print "\n";
|
|||
illustrates how to use this capability.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Add methods to <classname>QPDFPageObjectHelper</classname>
|
||||
to iterate through XObjects on a page or form XObjects,
|
||||
possibly recursing into nested form XObjects:
|
||||
<function>forEachXObject</function>,
|
||||
<function>ForEachImage</function>,
|
||||
<function>forEachFormXObject</function>.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Enhance several methods in
|
||||
<classname>QPDFPageObjectHelper</classname> to work with
|
||||
form XObjects as well as pages, as noted in comments. See
|
||||
<filename>ChangeLog</filename> for a full list.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Rename some functions in
|
||||
<classname>QPDFPageObjectHelper</classname>, while keeping
|
||||
old names for compatibility:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
<function>getPageImages</function> to
|
||||
<function>getImages</function>
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<function>filterPageContents</function> to
|
||||
<function>filterContents</function>
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Add method
|
||||
<function>QPDFPageObjectHelper::getFormXObjects</function>
|
||||
to return a map of form XObjects directly on a page or form
|
||||
XObject
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Add new helper methods to
|
||||
<classname>QPDFObjectHandle</classname>:
|
||||
<function>isFormXObject</function>, <function>isImage</function>
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Add the optional <function>allow_streams</function>
|
||||
|
|
|
@ -521,3 +521,4 @@ qpdf-c called qpdf_oh_unparse 0
|
|||
qpdf-c called qpdf_oh_unparse_resolved 0
|
||||
qpdf-c called qpdf_oh_unparse_binary 0
|
||||
QPDFWriter getFilterOnWrite false 0
|
||||
QPDFPageObjectHelper::forEachXObject 3
|
||||
|
|
|
@ -423,7 +423,7 @@ foreach my $i (@choice_values)
|
|||
show_ntests();
|
||||
# ----------
|
||||
$td->notify("--- Form XObject, underlay, overlay ---");
|
||||
$n_tests += 18;
|
||||
$n_tests += 19;
|
||||
|
||||
$td->runtest("form xobject creation",
|
||||
{$td->COMMAND => "test_driver 55 fxo-red.pdf"},
|
||||
|
@ -486,6 +486,11 @@ for (my $i = 1; $i <= scalar(@uo_cases); ++$i)
|
|||
{$td->FILE => "a.pdf"},
|
||||
{$td->FILE => "$outbase.pdf"});
|
||||
}
|
||||
$td->runtest("foreach",
|
||||
{$td->COMMAND => "test_driver 71 nested-form-xobjects.pdf"},
|
||||
{$td->FILE => "nested-form-xobjects.out",
|
||||
$td->EXIT_STATUS => 0},
|
||||
$td->NORMALIZE_NEWLINES);
|
||||
|
||||
show_ntests();
|
||||
# ----------
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
--- recursive, all ---
|
||||
<< /Fx1 12 0 R /Im1 14 0 R /Im2 16 0 R >> -> /Fx1 -> 12 0 R
|
||||
<< /Fx1 12 0 R /Im1 14 0 R /Im2 16 0 R >> -> /Im1 -> 14 0 R
|
||||
<< /Fx1 12 0 R /Im1 14 0 R /Im2 16 0 R >> -> /Im2 -> 16 0 R
|
||||
<< /Fx1 20 0 R /Im1 22 0 R /Im2 24 0 R >> -> /Fx1 -> 20 0 R
|
||||
<< /Fx1 20 0 R /Im1 22 0 R /Im2 24 0 R >> -> /Im1 -> 22 0 R
|
||||
<< /Fx1 20 0 R /Im1 22 0 R /Im2 24 0 R >> -> /Im2 -> 24 0 R
|
||||
<< /Im1 26 0 R /Im2 28 0 R >> -> /Im1 -> 26 0 R
|
||||
<< /Im1 26 0 R /Im2 28 0 R >> -> /Im2 -> 28 0 R
|
||||
--- non-recursive, all ---
|
||||
<< /Fx1 12 0 R /Im1 14 0 R /Im2 16 0 R >> -> /Fx1 -> 12 0 R
|
||||
<< /Fx1 12 0 R /Im1 14 0 R /Im2 16 0 R >> -> /Im1 -> 14 0 R
|
||||
<< /Fx1 12 0 R /Im1 14 0 R /Im2 16 0 R >> -> /Im2 -> 16 0 R
|
||||
--- recursive, images ---
|
||||
<< /Fx1 12 0 R /Im1 14 0 R /Im2 16 0 R >> -> /Im1 -> 14 0 R
|
||||
<< /Fx1 12 0 R /Im1 14 0 R /Im2 16 0 R >> -> /Im2 -> 16 0 R
|
||||
<< /Fx1 20 0 R /Im1 22 0 R /Im2 24 0 R >> -> /Im1 -> 22 0 R
|
||||
<< /Fx1 20 0 R /Im1 22 0 R /Im2 24 0 R >> -> /Im2 -> 24 0 R
|
||||
<< /Im1 26 0 R /Im2 28 0 R >> -> /Im1 -> 26 0 R
|
||||
<< /Im1 26 0 R /Im2 28 0 R >> -> /Im2 -> 28 0 R
|
||||
--- non-recursive, images ---
|
||||
<< /Fx1 12 0 R /Im1 14 0 R /Im2 16 0 R >> -> /Im1 -> 14 0 R
|
||||
<< /Fx1 12 0 R /Im1 14 0 R /Im2 16 0 R >> -> /Im2 -> 16 0 R
|
||||
--- recursive, form XObjects ---
|
||||
<< /Fx1 12 0 R /Im1 14 0 R /Im2 16 0 R >> -> /Fx1 -> 12 0 R
|
||||
<< /Fx1 20 0 R /Im1 22 0 R /Im2 24 0 R >> -> /Fx1 -> 20 0 R
|
||||
--- non-recursive, form XObjects ---
|
||||
<< /Fx1 12 0 R /Im1 14 0 R /Im2 16 0 R >> -> /Fx1 -> 12 0 R
|
||||
--- recursive, all, from fx1 ---
|
||||
<< /Fx1 20 0 R /Im1 22 0 R /Im2 24 0 R >> -> /Fx1 -> 20 0 R
|
||||
<< /Fx1 20 0 R /Im1 22 0 R /Im2 24 0 R >> -> /Im1 -> 22 0 R
|
||||
<< /Fx1 20 0 R /Im1 22 0 R /Im2 24 0 R >> -> /Im2 -> 24 0 R
|
||||
<< /Im1 26 0 R /Im2 28 0 R >> -> /Im1 -> 26 0 R
|
||||
<< /Im1 26 0 R /Im2 28 0 R >> -> /Im2 -> 28 0 R
|
||||
--- non-recursive, all, from fx1 ---
|
||||
<< /Fx1 20 0 R /Im1 22 0 R /Im2 24 0 R >> -> /Fx1 -> 20 0 R
|
||||
<< /Fx1 20 0 R /Im1 22 0 R /Im2 24 0 R >> -> /Im1 -> 22 0 R
|
||||
<< /Fx1 20 0 R /Im1 22 0 R /Im2 24 0 R >> -> /Im2 -> 24 0 R
|
||||
--- get images, page ---
|
||||
/Im1 -> 14 0 R
|
||||
/Im2 -> 16 0 R
|
||||
--- get images, fx ---
|
||||
/Im1 -> 22 0 R
|
||||
/Im2 -> 24 0 R
|
||||
--- get form XObjects, page ---
|
||||
/Fx1 -> 12 0 R
|
||||
--- get form XObjects, fx ---
|
||||
/Fx1 -> 20 0 R
|
||||
test 71 done
|
|
@ -0,0 +1,397 @@
|
|||
%PDF-1.3
|
||||
%¿÷¢þ
|
||||
%QDF-1.0
|
||||
|
||||
1 0 obj
|
||||
<<
|
||||
/Pages 2 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<<
|
||||
/Count 1
|
||||
/Kids [
|
||||
3 0 R
|
||||
]
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
%% Page 1
|
||||
3 0 obj
|
||||
<<
|
||||
/Contents [
|
||||
4 0 R
|
||||
6 0 R
|
||||
8 0 R
|
||||
]
|
||||
/MediaBox [
|
||||
0
|
||||
0
|
||||
612
|
||||
792
|
||||
]
|
||||
/Parent 2 0 R
|
||||
/Resources <<
|
||||
/Font <<
|
||||
/F1 10 0 R
|
||||
>>
|
||||
/ProcSet 11 0 R
|
||||
/XObject <<
|
||||
/Fx1 12 0 R
|
||||
/Im1 14 0 R
|
||||
/Im2 16 0 R
|
||||
>>
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
|
||||
%% Contents for page 1
|
||||
4 0 obj
|
||||
<<
|
||||
/Length 5 0 R
|
||||
>>
|
||||
stream
|
||||
q
|
||||
endstream
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
2
|
||||
endobj
|
||||
|
||||
%% Contents for page 1
|
||||
6 0 obj
|
||||
<<
|
||||
/Length 7 0 R
|
||||
>>
|
||||
stream
|
||||
BT
|
||||
/F1 24 Tf
|
||||
72 720 Td
|
||||
(Page) Tj
|
||||
ET
|
||||
q
|
||||
100 0 0 100 72 600 cm
|
||||
/Im1 Do
|
||||
Q
|
||||
q
|
||||
100 0 0 100 192 600 cm
|
||||
/Im2 Do
|
||||
Q
|
||||
endstream
|
||||
endobj
|
||||
|
||||
7 0 obj
|
||||
111
|
||||
endobj
|
||||
|
||||
%% Contents for page 1
|
||||
8 0 obj
|
||||
<<
|
||||
/Length 9 0 R
|
||||
>>
|
||||
stream
|
||||
|
||||
Q
|
||||
q
|
||||
1.00000 0.00000 0.00000 1.00000 72.00000 200.00000 cm
|
||||
/Fx1 Do
|
||||
Q
|
||||
endstream
|
||||
endobj
|
||||
|
||||
9 0 obj
|
||||
69
|
||||
endobj
|
||||
|
||||
10 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica
|
||||
/Encoding /WinAnsiEncoding
|
||||
/Name /F1
|
||||
/Subtype /Type1
|
||||
/Type /Font
|
||||
>>
|
||||
endobj
|
||||
|
||||
11 0 obj
|
||||
[
|
||||
/PDF
|
||||
/Text
|
||||
/ImageC
|
||||
]
|
||||
endobj
|
||||
|
||||
12 0 obj
|
||||
<<
|
||||
/BBox [
|
||||
0
|
||||
0
|
||||
300
|
||||
500
|
||||
]
|
||||
/Resources <<
|
||||
/Font <<
|
||||
/F1 18 0 R
|
||||
>>
|
||||
/ProcSet 19 0 R
|
||||
/XObject <<
|
||||
/Fx1 20 0 R
|
||||
/Im1 22 0 R
|
||||
/Im2 24 0 R
|
||||
>>
|
||||
>>
|
||||
/Subtype /Form
|
||||
/Type /XObject
|
||||
/Length 13 0 R
|
||||
>>
|
||||
stream
|
||||
BT
|
||||
/F1 24 Tf
|
||||
0 320 Td
|
||||
(FX1) Tj
|
||||
ET
|
||||
q
|
||||
100 0 0 100 000 200 cm
|
||||
/Im1 Do
|
||||
Q
|
||||
q
|
||||
100 0 0 100 120 200 cm
|
||||
/Im2 Do
|
||||
Q
|
||||
q
|
||||
1.00000 0.00000 0.00000 1.00000 0.00000 0.00000 cm
|
||||
/Fx1 Do
|
||||
Q
|
||||
endstream
|
||||
endobj
|
||||
|
||||
13 0 obj
|
||||
173
|
||||
endobj
|
||||
|
||||
14 0 obj
|
||||
<<
|
||||
/BitsPerComponent 8
|
||||
/ColorSpace /DeviceGray
|
||||
/Height 15
|
||||
/Subtype /Image
|
||||
/Type /XObject
|
||||
/Width 15
|
||||
/Length 15 0 R
|
||||
>>
|
||||
stream
|
||||
`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
endstream
|
||||
endobj
|
||||
|
||||
%QDF: ignore_newline
|
||||
15 0 obj
|
||||
225
|
||||
endobj
|
||||
|
||||
16 0 obj
|
||||
<<
|
||||
/BitsPerComponent 8
|
||||
/ColorSpace /DeviceGray
|
||||
/Height 15
|
||||
/Subtype /Image
|
||||
/Type /XObject
|
||||
/Width 15
|
||||
/Length 17 0 R
|
||||
>>
|
||||
stream
|
||||
@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
endstream
|
||||
endobj
|
||||
|
||||
%QDF: ignore_newline
|
||||
17 0 obj
|
||||
225
|
||||
endobj
|
||||
|
||||
18 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica
|
||||
/Encoding /WinAnsiEncoding
|
||||
/Name /F1
|
||||
/Subtype /Type1
|
||||
/Type /Font
|
||||
>>
|
||||
endobj
|
||||
|
||||
19 0 obj
|
||||
[
|
||||
/PDF
|
||||
/Text
|
||||
/ImageC
|
||||
]
|
||||
endobj
|
||||
|
||||
20 0 obj
|
||||
<<
|
||||
/BBox [
|
||||
0
|
||||
0
|
||||
300
|
||||
200
|
||||
]
|
||||
/Resources <<
|
||||
/Font <<
|
||||
/F1 18 0 R
|
||||
>>
|
||||
/ProcSet 19 0 R
|
||||
/XObject <<
|
||||
/Im1 26 0 R
|
||||
/Im2 28 0 R
|
||||
>>
|
||||
>>
|
||||
/Subtype /Form
|
||||
/Type /XObject
|
||||
/Length 21 0 R
|
||||
>>
|
||||
stream
|
||||
BT
|
||||
/F1 24 Tf
|
||||
0 120 Td
|
||||
(FX2) Tj
|
||||
ET
|
||||
q
|
||||
100 0 0 100 0 0 cm
|
||||
/Im1 Do
|
||||
Q
|
||||
q
|
||||
100 0 0 100 120 0 cm
|
||||
/Im2 Do
|
||||
Q
|
||||
endstream
|
||||
endobj
|
||||
|
||||
21 0 obj
|
||||
104
|
||||
endobj
|
||||
|
||||
22 0 obj
|
||||
<<
|
||||
/BitsPerComponent 8
|
||||
/ColorSpace /DeviceGray
|
||||
/Height 15
|
||||
/Subtype /Image
|
||||
/Type /XObject
|
||||
/Width 15
|
||||
/Length 23 0 R
|
||||
>>
|
||||
stream
|
||||
@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
endstream
|
||||
endobj
|
||||
|
||||
%QDF: ignore_newline
|
||||
23 0 obj
|
||||
225
|
||||
endobj
|
||||
|
||||
24 0 obj
|
||||
<<
|
||||
/BitsPerComponent 8
|
||||
/ColorSpace /DeviceGray
|
||||
/Height 15
|
||||
/Subtype /Image
|
||||
/Type /XObject
|
||||
/Width 15
|
||||
/Length 25 0 R
|
||||
>>
|
||||
stream
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
endstream
|
||||
endobj
|
||||
|
||||
%QDF: ignore_newline
|
||||
25 0 obj
|
||||
225
|
||||
endobj
|
||||
|
||||
26 0 obj
|
||||
<<
|
||||
/BitsPerComponent 8
|
||||
/ColorSpace /DeviceGray
|
||||
/Height 15
|
||||
/Subtype /Image
|
||||
/Type /XObject
|
||||
/Width 15
|
||||
/Length 27 0 R
|
||||
>>
|
||||
stream
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
endstream
|
||||
endobj
|
||||
|
||||
%QDF: ignore_newline
|
||||
27 0 obj
|
||||
225
|
||||
endobj
|
||||
|
||||
28 0 obj
|
||||
<<
|
||||
/BitsPerComponent 8
|
||||
/ColorSpace /DeviceGray
|
||||
/Height 15
|
||||
/Subtype /Image
|
||||
/Type /XObject
|
||||
/Width 15
|
||||
/Length 29 0 R
|
||||
>>
|
||||
stream
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@`````@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
endstream
|
||||
endobj
|
||||
|
||||
%QDF: ignore_newline
|
||||
29 0 obj
|
||||
225
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 30
|
||||
0000000000 65535 f
|
||||
0000000025 00000 n
|
||||
0000000079 00000 n
|
||||
0000000161 00000 n
|
||||
0000000485 00000 n
|
||||
0000000542 00000 n
|
||||
0000000583 00000 n
|
||||
0000000749 00000 n
|
||||
0000000792 00000 n
|
||||
0000000916 00000 n
|
||||
0000000935 00000 n
|
||||
0000001054 00000 n
|
||||
0000001100 00000 n
|
||||
0000001561 00000 n
|
||||
0000001582 00000 n
|
||||
0000001994 00000 n
|
||||
0000002015 00000 n
|
||||
0000002427 00000 n
|
||||
0000002448 00000 n
|
||||
0000002567 00000 n
|
||||
0000002613 00000 n
|
||||
0000002987 00000 n
|
||||
0000003008 00000 n
|
||||
0000003420 00000 n
|
||||
0000003441 00000 n
|
||||
0000003853 00000 n
|
||||
0000003874 00000 n
|
||||
0000004286 00000 n
|
||||
0000004307 00000 n
|
||||
0000004719 00000 n
|
||||
trailer <<
|
||||
/Root 1 0 R
|
||||
/Size 30
|
||||
/ID [<55269d37282af9edc76855e4cb859987><633b13d949c2f1d115e24c686f36e362>]
|
||||
>>
|
||||
startxref
|
||||
4740
|
||||
%%EOF
|
|
@ -2228,6 +2228,57 @@ void runtest(int n, char const* filename1, char const* arg2)
|
|||
w.setDecodeLevel(qpdf_dl_specialized);
|
||||
w.write();
|
||||
}
|
||||
else if (n == 71)
|
||||
{
|
||||
auto show = [](QPDFObjectHandle& obj,
|
||||
QPDFObjectHandle& xobj_dict,
|
||||
std::string const& key) {
|
||||
std::cout << xobj_dict.unparse() << " -> "
|
||||
<< key << " -> " << obj.unparse() << std::endl;
|
||||
};
|
||||
auto page = QPDFPageDocumentHelper(pdf).getAllPages().at(0);
|
||||
std::cout << "--- recursive, all ---" << std::endl;
|
||||
page.forEachXObject(true, show);
|
||||
std::cout << "--- non-recursive, all ---" << std::endl;
|
||||
page.forEachXObject(false, show);
|
||||
std::cout << "--- recursive, images ---" << std::endl;
|
||||
page.forEachImage(true, show);
|
||||
std::cout << "--- non-recursive, images ---" << std::endl;
|
||||
page.forEachImage(false, show);
|
||||
std::cout << "--- recursive, form XObjects ---" << std::endl;
|
||||
page.forEachFormXObject(true, show);
|
||||
std::cout << "--- non-recursive, form XObjects ---" << std::endl;
|
||||
page.forEachFormXObject(false, show);
|
||||
auto fx1 = QPDFPageObjectHelper(
|
||||
page.getObjectHandle()
|
||||
.getKey("/Resources")
|
||||
.getKey("/XObject")
|
||||
.getKey("/Fx1"));
|
||||
std::cout << "--- recursive, all, from fx1 ---" << std::endl;
|
||||
fx1.forEachXObject(true, show);
|
||||
std::cout << "--- non-recursive, all, from fx1 ---" << std::endl;
|
||||
fx1.forEachXObject(false, show);
|
||||
std::cout << "--- get images, page ---" << std::endl;
|
||||
for (auto& i: page.getImages())
|
||||
{
|
||||
std::cout << i.first << " -> " << i.second.unparse() << std::endl;
|
||||
}
|
||||
std::cout << "--- get images, fx ---" << std::endl;
|
||||
for (auto& i: fx1.getImages())
|
||||
{
|
||||
std::cout << i.first << " -> " << i.second.unparse() << std::endl;
|
||||
}
|
||||
std::cout << "--- get form XObjects, page ---" << std::endl;
|
||||
for (auto& i: page.getFormXObjects())
|
||||
{
|
||||
std::cout << i.first << " -> " << i.second.unparse() << std::endl;
|
||||
}
|
||||
std::cout << "--- get form XObjects, fx ---" << std::endl;
|
||||
for (auto& i: fx1.getFormXObjects())
|
||||
{
|
||||
std::cout << i.first << " -> " << i.second.unparse() << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error(std::string("invalid test ") +
|
||||
|
|
Loading…
Reference in New Issue