Commit f4589458370dc9dd9ed8ade3e4c25d4262f65e52

Authored by m-holger
Committed by GitHub
2 parents e50ee3b6 4aa6ee8d

Merge pull request #1537 from m-holger/oh_int

Add new private-API class Integer
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&amp; value) const @@ -806,16 +806,36 @@ QPDFObjectHandle::getValueAsBool(bool&amp; 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&amp; value) const @@ -833,16 +853,18 @@ QPDFObjectHandle::getValueAsInt(long long&amp; 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&amp; value) const @@ -858,12 +880,14 @@ QPDFObjectHandle::getValueAsInt(int&amp; 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&amp; value) const @@ -880,16 +904,18 @@ QPDFObjectHandle::getValueAsUInt(unsigned long long&amp; 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&amp; pl, qpdf_offset_t offset, size_t length) @@ -282,18 +250,14 @@ QPDF::readHintStream(Pipeline&amp; 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