mirror of
https://github.com/qpdf/qpdf.git
synced 2024-12-22 10:58:58 +00:00
Clarify and improve QPDFPageObjectHelper::get*Box methods
Add copy_if_fallback and explain how it differs from copy_if_shared.
This commit is contained in:
parent
c1def4ead4
commit
76cd7ea67a
@ -1,5 +1,13 @@
|
||||
2022-09-06 Jay Berkenbilt <ejb@ql.org>
|
||||
|
||||
* For all bounding box methods in QPDFPageObjectHelper other than
|
||||
MediaBox, add a parameter `copy_if_fallback`, and add comments
|
||||
explaining in depth exactly what copy_if_shared and
|
||||
copy_if_fallback mean. Fixes #664.
|
||||
|
||||
* Add new methods getArtBox and getBleedBox to
|
||||
QPDFPageObjectHelper, completing the set of bounding box methods.
|
||||
|
||||
* Add == equality for QPDFObjectHandle. Two QPDFObjectHandle
|
||||
objects are equal if they point to the same underlying object,
|
||||
meaning changes to one will be reflected in the other.
|
||||
|
@ -45,30 +45,152 @@ class QPDFPageObjectHelper: public QPDFObjectHelper
|
||||
QPDF_DLL
|
||||
virtual ~QPDFPageObjectHelper() = default;
|
||||
|
||||
// Works with pages and form XObjects. Return the effective value
|
||||
// of this attribute for the page/form XObject. For pages, if the
|
||||
// requested attribute is not present on the page but is
|
||||
// inheritable, look up through the page's ancestors in the page
|
||||
// tree. If copy_if_shared is true, then this method will replace
|
||||
// the attribute with a shallow copy if it is in indirect or
|
||||
// inherited and return the copy. You should do this if you are
|
||||
// going to modify the returned object and want the modifications
|
||||
// to apply to the current page/form XObject only.
|
||||
// PAGE ATTRIBUTES
|
||||
|
||||
// The getAttribute method works with pages and form XObjects. It
|
||||
// return the value of the requested attribute from the page/form
|
||||
// XObject's dictionary, taking inheritance from the pages tree
|
||||
// into consideration. For pages, the attributes /MediaBox,
|
||||
// /CropBox, /Resources, and /Rotate are inheritable, meaning that
|
||||
// if they are not present directly on the page node, they may be
|
||||
// inherited from ancestor nodes in the pages tree.
|
||||
//
|
||||
// There are two ways that an attribute can be "shared":
|
||||
//
|
||||
// * For inheritable attributes on pages, it may appear in a
|
||||
// higher level node of the pages tree
|
||||
//
|
||||
// * For any attribute, the attribute may be an indirect object
|
||||
// which may be referenced by more than one page/form XObject.
|
||||
//
|
||||
// If copy_if_shared is true, then this method will replace the
|
||||
// attribute with a shallow copy if it is indirect or inherited
|
||||
// and return the copy. You should do this if you are going to
|
||||
// modify the returned object and want the modifications to apply
|
||||
// to the current page/form XObject only.
|
||||
QPDF_DLL
|
||||
QPDFObjectHandle getAttribute(std::string const& name, bool copy_if_shared);
|
||||
|
||||
// Return the TrimBox. If not defined, fall back to CropBox
|
||||
QPDF_DLL
|
||||
QPDFObjectHandle getTrimBox(bool copy_if_shared = false);
|
||||
// PAGE BOXES
|
||||
//
|
||||
// Pages have various types of boundary boxes. These are described
|
||||
// in detail in the PDF specification (section 14.11.2 Page
|
||||
// boundaries). They are, by key in the page dictionary:
|
||||
//
|
||||
// * /MediaBox -- boundaries of physical page
|
||||
// * /CropBox -- clipping region of what is displayed
|
||||
// * /BleedBox -- clipping region for production environments
|
||||
// * /TrimBox -- dimensions of final printed page after trimming
|
||||
// * /ArtBox -- extent of meaningful content including margins
|
||||
//
|
||||
// Of these, only /MediaBox is required. If any are absent, the
|
||||
// fallback value for /CropBox is /MediaBox, and the fallback
|
||||
// values for the other boxes are /CropBox.
|
||||
//
|
||||
// As noted above (PAGE ATTRIBUTES), /MediaBox and /CropBox can be
|
||||
// inherited from parent nodes in the pages tree. The other boxes
|
||||
// can't be inherited.
|
||||
//
|
||||
// When the comments below refer to the "effective value" of an
|
||||
// box, this takes into consideration both inheritance through the
|
||||
// pages tree (in the case of /MediaBox and /CropBox) and fallback
|
||||
// values for missing attributes (for all except /MediaBox).
|
||||
//
|
||||
// For the methods below, copy_if_shared is passed to getAttribute
|
||||
// and therefore refers only to indirect objects and values that
|
||||
// are inherited through the pages tree.
|
||||
//
|
||||
// If copy_if_fallback is true, a copy is made if the object's
|
||||
// value was obtained by falling back to a different box.
|
||||
//
|
||||
// The copy_if_shared and copy_if_fallback parameters carry across
|
||||
// multiple layers. This is explained below.
|
||||
//
|
||||
// You should set copy_if_shared to true if you want to modify a
|
||||
// bounding box for the current page without affecting other pages
|
||||
// but you don't want to change the fallback behavior. For
|
||||
// example, if you want to modify the /TrimBox for the current
|
||||
// page only but have it continue to fall back to the value of
|
||||
// /CropBox or /MediaBox if they are not defined, you could set
|
||||
// copy_if_shared to true.
|
||||
//
|
||||
// You should set copy_if_fallback to true if you want to modify a
|
||||
// specific box as distinct from any other box. For example, if
|
||||
// you want to make /TrimBox differ from /CropBox, then you should
|
||||
// set copy_if_fallback to true.
|
||||
//
|
||||
// The copy_if_fallback flags were added in qpdf 11.
|
||||
//
|
||||
// For example, suppose that neither /CropBox nor /TrimBox is
|
||||
// present on a page but /CropBox is present in the page's parent
|
||||
// node in the page tree.
|
||||
//
|
||||
// * getTrimBox(false, false) would return the /CropBox from the
|
||||
// parent node.
|
||||
//
|
||||
// * getTrimBox(true, false) would make a shallow copy of the
|
||||
// /CropBox from the parent node into the current node and
|
||||
// return it.
|
||||
//
|
||||
// * getTrimBox(false, true) would make a shallow copy of the
|
||||
// /CropBox from the parent node into /TrimBox of the current
|
||||
// node and return it.
|
||||
//
|
||||
// * getTrimBox(true, true) would make a shallow copy of the
|
||||
// /CropBox from the parent node into the current node, then
|
||||
// make a shallow copy of the resulting copy to /TrimBox of the
|
||||
// current node, and then return that.
|
||||
//
|
||||
// To illustrate how these parameters carry across multiple
|
||||
// layers, suppose that neither /MediaBox, /CropBox, nor /TrimBox
|
||||
// is present on a page but /MediaBox is present on the parent. In
|
||||
// this case:
|
||||
//
|
||||
// * getTrimBox(false, false) would return the value of /MediaBox
|
||||
// from the parent node.
|
||||
//
|
||||
// * getTrimBox(true, false) would copy /MediaBox to the current
|
||||
// node and return it.
|
||||
//
|
||||
// * getTrimBox(false, true) would first copy /MediaBox from the
|
||||
// parent to /CropBox, then copy /CropBox to /TrimBox, and then
|
||||
// return the result.
|
||||
//
|
||||
// * getTrimBox(true, true) would first copy /MediaBox from the
|
||||
// parent to the current page, then copy it to /CropBox, then
|
||||
// copy /CropBox to /TrimBox, and then return the result.
|
||||
//
|
||||
// If you need different behavior, call getAttribute directly and
|
||||
// take care of your own copying.
|
||||
|
||||
// Return the CropBox. If not defined, fall back to MediaBox
|
||||
QPDF_DLL
|
||||
QPDFObjectHandle getCropBox(bool copy_if_shared = false);
|
||||
|
||||
// Return the MediaBox
|
||||
// Return the effective MediaBox
|
||||
QPDF_DLL
|
||||
QPDFObjectHandle getMediaBox(bool copy_if_shared = false);
|
||||
|
||||
// Return the effective CropBox. If not defined, fall back to
|
||||
// MediaBox
|
||||
QPDF_DLL
|
||||
QPDFObjectHandle
|
||||
getCropBox(bool copy_if_shared = false, bool copy_if_fallback = false);
|
||||
|
||||
// Return the effective BleedBox. If not defined, fall back to
|
||||
// CropBox.
|
||||
QPDF_DLL
|
||||
QPDFObjectHandle
|
||||
getBleedBox(bool copy_if_shared = false, bool copy_if_fallback = false);
|
||||
|
||||
// Return the effective TrimBox. If not defined, fall back to
|
||||
// CropBox.
|
||||
QPDF_DLL
|
||||
QPDFObjectHandle
|
||||
getTrimBox(bool copy_if_shared = false, bool copy_if_fallback = false);
|
||||
|
||||
// Return the effective ArtBox. If not defined, fall back to
|
||||
// CropBox.
|
||||
QPDF_DLL
|
||||
QPDFObjectHandle
|
||||
getArtBox(bool copy_if_shared = false, bool copy_if_fallback = 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.
|
||||
@ -373,6 +495,11 @@ class QPDFPageObjectHelper: public QPDFObjectHelper
|
||||
QPDFAcroFormDocumentHelper* from_afdh = nullptr);
|
||||
|
||||
private:
|
||||
QPDFObjectHandle getAttribute(
|
||||
std::string const& name,
|
||||
bool copy_if_shared,
|
||||
std::function<QPDFObjectHandle()> get_fallback,
|
||||
bool copy_if_fallback);
|
||||
static bool removeUnreferencedResourcesHelper(
|
||||
QPDFPageObjectHelper ph, std::set<std::string>& unresolved);
|
||||
|
||||
|
@ -237,6 +237,16 @@ QPDFPageObjectHelper::QPDFPageObjectHelper(QPDFObjectHandle oh) :
|
||||
|
||||
QPDFObjectHandle
|
||||
QPDFPageObjectHelper::getAttribute(std::string const& name, bool copy_if_shared)
|
||||
{
|
||||
return getAttribute(name, copy_if_shared, nullptr, false);
|
||||
}
|
||||
|
||||
QPDFObjectHandle
|
||||
QPDFPageObjectHelper::getAttribute(
|
||||
std::string const& name,
|
||||
bool copy_if_shared,
|
||||
std::function<QPDFObjectHandle()> get_fallback,
|
||||
bool copy_if_fallback)
|
||||
{
|
||||
QPDFObjectHandle result;
|
||||
QPDFObjectHandle dict;
|
||||
@ -272,28 +282,17 @@ QPDFPageObjectHelper::getAttribute(std::string const& name, bool copy_if_shared)
|
||||
"qpdf",
|
||||
"QPDFPageObjectHelper copy shared attribute",
|
||||
is_form_xobject ? 0 : 1);
|
||||
result = result.shallowCopy();
|
||||
dict.replaceKey(name, result);
|
||||
result = dict.replaceKeyAndGetNew(name, result.shallowCopy());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QPDFObjectHandle
|
||||
QPDFPageObjectHelper::getTrimBox(bool copy_if_shared)
|
||||
{
|
||||
QPDFObjectHandle result = getAttribute("/TrimBox", copy_if_shared);
|
||||
if (result.isNull()) {
|
||||
result = getCropBox(copy_if_shared);
|
||||
if (result.isNull() && get_fallback) {
|
||||
result = get_fallback();
|
||||
if (copy_if_fallback && !result.isNull()) {
|
||||
QTC::TC("qpdf", "QPDFPageObjectHelper copied fallback");
|
||||
result = dict.replaceKeyAndGetNew(name, result.shallowCopy());
|
||||
} else {
|
||||
QTC::TC(
|
||||
"qpdf", "QPDFPageObjectHelper used fallback without copying");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QPDFObjectHandle
|
||||
QPDFPageObjectHelper::getCropBox(bool copy_if_shared)
|
||||
{
|
||||
QPDFObjectHandle result = getAttribute("/CropBox", copy_if_shared);
|
||||
if (result.isNull()) {
|
||||
result = getMediaBox();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -304,6 +303,52 @@ QPDFPageObjectHelper::getMediaBox(bool copy_if_shared)
|
||||
return getAttribute("/MediaBox", copy_if_shared);
|
||||
}
|
||||
|
||||
QPDFObjectHandle
|
||||
QPDFPageObjectHelper::getCropBox(bool copy_if_shared, bool copy_if_fallback)
|
||||
{
|
||||
return getAttribute(
|
||||
"/CropBox",
|
||||
copy_if_shared,
|
||||
[this, copy_if_shared]() { return this->getMediaBox(copy_if_shared); },
|
||||
copy_if_fallback);
|
||||
}
|
||||
|
||||
QPDFObjectHandle
|
||||
QPDFPageObjectHelper::getTrimBox(bool copy_if_shared, bool copy_if_fallback)
|
||||
{
|
||||
return getAttribute(
|
||||
"/TrimBox",
|
||||
copy_if_shared,
|
||||
[this, copy_if_shared, copy_if_fallback]() {
|
||||
return this->getCropBox(copy_if_shared, copy_if_fallback);
|
||||
},
|
||||
copy_if_fallback);
|
||||
}
|
||||
|
||||
QPDFObjectHandle
|
||||
QPDFPageObjectHelper::getArtBox(bool copy_if_shared, bool copy_if_fallback)
|
||||
{
|
||||
return getAttribute(
|
||||
"/ArtBox",
|
||||
copy_if_shared,
|
||||
[this, copy_if_shared, copy_if_fallback]() {
|
||||
return this->getCropBox(copy_if_shared, copy_if_fallback);
|
||||
},
|
||||
copy_if_fallback);
|
||||
}
|
||||
|
||||
QPDFObjectHandle
|
||||
QPDFPageObjectHelper::getBleedBox(bool copy_if_shared, bool copy_if_fallback)
|
||||
{
|
||||
return getAttribute(
|
||||
"/BleedBox",
|
||||
copy_if_shared,
|
||||
[this, copy_if_shared, copy_if_fallback]() {
|
||||
return this->getCropBox(copy_if_shared, copy_if_fallback);
|
||||
},
|
||||
copy_if_fallback);
|
||||
}
|
||||
|
||||
void
|
||||
QPDFPageObjectHelper::forEachXObject(
|
||||
bool recursive,
|
||||
|
@ -261,6 +261,11 @@ For a detailed list of changes, please see the file
|
||||
generation parameters. The old versions will continue to be
|
||||
supported and are not deprecated.
|
||||
|
||||
- In ``QPDFPageObjectHelper``, add a ``copy_if_fallback``
|
||||
parameter to most of the page bounding box methods, and clarify
|
||||
in the comments about the difference between ``copy_if_shared``
|
||||
and ``copy_if_fallback``.
|
||||
|
||||
- Add a move constructor to the ``Buffer`` class.
|
||||
|
||||
- Other changes
|
||||
|
@ -676,3 +676,5 @@ QPDF_json missing json version 0
|
||||
QPDF_json bad json version 0
|
||||
QPDF_json bad calledgetallpages 0
|
||||
QPDF_json bad pushedinheritedpageresources 0
|
||||
QPDFPageObjectHelper copied fallback 0
|
||||
QPDFPageObjectHelper used fallback without copying 0
|
||||
|
@ -14,8 +14,6 @@ cleanup();
|
||||
|
||||
my $td = new TestDriver('page-api');
|
||||
|
||||
my $n_tests = 11;
|
||||
|
||||
$td->runtest("basic page API",
|
||||
{$td->COMMAND => "test_driver 15 page_api_1.pdf"},
|
||||
{$td->STRING => "test 15 done\n", $td->EXIT_STATUS => 0},
|
||||
@ -58,5 +56,10 @@ $td->runtest("flatten rotation",
|
||||
$td->runtest("check output",
|
||||
{$td->FILE => "a.pdf"},
|
||||
{$td->FILE => "boxes-flattened.pdf"});
|
||||
$td->runtest("get box methods",
|
||||
{$td->COMMAND => "test_driver 94 boxes2.pdf"},
|
||||
{$td->STRING => "test 94 done\n", $td->EXIT_STATUS => 0},
|
||||
$td->NORMALIZE_NEWLINES);
|
||||
|
||||
cleanup();
|
||||
$td->report($n_tests);
|
||||
$td->report(12);
|
||||
|
491
qpdf/qtest/qpdf/boxes2.pdf
Normal file
491
qpdf/qtest/qpdf/boxes2.pdf
Normal file
@ -0,0 +1,491 @@
|
||||
%PDF-1.3
|
||||
%¿÷¢þ
|
||||
%QDF-1.0
|
||||
|
||||
1 0 obj
|
||||
<<
|
||||
/Pages 2 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<<
|
||||
/Count 5
|
||||
/Kids [
|
||||
3 0 R
|
||||
4 0 R
|
||||
5 0 R
|
||||
6 0 R
|
||||
7 0 R
|
||||
]
|
||||
/MediaBox [
|
||||
0
|
||||
0
|
||||
612
|
||||
792
|
||||
]
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
%% Page 1
|
||||
3 0 obj
|
||||
<<
|
||||
/Contents 8 0 R
|
||||
/Parent 2 0 R
|
||||
/Resources <<
|
||||
/Font <<
|
||||
/F1 10 0 R
|
||||
>>
|
||||
/ProcSet 11 0 R
|
||||
/XObject <<
|
||||
/Fx1 12 0 R
|
||||
>>
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
|
||||
%% Page 2
|
||||
4 0 obj
|
||||
<<
|
||||
/Contents 14 0 R
|
||||
/CropBox [
|
||||
10
|
||||
20
|
||||
582
|
||||
752
|
||||
]
|
||||
/Parent 2 0 R
|
||||
/Resources <<
|
||||
/Font <<
|
||||
/F1 16 0 R
|
||||
>>
|
||||
/ProcSet 17 0 R
|
||||
/XObject <<
|
||||
/Fx1 12 0 R
|
||||
>>
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
|
||||
%% Page 3
|
||||
5 0 obj
|
||||
<<
|
||||
/Contents 18 0 R
|
||||
/CropBox [
|
||||
10
|
||||
20
|
||||
582
|
||||
752
|
||||
]
|
||||
/MediaBox [
|
||||
0
|
||||
0
|
||||
612
|
||||
792
|
||||
]
|
||||
/Parent 2 0 R
|
||||
/Resources <<
|
||||
/Font <<
|
||||
/F1 20 0 R
|
||||
>>
|
||||
/ProcSet 21 0 R
|
||||
/XObject <<
|
||||
/Fx1 12 0 R
|
||||
>>
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
|
||||
%% Page 4
|
||||
6 0 obj
|
||||
<<
|
||||
/BleedBox [
|
||||
20
|
||||
40
|
||||
552
|
||||
712
|
||||
]
|
||||
/Contents 22 0 R
|
||||
/CropBox 24 0 R
|
||||
/MediaBox [
|
||||
0
|
||||
0
|
||||
612
|
||||
792
|
||||
]
|
||||
/Parent 2 0 R
|
||||
/Resources <<
|
||||
/Font <<
|
||||
/F1 25 0 R
|
||||
>>
|
||||
/ProcSet 26 0 R
|
||||
/XObject <<
|
||||
/Fx1 12 0 R
|
||||
>>
|
||||
>>
|
||||
/TrimBox [
|
||||
30
|
||||
60
|
||||
522
|
||||
672
|
||||
]
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
|
||||
%% Page 5
|
||||
7 0 obj
|
||||
<<
|
||||
/ArtBox [
|
||||
25
|
||||
50
|
||||
527
|
||||
722
|
||||
]
|
||||
/Contents 27 0 R
|
||||
/Parent 2 0 R
|
||||
/Resources <<
|
||||
/Font <<
|
||||
/F1 29 0 R
|
||||
>>
|
||||
/ProcSet 30 0 R
|
||||
/XObject <<
|
||||
/Fx1 12 0 R
|
||||
>>
|
||||
>>
|
||||
/TrimBox [
|
||||
30
|
||||
60
|
||||
522
|
||||
672
|
||||
]
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
|
||||
%% Contents for page 1
|
||||
8 0 obj
|
||||
<<
|
||||
/Length 9 0 R
|
||||
>>
|
||||
stream
|
||||
q
|
||||
BT
|
||||
/F1 12 Tf
|
||||
144 470 Td
|
||||
(Media inherited) Tj
|
||||
ET
|
||||
Q
|
||||
q
|
||||
1.00000 0.00000 0.00000 1.00000 0.00000 0.00000 cm
|
||||
/Fx1 Do
|
||||
Q
|
||||
endstream
|
||||
endobj
|
||||
|
||||
9 0 obj
|
||||
121
|
||||
endobj
|
||||
|
||||
10 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica
|
||||
/Encoding /WinAnsiEncoding
|
||||
/Name /F1
|
||||
/Subtype /Type1
|
||||
/Type /Font
|
||||
>>
|
||||
endobj
|
||||
|
||||
11 0 obj
|
||||
[
|
||||
/PDF
|
||||
/Text
|
||||
]
|
||||
endobj
|
||||
|
||||
12 0 obj
|
||||
<<
|
||||
/BBox [
|
||||
0
|
||||
0
|
||||
612
|
||||
792
|
||||
]
|
||||
/Resources <<
|
||||
/Font <<
|
||||
/F1 10 0 R
|
||||
>>
|
||||
/ProcSet 31 0 R
|
||||
>>
|
||||
/Subtype /Form
|
||||
/Type /XObject
|
||||
/Length 13 0 R
|
||||
>>
|
||||
stream
|
||||
BT
|
||||
/F1 12 Tf
|
||||
144 600 Td
|
||||
1 0 0 rg
|
||||
(red rectangle at media [0 0 612 792]) Tj
|
||||
0 -15 Td
|
||||
0 1 0 rg
|
||||
(green at crop [10 20 582 752]) Tj
|
||||
0 -15 Td
|
||||
0 0 1 rg
|
||||
(blue at bleed [20 40 552 712]) Tj
|
||||
0 -15 Td
|
||||
1 .5 0 rg
|
||||
(orange at trim [30 60 522 672]) Tj
|
||||
0 -15 Td
|
||||
1 0 1 rg
|
||||
(purple at art [40 80 452 552]) Tj
|
||||
0 -15 Td
|
||||
0 0 0 rg
|
||||
(if crop is present, page is cropped) Tj
|
||||
ET
|
||||
5 w
|
||||
1 0 0 RG
|
||||
0 0 612 792 re s
|
||||
0 1 0 RG
|
||||
10 20 572 732 re s
|
||||
0 0 1 RG
|
||||
20 40 532 672 re s
|
||||
1 .5 0 RG
|
||||
30 60 492 612 re s
|
||||
1 0 1 RG
|
||||
40 80 452 552 re s
|
||||
endstream
|
||||
endobj
|
||||
|
||||
13 0 obj
|
||||
532
|
||||
endobj
|
||||
|
||||
%% Contents for page 2
|
||||
14 0 obj
|
||||
<<
|
||||
/Length 15 0 R
|
||||
>>
|
||||
stream
|
||||
q
|
||||
BT
|
||||
/F1 12 Tf
|
||||
144 470 Td
|
||||
(Media inherited, Crop present) Tj
|
||||
ET
|
||||
Q
|
||||
q
|
||||
1.00000 0.00000 0.00000 1.00000 0.00000 0.00000 cm
|
||||
/Fx1 Do
|
||||
Q
|
||||
endstream
|
||||
endobj
|
||||
|
||||
15 0 obj
|
||||
135
|
||||
endobj
|
||||
|
||||
16 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica
|
||||
/Encoding /WinAnsiEncoding
|
||||
/Name /F1
|
||||
/Subtype /Type1
|
||||
/Type /Font
|
||||
>>
|
||||
endobj
|
||||
|
||||
17 0 obj
|
||||
[
|
||||
/PDF
|
||||
/Text
|
||||
]
|
||||
endobj
|
||||
|
||||
%% Contents for page 3
|
||||
18 0 obj
|
||||
<<
|
||||
/Length 19 0 R
|
||||
>>
|
||||
stream
|
||||
q
|
||||
BT
|
||||
/F1 12 Tf
|
||||
144 470 Td
|
||||
(Media, Crop present) Tj
|
||||
ET
|
||||
Q
|
||||
q
|
||||
1.00000 0.00000 0.00000 1.00000 0.00000 0.00000 cm
|
||||
/Fx1 Do
|
||||
Q
|
||||
endstream
|
||||
endobj
|
||||
|
||||
19 0 obj
|
||||
125
|
||||
endobj
|
||||
|
||||
20 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica
|
||||
/Encoding /WinAnsiEncoding
|
||||
/Name /F1
|
||||
/Subtype /Type1
|
||||
/Type /Font
|
||||
>>
|
||||
endobj
|
||||
|
||||
21 0 obj
|
||||
[
|
||||
/PDF
|
||||
/Text
|
||||
]
|
||||
endobj
|
||||
|
||||
%% Contents for page 4
|
||||
22 0 obj
|
||||
<<
|
||||
/Length 23 0 R
|
||||
>>
|
||||
stream
|
||||
q
|
||||
BT
|
||||
/F1 12 Tf
|
||||
144 470 Td
|
||||
(Media, Trim, Bleed present, Crop indirect) Tj
|
||||
ET
|
||||
Q
|
||||
q
|
||||
1.00000 0.00000 0.00000 1.00000 0.00000 0.00000 cm
|
||||
/Fx1 Do
|
||||
Q
|
||||
endstream
|
||||
endobj
|
||||
|
||||
23 0 obj
|
||||
147
|
||||
endobj
|
||||
|
||||
24 0 obj
|
||||
[
|
||||
10
|
||||
20
|
||||
582
|
||||
752
|
||||
]
|
||||
endobj
|
||||
|
||||
25 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica
|
||||
/Encoding /WinAnsiEncoding
|
||||
/Name /F1
|
||||
/Subtype /Type1
|
||||
/Type /Font
|
||||
>>
|
||||
endobj
|
||||
|
||||
26 0 obj
|
||||
[
|
||||
/PDF
|
||||
/Text
|
||||
]
|
||||
endobj
|
||||
|
||||
%% Contents for page 5
|
||||
27 0 obj
|
||||
<<
|
||||
/Length 28 0 R
|
||||
>>
|
||||
stream
|
||||
q
|
||||
BT
|
||||
/F1 12 Tf
|
||||
144 470 Td
|
||||
(Media inherited, Trim, Art present) Tj
|
||||
ET
|
||||
Q
|
||||
q
|
||||
1.00000 0.00000 0.00000 1.00000 0.00000 0.00000 cm
|
||||
/Fx1 Do
|
||||
Q
|
||||
endstream
|
||||
endobj
|
||||
|
||||
28 0 obj
|
||||
140
|
||||
endobj
|
||||
|
||||
29 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica
|
||||
/Encoding /WinAnsiEncoding
|
||||
/Name /F1
|
||||
/Subtype /Type1
|
||||
/Type /Font
|
||||
>>
|
||||
endobj
|
||||
|
||||
30 0 obj
|
||||
[
|
||||
/PDF
|
||||
/Text
|
||||
]
|
||||
endobj
|
||||
|
||||
31 0 obj
|
||||
[
|
||||
/PDF
|
||||
/Text
|
||||
]
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 32
|
||||
0000000000 65535 f
|
||||
0000000025 00000 n
|
||||
0000000079 00000 n
|
||||
0000000247 00000 n
|
||||
0000000446 00000 n
|
||||
0000000693 00000 n
|
||||
0000000986 00000 n
|
||||
0000001345 00000 n
|
||||
0000001651 00000 n
|
||||
0000001827 00000 n
|
||||
0000001847 00000 n
|
||||
0000001966 00000 n
|
||||
0000002002 00000 n
|
||||
0000002745 00000 n
|
||||
0000002789 00000 n
|
||||
0000002981 00000 n
|
||||
0000003002 00000 n
|
||||
0000003121 00000 n
|
||||
0000003180 00000 n
|
||||
0000003362 00000 n
|
||||
0000003383 00000 n
|
||||
0000003502 00000 n
|
||||
0000003561 00000 n
|
||||
0000003765 00000 n
|
||||
0000003786 00000 n
|
||||
0000003829 00000 n
|
||||
0000003948 00000 n
|
||||
0000004007 00000 n
|
||||
0000004204 00000 n
|
||||
0000004225 00000 n
|
||||
0000004344 00000 n
|
||||
0000004380 00000 n
|
||||
trailer <<
|
||||
/Root 1 0 R
|
||||
/Size 32
|
||||
/ID [<42ed290ee4e4c51171853f92a1a7642d><4529bd7e2686f4deaa59ab2fa8e0338d>]
|
||||
>>
|
||||
startxref
|
||||
4416
|
||||
%%EOF
|
@ -3302,6 +3302,108 @@ test_93(QPDF& pdf, char const* arg2)
|
||||
assert(trailer.getKey("/Potato") == oh2);
|
||||
}
|
||||
|
||||
static void
|
||||
test_94(QPDF& pdf, char const* arg2)
|
||||
{
|
||||
// Exercise methods to get page boxes. This test is built for
|
||||
// boxes2.pdf.
|
||||
|
||||
// /MediaBox is present in the pages tree root.
|
||||
// Each page has the following boxes present directly:
|
||||
// 1. none
|
||||
// 2. crop
|
||||
// 3. media, crop
|
||||
// 4. media, crop, trim, bleed; crop is indirect
|
||||
// 5. trim, art
|
||||
|
||||
auto pages_root = pdf.getRoot().getKey("/Pages");
|
||||
auto root_media = pages_root.getKey("/MediaBox");
|
||||
auto root_media_unparse = root_media.unparse();
|
||||
auto pages = QPDFPageDocumentHelper(pdf).getAllPages();
|
||||
assert(pages.size() == 5);
|
||||
auto& p1 = pages[0];
|
||||
auto& p2 = pages[1];
|
||||
auto& p3 = pages[2];
|
||||
auto& p4 = pages[3];
|
||||
auto& p5 = pages[4];
|
||||
|
||||
assert(p1.getObjectHandle().getKey("/MediaBox").isNull());
|
||||
// MediaBox not present, so get inherited one
|
||||
assert(p1.getMediaBox(false) == root_media);
|
||||
// Other boxesBox not present, so fall back to MediaBox
|
||||
assert(p1.getCropBox(false, false) == root_media);
|
||||
assert(p1.getBleedBox(false, false) == root_media);
|
||||
assert(p1.getTrimBox(false, false) == root_media);
|
||||
assert(p1.getArtBox(false, false) == root_media);
|
||||
// Make copy of artbox
|
||||
auto p1_new_art = p1.getArtBox(false, true);
|
||||
assert(p1_new_art.unparse() == root_media_unparse);
|
||||
assert(p1_new_art != root_media);
|
||||
// This also copied cropbox
|
||||
auto p1_new_crop = p1.getCropBox(false, false);
|
||||
assert(p1_new_crop != root_media);
|
||||
assert(p1_new_crop != p1_new_art);
|
||||
assert(p1_new_crop.unparse() == root_media_unparse);
|
||||
// But it didn't copy Media
|
||||
assert(p1.getMediaBox(false) == root_media);
|
||||
// Now fall back to new crop
|
||||
assert(p1.getTrimBox(false, false) == p1_new_crop);
|
||||
// Request copy. The value returned has the same structure but is
|
||||
// a different object.
|
||||
auto p1_effective_media = p1.getMediaBox(true);
|
||||
assert(p1_effective_media.unparse() == root_media_unparse);
|
||||
assert(p1_effective_media != root_media);
|
||||
|
||||
// copy_on_fallback didn't have to copy media to crop
|
||||
assert(p2.getMediaBox(false) == root_media);
|
||||
auto p2_crop = p2.getCropBox(false, false);
|
||||
auto p2_new_trim = p2.getTrimBox(false, true);
|
||||
assert(p2_new_trim.unparse() == p2_crop.unparse());
|
||||
assert(p2_new_trim != p2_crop);
|
||||
assert(p2.getMediaBox(false) == root_media);
|
||||
|
||||
// We didn't need to copy anything
|
||||
auto p3_media = p3.getMediaBox(false);
|
||||
auto p3_crop = p3.getCropBox(false, false);
|
||||
assert(p3.getMediaBox(true) == p3_media);
|
||||
assert(p3.getCropBox(true, true) == p3_crop);
|
||||
|
||||
// We didn't have to copy for bleed but we did for art
|
||||
auto p4_orig_crop = p4.getObjectHandle().getKey("/CropBox");
|
||||
auto p4_crop = p4.getCropBox(false, false);
|
||||
assert(p4_orig_crop == p4_crop);
|
||||
auto p4_bleed1 = p4.getBleedBox(false, false);
|
||||
auto p4_bleed2 = p4.getBleedBox(false, true);
|
||||
assert(p4_bleed1 != p4_crop);
|
||||
assert(p4_bleed1 == p4_bleed2);
|
||||
auto p4_art1 = p4.getArtBox(false, false);
|
||||
assert(p4_art1 == p4_crop);
|
||||
auto p4_art2 = p4.getArtBox(false, true);
|
||||
assert(p4_art2 != p4_crop);
|
||||
auto p4_new_crop = p4.getCropBox(true, false);
|
||||
assert(p4_new_crop != p4_orig_crop);
|
||||
assert(p4_orig_crop.isIndirect());
|
||||
assert(!p4_new_crop.isIndirect());
|
||||
assert(p4_new_crop.unparse() == p4_orig_crop.unparseResolved());
|
||||
|
||||
// Exercise copying for inheritence and fallback
|
||||
assert(p5.getMediaBox(false) == root_media);
|
||||
assert(p5.getCropBox(false, false) == root_media);
|
||||
assert(p5.getBleedBox(false, false) == root_media);
|
||||
auto p5_new_bleed = p5.getBleedBox(true, true);
|
||||
auto p5_new_media = p5.getMediaBox(false);
|
||||
auto p5_new_crop = p5.getCropBox(false, false);
|
||||
assert(p5_new_media != root_media);
|
||||
assert(p5_new_crop != root_media);
|
||||
assert(p5_new_crop != p5_new_media);
|
||||
assert(p5_new_bleed != root_media);
|
||||
assert(p5_new_bleed != p5_new_media);
|
||||
assert(p5_new_bleed != p5_new_crop);
|
||||
assert(p5_new_media.unparse() == root_media_unparse);
|
||||
assert(p5_new_crop.unparse() == root_media_unparse);
|
||||
assert(p5_new_bleed.unparse() == root_media_unparse);
|
||||
}
|
||||
|
||||
void
|
||||
runtest(int n, char const* filename1, char const* arg2)
|
||||
{
|
||||
@ -3411,7 +3513,7 @@ runtest(int n, char const* filename1, char const* arg2)
|
||||
{80, test_80}, {81, test_81}, {82, test_82}, {83, test_83},
|
||||
{84, test_84}, {85, test_85}, {86, test_86}, {87, test_87},
|
||||
{88, test_88}, {89, test_89}, {90, test_90}, {91, test_91},
|
||||
{92, test_92}, {93, test_93}};
|
||||
{92, test_92}, {93, test_93}, {94, test_94}};
|
||||
|
||||
auto fn = test_functions.find(n);
|
||||
if (fn == test_functions.end()) {
|
||||
|
Loading…
Reference in New Issue
Block a user