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 | 27 | #include <qpdf/JSON.hh> |
| 28 | 28 | #include <qpdf/QPDFExc.hh> |
| 29 | 29 | #include <qpdf/QPDFObjGen.hh> |
| 30 | -#include <qpdf/Types.h> | |
| 31 | 30 | |
| 32 | 31 | #include <cstdint> |
| 33 | 32 | #include <memory> |
| ... | ... | @@ -42,6 +41,7 @@ namespace qpdf |
| 42 | 41 | class Array; |
| 43 | 42 | class BaseDictionary; |
| 44 | 43 | class Dictionary; |
| 44 | + class Integer; | |
| 45 | 45 | class Stream; |
| 46 | 46 | |
| 47 | 47 | enum typed : std::uint8_t { strict = 0, any_flag = 1, optional = 2, any = 3, error = 4 }; |
| ... | ... | @@ -127,6 +127,7 @@ namespace qpdf |
| 127 | 127 | inline void assign(qpdf_object_type_e required, BaseHandle&& other); |
| 128 | 128 | |
| 129 | 129 | std::string description() const; |
| 130 | + std::invalid_argument invalid_error(std::string const& method) const; | |
| 130 | 131 | std::runtime_error type_error(char const* expected_type) const; |
| 131 | 132 | QPDFExc type_error(char const* expected_type, std::string const& message) const; |
| 132 | 133 | char const* type_name() const; | ... | ... |
include/qpdf/QPDF.hh
| ... | ... | @@ -46,10 +46,15 @@ |
| 46 | 46 | #include <qpdf/QPDFWriter.hh> |
| 47 | 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 | 59 | class QPDF_Stream; |
| 55 | 60 | class BitStream; |
| ... | ... | @@ -1006,7 +1011,7 @@ class QPDF |
| 1006 | 1011 | void checkLinearizationInternal(); |
| 1007 | 1012 | void dumpLinearizationDataInternal(); |
| 1008 | 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 | 1015 | void readHPageOffset(BitStream); |
| 1011 | 1016 | void readHSharedObject(BitStream); |
| 1012 | 1017 | void readHGeneric(BitStream, HGeneric&); | ... | ... |
libqpdf/QPDFObjectHandle.cc
| ... | ... | @@ -806,16 +806,36 @@ QPDFObjectHandle::getValueAsBool(bool& value) const |
| 806 | 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 | 832 | long long |
| 812 | 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 | 837 | } else { |
| 817 | 838 | typeWarning("integer", "returning 0"); |
| 818 | - QTC::TC("qpdf", "QPDFObjectHandle integer returning 0"); | |
| 819 | 839 | return 0; |
| 820 | 840 | } |
| 821 | 841 | } |
| ... | ... | @@ -823,8 +843,8 @@ QPDFObjectHandle::getIntValue() const |
| 823 | 843 | bool |
| 824 | 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 | 848 | return true; |
| 829 | 849 | } |
| 830 | 850 | return false; |
| ... | ... | @@ -833,16 +853,18 @@ QPDFObjectHandle::getValueAsInt(long long& value) const |
| 833 | 853 | int |
| 834 | 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 | 859 | warn("requested value of integer is too small; returning INT_MIN"); |
| 839 | 860 | return INT_MIN; |
| 840 | - } | |
| 841 | - if (v > INT_MAX) { | |
| 861 | + } catch (std::overflow_error&) { | |
| 842 | 862 | warn("requested value of integer is too big; returning INT_MAX"); |
| 843 | 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 | 870 | bool |
| ... | ... | @@ -858,12 +880,14 @@ QPDFObjectHandle::getValueAsInt(int& value) const |
| 858 | 880 | unsigned long long |
| 859 | 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 | 886 | warn("unsigned value request for negative number; returning 0"); |
| 864 | 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 | 904 | unsigned int |
| 881 | 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 | 910 | warn("unsigned integer value request for negative number; returning 0"); |
| 886 | 911 | return 0; |
| 887 | - } | |
| 888 | - if (v > UINT_MAX) { | |
| 912 | + } catch (std::overflow_error&) { | |
| 889 | 913 | warn("requested value of unsigned integer is too big; returning UINT_MAX"); |
| 890 | 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 | 921 | bool |
| ... | ... | @@ -1329,7 +1355,7 @@ QPDFObjectHandle::rotatePage(int angle, bool relative) |
| 1329 | 1355 | } |
| 1330 | 1356 | new_angle = (new_angle + 360) % 360; |
| 1331 | 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 | 1361 | void |
| ... | ... | @@ -1595,12 +1621,11 @@ QPDFObjectHandle::parseContentStream_data( |
| 1595 | 1621 | QTC::TC("qpdf", "QPDFObjectHandle EOF in inline image"); |
| 1596 | 1622 | warn( |
| 1597 | 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 | 1629 | } else { |
| 1605 | 1630 | QTC::TC("qpdf", "QPDFObjectHandle inline image token"); |
| 1606 | 1631 | if (callbacks) { |
| ... | ... | @@ -1658,12 +1683,6 @@ QPDFObjectHandle::newNull() |
| 1658 | 1683 | } |
| 1659 | 1684 | |
| 1660 | 1685 | QPDFObjectHandle |
| 1661 | -QPDFObjectHandle::newInteger(long long value) | |
| 1662 | -{ | |
| 1663 | - return {QPDFObject::create<QPDF_Integer>(value)}; | |
| 1664 | -} | |
| 1665 | - | |
| 1666 | -QPDFObjectHandle | |
| 1667 | 1686 | QPDFObjectHandle::newReal(std::string const& value) |
| 1668 | 1687 | { |
| 1669 | 1688 | return {QPDFObject::create<QPDF_Real>(value)}; |
| ... | ... | @@ -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 | 1959 | std::runtime_error |
| 1936 | 1960 | BaseHandle::type_error(char const* expected_type) const |
| 1937 | 1961 | { |
| ... | ... | @@ -1976,9 +2000,7 @@ void |
| 1976 | 2000 | QPDFObjectHandle::assertType(char const* type_name, bool istype) const |
| 1977 | 2001 | { |
| 1978 | 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 | 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 | 127 | if (!(linkey.isNumber() && toI(floor(linkey.getNumericValue())) == 1)) { |
| 132 | 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 | 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 | 134 | return false; |
| 144 | 135 | } |
| 145 | - m->linp.file_size = Li; | |
| 136 | + m->linp.file_size = L; | |
| 146 | 137 | m->lindict = candidate; |
| 147 | 138 | return true; |
| 148 | 139 | } |
| ... | ... | @@ -159,80 +150,57 @@ QPDF::readLinearizationData() |
| 159 | 150 | } |
| 160 | 151 | |
| 161 | 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 | 168 | throw damagedPDF( |
| 172 | 169 | "linearization dictionary", |
| 173 | 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 | 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 | 181 | // Store linearization parameter data |
| 214 | 182 | |
| 215 | 183 | // Various places in the code use linp.npages, which is initialized from N, to pre-allocate |
| 216 | 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 | 186 | throw damagedPDF("linearization hint table", "/N does not match number of pages"); |
| 219 | 187 | } |
| 220 | 188 | |
| 221 | 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 | 198 | // Read hint streams |
| 231 | 199 | |
| 232 | 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 | 206 | // PDF 1.4 hint tables that we ignore: |
| ... | ... | @@ -246,8 +214,8 @@ QPDF::readLinearizationData() |
| 246 | 214 | // /L page label |
| 247 | 215 | |
| 248 | 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 | 220 | auto hbp = pb.getBufferSharedPointer(); |
| 253 | 221 | Buffer* hb = hbp.get(); |
| ... | ... | @@ -256,22 +224,22 @@ QPDF::readLinearizationData() |
| 256 | 224 | |
| 257 | 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 | 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 | 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 | 243 | QPDF::readHintStream(Pipeline& pl, qpdf_offset_t offset, size_t length) |
| 276 | 244 | { |
| 277 | 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 | 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 | 255 | // Some versions of Acrobat make /Length indirect and place it immediately after the stream, |
| 288 | 256 | // increasing length to cover it, even though the specification says all objects in the |
| 289 | 257 | // linearization parameter dictionary must be direct. We have to get the file position of the |
| 290 | 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 | 261 | min_end_offset = oc2.end_before_space; |
| 298 | 262 | max_end_offset = oc2.end_after_space; |
| 299 | 263 | } else { | ... | ... |
libqpdf/qpdf/QPDFObjectHandle_private.hh
| ... | ... | @@ -7,6 +7,11 @@ |
| 7 | 7 | #include <qpdf/QPDF_private.hh> |
| 8 | 8 | #include <qpdf/QUtil.hh> |
| 9 | 9 | |
| 10 | +#include <concepts> | |
| 11 | +#include <utility> | |
| 12 | + | |
| 13 | +using namespace std::literals; | |
| 14 | + | |
| 10 | 15 | namespace qpdf |
| 11 | 16 | { |
| 12 | 17 | class Array final: public BaseHandle |
| ... | ... | @@ -285,6 +290,97 @@ namespace qpdf |
| 285 | 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 | 384 | class Name final: public BaseHandle |
| 289 | 385 | { |
| 290 | 386 | public: | ... | ... |
libqpdf/qpdf/QPDFObject_private.hh
| ... | ... | @@ -27,6 +27,7 @@ namespace qpdf |
| 27 | 27 | class Array; |
| 28 | 28 | class BaseDictionary; |
| 29 | 29 | class Dictionary; |
| 30 | + class Integer; | |
| 30 | 31 | class Stream; |
| 31 | 32 | } // namespace qpdf |
| 32 | 33 | |
| ... | ... | @@ -123,6 +124,7 @@ class QPDF_Integer final |
| 123 | 124 | { |
| 124 | 125 | friend class QPDFObject; |
| 125 | 126 | friend class qpdf::BaseHandle; |
| 127 | + friend class qpdf::Integer; | |
| 126 | 128 | friend class QPDFObjectHandle; |
| 127 | 129 | |
| 128 | 130 | QPDF_Integer(long long val) : | ... | ... |
qpdf/qpdf.testcov
| ... | ... | @@ -7,12 +7,9 @@ QPDF check obj 1 |
| 7 | 7 | QPDF object stream offsets not increasing 0 |
| 8 | 8 | QPDF ignore self-referential object stream 0 |
| 9 | 9 | QPDF object stream contains id < 1 0 |
| 10 | -QPDF hint table length indirect 0 | |
| 11 | 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 | 12 | QPDF expected n n obj 0 |
| 15 | -QPDF /L mismatch 0 | |
| 16 | 13 | QPDF err /T mismatch 0 |
| 17 | 14 | QPDF err /O mismatch 0 |
| 18 | 15 | QPDF opt direct pages resource 1 |
| ... | ... | @@ -277,7 +274,6 @@ QPDFParser bad token in parseRemainder 0 |
| 277 | 274 | QPDFParser eof in parse 0 |
| 278 | 275 | QPDFParser eof in parseRemainder 0 |
| 279 | 276 | QPDFObjectHandle boolean returning false 0 |
| 280 | -QPDFObjectHandle integer returning 0 0 | |
| 281 | 277 | QPDFObjectHandle real returning 0.0 0 |
| 282 | 278 | QPDFObjectHandle name returning dummy name 0 |
| 283 | 279 | QPDFObjectHandle string returning empty string 0 | ... | ... |