From 76cd7ea67aee7edd1004a68a28e63a00a38239dd Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Tue, 6 Sep 2022 19:00:40 -0400 Subject: Clarify and improve QPDFPageObjectHelper::get*Box methods Add copy_if_fallback and explain how it differs from copy_if_shared. --- include/qpdf/QPDFPageObjectHelper.hh | 157 +++++++++++++++++++++++++++++++---- 1 file changed, 142 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/qpdf/QPDFPageObjectHelper.hh b/include/qpdf/QPDFPageObjectHelper.hh index df4c7864..ef3c09ca 100644 --- a/include/qpdf/QPDFPageObjectHelper.hh +++ b/include/qpdf/QPDFPageObjectHelper.hh @@ -45,29 +45,151 @@ 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 + // 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 effective MediaBox QPDF_DLL - QPDFObjectHandle getTrimBox(bool copy_if_shared = false); + QPDFObjectHandle getMediaBox(bool copy_if_shared = false); - // Return the CropBox. If not defined, fall back to MediaBox + // Return the effective CropBox. If not defined, fall back to + // MediaBox QPDF_DLL - QPDFObjectHandle getCropBox(bool copy_if_shared = false); + QPDFObjectHandle + getCropBox(bool copy_if_shared = false, bool copy_if_fallback = false); - // Return the MediaBox + // Return the effective BleedBox. If not defined, fall back to + // CropBox. QPDF_DLL - QPDFObjectHandle getMediaBox(bool copy_if_shared = false); + 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 @@ -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 get_fallback, + bool copy_if_fallback); static bool removeUnreferencedResourcesHelper( QPDFPageObjectHelper ph, std::set& unresolved); -- cgit v1.2.3-54-g00ecf