2
1
mirror of https://github.com/qpdf/qpdf.git synced 2024-05-31 01:10:51 +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:
Jay Berkenbilt 2022-09-06 19:00:40 -04:00
parent c1def4ead4
commit 76cd7ea67a
8 changed files with 825 additions and 42 deletions

View File

@ -1,5 +1,13 @@
2022-09-06 Jay Berkenbilt <ejb@ql.org> 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 * Add == equality for QPDFObjectHandle. Two QPDFObjectHandle
objects are equal if they point to the same underlying object, objects are equal if they point to the same underlying object,
meaning changes to one will be reflected in the other. meaning changes to one will be reflected in the other.

View File

@ -45,30 +45,152 @@ class QPDFPageObjectHelper: public QPDFObjectHelper
QPDF_DLL QPDF_DLL
virtual ~QPDFPageObjectHelper() = default; virtual ~QPDFPageObjectHelper() = default;
// Works with pages and form XObjects. Return the effective value // PAGE ATTRIBUTES
// of this attribute for the page/form XObject. For pages, if the
// requested attribute is not present on the page but is // The getAttribute method works with pages and form XObjects. It
// inheritable, look up through the page's ancestors in the page // return the value of the requested attribute from the page/form
// tree. If copy_if_shared is true, then this method will replace // XObject's dictionary, taking inheritance from the pages tree
// the attribute with a shallow copy if it is in indirect or // into consideration. For pages, the attributes /MediaBox,
// inherited and return the copy. You should do this if you are // /CropBox, /Resources, and /Rotate are inheritable, meaning that
// going to modify the returned object and want the modifications // if they are not present directly on the page node, they may be
// to apply to the current page/form XObject only. // 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 QPDF_DLL
QPDFObjectHandle getAttribute(std::string const& name, bool copy_if_shared); QPDFObjectHandle getAttribute(std::string const& name, bool copy_if_shared);
// Return the TrimBox. If not defined, fall back to CropBox // PAGE BOXES
QPDF_DLL //
QPDFObjectHandle getTrimBox(bool copy_if_shared = false); // 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 // Return the effective MediaBox
QPDF_DLL
QPDFObjectHandle getCropBox(bool copy_if_shared = false);
// Return the MediaBox
QPDF_DLL QPDF_DLL
QPDFObjectHandle getMediaBox(bool copy_if_shared = false); 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 // Iterate through XObjects, possibly recursing into form
// XObjects. This works with pages or form XObjects. Call action // XObjects. This works with pages or form XObjects. Call action
// on each XObject for which selector, if specified, returns true. // on each XObject for which selector, if specified, returns true.
@ -373,6 +495,11 @@ class QPDFPageObjectHelper: public QPDFObjectHelper
QPDFAcroFormDocumentHelper* from_afdh = nullptr); QPDFAcroFormDocumentHelper* from_afdh = nullptr);
private: private:
QPDFObjectHandle getAttribute(
std::string const& name,
bool copy_if_shared,
std::function<QPDFObjectHandle()> get_fallback,
bool copy_if_fallback);
static bool removeUnreferencedResourcesHelper( static bool removeUnreferencedResourcesHelper(
QPDFPageObjectHelper ph, std::set<std::string>& unresolved); QPDFPageObjectHelper ph, std::set<std::string>& unresolved);

View File

@ -237,6 +237,16 @@ QPDFPageObjectHelper::QPDFPageObjectHelper(QPDFObjectHandle oh) :
QPDFObjectHandle QPDFObjectHandle
QPDFPageObjectHelper::getAttribute(std::string const& name, bool copy_if_shared) 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 result;
QPDFObjectHandle dict; QPDFObjectHandle dict;
@ -272,28 +282,17 @@ QPDFPageObjectHelper::getAttribute(std::string const& name, bool copy_if_shared)
"qpdf", "qpdf",
"QPDFPageObjectHelper copy shared attribute", "QPDFPageObjectHelper copy shared attribute",
is_form_xobject ? 0 : 1); is_form_xobject ? 0 : 1);
result = result.shallowCopy(); result = dict.replaceKeyAndGetNew(name, result.shallowCopy());
dict.replaceKey(name, result);
} }
return result; if (result.isNull() && get_fallback) {
} result = get_fallback();
if (copy_if_fallback && !result.isNull()) {
QPDFObjectHandle QTC::TC("qpdf", "QPDFPageObjectHelper copied fallback");
QPDFPageObjectHelper::getTrimBox(bool copy_if_shared) result = dict.replaceKeyAndGetNew(name, result.shallowCopy());
{ } else {
QPDFObjectHandle result = getAttribute("/TrimBox", copy_if_shared); QTC::TC(
if (result.isNull()) { "qpdf", "QPDFPageObjectHelper used fallback without copying");
result = getCropBox(copy_if_shared); }
}
return result;
}
QPDFObjectHandle
QPDFPageObjectHelper::getCropBox(bool copy_if_shared)
{
QPDFObjectHandle result = getAttribute("/CropBox", copy_if_shared);
if (result.isNull()) {
result = getMediaBox();
} }
return result; return result;
} }
@ -304,6 +303,52 @@ QPDFPageObjectHelper::getMediaBox(bool copy_if_shared)
return getAttribute("/MediaBox", 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 void
QPDFPageObjectHelper::forEachXObject( QPDFPageObjectHelper::forEachXObject(
bool recursive, bool recursive,

View File

@ -261,6 +261,11 @@ For a detailed list of changes, please see the file
generation parameters. The old versions will continue to be generation parameters. The old versions will continue to be
supported and are not deprecated. 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. - Add a move constructor to the ``Buffer`` class.
- Other changes - Other changes

View File

@ -676,3 +676,5 @@ QPDF_json missing json version 0
QPDF_json bad json version 0 QPDF_json bad json version 0
QPDF_json bad calledgetallpages 0 QPDF_json bad calledgetallpages 0
QPDF_json bad pushedinheritedpageresources 0 QPDF_json bad pushedinheritedpageresources 0
QPDFPageObjectHelper copied fallback 0
QPDFPageObjectHelper used fallback without copying 0

View File

@ -14,8 +14,6 @@ cleanup();
my $td = new TestDriver('page-api'); my $td = new TestDriver('page-api');
my $n_tests = 11;
$td->runtest("basic page API", $td->runtest("basic page API",
{$td->COMMAND => "test_driver 15 page_api_1.pdf"}, {$td->COMMAND => "test_driver 15 page_api_1.pdf"},
{$td->STRING => "test 15 done\n", $td->EXIT_STATUS => 0}, {$td->STRING => "test 15 done\n", $td->EXIT_STATUS => 0},
@ -58,5 +56,10 @@ $td->runtest("flatten rotation",
$td->runtest("check output", $td->runtest("check output",
{$td->FILE => "a.pdf"}, {$td->FILE => "a.pdf"},
{$td->FILE => "boxes-flattened.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(); cleanup();
$td->report($n_tests); $td->report(12);

491
qpdf/qtest/qpdf/boxes2.pdf Normal file
View 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

View File

@ -3302,6 +3302,108 @@ test_93(QPDF& pdf, char const* arg2)
assert(trailer.getKey("/Potato") == oh2); 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 void
runtest(int n, char const* filename1, char const* arg2) 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}, {80, test_80}, {81, test_81}, {82, test_82}, {83, test_83},
{84, test_84}, {85, test_85}, {86, test_86}, {87, test_87}, {84, test_84}, {85, test_85}, {86, test_86}, {87, test_87},
{88, test_88}, {89, test_89}, {90, test_90}, {91, test_91}, {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); auto fn = test_functions.find(n);
if (fn == test_functions.end()) { if (fn == test_functions.end()) {