Commit f4589458370dc9dd9ed8ade3e4c25d4262f65e52
Committed by
GitHub
Merge pull request #1537 from m-holger/oh_int
Add new private-API class Integer
Showing
7 changed files
with
215 additions
and
129 deletions
include/qpdf/ObjectHandle.hh
| @@ -27,7 +27,6 @@ | @@ -27,7 +27,6 @@ | ||
| 27 | #include <qpdf/JSON.hh> | 27 | #include <qpdf/JSON.hh> |
| 28 | #include <qpdf/QPDFExc.hh> | 28 | #include <qpdf/QPDFExc.hh> |
| 29 | #include <qpdf/QPDFObjGen.hh> | 29 | #include <qpdf/QPDFObjGen.hh> |
| 30 | -#include <qpdf/Types.h> | ||
| 31 | 30 | ||
| 32 | #include <cstdint> | 31 | #include <cstdint> |
| 33 | #include <memory> | 32 | #include <memory> |
| @@ -42,6 +41,7 @@ namespace qpdf | @@ -42,6 +41,7 @@ namespace qpdf | ||
| 42 | class Array; | 41 | class Array; |
| 43 | class BaseDictionary; | 42 | class BaseDictionary; |
| 44 | class Dictionary; | 43 | class Dictionary; |
| 44 | + class Integer; | ||
| 45 | class Stream; | 45 | class Stream; |
| 46 | 46 | ||
| 47 | enum typed : std::uint8_t { strict = 0, any_flag = 1, optional = 2, any = 3, error = 4 }; | 47 | enum typed : std::uint8_t { strict = 0, any_flag = 1, optional = 2, any = 3, error = 4 }; |
| @@ -127,6 +127,7 @@ namespace qpdf | @@ -127,6 +127,7 @@ namespace qpdf | ||
| 127 | inline void assign(qpdf_object_type_e required, BaseHandle&& other); | 127 | inline void assign(qpdf_object_type_e required, BaseHandle&& other); |
| 128 | 128 | ||
| 129 | std::string description() const; | 129 | std::string description() const; |
| 130 | + std::invalid_argument invalid_error(std::string const& method) const; | ||
| 130 | std::runtime_error type_error(char const* expected_type) const; | 131 | std::runtime_error type_error(char const* expected_type) const; |
| 131 | QPDFExc type_error(char const* expected_type, std::string const& message) const; | 132 | QPDFExc type_error(char const* expected_type, std::string const& message) const; |
| 132 | char const* type_name() const; | 133 | char const* type_name() const; |
include/qpdf/QPDF.hh
| @@ -46,10 +46,15 @@ | @@ -46,10 +46,15 @@ | ||
| 46 | #include <qpdf/QPDFWriter.hh> | 46 | #include <qpdf/QPDFWriter.hh> |
| 47 | #include <qpdf/QPDFXRefEntry.hh> | 47 | #include <qpdf/QPDFXRefEntry.hh> |
| 48 | 48 | ||
| 49 | -namespace qpdf::is | 49 | +namespace qpdf |
| 50 | { | 50 | { |
| 51 | - class OffsetBuffer; | ||
| 52 | -} | 51 | + class Dictionary; |
| 52 | + | ||
| 53 | + namespace is | ||
| 54 | + { | ||
| 55 | + class OffsetBuffer; | ||
| 56 | + } | ||
| 57 | +} // namespace qpdf | ||
| 53 | 58 | ||
| 54 | class QPDF_Stream; | 59 | class QPDF_Stream; |
| 55 | class BitStream; | 60 | class BitStream; |
| @@ -1006,7 +1011,7 @@ class QPDF | @@ -1006,7 +1011,7 @@ class QPDF | ||
| 1006 | void checkLinearizationInternal(); | 1011 | void checkLinearizationInternal(); |
| 1007 | void dumpLinearizationDataInternal(); | 1012 | void dumpLinearizationDataInternal(); |
| 1008 | void linearizationWarning(std::string_view); | 1013 | void linearizationWarning(std::string_view); |
| 1009 | - QPDFObjectHandle readHintStream(Pipeline&, qpdf_offset_t offset, size_t length); | 1014 | + qpdf::Dictionary readHintStream(Pipeline&, qpdf_offset_t offset, size_t length); |
| 1010 | void readHPageOffset(BitStream); | 1015 | void readHPageOffset(BitStream); |
| 1011 | void readHSharedObject(BitStream); | 1016 | void readHSharedObject(BitStream); |
| 1012 | void readHGeneric(BitStream, HGeneric&); | 1017 | void readHGeneric(BitStream, HGeneric&); |
libqpdf/QPDFObjectHandle.cc
| @@ -806,16 +806,36 @@ QPDFObjectHandle::getValueAsBool(bool& value) const | @@ -806,16 +806,36 @@ QPDFObjectHandle::getValueAsBool(bool& value) const | ||
| 806 | return false; | 806 | return false; |
| 807 | } | 807 | } |
| 808 | 808 | ||
| 809 | -// Integer accessors | 809 | +// Integer methods |
| 810 | + | ||
| 811 | +Integer::Integer(long long value) : | ||
| 812 | + BaseHandle(QPDFObject::create<QPDF_Integer>(value)) | ||
| 813 | +{ | ||
| 814 | +} | ||
| 815 | + | ||
| 816 | +QPDFObjectHandle | ||
| 817 | +QPDFObjectHandle::newInteger(long long value) | ||
| 818 | +{ | ||
| 819 | + return {QPDFObject::create<QPDF_Integer>(value)}; | ||
| 820 | +} | ||
| 821 | + | ||
| 822 | +int64_t | ||
| 823 | +Integer::value() const | ||
| 824 | +{ | ||
| 825 | + auto* i = as<QPDF_Integer>(); | ||
| 826 | + if (!i) { | ||
| 827 | + throw invalid_error("Integer"); | ||
| 828 | + } | ||
| 829 | + return i->val; | ||
| 830 | +} | ||
| 810 | 831 | ||
| 811 | long long | 832 | long long |
| 812 | QPDFObjectHandle::getIntValue() const | 833 | QPDFObjectHandle::getIntValue() const |
| 813 | { | 834 | { |
| 814 | - if (auto integer = as<QPDF_Integer>()) { | ||
| 815 | - return integer->val; | 835 | + if (auto const integer = Integer(*this)) { |
| 836 | + return integer; | ||
| 816 | } else { | 837 | } else { |
| 817 | typeWarning("integer", "returning 0"); | 838 | typeWarning("integer", "returning 0"); |
| 818 | - QTC::TC("qpdf", "QPDFObjectHandle integer returning 0"); | ||
| 819 | return 0; | 839 | return 0; |
| 820 | } | 840 | } |
| 821 | } | 841 | } |
| @@ -823,8 +843,8 @@ QPDFObjectHandle::getIntValue() const | @@ -823,8 +843,8 @@ QPDFObjectHandle::getIntValue() const | ||
| 823 | bool | 843 | bool |
| 824 | QPDFObjectHandle::getValueAsInt(long long& value) const | 844 | QPDFObjectHandle::getValueAsInt(long long& value) const |
| 825 | { | 845 | { |
| 826 | - if (auto integer = as<QPDF_Integer>()) { | ||
| 827 | - value = integer->val; | 846 | + if (auto const integer = Integer(*this)) { |
| 847 | + value = integer; | ||
| 828 | return true; | 848 | return true; |
| 829 | } | 849 | } |
| 830 | return false; | 850 | return false; |
| @@ -833,16 +853,18 @@ QPDFObjectHandle::getValueAsInt(long long& value) const | @@ -833,16 +853,18 @@ QPDFObjectHandle::getValueAsInt(long long& value) const | ||
| 833 | int | 853 | int |
| 834 | QPDFObjectHandle::getIntValueAsInt() const | 854 | QPDFObjectHandle::getIntValueAsInt() const |
| 835 | { | 855 | { |
| 836 | - long long v = getIntValue(); | ||
| 837 | - if (v < INT_MIN) { | 856 | + try { |
| 857 | + return Integer(*this); | ||
| 858 | + } catch (std::underflow_error&) { | ||
| 838 | warn("requested value of integer is too small; returning INT_MIN"); | 859 | warn("requested value of integer is too small; returning INT_MIN"); |
| 839 | return INT_MIN; | 860 | return INT_MIN; |
| 840 | - } | ||
| 841 | - if (v > INT_MAX) { | 861 | + } catch (std::overflow_error&) { |
| 842 | warn("requested value of integer is too big; returning INT_MAX"); | 862 | warn("requested value of integer is too big; returning INT_MAX"); |
| 843 | return INT_MAX; | 863 | return INT_MAX; |
| 864 | + } catch (std::invalid_argument&) { | ||
| 865 | + typeWarning("integer", "returning 0"); | ||
| 866 | + return 0; | ||
| 844 | } | 867 | } |
| 845 | - return static_cast<int>(v); | ||
| 846 | } | 868 | } |
| 847 | 869 | ||
| 848 | bool | 870 | bool |
| @@ -858,12 +880,14 @@ QPDFObjectHandle::getValueAsInt(int& value) const | @@ -858,12 +880,14 @@ QPDFObjectHandle::getValueAsInt(int& value) const | ||
| 858 | unsigned long long | 880 | unsigned long long |
| 859 | QPDFObjectHandle::getUIntValue() const | 881 | QPDFObjectHandle::getUIntValue() const |
| 860 | { | 882 | { |
| 861 | - long long v = getIntValue(); | ||
| 862 | - if (v < 0) { | 883 | + try { |
| 884 | + return Integer(*this); | ||
| 885 | + } catch (std::underflow_error&) { | ||
| 863 | warn("unsigned value request for negative number; returning 0"); | 886 | warn("unsigned value request for negative number; returning 0"); |
| 864 | return 0; | 887 | return 0; |
| 865 | - } else { | ||
| 866 | - return static_cast<unsigned long long>(v); | 888 | + } catch (std::invalid_argument&) { |
| 889 | + typeWarning("integer", "returning 0"); | ||
| 890 | + return 0; | ||
| 867 | } | 891 | } |
| 868 | } | 892 | } |
| 869 | 893 | ||
| @@ -880,16 +904,18 @@ QPDFObjectHandle::getValueAsUInt(unsigned long long& value) const | @@ -880,16 +904,18 @@ QPDFObjectHandle::getValueAsUInt(unsigned long long& value) const | ||
| 880 | unsigned int | 904 | unsigned int |
| 881 | QPDFObjectHandle::getUIntValueAsUInt() const | 905 | QPDFObjectHandle::getUIntValueAsUInt() const |
| 882 | { | 906 | { |
| 883 | - long long v = getIntValue(); | ||
| 884 | - if (v < 0) { | 907 | + try { |
| 908 | + return Integer(*this); | ||
| 909 | + } catch (std::underflow_error&) { | ||
| 885 | warn("unsigned integer value request for negative number; returning 0"); | 910 | warn("unsigned integer value request for negative number; returning 0"); |
| 886 | return 0; | 911 | return 0; |
| 887 | - } | ||
| 888 | - if (v > UINT_MAX) { | 912 | + } catch (std::overflow_error&) { |
| 889 | warn("requested value of unsigned integer is too big; returning UINT_MAX"); | 913 | warn("requested value of unsigned integer is too big; returning UINT_MAX"); |
| 890 | return UINT_MAX; | 914 | return UINT_MAX; |
| 915 | + } catch (std::invalid_argument&) { | ||
| 916 | + typeWarning("integer", "returning 0"); | ||
| 917 | + return 0; | ||
| 891 | } | 918 | } |
| 892 | - return static_cast<unsigned int>(v); | ||
| 893 | } | 919 | } |
| 894 | 920 | ||
| 895 | bool | 921 | bool |
| @@ -1329,7 +1355,7 @@ QPDFObjectHandle::rotatePage(int angle, bool relative) | @@ -1329,7 +1355,7 @@ QPDFObjectHandle::rotatePage(int angle, bool relative) | ||
| 1329 | } | 1355 | } |
| 1330 | new_angle = (new_angle + 360) % 360; | 1356 | new_angle = (new_angle + 360) % 360; |
| 1331 | // Make this explicit even with new_angle == 0 since /Rotate can be inherited. | 1357 | // Make this explicit even with new_angle == 0 since /Rotate can be inherited. |
| 1332 | - replaceKey("/Rotate", QPDFObjectHandle::newInteger(new_angle)); | 1358 | + replaceKey("/Rotate", Integer(new_angle)); |
| 1333 | } | 1359 | } |
| 1334 | 1360 | ||
| 1335 | void | 1361 | void |
| @@ -1595,12 +1621,11 @@ QPDFObjectHandle::parseContentStream_data( | @@ -1595,12 +1621,11 @@ QPDFObjectHandle::parseContentStream_data( | ||
| 1595 | QTC::TC("qpdf", "QPDFObjectHandle EOF in inline image"); | 1621 | QTC::TC("qpdf", "QPDFObjectHandle EOF in inline image"); |
| 1596 | warn( | 1622 | warn( |
| 1597 | context, | 1623 | context, |
| 1598 | - QPDFExc( | ||
| 1599 | - qpdf_e_damaged_pdf, | ||
| 1600 | - description, | ||
| 1601 | - "stream data", | ||
| 1602 | - input.tell(), | ||
| 1603 | - "EOF found while reading inline image")); | 1624 | + {qpdf_e_damaged_pdf, |
| 1625 | + description, | ||
| 1626 | + "stream data", | ||
| 1627 | + input.tell(), | ||
| 1628 | + "EOF found while reading inline image"}); | ||
| 1604 | } else { | 1629 | } else { |
| 1605 | QTC::TC("qpdf", "QPDFObjectHandle inline image token"); | 1630 | QTC::TC("qpdf", "QPDFObjectHandle inline image token"); |
| 1606 | if (callbacks) { | 1631 | if (callbacks) { |
| @@ -1658,12 +1683,6 @@ QPDFObjectHandle::newNull() | @@ -1658,12 +1683,6 @@ QPDFObjectHandle::newNull() | ||
| 1658 | } | 1683 | } |
| 1659 | 1684 | ||
| 1660 | QPDFObjectHandle | 1685 | QPDFObjectHandle |
| 1661 | -QPDFObjectHandle::newInteger(long long value) | ||
| 1662 | -{ | ||
| 1663 | - return {QPDFObject::create<QPDF_Integer>(value)}; | ||
| 1664 | -} | ||
| 1665 | - | ||
| 1666 | -QPDFObjectHandle | ||
| 1667 | QPDFObjectHandle::newReal(std::string const& value) | 1686 | QPDFObjectHandle::newReal(std::string const& value) |
| 1668 | { | 1687 | { |
| 1669 | return {QPDFObject::create<QPDF_Real>(value)}; | 1688 | return {QPDFObject::create<QPDF_Real>(value)}; |
| @@ -1932,6 +1951,11 @@ QPDFObjectHandle::assertInitialized() const | @@ -1932,6 +1951,11 @@ QPDFObjectHandle::assertInitialized() const | ||
| 1932 | } | 1951 | } |
| 1933 | } | 1952 | } |
| 1934 | 1953 | ||
| 1954 | +std::invalid_argument | ||
| 1955 | +BaseHandle::invalid_error(std::string const& method) const | ||
| 1956 | +{ | ||
| 1957 | + return std::invalid_argument(method + " operation attempted on invalid object"); | ||
| 1958 | +} | ||
| 1935 | std::runtime_error | 1959 | std::runtime_error |
| 1936 | BaseHandle::type_error(char const* expected_type) const | 1960 | BaseHandle::type_error(char const* expected_type) const |
| 1937 | { | 1961 | { |
| @@ -1976,9 +2000,7 @@ void | @@ -1976,9 +2000,7 @@ void | ||
| 1976 | QPDFObjectHandle::assertType(char const* type_name, bool istype) const | 2000 | QPDFObjectHandle::assertType(char const* type_name, bool istype) const |
| 1977 | { | 2001 | { |
| 1978 | if (!istype) { | 2002 | if (!istype) { |
| 1979 | - throw std::runtime_error( | ||
| 1980 | - std::string("operation for ") + type_name + " attempted on object of type " + | ||
| 1981 | - QPDFObjectHandle(*this).getTypeName()); | 2003 | + throw type_error(type_name); |
| 1982 | } | 2004 | } |
| 1983 | } | 2005 | } |
| 1984 | 2006 |
libqpdf/QPDF_linearization.cc
| @@ -122,27 +122,18 @@ QPDF::isLinearized() | @@ -122,27 +122,18 @@ QPDF::isLinearized() | ||
| 122 | continue; | 122 | continue; |
| 123 | } | 123 | } |
| 124 | 124 | ||
| 125 | - auto candidate = getObject(toI(QUtil::string_to_ll(t1.getValue().data())), 0); | ||
| 126 | - if (!candidate.isDictionary()) { | ||
| 127 | - return false; | ||
| 128 | - } | ||
| 129 | - | ||
| 130 | - auto linkey = candidate.getKey("/Linearized"); | 125 | + Dictionary candidate = getObject(toI(QUtil::string_to_ll(t1.getValue().data())), 0); |
| 126 | + auto linkey = candidate["/Linearized"]; | ||
| 131 | if (!(linkey.isNumber() && toI(floor(linkey.getNumericValue())) == 1)) { | 127 | if (!(linkey.isNumber() && toI(floor(linkey.getNumericValue())) == 1)) { |
| 132 | return false; | 128 | return false; |
| 133 | } | 129 | } |
| 134 | 130 | ||
| 135 | - auto L = candidate.getKey("/L"); | ||
| 136 | - if (!L.isInteger()) { | ||
| 137 | - return false; | ||
| 138 | - } | ||
| 139 | - qpdf_offset_t Li = L.getIntValue(); | ||
| 140 | m->file->seek(0, SEEK_END); | 131 | m->file->seek(0, SEEK_END); |
| 141 | - if (Li != m->file->tell()) { | ||
| 142 | - QTC::TC("qpdf", "QPDF /L mismatch"); | 132 | + Integer L = candidate["/L"]; |
| 133 | + if (L != m->file->tell()) { | ||
| 143 | return false; | 134 | return false; |
| 144 | } | 135 | } |
| 145 | - m->linp.file_size = Li; | 136 | + m->linp.file_size = L; |
| 146 | m->lindict = candidate; | 137 | m->lindict = candidate; |
| 147 | return true; | 138 | return true; |
| 148 | } | 139 | } |
| @@ -159,80 +150,57 @@ QPDF::readLinearizationData() | @@ -159,80 +150,57 @@ QPDF::readLinearizationData() | ||
| 159 | } | 150 | } |
| 160 | 151 | ||
| 161 | // /L is read and stored in linp by isLinearized() | 152 | // /L is read and stored in linp by isLinearized() |
| 162 | - QPDFObjectHandle H = m->lindict.getKey("/H"); | ||
| 163 | - QPDFObjectHandle O = m->lindict.getKey("/O"); | ||
| 164 | - QPDFObjectHandle E = m->lindict.getKey("/E"); | ||
| 165 | - QPDFObjectHandle N = m->lindict.getKey("/N"); | ||
| 166 | - QPDFObjectHandle T = m->lindict.getKey("/T"); | ||
| 167 | - QPDFObjectHandle P = m->lindict.getKey("/P"); | ||
| 168 | - | ||
| 169 | - if (!(H.isArray() && O.isInteger() && E.isInteger() && N.isInteger() && T.isInteger() && | ||
| 170 | - (P.isInteger() || P.null()))) { | 153 | + Array H = m->lindict["/H"]; // hint table offset/length for primary and overflow hint tables |
| 154 | + auto H_size = H.size(); | ||
| 155 | + Integer H_0 = H[0]; // hint table offset | ||
| 156 | + Integer H_1 = H[1]; // hint table length | ||
| 157 | + Integer H_2 = H[2]; // hint table offset for overflow hint table | ||
| 158 | + Integer H_3 = H[3]; // hint table length for overflow hint table | ||
| 159 | + Integer O = m->lindict["/O"]; | ||
| 160 | + Integer E = m->lindict["/E"]; | ||
| 161 | + Integer N = m->lindict["/N"]; | ||
| 162 | + Integer T = m->lindict["/T"]; | ||
| 163 | + auto P_oh = m->lindict["/P"]; | ||
| 164 | + Integer P = P_oh; // first page number | ||
| 165 | + QTC::TC("qpdf", "QPDF P absent in lindict", P ? 0 : 1); | ||
| 166 | + | ||
| 167 | + if (!(H && O && E && N && T && (P || P_oh.null()))) { | ||
| 171 | throw damagedPDF( | 168 | throw damagedPDF( |
| 172 | "linearization dictionary", | 169 | "linearization dictionary", |
| 173 | "some keys in linearization dictionary are of the wrong type"); | 170 | "some keys in linearization dictionary are of the wrong type"); |
| 174 | } | 171 | } |
| 175 | 172 | ||
| 176 | - // Hint table array: offset length [ offset length ] | ||
| 177 | - size_t n_H_items = H.size(); | ||
| 178 | - if (!(n_H_items == 2 || n_H_items == 4)) { | 173 | + if (!(H_size == 2 || H_size == 4)) { |
| 179 | throw damagedPDF("linearization dictionary", "H has the wrong number of items"); | 174 | throw damagedPDF("linearization dictionary", "H has the wrong number of items"); |
| 180 | } | 175 | } |
| 181 | 176 | ||
| 182 | - std::vector<int> H_items; | ||
| 183 | - for (auto const& oh: H.as_array()) { | ||
| 184 | - if (oh.isInteger()) { | ||
| 185 | - H_items.push_back(oh.getIntValueAsInt()); | ||
| 186 | - } else { | ||
| 187 | - throw damagedPDF("linearization dictionary", "some H items are of the wrong type"); | ||
| 188 | - } | ||
| 189 | - } | ||
| 190 | - | ||
| 191 | - // H: hint table offset/length for primary and overflow hint tables | ||
| 192 | - int H0_offset = H_items.at(0); | ||
| 193 | - int H0_length = H_items.at(1); | ||
| 194 | - int H1_offset = 0; | ||
| 195 | - int H1_length = 0; | ||
| 196 | - if (H_items.size() == 4) { | ||
| 197 | - // Acrobat doesn't read or write these (as PDF 1.4), so we don't have a way to generate a | ||
| 198 | - // test case. | ||
| 199 | - // QTC::TC("qpdf", "QPDF overflow hint table"); | ||
| 200 | - H1_offset = H_items.at(2); | ||
| 201 | - H1_length = H_items.at(3); | ||
| 202 | - } | ||
| 203 | - | ||
| 204 | - // P: first page number | ||
| 205 | - int first_page = 0; | ||
| 206 | - if (P.isInteger()) { | ||
| 207 | - QTC::TC("qpdf", "QPDF P present in lindict"); | ||
| 208 | - first_page = P.getIntValueAsInt(); | ||
| 209 | - } else { | ||
| 210 | - QTC::TC("qpdf", "QPDF P absent in lindict"); | 177 | + if (!(H_0 && H_1 && (H_size == 2 || (H_2 && H_3)))) { |
| 178 | + throw damagedPDF("linearization dictionary", "some H items are of the wrong type"); | ||
| 211 | } | 179 | } |
| 212 | 180 | ||
| 213 | // Store linearization parameter data | 181 | // Store linearization parameter data |
| 214 | 182 | ||
| 215 | // Various places in the code use linp.npages, which is initialized from N, to pre-allocate | 183 | // Various places in the code use linp.npages, which is initialized from N, to pre-allocate |
| 216 | // memory, so make sure it's accurate and bail right now if it's not. | 184 | // memory, so make sure it's accurate and bail right now if it's not. |
| 217 | - if (N.getIntValue() != static_cast<long long>(getAllPages().size())) { | 185 | + if (N != getAllPages().size()) { |
| 218 | throw damagedPDF("linearization hint table", "/N does not match number of pages"); | 186 | throw damagedPDF("linearization hint table", "/N does not match number of pages"); |
| 219 | } | 187 | } |
| 220 | 188 | ||
| 221 | // file_size initialized by isLinearized() | 189 | // file_size initialized by isLinearized() |
| 222 | - m->linp.first_page_object = O.getIntValueAsInt(); | ||
| 223 | - m->linp.first_page_end = E.getIntValue(); | ||
| 224 | - m->linp.npages = N.getUIntValueAsUInt(); | ||
| 225 | - m->linp.xref_zero_offset = T.getIntValue(); | ||
| 226 | - m->linp.first_page = first_page; | ||
| 227 | - m->linp.H_offset = H0_offset; | ||
| 228 | - m->linp.H_length = H0_length; | 190 | + m->linp.first_page_object = O; |
| 191 | + m->linp.first_page_end = E; | ||
| 192 | + m->linp.npages = N; | ||
| 193 | + m->linp.xref_zero_offset = T; | ||
| 194 | + m->linp.first_page = P ? P : 0; | ||
| 195 | + m->linp.H_offset = H_0; | ||
| 196 | + m->linp.H_length = H_1; | ||
| 229 | 197 | ||
| 230 | // Read hint streams | 198 | // Read hint streams |
| 231 | 199 | ||
| 232 | Pl_Buffer pb("hint buffer"); | 200 | Pl_Buffer pb("hint buffer"); |
| 233 | - QPDFObjectHandle H0 = readHintStream(pb, H0_offset, toS(H0_length)); | ||
| 234 | - if (H1_offset) { | ||
| 235 | - (void)readHintStream(pb, H1_offset, toS(H1_length)); | 201 | + auto H0 = readHintStream(pb, H_0, H_1); |
| 202 | + if (H_2) { | ||
| 203 | + (void)readHintStream(pb, H_2, H_3); | ||
| 236 | } | 204 | } |
| 237 | 205 | ||
| 238 | // PDF 1.4 hint tables that we ignore: | 206 | // PDF 1.4 hint tables that we ignore: |
| @@ -246,8 +214,8 @@ QPDF::readLinearizationData() | @@ -246,8 +214,8 @@ QPDF::readLinearizationData() | ||
| 246 | // /L page label | 214 | // /L page label |
| 247 | 215 | ||
| 248 | // Individual hint table offsets | 216 | // Individual hint table offsets |
| 249 | - QPDFObjectHandle HS = H0.getKey("/S"); // shared object | ||
| 250 | - QPDFObjectHandle HO = H0.getKey("/O"); // outline | 217 | + Integer HS = H0["/S"]; // shared object |
| 218 | + Integer HO = H0["/O"]; // outline | ||
| 251 | 219 | ||
| 252 | auto hbp = pb.getBufferSharedPointer(); | 220 | auto hbp = pb.getBufferSharedPointer(); |
| 253 | Buffer* hb = hbp.get(); | 221 | Buffer* hb = hbp.get(); |
| @@ -256,22 +224,22 @@ QPDF::readLinearizationData() | @@ -256,22 +224,22 @@ QPDF::readLinearizationData() | ||
| 256 | 224 | ||
| 257 | readHPageOffset(BitStream(h_buf, h_size)); | 225 | readHPageOffset(BitStream(h_buf, h_size)); |
| 258 | 226 | ||
| 259 | - int HSi = HS.getIntValueAsInt(); | ||
| 260 | - if ((HSi < 0) || (toS(HSi) >= h_size)) { | 227 | + size_t HSi = HS; |
| 228 | + if (HSi < 0 || HSi >= h_size) { | ||
| 261 | throw damagedPDF("linearization hint table", "/S (shared object) offset is out of bounds"); | 229 | throw damagedPDF("linearization hint table", "/S (shared object) offset is out of bounds"); |
| 262 | } | 230 | } |
| 263 | - readHSharedObject(BitStream(h_buf + HSi, h_size - toS(HSi))); | 231 | + readHSharedObject(BitStream(h_buf + HSi, h_size - HSi)); |
| 264 | 232 | ||
| 265 | - if (HO.isInteger()) { | ||
| 266 | - int HOi = HO.getIntValueAsInt(); | ||
| 267 | - if ((HOi < 0) || (toS(HOi) >= h_size)) { | 233 | + if (HO) { |
| 234 | + if (HO < 0 || HO >= h_size) { | ||
| 268 | throw damagedPDF("linearization hint table", "/O (outline) offset is out of bounds"); | 235 | throw damagedPDF("linearization hint table", "/O (outline) offset is out of bounds"); |
| 269 | } | 236 | } |
| 270 | - readHGeneric(BitStream(h_buf + HOi, h_size - toS(HOi)), m->outline_hints); | 237 | + size_t HOi = HO; |
| 238 | + readHGeneric(BitStream(h_buf + HO, h_size - HOi), m->outline_hints); | ||
| 271 | } | 239 | } |
| 272 | } | 240 | } |
| 273 | 241 | ||
| 274 | -QPDFObjectHandle | 242 | +Dictionary |
| 275 | QPDF::readHintStream(Pipeline& pl, qpdf_offset_t offset, size_t length) | 243 | QPDF::readHintStream(Pipeline& pl, qpdf_offset_t offset, size_t length) |
| 276 | { | 244 | { |
| 277 | auto H = readObjectAtOffset(offset, "linearization hint stream", false); | 245 | auto H = readObjectAtOffset(offset, "linearization hint stream", false); |
| @@ -282,18 +250,14 @@ QPDF::readHintStream(Pipeline& pl, qpdf_offset_t offset, size_t length) | @@ -282,18 +250,14 @@ QPDF::readHintStream(Pipeline& pl, qpdf_offset_t offset, size_t length) | ||
| 282 | throw damagedPDF("linearization dictionary", "hint table is not a stream"); | 250 | throw damagedPDF("linearization dictionary", "hint table is not a stream"); |
| 283 | } | 251 | } |
| 284 | 252 | ||
| 285 | - QPDFObjectHandle Hdict = H.getDict(); | 253 | + Dictionary Hdict = H.getDict(); |
| 286 | 254 | ||
| 287 | // Some versions of Acrobat make /Length indirect and place it immediately after the stream, | 255 | // Some versions of Acrobat make /Length indirect and place it immediately after the stream, |
| 288 | // increasing length to cover it, even though the specification says all objects in the | 256 | // increasing length to cover it, even though the specification says all objects in the |
| 289 | // linearization parameter dictionary must be direct. We have to get the file position of the | 257 | // linearization parameter dictionary must be direct. We have to get the file position of the |
| 290 | // end of length in this case. | 258 | // end of length in this case. |
| 291 | - QPDFObjectHandle length_obj = Hdict.getKey("/Length"); | ||
| 292 | - if (length_obj.isIndirect()) { | ||
| 293 | - QTC::TC("qpdf", "QPDF hint table length indirect"); | ||
| 294 | - // Force resolution | ||
| 295 | - (void)length_obj.getIntValue(); | ||
| 296 | - ObjCache& oc2 = m->obj_cache[length_obj.getObjGen()]; | 259 | + if (Hdict["/Length"].indirect()) { |
| 260 | + ObjCache& oc2 = m->obj_cache[Hdict["/Length"]]; | ||
| 297 | min_end_offset = oc2.end_before_space; | 261 | min_end_offset = oc2.end_before_space; |
| 298 | max_end_offset = oc2.end_after_space; | 262 | max_end_offset = oc2.end_after_space; |
| 299 | } else { | 263 | } else { |
libqpdf/qpdf/QPDFObjectHandle_private.hh
| @@ -7,6 +7,11 @@ | @@ -7,6 +7,11 @@ | ||
| 7 | #include <qpdf/QPDF_private.hh> | 7 | #include <qpdf/QPDF_private.hh> |
| 8 | #include <qpdf/QUtil.hh> | 8 | #include <qpdf/QUtil.hh> |
| 9 | 9 | ||
| 10 | +#include <concepts> | ||
| 11 | +#include <utility> | ||
| 12 | + | ||
| 13 | +using namespace std::literals; | ||
| 14 | + | ||
| 10 | namespace qpdf | 15 | namespace qpdf |
| 11 | { | 16 | { |
| 12 | class Array final: public BaseHandle | 17 | class Array final: public BaseHandle |
| @@ -285,6 +290,97 @@ namespace qpdf | @@ -285,6 +290,97 @@ namespace qpdf | ||
| 285 | ~Dictionary() = default; | 290 | ~Dictionary() = default; |
| 286 | }; | 291 | }; |
| 287 | 292 | ||
| 293 | + class Integer final: public BaseHandle | ||
| 294 | + { | ||
| 295 | + public: | ||
| 296 | + Integer() = default; | ||
| 297 | + Integer(Integer const&) = default; | ||
| 298 | + Integer(Integer&&) = default; | ||
| 299 | + Integer& operator=(Integer const&) = default; | ||
| 300 | + Integer& operator=(Integer&&) = default; | ||
| 301 | + ~Integer() = default; | ||
| 302 | + | ||
| 303 | + explicit Integer(long long value); | ||
| 304 | + | ||
| 305 | + explicit Integer(std::integral auto value) : | ||
| 306 | + Integer(static_cast<long long>(value)) | ||
| 307 | + { | ||
| 308 | + if constexpr ( | ||
| 309 | + std::numeric_limits<decltype(value)>::max() > | ||
| 310 | + std::numeric_limits<long long>::max()) { | ||
| 311 | + if (value > std::numeric_limits<long long>::max()) { | ||
| 312 | + throw std::overflow_error("overflow constructing Integer"); | ||
| 313 | + } | ||
| 314 | + } | ||
| 315 | + } | ||
| 316 | + | ||
| 317 | + Integer(QPDFObjectHandle const& oh) : | ||
| 318 | + BaseHandle(oh.type_code() == ::ot_integer ? oh : QPDFObjectHandle()) | ||
| 319 | + { | ||
| 320 | + } | ||
| 321 | + | ||
| 322 | + Integer(QPDFObjectHandle&& oh) : | ||
| 323 | + BaseHandle(oh.type_code() == ::ot_integer ? std::move(oh) : QPDFObjectHandle()) | ||
| 324 | + { | ||
| 325 | + } | ||
| 326 | + | ||
| 327 | + // Return the integer value. If the object is not a valid integer, throw a | ||
| 328 | + // std::invalid_argument exception. If the object is out of range for the target type, | ||
| 329 | + // throw a std::overflow_error or std::underflow_error exception. | ||
| 330 | + template <std::integral T> | ||
| 331 | + operator T() const | ||
| 332 | + { | ||
| 333 | + auto v = value(); | ||
| 334 | + | ||
| 335 | + if (std::cmp_greater(v, std::numeric_limits<T>::max())) { | ||
| 336 | + throw std::overflow_error("Integer conversion overflow"); | ||
| 337 | + } | ||
| 338 | + if (std::cmp_less(v, std::numeric_limits<T>::min())) { | ||
| 339 | + throw std::underflow_error("Integer conversion underflow"); | ||
| 340 | + } | ||
| 341 | + return static_cast<T>(v); | ||
| 342 | + } | ||
| 343 | + | ||
| 344 | + // Return the integer value. If the object is not a valid integer, throw a | ||
| 345 | + // std::invalid_argument exception. | ||
| 346 | + int64_t value() const; | ||
| 347 | + | ||
| 348 | + // Return true if object value is equal to the 'rhs' value. Return false if the object is | ||
| 349 | + // not a valid Integer. | ||
| 350 | + friend bool | ||
| 351 | + operator==(Integer const& lhs, std::integral auto rhs) | ||
| 352 | + { | ||
| 353 | + return lhs && std::cmp_equal(lhs.value(), rhs); | ||
| 354 | + } | ||
| 355 | + | ||
| 356 | + // Compare the object value to the 'rhs' value. Throw a std::invalid_argument exception if | ||
| 357 | + // the object is not a valid Integer. | ||
| 358 | + friend std::strong_ordering | ||
| 359 | + operator<=>(Integer const& lhs, std::integral auto rhs) | ||
| 360 | + { | ||
| 361 | + if (!lhs) { | ||
| 362 | + throw lhs.invalid_error("Integer"); | ||
| 363 | + } | ||
| 364 | + if (std::cmp_less(lhs.value(), rhs)) { | ||
| 365 | + return std::strong_ordering::less; | ||
| 366 | + } | ||
| 367 | + return std::cmp_greater(lhs.value(), rhs) ? std::strong_ordering::greater | ||
| 368 | + : std::strong_ordering::equal; | ||
| 369 | + } | ||
| 370 | + }; | ||
| 371 | + | ||
| 372 | + bool | ||
| 373 | + operator==(std::integral auto lhs, Integer const& rhs) | ||
| 374 | + { | ||
| 375 | + return rhs == lhs; | ||
| 376 | + } | ||
| 377 | + | ||
| 378 | + std::strong_ordering | ||
| 379 | + operator<=>(std::integral auto lhs, Integer const& rhs) | ||
| 380 | + { | ||
| 381 | + return rhs <=> lhs; | ||
| 382 | + } | ||
| 383 | + | ||
| 288 | class Name final: public BaseHandle | 384 | class Name final: public BaseHandle |
| 289 | { | 385 | { |
| 290 | public: | 386 | public: |
libqpdf/qpdf/QPDFObject_private.hh
| @@ -27,6 +27,7 @@ namespace qpdf | @@ -27,6 +27,7 @@ namespace qpdf | ||
| 27 | class Array; | 27 | class Array; |
| 28 | class BaseDictionary; | 28 | class BaseDictionary; |
| 29 | class Dictionary; | 29 | class Dictionary; |
| 30 | + class Integer; | ||
| 30 | class Stream; | 31 | class Stream; |
| 31 | } // namespace qpdf | 32 | } // namespace qpdf |
| 32 | 33 | ||
| @@ -123,6 +124,7 @@ class QPDF_Integer final | @@ -123,6 +124,7 @@ class QPDF_Integer final | ||
| 123 | { | 124 | { |
| 124 | friend class QPDFObject; | 125 | friend class QPDFObject; |
| 125 | friend class qpdf::BaseHandle; | 126 | friend class qpdf::BaseHandle; |
| 127 | + friend class qpdf::Integer; | ||
| 126 | friend class QPDFObjectHandle; | 128 | friend class QPDFObjectHandle; |
| 127 | 129 | ||
| 128 | QPDF_Integer(long long val) : | 130 | QPDF_Integer(long long val) : |
qpdf/qpdf.testcov
| @@ -7,12 +7,9 @@ QPDF check obj 1 | @@ -7,12 +7,9 @@ QPDF check obj 1 | ||
| 7 | QPDF object stream offsets not increasing 0 | 7 | QPDF object stream offsets not increasing 0 |
| 8 | QPDF ignore self-referential object stream 0 | 8 | QPDF ignore self-referential object stream 0 |
| 9 | QPDF object stream contains id < 1 0 | 9 | QPDF object stream contains id < 1 0 |
| 10 | -QPDF hint table length indirect 0 | ||
| 11 | QPDF hint table length direct 0 | 10 | QPDF hint table length direct 0 |
| 12 | -QPDF P absent in lindict 0 | ||
| 13 | -QPDF P present in lindict 0 | 11 | +QPDF P absent in lindict 1 |
| 14 | QPDF expected n n obj 0 | 12 | QPDF expected n n obj 0 |
| 15 | -QPDF /L mismatch 0 | ||
| 16 | QPDF err /T mismatch 0 | 13 | QPDF err /T mismatch 0 |
| 17 | QPDF err /O mismatch 0 | 14 | QPDF err /O mismatch 0 |
| 18 | QPDF opt direct pages resource 1 | 15 | QPDF opt direct pages resource 1 |
| @@ -277,7 +274,6 @@ QPDFParser bad token in parseRemainder 0 | @@ -277,7 +274,6 @@ QPDFParser bad token in parseRemainder 0 | ||
| 277 | QPDFParser eof in parse 0 | 274 | QPDFParser eof in parse 0 |
| 278 | QPDFParser eof in parseRemainder 0 | 275 | QPDFParser eof in parseRemainder 0 |
| 279 | QPDFObjectHandle boolean returning false 0 | 276 | QPDFObjectHandle boolean returning false 0 |
| 280 | -QPDFObjectHandle integer returning 0 0 | ||
| 281 | QPDFObjectHandle real returning 0.0 0 | 277 | QPDFObjectHandle real returning 0.0 0 |
| 282 | QPDFObjectHandle name returning dummy name 0 | 278 | QPDFObjectHandle name returning dummy name 0 |
| 283 | QPDFObjectHandle string returning empty string 0 | 279 | QPDFObjectHandle string returning empty string 0 |