diff --git a/ChangeLog b/ChangeLog index a0838606..a2f84f00 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2018-06-19 Jay Berkenbilt + + * New QPDFObject::Rectangle class will convert to and from arrays + of four numerical values. Rectangles are used in various places + within the PDF file format and are called out as a specific data + type in the PDF specification. + 2018-05-12 Jay Berkenbilt * In newline before endstream mode, an extra newline was not diff --git a/include/qpdf/QPDFObjectHandle.hh b/include/qpdf/QPDFObjectHandle.hh index 6f78e9a9..4f5cde37 100644 --- a/include/qpdf/QPDFObjectHandle.hh +++ b/include/qpdf/QPDFObjectHandle.hh @@ -172,6 +172,31 @@ class QPDFObjectHandle void terminateParsing(); }; + // Convenience object for rectangles + class Rectangle + { + public: + Rectangle() : + llx(0.0), + lly(0.0), + urx(0.0), + ury(0.0) + { + } + Rectangle(double llx, double lly, + double urx, double ury) : + llx(llx), + lly(lly), + urx(urx), + ury(ury) + { + } + + double llx; + double lly; + double urx; + double ury; + }; QPDF_DLL QPDFObjectHandle(); @@ -344,11 +369,18 @@ class QPDFObjectHandle static QPDFObjectHandle newArray( std::vector const& items); QPDF_DLL + static QPDFObjectHandle newArray(Rectangle const&); + QPDF_DLL static QPDFObjectHandle newDictionary(); QPDF_DLL static QPDFObjectHandle newDictionary( std::map const& items); + // Create an array from a rectangle. Equivalent to the rectangle + // form of newArray. + QPDF_DLL + static QPDFObjectHandle newFromRectangle(Rectangle const&); + // Create a new stream and associate it with the given qpdf // object. A subsequent call must be made to replaceStreamData() // to provide data for the stream. The stream's dictionary may be @@ -465,6 +497,12 @@ class QPDFObjectHandle QPDFObjectHandle getArrayItem(int n); QPDF_DLL std::vector getArrayAsVector(); + QPDF_DLL + bool isRectangle(); + // If the array an array of four numeric values, return as a + // rectangle. Otherwise, return the rectangle [0, 0, 0, 0] + QPDF_DLL + Rectangle getArrayAsRectangle(); // Methods for dictionary objects QPDF_DLL @@ -739,6 +777,15 @@ class QPDFObjectHandle QPDF_DLL void coalesceContentStreams(); + // Issue a warning about this object if possible. If the object + // has a description, a warning will be issued. Otherwise, if + // throw_if_no_description is true, throw an exception. Otherwise + // do nothing. Objects read normally from the file have + // descriptions. See comments on setObjectDescription for + // additional details. + void warnIfPossible(std::string const& warning, + bool throw_if_no_description = false); + // Initializers for objects. This Factory class gives the QPDF // class specific permission to call factory methods without // making it a friend of the whole QPDFObjectHandle class. diff --git a/libqpdf/QPDFObjectHandle.cc b/libqpdf/QPDFObjectHandle.cc index 149668eb..5c111cc8 100644 --- a/libqpdf/QPDFObjectHandle.cc +++ b/libqpdf/QPDFObjectHandle.cc @@ -554,6 +554,42 @@ QPDFObjectHandle::getArrayItem(int n) return result; } +bool +QPDFObjectHandle::isRectangle() +{ + if (! isArray()) + { + return false; + } + if (getArrayNItems() != 4) + { + return false; + } + for (size_t i = 0; i < 4; ++i) + { + if (! getArrayItem(i).isNumber()) + { + return false; + } + } + return true; +} + + +QPDFObjectHandle::Rectangle +QPDFObjectHandle::getArrayAsRectangle() +{ + Rectangle result; + if (isRectangle()) + { + result = Rectangle(getArrayItem(0).getNumericValue(), + getArrayItem(1).getNumericValue(), + getArrayItem(2).getNumericValue(), + getArrayItem(3).getNumericValue()); + } + return result; +} + std::vector QPDFObjectHandle::getArrayAsVector() { @@ -1833,6 +1869,23 @@ QPDFObjectHandle::newArray(std::vector const& items) return QPDFObjectHandle(new QPDF_Array(items)); } +QPDFObjectHandle +QPDFObjectHandle::newArray(Rectangle const& rect) +{ + std::vector items; + items.push_back(newReal(rect.llx)); + items.push_back(newReal(rect.lly)); + items.push_back(newReal(rect.urx)); + items.push_back(newReal(rect.ury)); + return newArray(items); +} + +QPDFObjectHandle +QPDFObjectHandle::newFromRectangle(Rectangle const& rect) +{ + return newArray(rect); +} + QPDFObjectHandle QPDFObjectHandle::newDictionary() { @@ -2103,7 +2156,8 @@ QPDFObjectHandle::typeWarning(char const* expected_type, } void -QPDFObjectHandle::objectWarning(std::string const& warning) +QPDFObjectHandle::warnIfPossible(std::string const& warning, + bool throw_if_no_description) { QPDF* context = 0; std::string description; @@ -2115,12 +2169,18 @@ QPDFObjectHandle::objectWarning(std::string const& warning) "", description, 0, warning)); } - else + else if (throw_if_no_description) { throw std::logic_error(warning); } } +void +QPDFObjectHandle::objectWarning(std::string const& warning) +{ + warnIfPossible(warning, true); +} + void QPDFObjectHandle::assertType(char const* type_name, bool istype) { diff --git a/qpdf/test_driver.cc b/qpdf/test_driver.cc index 3ec20dd8..7dd6c595 100644 --- a/qpdf/test_driver.cc +++ b/qpdf/test_driver.cc @@ -1449,6 +1449,17 @@ void runtest(int n, char const* filename1, char const* arg2) QPDFObjectHandle page = pdf.getAllPages()[0]; assert("/QPDFFakeName" == page.getKey("/Contents").getDict().getKey("/Potato").getName()); + // Rectangles + QPDFObjectHandle::Rectangle r0 = integer.getArrayAsRectangle(); + assert((r0.llx == 0) && (r0.lly == 0) && + (r0.urx == 0) && (r0.ury == 0)); + QPDFObjectHandle rect = QPDFObjectHandle::newFromRectangle( + QPDFObjectHandle::Rectangle(1.2, 3.4, 5.6, 7.8)); + QPDFObjectHandle::Rectangle r1 = rect.getArrayAsRectangle(); + assert((r1.llx > 1.19) && (r1.llx < 1.21) && + (r1.lly > 3.39) && (r1.lly < 3.41) && + (r1.urx > 5.59) && (r1.urx < 5.61) && + (r1.ury > 7.79) && (r1.ury < 7.81)); } else {