Commit a8e30477587fbf72b703064fcc53ff2200bd58a3

Authored by m-holger
1 parent 0f07ecdd

Create `Linearization` class in `QPDF::Doc` and update references

Encapsulate linearization-related logic in `QPDF::Doc::Linearization`. Refactor methods, relocate implementations, and update all references accordingly to streamline and centralize linearization functionality.
include/qpdf/QPDF.hh
@@ -46,13 +46,6 @@ @@ -46,13 +46,6 @@
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  
50 -{  
51 - class Dictionary;  
52 -} // namespace qpdf  
53 -  
54 -class BitStream;  
55 -class BitWriter;  
56 class QPDFLogger; 49 class QPDFLogger;
57 50
58 class QPDF 51 class QPDF
@@ -800,33 +793,6 @@ class QPDF @@ -800,33 +793,6 @@ class QPDF
800 // For QPDFWriter: 793 // For QPDFWriter:
801 794
802 std::map<QPDFObjGen, QPDFXRefEntry> const& getXRefTableInternal(); 795 std::map<QPDFObjGen, QPDFXRefEntry> const& getXRefTableInternal();
803 - template <typename T>  
804 - void optimize_internal(  
805 - T const& object_stream_data,  
806 - bool allow_changes = true,  
807 - std::function<int(QPDFObjectHandle&)> skip_stream_parameters = nullptr);  
808 - void optimize(  
809 - QPDFWriter::ObjTable const& obj,  
810 - std::function<int(QPDFObjectHandle&)> skip_stream_parameters);  
811 -  
812 - // Get lists of all objects in order according to the part of a linearized file that they belong  
813 - // to.  
814 - void getLinearizedParts(  
815 - QPDFWriter::ObjTable const& obj,  
816 - std::vector<QPDFObjectHandle>& part4,  
817 - std::vector<QPDFObjectHandle>& part6,  
818 - std::vector<QPDFObjectHandle>& part7,  
819 - std::vector<QPDFObjectHandle>& part8,  
820 - std::vector<QPDFObjectHandle>& part9);  
821 -  
822 - void generateHintStream(  
823 - QPDFWriter::NewObjTable const& new_obj,  
824 - QPDFWriter::ObjTable const& obj,  
825 - std::string& hint_stream,  
826 - int& S,  
827 - int& O,  
828 - bool compressed);  
829 -  
830 // Get a list of objects that would be permitted in an object stream. 796 // Get a list of objects that would be permitted in an object stream.
831 template <typename T> 797 template <typename T>
832 std::vector<T> getCompressibleObjGens(); 798 std::vector<T> getCompressibleObjGens();
@@ -886,60 +852,6 @@ class QPDF @@ -886,60 +852,6 @@ class QPDF
886 bool findStartxref(); 852 bool findStartxref();
887 bool findEndstream(); 853 bool findEndstream();
888 854
889 - // methods to support linearization checking -- implemented in QPDF_linearization.cc  
890 - void readLinearizationData();  
891 - void checkLinearizationInternal();  
892 - void dumpLinearizationDataInternal();  
893 - void linearizationWarning(std::string_view);  
894 - qpdf::Dictionary readHintStream(Pipeline&, qpdf_offset_t offset, size_t length);  
895 - void readHPageOffset(BitStream);  
896 - void readHSharedObject(BitStream);  
897 - void readHGeneric(BitStream, HGeneric&);  
898 - qpdf_offset_t maxEnd(ObjUser const& ou);  
899 - qpdf_offset_t getLinearizationOffset(QPDFObjGen);  
900 - QPDFObjectHandle  
901 - getUncompressedObject(QPDFObjectHandle&, std::map<int, int> const& object_stream_data);  
902 - QPDFObjectHandle getUncompressedObject(QPDFObjectHandle&, QPDFWriter::ObjTable const& obj);  
903 - int lengthNextN(int first_object, int n);  
904 - void  
905 - checkHPageOffset(std::vector<QPDFObjectHandle> const& pages, std::map<int, int>& idx_to_obj);  
906 - void  
907 - checkHSharedObject(std::vector<QPDFObjectHandle> const& pages, std::map<int, int>& idx_to_obj);  
908 - void checkHOutlines();  
909 - void dumpHPageOffset();  
910 - void dumpHSharedObject();  
911 - void dumpHGeneric(HGeneric&);  
912 - qpdf_offset_t adjusted_offset(qpdf_offset_t offset);  
913 - template <typename T>  
914 - void calculateLinearizationData(T const& object_stream_data);  
915 - template <typename T>  
916 - void pushOutlinesToPart(  
917 - std::vector<QPDFObjectHandle>& part,  
918 - std::set<QPDFObjGen>& lc_outlines,  
919 - T const& object_stream_data);  
920 - int outputLengthNextN(  
921 - int in_object,  
922 - int n,  
923 - QPDFWriter::NewObjTable const& new_obj,  
924 - QPDFWriter::ObjTable const& obj);  
925 - void  
926 - calculateHPageOffset(QPDFWriter::NewObjTable const& new_obj, QPDFWriter::ObjTable const& obj);  
927 - void  
928 - calculateHSharedObject(QPDFWriter::NewObjTable const& new_obj, QPDFWriter::ObjTable const& obj);  
929 - void calculateHOutline(QPDFWriter::NewObjTable const& new_obj, QPDFWriter::ObjTable const& obj);  
930 - void writeHPageOffset(BitWriter&);  
931 - void writeHSharedObject(BitWriter&);  
932 - void writeHGeneric(BitWriter&, HGeneric&);  
933 -  
934 - // Methods to support optimization  
935 -  
936 - void updateObjectMaps(  
937 - ObjUser const& ou,  
938 - QPDFObjectHandle oh,  
939 - std::function<int(QPDFObjectHandle&)> skip_stream_parameters);  
940 - void filterCompressedObjects(std::map<int, int> const& object_stream_data);  
941 - void filterCompressedObjects(QPDFWriter::ObjTable const& object_stream_data);  
942 -  
943 // JSON import 855 // JSON import
944 void importJSON(std::shared_ptr<InputSource>, bool must_be_complete); 856 void importJSON(std::shared_ptr<InputSource>, bool must_be_complete);
945 857
libqpdf/QPDF.cc
@@ -180,6 +180,7 @@ QPDF::QPDFVersion() @@ -180,6 +180,7 @@ QPDF::QPDFVersion()
180 180
181 QPDF::Members::Members(QPDF& qpdf) : 181 QPDF::Members::Members(QPDF& qpdf) :
182 doc(qpdf, *this), 182 doc(qpdf, *this),
  183 + lin(doc.linearization()),
183 objects(doc.objects()), 184 objects(doc.objects()),
184 pages(doc.pages()), 185 pages(doc.pages()),
185 log(QPDFLogger::defaultLogger()), 186 log(QPDFLogger::defaultLogger()),
libqpdf/QPDFWriter.cc
@@ -267,6 +267,7 @@ class QPDF::Doc::Writer @@ -267,6 +267,7 @@ class QPDF::Doc::Writer
267 friend class QPDFWriter; 267 friend class QPDFWriter;
268 Writer(QPDF& pdf) : 268 Writer(QPDF& pdf) :
269 pdf(pdf), 269 pdf(pdf),
  270 + lin(pdf.m->lin),
270 objects(pdf.m->objects) 271 objects(pdf.m->objects)
271 { 272 {
272 } 273 }
@@ -277,7 +278,7 @@ class QPDF::Doc::Writer @@ -277,7 +278,7 @@ class QPDF::Doc::Writer
277 QPDFWriter::ObjTable const& obj, 278 QPDFWriter::ObjTable const& obj,
278 std::function<int(QPDFObjectHandle&)> skip_stream_parameters) 279 std::function<int(QPDFObjectHandle&)> skip_stream_parameters)
279 { 280 {
280 - pdf.optimize(obj, skip_stream_parameters); 281 + lin.optimize(obj, skip_stream_parameters);
281 } 282 }
282 283
283 void 284 void
@@ -289,7 +290,7 @@ class QPDF::Doc::Writer @@ -289,7 +290,7 @@ class QPDF::Doc::Writer
289 std::vector<QPDFObjectHandle>& part8, 290 std::vector<QPDFObjectHandle>& part8,
290 std::vector<QPDFObjectHandle>& part9) 291 std::vector<QPDFObjectHandle>& part9)
291 { 292 {
292 - pdf.getLinearizedParts(obj, part4, part6, part7, part8, part9); 293 + lin.getLinearizedParts(obj, part4, part6, part7, part8, part9);
293 } 294 }
294 295
295 void 296 void
@@ -301,7 +302,7 @@ class QPDF::Doc::Writer @@ -301,7 +302,7 @@ class QPDF::Doc::Writer
301 int& O, 302 int& O,
302 bool compressed) 303 bool compressed)
303 { 304 {
304 - pdf.generateHintStream(new_obj, obj, hint_stream, S, O, compressed); 305 + lin.generateHintStream(new_obj, obj, hint_stream, S, O, compressed);
305 } 306 }
306 307
307 std::vector<QPDFObjGen> 308 std::vector<QPDFObjGen>
@@ -329,6 +330,7 @@ class QPDF::Doc::Writer @@ -329,6 +330,7 @@ class QPDF::Doc::Writer
329 } 330 }
330 331
331 QPDF& pdf; 332 QPDF& pdf;
  333 + QPDF::Doc::Linearization& lin;
332 QPDF::Doc::Objects& objects; 334 QPDF::Doc::Objects& objects;
333 }; 335 };
334 336
libqpdf/QPDF_linearization.cc
@@ -24,6 +24,8 @@ @@ -24,6 +24,8 @@
24 using namespace qpdf; 24 using namespace qpdf;
25 using namespace std::literals; 25 using namespace std::literals;
26 26
  27 +using Lin = QPDF::Doc::Linearization;
  28 +
27 template <class T, class int_type> 29 template <class T, class int_type>
28 static void 30 static void
29 load_vector_int( 31 load_vector_int(
@@ -68,21 +70,21 @@ load_vector_vector( @@ -68,21 +70,21 @@ load_vector_vector(
68 } 70 }
69 71
70 void 72 void
71 -QPDF::linearizationWarning(std::string_view msg) 73 +Lin::linearizationWarning(std::string_view msg)
72 { 74 {
73 m->linearization_warnings = true; 75 m->linearization_warnings = true;
74 - warn(qpdf_e_linearization, "", 0, std::string(msg)); 76 + qpdf.warn(qpdf_e_linearization, "", 0, std::string(msg));
75 } 77 }
76 78
77 bool 79 bool
78 QPDF::checkLinearization() 80 QPDF::checkLinearization()
79 { 81 {
80 try { 82 try {
81 - readLinearizationData();  
82 - checkLinearizationInternal(); 83 + m->lin.readLinearizationData();
  84 + m->lin.checkLinearizationInternal();
83 return !m->linearization_warnings; 85 return !m->linearization_warnings;
84 } catch (std::runtime_error& e) { 86 } catch (std::runtime_error& e) {
85 - linearizationWarning( 87 + m->lin.linearizationWarning(
86 "error encountered while checking linearization data: " + std::string(e.what())); 88 "error encountered while checking linearization data: " + std::string(e.what()));
87 return false; 89 return false;
88 } 90 }
@@ -140,10 +142,10 @@ QPDF::isLinearized() @@ -140,10 +142,10 @@ QPDF::isLinearized()
140 } 142 }
141 143
142 void 144 void
143 -QPDF::readLinearizationData() 145 +Lin::readLinearizationData()
144 { 146 {
145 util::assertion( 147 util::assertion(
146 - isLinearized(), "called readLinearizationData for file that is not linearized" // 148 + qpdf.isLinearized(), "called readLinearizationData for file that is not linearized" //
147 ); 149 );
148 150
149 // This function throws an exception (which is trapped by checkLinearization()) for any errors 151 // This function throws an exception (which is trapped by checkLinearization()) for any errors
@@ -164,19 +166,19 @@ QPDF::readLinearizationData() @@ -164,19 +166,19 @@ QPDF::readLinearizationData()
164 Integer P = P_oh; // first page number 166 Integer P = P_oh; // first page number
165 QTC::TC("qpdf", "QPDF P absent in lindict", P ? 0 : 1); 167 QTC::TC("qpdf", "QPDF P absent in lindict", P ? 0 : 1);
166 168
167 - no_ci_stop_if( 169 + qpdf.no_ci_stop_if(
168 !(H && O && E && N && T && (P || P_oh.null())), 170 !(H && O && E && N && T && (P || P_oh.null())),
169 "some keys in linearization dictionary are of the wrong type", 171 "some keys in linearization dictionary are of the wrong type",
170 "linearization dictionary" // 172 "linearization dictionary" //
171 ); 173 );
172 174
173 - no_ci_stop_if( 175 + qpdf.no_ci_stop_if(
174 !(H_size == 2 || H_size == 4), 176 !(H_size == 2 || H_size == 4),
175 "H has the wrong number of items", 177 "H has the wrong number of items",
176 "linearization dictionary" // 178 "linearization dictionary" //
177 ); 179 );
178 180
179 - no_ci_stop_if( 181 + qpdf.no_ci_stop_if(
180 !(H_0 && H_1 && (H_size == 2 || (H_2 && H_3))), 182 !(H_0 && H_1 && (H_size == 2 || (H_2 && H_3))),
181 "some H items are of the wrong type", 183 "some H items are of the wrong type",
182 "linearization dictionary" // 184 "linearization dictionary" //
@@ -186,8 +188,8 @@ QPDF::readLinearizationData() @@ -186,8 +188,8 @@ QPDF::readLinearizationData()
186 188
187 // Various places in the code use linp.npages, which is initialized from N, to pre-allocate 189 // Various places in the code use linp.npages, which is initialized from N, to pre-allocate
188 // memory, so make sure it's accurate and bail right now if it's not. 190 // memory, so make sure it's accurate and bail right now if it's not.
189 - no_ci_stop_if(  
190 - N != getAllPages().size(), 191 + qpdf.no_ci_stop_if(
  192 + N != qpdf.getAllPages().size(),
191 "/N does not match number of pages", 193 "/N does not match number of pages",
192 "linearization dictionary" // 194 "linearization dictionary" //
193 ); 195 );
@@ -232,12 +234,13 @@ QPDF::readLinearizationData() @@ -232,12 +234,13 @@ QPDF::readLinearizationData()
232 234
233 size_t HSi = HS; 235 size_t HSi = HS;
234 if (HSi < 0 || HSi >= h_size) { 236 if (HSi < 0 || HSi >= h_size) {
235 - throw damagedPDF("linearization hint table", "/S (shared object) offset is out of bounds"); 237 + throw qpdf.damagedPDF(
  238 + "linearization hint table", "/S (shared object) offset is out of bounds");
236 } 239 }
237 readHSharedObject(BitStream(h_buf + HSi, h_size - HSi)); 240 readHSharedObject(BitStream(h_buf + HSi, h_size - HSi));
238 241
239 if (HO) { 242 if (HO) {
240 - no_ci_stop_if( 243 + qpdf.no_ci_stop_if(
241 HO < 0 || HO >= h_size, 244 HO < 0 || HO >= h_size,
242 "/O (outline) offset is out of bounds", 245 "/O (outline) offset is out of bounds",
243 "linearization dictionary" // 246 "linearization dictionary" //
@@ -248,13 +251,13 @@ QPDF::readLinearizationData() @@ -248,13 +251,13 @@ QPDF::readLinearizationData()
248 } 251 }
249 252
250 Dictionary 253 Dictionary
251 -QPDF::readHintStream(Pipeline& pl, qpdf_offset_t offset, size_t length) 254 +Lin::readHintStream(Pipeline& pl, qpdf_offset_t offset, size_t length)
252 { 255 {
253 auto H = m->objects.readObjectAtOffset(offset, "linearization hint stream", false); 256 auto H = m->objects.readObjectAtOffset(offset, "linearization hint stream", false);
254 ObjCache& oc = m->obj_cache[H]; 257 ObjCache& oc = m->obj_cache[H];
255 qpdf_offset_t min_end_offset = oc.end_before_space; 258 qpdf_offset_t min_end_offset = oc.end_before_space;
256 qpdf_offset_t max_end_offset = oc.end_after_space; 259 qpdf_offset_t max_end_offset = oc.end_after_space;
257 - no_ci_stop_if( 260 + qpdf.no_ci_stop_if(
258 !H.isStream(), "hint table is not a stream", "linearization dictionary" // 261 !H.isStream(), "hint table is not a stream", "linearization dictionary" //
259 ); 262 );
260 263
@@ -272,7 +275,7 @@ QPDF::readHintStream(Pipeline&amp; pl, qpdf_offset_t offset, size_t length) @@ -272,7 +275,7 @@ QPDF::readHintStream(Pipeline&amp; pl, qpdf_offset_t offset, size_t length)
272 QTC::TC("qpdf", "QPDF hint table length direct"); 275 QTC::TC("qpdf", "QPDF hint table length direct");
273 } 276 }
274 qpdf_offset_t computed_end = offset + toO(length); 277 qpdf_offset_t computed_end = offset + toO(length);
275 - no_ci_stop_if( 278 + qpdf.no_ci_stop_if(
276 computed_end < min_end_offset || computed_end > max_end_offset, 279 computed_end < min_end_offset || computed_end > max_end_offset,
277 "hint table length mismatch (expected = " + std::to_string(computed_end) + "; actual = " + 280 "hint table length mismatch (expected = " + std::to_string(computed_end) + "; actual = " +
278 std::to_string(min_end_offset) + ".." + std::to_string(max_end_offset) + ")", 281 std::to_string(min_end_offset) + ".." + std::to_string(max_end_offset) + ")",
@@ -283,7 +286,7 @@ QPDF::readHintStream(Pipeline&amp; pl, qpdf_offset_t offset, size_t length) @@ -283,7 +286,7 @@ QPDF::readHintStream(Pipeline&amp; pl, qpdf_offset_t offset, size_t length)
283 } 286 }
284 287
285 void 288 void
286 -QPDF::readHPageOffset(BitStream h) 289 +Lin::readHPageOffset(BitStream h)
287 { 290 {
288 // All comments referring to the PDF spec refer to the spec for version 1.4. 291 // All comments referring to the PDF spec refer to the spec for version 1.4.
289 292
@@ -332,7 +335,7 @@ QPDF::readHPageOffset(BitStream h) @@ -332,7 +335,7 @@ QPDF::readHPageOffset(BitStream h)
332 } 335 }
333 336
334 void 337 void
335 -QPDF::readHSharedObject(BitStream h) 338 +Lin::readHSharedObject(BitStream h)
336 { 339 {
337 HSharedObject& t = m->shared_object_hints; 340 HSharedObject& t = m->shared_object_hints;
338 341
@@ -368,7 +371,7 @@ QPDF::readHSharedObject(BitStream h) @@ -368,7 +371,7 @@ QPDF::readHSharedObject(BitStream h)
368 } 371 }
369 372
370 void 373 void
371 -QPDF::readHGeneric(BitStream h, HGeneric& t) 374 +Lin::readHGeneric(BitStream h, HGeneric& t)
372 { 375 {
373 t.first_object = h.getBitsInt(32); // 1 376 t.first_object = h.getBitsInt(32); // 1
374 t.first_object_offset = h.getBitsInt(32); // 2 377 t.first_object_offset = h.getBitsInt(32); // 2
@@ -377,7 +380,7 @@ QPDF::readHGeneric(BitStream h, HGeneric&amp; t) @@ -377,7 +380,7 @@ QPDF::readHGeneric(BitStream h, HGeneric&amp; t)
377 } 380 }
378 381
379 void 382 void
380 -QPDF::checkLinearizationInternal() 383 +Lin::checkLinearizationInternal()
381 { 384 {
382 // All comments referring to the PDF spec refer to the spec for version 1.4. 385 // All comments referring to the PDF spec refer to the spec for version 1.4.
383 386
@@ -388,7 +391,7 @@ QPDF::checkLinearizationInternal() @@ -388,7 +391,7 @@ QPDF::checkLinearizationInternal()
388 // L: file size in bytes -- checked by isLinearized 391 // L: file size in bytes -- checked by isLinearized
389 392
390 // O: object number of first page 393 // O: object number of first page
391 - std::vector<QPDFObjectHandle> const& pages = getAllPages(); 394 + std::vector<QPDFObjectHandle> const& pages = qpdf.getAllPages();
392 if (p.first_page_object != pages.at(0).getObjectID()) { 395 if (p.first_page_object != pages.at(0).getObjectID()) {
393 linearizationWarning("first page object (/O) mismatch"); 396 linearizationWarning("first page object (/O) mismatch");
394 } 397 }
@@ -461,7 +464,7 @@ QPDF::checkLinearizationInternal() @@ -461,7 +464,7 @@ QPDF::checkLinearizationInternal()
461 // are present. In that case, it would probably agree with pdlin. As of this writing, the test 464 // are present. In that case, it would probably agree with pdlin. As of this writing, the test
462 // suite doesn't contain any files with threads. 465 // suite doesn't contain any files with threads.
463 466
464 - no_ci_stop_if( 467 + qpdf.no_ci_stop_if(
465 m->part6.empty(), "linearization part 6 unexpectedly empty" // 468 m->part6.empty(), "linearization part 6 unexpectedly empty" //
466 ); 469 );
467 qpdf_offset_t min_E = -1; 470 qpdf_offset_t min_E = -1;
@@ -489,16 +492,16 @@ QPDF::checkLinearizationInternal() @@ -489,16 +492,16 @@ QPDF::checkLinearizationInternal()
489 } 492 }
490 493
491 qpdf_offset_t 494 qpdf_offset_t
492 -QPDF::maxEnd(ObjUser const& ou) 495 +Lin::maxEnd(ObjUser const& ou)
493 { 496 {
494 - no_ci_stop_if( 497 + qpdf.no_ci_stop_if(
495 !m->obj_user_to_objects.contains(ou), 498 !m->obj_user_to_objects.contains(ou),
496 "no entry in object user table for requested object user" // 499 "no entry in object user table for requested object user" //
497 ); 500 );
498 501
499 qpdf_offset_t end = 0; 502 qpdf_offset_t end = 0;
500 for (auto const& og: m->obj_user_to_objects[ou]) { 503 for (auto const& og: m->obj_user_to_objects[ou]) {
501 - no_ci_stop_if( 504 + qpdf.no_ci_stop_if(
502 !m->obj_cache.contains(og), "unknown object referenced in object user table" // 505 !m->obj_cache.contains(og), "unknown object referenced in object user table" //
503 ); 506 );
504 end = std::max(end, m->obj_cache[og].end_after_space); 507 end = std::max(end, m->obj_cache[og].end_after_space);
@@ -507,14 +510,14 @@ QPDF::maxEnd(ObjUser const&amp; ou) @@ -507,14 +510,14 @@ QPDF::maxEnd(ObjUser const&amp; ou)
507 } 510 }
508 511
509 qpdf_offset_t 512 qpdf_offset_t
510 -QPDF::getLinearizationOffset(QPDFObjGen og) 513 +Lin::getLinearizationOffset(QPDFObjGen og)
511 { 514 {
512 QPDFXRefEntry const& entry = m->xref_table[og]; 515 QPDFXRefEntry const& entry = m->xref_table[og];
513 auto typ = entry.getType(); 516 auto typ = entry.getType();
514 if (typ == 1) { 517 if (typ == 1) {
515 return entry.getOffset(); 518 return entry.getOffset();
516 } 519 }
517 - no_ci_stop_if( 520 + qpdf.no_ci_stop_if(
518 typ != 2, "getLinearizationOffset called for xref entry not of type 1 or 2" // 521 typ != 2, "getLinearizationOffset called for xref entry not of type 1 or 2" //
519 ); 522 );
520 // For compressed objects, return the offset of the object stream that contains them. 523 // For compressed objects, return the offset of the object stream that contains them.
@@ -522,33 +525,33 @@ QPDF::getLinearizationOffset(QPDFObjGen og) @@ -522,33 +525,33 @@ QPDF::getLinearizationOffset(QPDFObjGen og)
522 } 525 }
523 526
524 QPDFObjectHandle 527 QPDFObjectHandle
525 -QPDF::getUncompressedObject(QPDFObjectHandle& obj, std::map<int, int> const& object_stream_data) 528 +Lin::getUncompressedObject(QPDFObjectHandle& obj, std::map<int, int> const& object_stream_data)
526 { 529 {
527 if (obj.null() || !object_stream_data.contains(obj.getObjectID())) { 530 if (obj.null() || !object_stream_data.contains(obj.getObjectID())) {
528 return obj; 531 return obj;
529 } 532 }
530 - return getObject((*(object_stream_data.find(obj.getObjectID()))).second, 0); 533 + return qpdf.getObject((*(object_stream_data.find(obj.getObjectID()))).second, 0);
531 } 534 }
532 535
533 QPDFObjectHandle 536 QPDFObjectHandle
534 -QPDF::getUncompressedObject(QPDFObjectHandle& oh, QPDFWriter::ObjTable const& obj) 537 +Lin::getUncompressedObject(QPDFObjectHandle& oh, QPDFWriter::ObjTable const& obj)
535 { 538 {
536 if (obj.contains(oh)) { 539 if (obj.contains(oh)) {
537 if (auto id = obj[oh].object_stream; id > 0) { 540 if (auto id = obj[oh].object_stream; id > 0) {
538 - return oh.null() ? oh : getObject(id, 0); 541 + return oh.null() ? oh : qpdf.getObject(id, 0);
539 } 542 }
540 } 543 }
541 return oh; 544 return oh;
542 } 545 }
543 546
544 int 547 int
545 -QPDF::lengthNextN(int first_object, int n) 548 +Lin::lengthNextN(int first_object, int n)
546 { 549 {
547 int length = 0; 550 int length = 0;
548 for (int i = 0; i < n; ++i) { 551 for (int i = 0; i < n; ++i) {
549 QPDFObjGen og(first_object + i, 0); 552 QPDFObjGen og(first_object + i, 0);
550 if (m->xref_table.contains(og)) { 553 if (m->xref_table.contains(og)) {
551 - no_ci_stop_if( 554 + qpdf.no_ci_stop_if(
552 !m->obj_cache.contains(og), 555 !m->obj_cache.contains(og),
553 "found unknown object while calculating length for linearization data" // 556 "found unknown object while calculating length for linearization data" //
554 ); 557 );
@@ -563,7 +566,7 @@ QPDF::lengthNextN(int first_object, int n) @@ -563,7 +566,7 @@ QPDF::lengthNextN(int first_object, int n)
563 } 566 }
564 567
565 void 568 void
566 -QPDF::checkHPageOffset( 569 +Lin::checkHPageOffset(
567 std::vector<QPDFObjectHandle> const& pages, std::map<int, int>& shared_idx_to_obj) 570 std::vector<QPDFObjectHandle> const& pages, std::map<int, int>& shared_idx_to_obj)
568 { 571 {
569 // Implementation note 126 says Acrobat always sets delta_content_offset and 572 // Implementation note 126 says Acrobat always sets delta_content_offset and
@@ -582,7 +585,7 @@ QPDF::checkHPageOffset( @@ -582,7 +585,7 @@ QPDF::checkHPageOffset(
582 qpdf_offset_t table_offset = adjusted_offset(m->page_offset_hints.first_page_offset); 585 qpdf_offset_t table_offset = adjusted_offset(m->page_offset_hints.first_page_offset);
583 QPDFObjGen first_page_og(pages.at(0).getObjGen()); 586 QPDFObjGen first_page_og(pages.at(0).getObjGen());
584 if (!m->xref_table.contains(first_page_og)) { 587 if (!m->xref_table.contains(first_page_og)) {
585 - stopOnError("supposed first page object is not known"); 588 + qpdf.stopOnError("supposed first page object is not known");
586 } 589 }
587 qpdf_offset_t offset = getLinearizationOffset(first_page_og); 590 qpdf_offset_t offset = getLinearizationOffset(first_page_og);
588 if (table_offset != offset) { 591 if (table_offset != offset) {
@@ -593,7 +596,7 @@ QPDF::checkHPageOffset( @@ -593,7 +596,7 @@ QPDF::checkHPageOffset(
593 QPDFObjGen page_og(pages.at(pageno).getObjGen()); 596 QPDFObjGen page_og(pages.at(pageno).getObjGen());
594 int first_object = page_og.getObj(); 597 int first_object = page_og.getObj();
595 if (!m->xref_table.contains(page_og)) { 598 if (!m->xref_table.contains(page_og)) {
596 - stopOnError("unknown object in page offset hint table"); 599 + qpdf.stopOnError("unknown object in page offset hint table");
597 } 600 }
598 offset = getLinearizationOffset(page_og); 601 offset = getLinearizationOffset(page_og);
599 602
@@ -633,7 +636,7 @@ QPDF::checkHPageOffset( @@ -633,7 +636,7 @@ QPDF::checkHPageOffset(
633 636
634 for (size_t i = 0; i < toS(he.nshared_objects); ++i) { 637 for (size_t i = 0; i < toS(he.nshared_objects); ++i) {
635 int idx = he.shared_identifiers.at(i); 638 int idx = he.shared_identifiers.at(i);
636 - no_ci_stop_if( 639 + qpdf.no_ci_stop_if(
637 !shared_idx_to_obj.contains(idx), 640 !shared_idx_to_obj.contains(idx),
638 "unable to get object for item in shared objects hint table"); 641 "unable to get object for item in shared objects hint table");
639 642
@@ -642,7 +645,7 @@ QPDF::checkHPageOffset( @@ -642,7 +645,7 @@ QPDF::checkHPageOffset(
642 645
643 for (size_t i = 0; i < toS(ce.nshared_objects); ++i) { 646 for (size_t i = 0; i < toS(ce.nshared_objects); ++i) {
644 int idx = ce.shared_identifiers.at(i); 647 int idx = ce.shared_identifiers.at(i);
645 - no_ci_stop_if( 648 + qpdf.no_ci_stop_if(
646 idx >= m->c_shared_object_data.nshared_total, 649 idx >= m->c_shared_object_data.nshared_total,
647 "index out of bounds for shared object hint table" // 650 "index out of bounds for shared object hint table" //
648 ); 651 );
@@ -673,7 +676,7 @@ QPDF::checkHPageOffset( @@ -673,7 +676,7 @@ QPDF::checkHPageOffset(
673 } 676 }
674 677
675 void 678 void
676 -QPDF::checkHSharedObject(std::vector<QPDFObjectHandle> const& pages, std::map<int, int>& idx_to_obj) 679 +Lin::checkHSharedObject(std::vector<QPDFObjectHandle> const& pages, std::map<int, int>& idx_to_obj)
677 { 680 {
678 // Implementation note 125 says shared object groups always contain only one object. 681 // Implementation note 125 says shared object groups always contain only one object.
679 // Implementation note 128 says that Acrobat always nbits_nobjects to zero. Implementation note 682 // Implementation note 128 says that Acrobat always nbits_nobjects to zero. Implementation note
@@ -715,7 +718,7 @@ QPDF::checkHSharedObject(std::vector&lt;QPDFObjectHandle&gt; const&amp; pages, std::map&lt;in @@ -715,7 +718,7 @@ QPDF::checkHSharedObject(std::vector&lt;QPDFObjectHandle&gt; const&amp; pages, std::map&lt;in
715 718
716 QPDFObjGen og(cur_object, 0); 719 QPDFObjGen og(cur_object, 0);
717 if (!m->xref_table.contains(og)) { 720 if (!m->xref_table.contains(og)) {
718 - stopOnError("unknown object in shared object hint table"); 721 + qpdf.stopOnError("unknown object in shared object hint table");
719 } 722 }
720 qpdf_offset_t offset = getLinearizationOffset(og); 723 qpdf_offset_t offset = getLinearizationOffset(og);
721 qpdf_offset_t h_offset = adjusted_offset(so.first_shared_offset); 724 qpdf_offset_t h_offset = adjusted_offset(so.first_shared_offset);
@@ -742,7 +745,7 @@ QPDF::checkHSharedObject(std::vector&lt;QPDFObjectHandle&gt; const&amp; pages, std::map&lt;in @@ -742,7 +745,7 @@ QPDF::checkHSharedObject(std::vector&lt;QPDFObjectHandle&gt; const&amp; pages, std::map&lt;in
742 } 745 }
743 746
744 void 747 void
745 -QPDF::checkHOutlines() 748 +Lin::checkHOutlines()
746 { 749 {
747 // Empirically, Acrobat generates the correct value for the object number but incorrectly stores 750 // Empirically, Acrobat generates the correct value for the object number but incorrectly stores
748 // the next object number's offset as the offset, at least when outlines appear in part 6. It 751 // the next object number's offset as the offset, at least when outlines appear in part 6. It
@@ -757,7 +760,7 @@ QPDF::checkHOutlines() @@ -757,7 +760,7 @@ QPDF::checkHOutlines()
757 760
758 if (m->c_outline_data.first_object == m->outline_hints.first_object) { 761 if (m->c_outline_data.first_object == m->outline_hints.first_object) {
759 // Check length and offset. Acrobat gets these wrong. 762 // Check length and offset. Acrobat gets these wrong.
760 - QPDFObjectHandle outlines = getRoot().getKey("/Outlines"); 763 + QPDFObjectHandle outlines = qpdf.getRoot().getKey("/Outlines");
761 if (!outlines.isIndirect()) { 764 if (!outlines.isIndirect()) {
762 // This case is not exercised in test suite since not permitted by the spec, but if 765 // This case is not exercised in test suite since not permitted by the spec, but if
763 // this does occur, the code below would fail. 766 // this does occur, the code below would fail.
@@ -765,7 +768,7 @@ QPDF::checkHOutlines() @@ -765,7 +768,7 @@ QPDF::checkHOutlines()
765 return; 768 return;
766 } 769 }
767 QPDFObjGen og(outlines.getObjGen()); 770 QPDFObjGen og(outlines.getObjGen());
768 - no_ci_stop_if( 771 + qpdf.no_ci_stop_if(
769 !m->xref_table.contains(og), "unknown object in outlines hint table" // 772 !m->xref_table.contains(og), "unknown object in outlines hint table" //
770 ); 773 );
771 qpdf_offset_t offset = getLinearizationOffset(og); 774 qpdf_offset_t offset = getLinearizationOffset(og);
@@ -795,16 +798,16 @@ void @@ -795,16 +798,16 @@ void
795 QPDF::showLinearizationData() 798 QPDF::showLinearizationData()
796 { 799 {
797 try { 800 try {
798 - readLinearizationData();  
799 - checkLinearizationInternal();  
800 - dumpLinearizationDataInternal(); 801 + m->lin.readLinearizationData();
  802 + m->lin.checkLinearizationInternal();
  803 + m->lin.dumpLinearizationDataInternal();
801 } catch (QPDFExc& e) { 804 } catch (QPDFExc& e) {
802 - linearizationWarning(e.what()); 805 + m->lin.linearizationWarning(e.what());
803 } 806 }
804 } 807 }
805 808
806 void 809 void
807 -QPDF::dumpLinearizationDataInternal() 810 +Lin::dumpLinearizationDataInternal()
808 { 811 {
809 *m->log->getInfo() << m->file->getName() << ": linearization data:\n\n"; 812 *m->log->getInfo() << m->file->getName() << ": linearization data:\n\n";
810 813
@@ -830,7 +833,7 @@ QPDF::dumpLinearizationDataInternal() @@ -830,7 +833,7 @@ QPDF::dumpLinearizationDataInternal()
830 } 833 }
831 834
832 qpdf_offset_t 835 qpdf_offset_t
833 -QPDF::adjusted_offset(qpdf_offset_t offset) 836 +Lin::adjusted_offset(qpdf_offset_t offset)
834 { 837 {
835 // All offsets >= H_offset have to be increased by H_length since all hint table location values 838 // All offsets >= H_offset have to be increased by H_length since all hint table location values
836 // disregard the hint table itself. 839 // disregard the hint table itself.
@@ -841,7 +844,7 @@ QPDF::adjusted_offset(qpdf_offset_t offset) @@ -841,7 +844,7 @@ QPDF::adjusted_offset(qpdf_offset_t offset)
841 } 844 }
842 845
843 void 846 void
844 -QPDF::dumpHPageOffset() 847 +Lin::dumpHPageOffset()
845 { 848 {
846 HPageOffset& t = m->page_offset_hints; 849 HPageOffset& t = m->page_offset_hints;
847 *m->log->getInfo() << "min_nobjects: " << t.min_nobjects << "\n" 850 *m->log->getInfo() << "min_nobjects: " << t.min_nobjects << "\n"
@@ -880,7 +883,7 @@ QPDF::dumpHPageOffset() @@ -880,7 +883,7 @@ QPDF::dumpHPageOffset()
880 } 883 }
881 884
882 void 885 void
883 -QPDF::dumpHSharedObject() 886 +Lin::dumpHSharedObject()
884 { 887 {
885 HSharedObject& t = m->shared_object_hints; 888 HSharedObject& t = m->shared_object_hints;
886 *m->log->getInfo() << "first_shared_obj: " << t.first_shared_obj << "\n" 889 *m->log->getInfo() << "first_shared_obj: " << t.first_shared_obj << "\n"
@@ -908,7 +911,7 @@ QPDF::dumpHSharedObject() @@ -908,7 +911,7 @@ QPDF::dumpHSharedObject()
908 } 911 }
909 912
910 void 913 void
911 -QPDF::dumpHGeneric(HGeneric& t) 914 +Lin::dumpHGeneric(HGeneric& t)
912 { 915 {
913 *m->log->getInfo() << "first_object: " << t.first_object << "\n" 916 *m->log->getInfo() << "first_object: " << t.first_object << "\n"
914 << "first_object_offset: " << adjusted_offset(t.first_object_offset) << "\n" 917 << "first_object_offset: " << adjusted_offset(t.first_object_offset) << "\n"
@@ -918,7 +921,7 @@ QPDF::dumpHGeneric(HGeneric&amp; t) @@ -918,7 +921,7 @@ QPDF::dumpHGeneric(HGeneric&amp; t)
918 921
919 template <typename T> 922 template <typename T>
920 void 923 void
921 -QPDF::calculateLinearizationData(T const& object_stream_data) 924 +Lin::calculateLinearizationData(T const& object_stream_data)
922 { 925 {
923 // This function calculates the ordering of objects, divides them into the appropriate parts, 926 // This function calculates the ordering of objects, divides them into the appropriate parts,
924 // and computes some values for the linearization parameter dictionary and hint tables. The 927 // and computes some values for the linearization parameter dictionary and hint tables. The
@@ -985,7 +988,7 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data) @@ -985,7 +988,7 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data)
985 m->c_shared_object_data = CHSharedObject(); 988 m->c_shared_object_data = CHSharedObject();
986 m->c_outline_data = HGeneric(); 989 m->c_outline_data = HGeneric();
987 990
988 - QPDFObjectHandle root = getRoot(); 991 + QPDFObjectHandle root = qpdf.getRoot();
989 bool outlines_in_first_page = false; 992 bool outlines_in_first_page = false;
990 QPDFObjectHandle pagemode = root.getKey("/PageMode"); 993 QPDFObjectHandle pagemode = root.getKey("/PageMode");
991 QTC::TC("qpdf", "QPDF categorize pagemode present", pagemode.isName() ? 1 : 0); 994 QTC::TC("qpdf", "QPDF categorize pagemode present", pagemode.isName() ? 1 : 0);
@@ -1106,7 +1109,7 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data) @@ -1106,7 +1109,7 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data)
1106 { // local scope 1109 { // local scope
1107 // Map all page objects to the containing object stream. This should be a no-op in a 1110 // Map all page objects to the containing object stream. This should be a no-op in a
1108 // properly linearized file. 1111 // properly linearized file.
1109 - for (auto oh: getAllPages()) { 1112 + for (auto oh: qpdf.getAllPages()) {
1110 pages.emplace_back(getUncompressedObject(oh, object_stream_data)); 1113 pages.emplace_back(getUncompressedObject(oh, object_stream_data));
1111 } 1114 }
1112 } 1115 }
@@ -1125,13 +1128,13 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data) @@ -1125,13 +1128,13 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data)
1125 1128
1126 // Part 4: open document objects. We don't care about the order. 1129 // Part 4: open document objects. We don't care about the order.
1127 1130
1128 - no_ci_stop_if( 1131 + qpdf.no_ci_stop_if(
1129 lc_root.size() != 1, "found other than one root while calculating linearization data" // 1132 lc_root.size() != 1, "found other than one root while calculating linearization data" //
1130 ); 1133 );
1131 1134
1132 - m->part4.emplace_back(getObject(*(lc_root.begin()))); 1135 + m->part4.emplace_back(qpdf.getObject(*(lc_root.begin())));
1133 for (auto const& og: lc_open_document) { 1136 for (auto const& og: lc_open_document) {
1134 - m->part4.emplace_back(getObject(og)); 1137 + m->part4.emplace_back(qpdf.getObject(og));
1135 } 1138 }
1136 1139
1137 // Part 6: first page objects. Note: implementation note 124 states that Acrobat always treats 1140 // Part 6: first page objects. Note: implementation note 124 states that Acrobat always treats
@@ -1139,11 +1142,11 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data) @@ -1139,11 +1142,11 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data)
1139 // any option to set this and also disregards /OpenAction. We will do the same. 1142 // any option to set this and also disregards /OpenAction. We will do the same.
1140 1143
1141 // First, place the actual first page object itself. 1144 // First, place the actual first page object itself.
1142 - no_ci_stop_if( 1145 + qpdf.no_ci_stop_if(
1143 pages.empty(), "no pages found while calculating linearization data" // 1146 pages.empty(), "no pages found while calculating linearization data" //
1144 ); 1147 );
1145 QPDFObjGen first_page_og(pages.at(0).getObjGen()); 1148 QPDFObjGen first_page_og(pages.at(0).getObjGen());
1146 - no_ci_stop_if( 1149 + qpdf.no_ci_stop_if(
1147 !lc_first_page_private.erase(first_page_og), "unable to linearize first page" // 1150 !lc_first_page_private.erase(first_page_og), "unable to linearize first page" //
1148 ); 1151 );
1149 m->c_linp.first_page_object = pages.at(0).getObjectID(); 1152 m->c_linp.first_page_object = pages.at(0).getObjectID();
@@ -1154,11 +1157,11 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data) @@ -1154,11 +1157,11 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data)
1154 // of hint tables. 1157 // of hint tables.
1155 1158
1156 for (auto const& og: lc_first_page_private) { 1159 for (auto const& og: lc_first_page_private) {
1157 - m->part6.emplace_back(getObject(og)); 1160 + m->part6.emplace_back(qpdf.getObject(og));
1158 } 1161 }
1159 1162
1160 for (auto const& og: lc_first_page_shared) { 1163 for (auto const& og: lc_first_page_shared) {
1161 - m->part6.emplace_back(getObject(og)); 1164 + m->part6.emplace_back(qpdf.getObject(og));
1162 } 1165 }
1163 1166
1164 // Place the outline dictionary if it goes in the first page section. 1167 // Place the outline dictionary if it goes in the first page section.
@@ -1179,7 +1182,7 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data) @@ -1179,7 +1182,7 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data)
1179 // Place this page's page object 1182 // Place this page's page object
1180 1183
1181 QPDFObjGen page_og(pages.at(i).getObjGen()); 1184 QPDFObjGen page_og(pages.at(i).getObjGen());
1182 - no_ci_stop_if( 1185 + qpdf.no_ci_stop_if(
1183 !lc_other_page_private.erase(page_og), 1186 !lc_other_page_private.erase(page_og),
1184 "unable to linearize page " + std::to_string(i) // 1187 "unable to linearize page " + std::to_string(i) //
1185 ); 1188 );
@@ -1192,14 +1195,14 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data) @@ -1192,14 +1195,14 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data)
1192 m->c_page_offset_data.entries.at(i).nobjects = 1; 1195 m->c_page_offset_data.entries.at(i).nobjects = 1;
1193 1196
1194 ObjUser ou(ObjUser::ou_page, i); 1197 ObjUser ou(ObjUser::ou_page, i);
1195 - no_ci_stop_if( 1198 + qpdf.no_ci_stop_if(
1196 !m->obj_user_to_objects.contains(ou), 1199 !m->obj_user_to_objects.contains(ou),
1197 "found unreferenced page while calculating linearization data" // 1200 "found unreferenced page while calculating linearization data" //
1198 ); 1201 );
1199 1202
1200 for (auto const& og: m->obj_user_to_objects[ou]) { 1203 for (auto const& og: m->obj_user_to_objects[ou]) {
1201 if (lc_other_page_private.erase(og)) { 1204 if (lc_other_page_private.erase(og)) {
1202 - m->part7.emplace_back(getObject(og)); 1205 + m->part7.emplace_back(qpdf.getObject(og));
1203 ++m->c_page_offset_data.entries.at(i).nobjects; 1206 ++m->c_page_offset_data.entries.at(i).nobjects;
1204 } 1207 }
1205 } 1208 }
@@ -1215,7 +1218,7 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data) @@ -1215,7 +1218,7 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data)
1215 1218
1216 // Order is unimportant. 1219 // Order is unimportant.
1217 for (auto const& og: lc_other_page_shared) { 1220 for (auto const& og: lc_other_page_shared) {
1218 - m->part8.emplace_back(getObject(og)); 1221 + m->part8.emplace_back(qpdf.getObject(og));
1219 } 1222 }
1220 1223
1221 // Part 9: other objects 1224 // Part 9: other objects
@@ -1228,12 +1231,12 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data) @@ -1228,12 +1231,12 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data)
1228 // Place the pages tree. 1231 // Place the pages tree.
1229 std::set<QPDFObjGen> pages_ogs = 1232 std::set<QPDFObjGen> pages_ogs =
1230 m->obj_user_to_objects[ObjUser(ObjUser::ou_root_key, "/Pages")]; 1233 m->obj_user_to_objects[ObjUser(ObjUser::ou_root_key, "/Pages")];
1231 - no_ci_stop_if( 1234 + qpdf.no_ci_stop_if(
1232 pages_ogs.empty(), "found empty pages tree while calculating linearization data" // 1235 pages_ogs.empty(), "found empty pages tree while calculating linearization data" //
1233 ); 1236 );
1234 for (auto const& og: pages_ogs) { 1237 for (auto const& og: pages_ogs) {
1235 if (lc_other.erase(og)) { 1238 if (lc_other.erase(og)) {
1236 - m->part9.emplace_back(getObject(og)); 1239 + m->part9.emplace_back(qpdf.getObject(og));
1237 } 1240 }
1238 } 1241 }
1239 1242
@@ -1255,7 +1258,7 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data) @@ -1255,7 +1258,7 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data)
1255 std::set<QPDFObjGen>& ogs = m->obj_user_to_objects[ObjUser(ObjUser::ou_thumb, i)]; 1258 std::set<QPDFObjGen>& ogs = m->obj_user_to_objects[ObjUser(ObjUser::ou_thumb, i)];
1256 for (auto const& og: ogs) { 1259 for (auto const& og: ogs) {
1257 if (lc_thumbnail_private.erase(og)) { 1260 if (lc_thumbnail_private.erase(og)) {
1258 - m->part9.emplace_back(getObject(og)); 1261 + m->part9.emplace_back(qpdf.getObject(og));
1259 } 1262 }
1260 } 1263 }
1261 } 1264 }
@@ -1267,7 +1270,7 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data) @@ -1267,7 +1270,7 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data)
1267 1270
1268 // Place shared thumbnail objects 1271 // Place shared thumbnail objects
1269 for (auto const& og: lc_thumbnail_shared) { 1272 for (auto const& og: lc_thumbnail_shared) {
1270 - m->part9.emplace_back(getObject(og)); 1273 + m->part9.emplace_back(qpdf.getObject(og));
1271 } 1274 }
1272 1275
1273 // Place outlines unless in first page 1276 // Place outlines unless in first page
@@ -1277,7 +1280,7 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data) @@ -1277,7 +1280,7 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data)
1277 1280
1278 // Place all remaining objects 1281 // Place all remaining objects
1279 for (auto const& og: lc_other) { 1282 for (auto const& og: lc_other) {
1280 - m->part9.emplace_back(getObject(og)); 1283 + m->part9.emplace_back(qpdf.getObject(og));
1281 } 1284 }
1282 1285
1283 // Make sure we got everything exactly once. 1286 // Make sure we got everything exactly once.
@@ -1285,7 +1288,7 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data) @@ -1285,7 +1288,7 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data)
1285 size_t num_placed = 1288 size_t num_placed =
1286 m->part4.size() + m->part6.size() + m->part7.size() + m->part8.size() + m->part9.size(); 1289 m->part4.size() + m->part6.size() + m->part7.size() + m->part8.size() + m->part9.size();
1287 size_t num_wanted = m->object_to_obj_users.size(); 1290 size_t num_wanted = m->object_to_obj_users.size();
1288 - no_ci_stop_if( 1291 + qpdf.no_ci_stop_if(
1289 // This can happen with damaged files, e.g. if the root is part of the the pages tree. 1292 // This can happen with damaged files, e.g. if the root is part of the the pages tree.
1290 num_placed != num_wanted, 1293 num_placed != num_wanted,
1291 "QPDF::calculateLinearizationData: wrong number of objects placed (num_placed = " + 1294 "QPDF::calculateLinearizationData: wrong number of objects placed (num_placed = " +
@@ -1323,7 +1326,7 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data) @@ -1323,7 +1326,7 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data)
1323 shared.emplace_back(obj); 1326 shared.emplace_back(obj);
1324 } 1327 }
1325 } 1328 }
1326 - no_ci_stop_if( 1329 + qpdf.no_ci_stop_if(
1327 std::cmp_not_equal( 1330 std::cmp_not_equal(
1328 m->c_shared_object_data.nshared_total, m->c_shared_object_data.entries.size()), 1331 m->c_shared_object_data.nshared_total, m->c_shared_object_data.entries.size()),
1329 "shared object hint table has wrong number of entries" // 1332 "shared object hint table has wrong number of entries" //
@@ -1334,7 +1337,7 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data) @@ -1334,7 +1337,7 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data)
1334 for (size_t i = 1; i < npages; ++i) { 1337 for (size_t i = 1; i < npages; ++i) {
1335 CHPageOffsetEntry& pe = m->c_page_offset_data.entries.at(i); 1338 CHPageOffsetEntry& pe = m->c_page_offset_data.entries.at(i);
1336 ObjUser ou(ObjUser::ou_page, i); 1339 ObjUser ou(ObjUser::ou_page, i);
1337 - no_ci_stop_if( 1340 + qpdf.no_ci_stop_if(
1338 !m->obj_user_to_objects.contains(ou), 1341 !m->obj_user_to_objects.contains(ou),
1339 "found unreferenced page while calculating linearization data" // 1342 "found unreferenced page while calculating linearization data" //
1340 ); 1343 );
@@ -1351,12 +1354,12 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data) @@ -1351,12 +1354,12 @@ QPDF::calculateLinearizationData(T const&amp; object_stream_data)
1351 1354
1352 template <typename T> 1355 template <typename T>
1353 void 1356 void
1354 -QPDF::pushOutlinesToPart( 1357 +Lin::pushOutlinesToPart(
1355 std::vector<QPDFObjectHandle>& part, 1358 std::vector<QPDFObjectHandle>& part,
1356 std::set<QPDFObjGen>& lc_outlines, 1359 std::set<QPDFObjGen>& lc_outlines,
1357 T const& object_stream_data) 1360 T const& object_stream_data)
1358 { 1361 {
1359 - QPDFObjectHandle root = getRoot(); 1362 + QPDFObjectHandle root = qpdf.getRoot();
1360 QPDFObjectHandle outlines = root.getKey("/Outlines"); 1363 QPDFObjectHandle outlines = root.getKey("/Outlines");
1361 if (outlines.null()) { 1364 if (outlines.null()) {
1362 return; 1365 return;
@@ -1380,13 +1383,13 @@ QPDF::pushOutlinesToPart( @@ -1380,13 +1383,13 @@ QPDF::pushOutlinesToPart(
1380 if (!m->c_outline_data.first_object) { 1383 if (!m->c_outline_data.first_object) {
1381 m->c_outline_data.first_object = og.getObj(); 1384 m->c_outline_data.first_object = og.getObj();
1382 } 1385 }
1383 - part.emplace_back(getObject(og)); 1386 + part.emplace_back(qpdf.getObject(og));
1384 ++m->c_outline_data.nobjects; 1387 ++m->c_outline_data.nobjects;
1385 } 1388 }
1386 } 1389 }
1387 1390
1388 void 1391 void
1389 -QPDF::getLinearizedParts( 1392 +Lin::getLinearizedParts(
1390 QPDFWriter::ObjTable const& obj, 1393 QPDFWriter::ObjTable const& obj,
1391 std::vector<QPDFObjectHandle>& part4, 1394 std::vector<QPDFObjectHandle>& part4,
1392 std::vector<QPDFObjectHandle>& part6, 1395 std::vector<QPDFObjectHandle>& part6,
@@ -1409,7 +1412,7 @@ nbits(int val) @@ -1409,7 +1412,7 @@ nbits(int val)
1409 } 1412 }
1410 1413
1411 int 1414 int
1412 -QPDF::outputLengthNextN( 1415 +Lin::outputLengthNextN(
1413 int in_object, int n, QPDFWriter::NewObjTable const& new_obj, QPDFWriter::ObjTable const& obj) 1416 int in_object, int n, QPDFWriter::NewObjTable const& new_obj, QPDFWriter::ObjTable const& obj)
1414 { 1417 {
1415 // Figure out the length of a series of n consecutive objects in the output file starting with 1418 // Figure out the length of a series of n consecutive objects in the output file starting with
@@ -1417,12 +1420,12 @@ QPDF::outputLengthNextN( @@ -1417,12 +1420,12 @@ QPDF::outputLengthNextN(
1417 1420
1418 int first = obj[in_object].renumber; 1421 int first = obj[in_object].renumber;
1419 int last = first + n; 1422 int last = first + n;
1420 - no_ci_stop_if( 1423 + qpdf.no_ci_stop_if(
1421 first <= 0, "found object that is not renumbered while writing linearization data"); 1424 first <= 0, "found object that is not renumbered while writing linearization data");
1422 qpdf_offset_t length = 0; 1425 qpdf_offset_t length = 0;
1423 for (int i = first; i < last; ++i) { 1426 for (int i = first; i < last; ++i) {
1424 auto l = new_obj[i].length; 1427 auto l = new_obj[i].length;
1425 - no_ci_stop_if( 1428 + qpdf.no_ci_stop_if(
1426 l == 0, "found item with unknown length while writing linearization data" // 1429 l == 0, "found item with unknown length while writing linearization data" //
1427 ); 1430 );
1428 length += l; 1431 length += l;
@@ -1431,13 +1434,13 @@ QPDF::outputLengthNextN( @@ -1431,13 +1434,13 @@ QPDF::outputLengthNextN(
1431 } 1434 }
1432 1435
1433 void 1436 void
1434 -QPDF::calculateHPageOffset(QPDFWriter::NewObjTable const& new_obj, QPDFWriter::ObjTable const& obj) 1437 +Lin::calculateHPageOffset(QPDFWriter::NewObjTable const& new_obj, QPDFWriter::ObjTable const& obj)
1435 { 1438 {
1436 // Page Offset Hint Table 1439 // Page Offset Hint Table
1437 1440
1438 // We are purposely leaving some values set to their initial zero values. 1441 // We are purposely leaving some values set to their initial zero values.
1439 1442
1440 - std::vector<QPDFObjectHandle> const& pages = getAllPages(); 1443 + std::vector<QPDFObjectHandle> const& pages = qpdf.getAllPages();
1441 size_t npages = pages.size(); 1444 size_t npages = pages.size();
1442 CHPageOffset& cph = m->c_page_offset_data; 1445 CHPageOffset& cph = m->c_page_offset_data;
1443 std::vector<CHPageOffsetEntry>& cphe = cph.entries; 1446 std::vector<CHPageOffsetEntry>& cphe = cph.entries;
@@ -1499,7 +1502,7 @@ QPDF::calculateHPageOffset(QPDFWriter::NewObjTable const&amp; new_obj, QPDFWriter::O @@ -1499,7 +1502,7 @@ QPDF::calculateHPageOffset(QPDFWriter::NewObjTable const&amp; new_obj, QPDFWriter::O
1499 for (auto& phe_i: phe) { 1502 for (auto& phe_i: phe) {
1500 // Adjust delta entries 1503 // Adjust delta entries
1501 if (phe_i.delta_nobjects < min_nobjects || phe_i.delta_page_length < min_length) { 1504 if (phe_i.delta_nobjects < min_nobjects || phe_i.delta_page_length < min_length) {
1502 - stopOnError( 1505 + qpdf.stopOnError(
1503 "found too small delta nobjects or delta page length while writing " 1506 "found too small delta nobjects or delta page length while writing "
1504 "linearization data"); 1507 "linearization data");
1505 } 1508 }
@@ -1515,8 +1518,7 @@ QPDF::calculateHPageOffset(QPDFWriter::NewObjTable const&amp; new_obj, QPDFWriter::O @@ -1515,8 +1518,7 @@ QPDF::calculateHPageOffset(QPDFWriter::NewObjTable const&amp; new_obj, QPDFWriter::O
1515 } 1518 }
1516 1519
1517 void 1520 void
1518 -QPDF::calculateHSharedObject(  
1519 - QPDFWriter::NewObjTable const& new_obj, QPDFWriter::ObjTable const& obj) 1521 +Lin::calculateHSharedObject(QPDFWriter::NewObjTable const& new_obj, QPDFWriter::ObjTable const& obj)
1520 { 1522 {
1521 CHSharedObject& cso = m->c_shared_object_data; 1523 CHSharedObject& cso = m->c_shared_object_data;
1522 std::vector<CHSharedObjectEntry>& csoe = cso.entries; 1524 std::vector<CHSharedObjectEntry>& csoe = cso.entries;
@@ -1535,7 +1537,7 @@ QPDF::calculateHSharedObject( @@ -1535,7 +1537,7 @@ QPDF::calculateHSharedObject(
1535 soe.emplace_back(); 1537 soe.emplace_back();
1536 soe.at(i).delta_group_length = length; 1538 soe.at(i).delta_group_length = length;
1537 } 1539 }
1538 - no_ci_stop_if( 1540 + qpdf.no_ci_stop_if(
1539 soe.size() != toS(cso.nshared_total), "soe has wrong size after initialization" // 1541 soe.size() != toS(cso.nshared_total), "soe has wrong size after initialization" //
1540 ); 1542 );
1541 1543
@@ -1551,7 +1553,7 @@ QPDF::calculateHSharedObject( @@ -1551,7 +1553,7 @@ QPDF::calculateHSharedObject(
1551 1553
1552 for (size_t i = 0; i < toS(cso.nshared_total); ++i) { 1554 for (size_t i = 0; i < toS(cso.nshared_total); ++i) {
1553 // Adjust deltas 1555 // Adjust deltas
1554 - no_ci_stop_if( 1556 + qpdf.no_ci_stop_if(
1555 soe.at(i).delta_group_length < min_length, 1557 soe.at(i).delta_group_length < min_length,
1556 "found too small group length while writing linearization data" // 1558 "found too small group length while writing linearization data" //
1557 ); 1559 );
@@ -1561,7 +1563,7 @@ QPDF::calculateHSharedObject( @@ -1561,7 +1563,7 @@ QPDF::calculateHSharedObject(
1561 } 1563 }
1562 1564
1563 void 1565 void
1564 -QPDF::calculateHOutline(QPDFWriter::NewObjTable const& new_obj, QPDFWriter::ObjTable const& obj) 1566 +Lin::calculateHOutline(QPDFWriter::NewObjTable const& new_obj, QPDFWriter::ObjTable const& obj)
1565 { 1567 {
1566 HGeneric& cho = m->c_outline_data; 1568 HGeneric& cho = m->c_outline_data;
1567 1569
@@ -1612,7 +1614,7 @@ write_vector_vector( @@ -1612,7 +1614,7 @@ write_vector_vector(
1612 } 1614 }
1613 1615
1614 void 1616 void
1615 -QPDF::writeHPageOffset(BitWriter& w) 1617 +Lin::writeHPageOffset(BitWriter& w)
1616 { 1618 {
1617 HPageOffset& t = m->page_offset_hints; 1619 HPageOffset& t = m->page_offset_hints;
1618 1620
@@ -1630,7 +1632,7 @@ QPDF::writeHPageOffset(BitWriter&amp; w) @@ -1630,7 +1632,7 @@ QPDF::writeHPageOffset(BitWriter&amp; w)
1630 w.writeBitsInt(t.nbits_shared_numerator, 16); // 12 1632 w.writeBitsInt(t.nbits_shared_numerator, 16); // 12
1631 w.writeBitsInt(t.shared_denominator, 16); // 13 1633 w.writeBitsInt(t.shared_denominator, 16); // 13
1632 1634
1633 - int nitems = toI(getAllPages().size()); 1635 + int nitems = toI(qpdf.getAllPages().size());
1634 std::vector<HPageOffsetEntry>& entries = t.entries; 1636 std::vector<HPageOffsetEntry>& entries = t.entries;
1635 1637
1636 write_vector_int(w, nitems, entries, t.nbits_delta_nobjects, &HPageOffsetEntry::delta_nobjects); 1638 write_vector_int(w, nitems, entries, t.nbits_delta_nobjects, &HPageOffsetEntry::delta_nobjects);
@@ -1659,7 +1661,7 @@ QPDF::writeHPageOffset(BitWriter&amp; w) @@ -1659,7 +1661,7 @@ QPDF::writeHPageOffset(BitWriter&amp; w)
1659 } 1661 }
1660 1662
1661 void 1663 void
1662 -QPDF::writeHSharedObject(BitWriter& w) 1664 +Lin::writeHSharedObject(BitWriter& w)
1663 { 1665 {
1664 HSharedObject& t = m->shared_object_hints; 1666 HSharedObject& t = m->shared_object_hints;
1665 1667
@@ -1685,14 +1687,14 @@ QPDF::writeHSharedObject(BitWriter&amp; w) @@ -1685,14 +1687,14 @@ QPDF::writeHSharedObject(BitWriter&amp; w)
1685 for (size_t i = 0; i < toS(nitems); ++i) { 1687 for (size_t i = 0; i < toS(nitems); ++i) {
1686 // If signature were present, we'd have to write a 128-bit hash. 1688 // If signature were present, we'd have to write a 128-bit hash.
1687 if (entries.at(i).signature_present != 0) { 1689 if (entries.at(i).signature_present != 0) {
1688 - stopOnError("found unexpected signature present while writing linearization data"); 1690 + qpdf.stopOnError("found unexpected signature present while writing linearization data");
1689 } 1691 }
1690 } 1692 }
1691 write_vector_int(w, nitems, entries, t.nbits_nobjects, &HSharedObjectEntry::nobjects_minus_one); 1693 write_vector_int(w, nitems, entries, t.nbits_nobjects, &HSharedObjectEntry::nobjects_minus_one);
1692 } 1694 }
1693 1695
1694 void 1696 void
1695 -QPDF::writeHGeneric(BitWriter& w, HGeneric& t) 1697 +Lin::writeHGeneric(BitWriter& w, HGeneric& t)
1696 { 1698 {
1697 w.writeBitsInt(t.first_object, 32); // 1 1699 w.writeBitsInt(t.first_object, 32); // 1
1698 w.writeBits(toULL(t.first_object_offset), 32); // 2 1700 w.writeBits(toULL(t.first_object_offset), 32); // 2
@@ -1701,7 +1703,7 @@ QPDF::writeHGeneric(BitWriter&amp; w, HGeneric&amp; t) @@ -1701,7 +1703,7 @@ QPDF::writeHGeneric(BitWriter&amp; w, HGeneric&amp; t)
1701 } 1703 }
1702 1704
1703 void 1705 void
1704 -QPDF::generateHintStream( 1706 +Lin::generateHintStream(
1705 QPDFWriter::NewObjTable const& new_obj, 1707 QPDFWriter::NewObjTable const& new_obj,
1706 QPDFWriter::ObjTable const& obj, 1708 QPDFWriter::ObjTable const& obj,
1707 std::string& hint_buffer, 1709 std::string& hint_buffer,
libqpdf/QPDF_optimization.cc
@@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
7 #include <qpdf/QPDFWriter_private.hh> 7 #include <qpdf/QPDFWriter_private.hh>
8 #include <qpdf/QTC.hh> 8 #include <qpdf/QTC.hh>
9 9
  10 +using Lin = QPDF::Doc::Linearization;
10 using Pages = QPDF::Doc::Pages; 11 using Pages = QPDF::Doc::Pages;
11 12
12 QPDF::ObjUser::ObjUser(user_e type) : 13 QPDF::ObjUser::ObjUser(user_e type) :
@@ -60,11 +61,11 @@ QPDF::optimize( @@ -60,11 +61,11 @@ QPDF::optimize(
60 bool allow_changes, 61 bool allow_changes,
61 std::function<int(QPDFObjectHandle&)> skip_stream_parameters) 62 std::function<int(QPDFObjectHandle&)> skip_stream_parameters)
62 { 63 {
63 - optimize_internal(object_stream_data, allow_changes, skip_stream_parameters); 64 + m->lin.optimize_internal(object_stream_data, allow_changes, skip_stream_parameters);
64 } 65 }
65 66
66 void 67 void
67 -QPDF::optimize( 68 +Lin::optimize(
68 QPDFWriter::ObjTable const& obj, std::function<int(QPDFObjectHandle&)> skip_stream_parameters) 69 QPDFWriter::ObjTable const& obj, std::function<int(QPDFObjectHandle&)> skip_stream_parameters)
69 { 70 {
70 optimize_internal(obj, true, skip_stream_parameters); 71 optimize_internal(obj, true, skip_stream_parameters);
@@ -72,7 +73,7 @@ QPDF::optimize( @@ -72,7 +73,7 @@ QPDF::optimize(
72 73
73 template <typename T> 74 template <typename T>
74 void 75 void
75 -QPDF::optimize_internal( 76 +Lin::optimize_internal(
76 T const& object_stream_data, 77 T const& object_stream_data,
77 bool allow_changes, 78 bool allow_changes,
78 std::function<int(QPDFObjectHandle&)> skip_stream_parameters) 79 std::function<int(QPDFObjectHandle&)> skip_stream_parameters)
@@ -84,11 +85,11 @@ QPDF::optimize_internal( @@ -84,11 +85,11 @@ QPDF::optimize_internal(
84 85
85 // The PDF specification indicates that /Outlines is supposed to be an indirect reference. Force 86 // The PDF specification indicates that /Outlines is supposed to be an indirect reference. Force
86 // it to be so if it exists and is direct. (This has been seen in the wild.) 87 // it to be so if it exists and is direct. (This has been seen in the wild.)
87 - QPDFObjectHandle root = getRoot(); 88 + QPDFObjectHandle root = qpdf.getRoot();
88 if (root.getKey("/Outlines").isDictionary()) { 89 if (root.getKey("/Outlines").isDictionary()) {
89 QPDFObjectHandle outlines = root.getKey("/Outlines"); 90 QPDFObjectHandle outlines = root.getKey("/Outlines");
90 if (!outlines.isIndirect()) { 91 if (!outlines.isIndirect()) {
91 - root.replaceKey("/Outlines", makeIndirectObject(outlines)); 92 + root.replaceKey("/Outlines", qpdf.makeIndirectObject(outlines));
92 } 93 }
93 } 94 }
94 95
@@ -265,7 +266,7 @@ Pages ::pushInheritedAttributesToPageInternal( @@ -265,7 +266,7 @@ Pages ::pushInheritedAttributesToPageInternal(
265 } 266 }
266 267
267 void 268 void
268 -QPDF::updateObjectMaps( 269 +Lin::updateObjectMaps(
269 ObjUser const& first_ou, 270 ObjUser const& first_ou,
270 QPDFObjectHandle first_oh, 271 QPDFObjectHandle first_oh,
271 std::function<int(QPDFObjectHandle&)> skip_stream_parameters) 272 std::function<int(QPDFObjectHandle&)> skip_stream_parameters)
@@ -338,7 +339,7 @@ QPDF::updateObjectMaps( @@ -338,7 +339,7 @@ QPDF::updateObjectMaps(
338 } 339 }
339 340
340 void 341 void
341 -QPDF::filterCompressedObjects(std::map<int, int> const& object_stream_data) 342 +Lin::filterCompressedObjects(std::map<int, int> const& object_stream_data)
342 { 343 {
343 if (object_stream_data.empty()) { 344 if (object_stream_data.empty()) {
344 return; 345 return;
@@ -382,7 +383,7 @@ QPDF::filterCompressedObjects(std::map&lt;int, int&gt; const&amp; object_stream_data) @@ -382,7 +383,7 @@ QPDF::filterCompressedObjects(std::map&lt;int, int&gt; const&amp; object_stream_data)
382 } 383 }
383 384
384 void 385 void
385 -QPDF::filterCompressedObjects(QPDFWriter::ObjTable const& obj) 386 +Lin::filterCompressedObjects(QPDFWriter::ObjTable const& obj)
386 { 387 {
387 if (obj.getStreamsEmpty()) { 388 if (obj.getStreamsEmpty()) {
388 return; 389 return;
libqpdf/qpdf/QPDF_private.hh
@@ -18,6 +18,9 @@ namespace qpdf::is @@ -18,6 +18,9 @@ namespace qpdf::is
18 class OffsetBuffer; 18 class OffsetBuffer;
19 } // namespace qpdf::is 19 } // namespace qpdf::is
20 20
  21 +class BitStream;
  22 +class BitWriter;
  23 +
21 class QPDF::ObjCache 24 class QPDF::ObjCache
22 { 25 {
23 public: 26 public:
@@ -458,7 +461,113 @@ class QPDF::Doc @@ -458,7 +461,113 @@ class QPDF::Doc
458 std::string Perms; 461 std::string Perms;
459 std::string id1; 462 std::string id1;
460 bool encrypt_metadata; 463 bool encrypt_metadata;
461 - }; // class Encryption 464 + }; // class QPDF::Doc::Encryption
  465 +
  466 + class Linearization
  467 + {
  468 + public:
  469 + Linearization() = delete;
  470 + Linearization(Linearization const&) = delete;
  471 + Linearization(Linearization&&) = delete;
  472 + Linearization& operator=(Linearization const&) = delete;
  473 + Linearization& operator=(Linearization&&) = delete;
  474 + ~Linearization() = default;
  475 +
  476 + Linearization(QPDF& qpdf, QPDF::Members* m) :
  477 + qpdf(qpdf),
  478 + m(m)
  479 + {
  480 + }
  481 +
  482 + // For QPDFWriter:
  483 +
  484 + template <typename T>
  485 + void optimize_internal(
  486 + T const& object_stream_data,
  487 + bool allow_changes = true,
  488 + std::function<int(QPDFObjectHandle&)> skip_stream_parameters = nullptr);
  489 + void optimize(
  490 + QPDFWriter::ObjTable const& obj,
  491 + std::function<int(QPDFObjectHandle&)> skip_stream_parameters);
  492 +
  493 + // Get lists of all objects in order according to the part of a linearized file that they
  494 + // belong to.
  495 + void getLinearizedParts(
  496 + QPDFWriter::ObjTable const& obj,
  497 + std::vector<QPDFObjectHandle>& part4,
  498 + std::vector<QPDFObjectHandle>& part6,
  499 + std::vector<QPDFObjectHandle>& part7,
  500 + std::vector<QPDFObjectHandle>& part8,
  501 + std::vector<QPDFObjectHandle>& part9);
  502 +
  503 + void generateHintStream(
  504 + QPDFWriter::NewObjTable const& new_obj,
  505 + QPDFWriter::ObjTable const& obj,
  506 + std::string& hint_stream,
  507 + int& S,
  508 + int& O,
  509 + bool compressed);
  510 +
  511 + // methods to support linearization checking -- implemented in QPDF_linearization.cc
  512 +
  513 + void readLinearizationData();
  514 + void checkLinearizationInternal();
  515 + void dumpLinearizationDataInternal();
  516 + void linearizationWarning(std::string_view);
  517 + qpdf::Dictionary readHintStream(Pipeline&, qpdf_offset_t offset, size_t length);
  518 + void readHPageOffset(BitStream);
  519 + void readHSharedObject(BitStream);
  520 + void readHGeneric(BitStream, HGeneric&);
  521 + qpdf_offset_t maxEnd(ObjUser const& ou);
  522 + qpdf_offset_t getLinearizationOffset(QPDFObjGen);
  523 + QPDFObjectHandle
  524 + getUncompressedObject(QPDFObjectHandle&, std::map<int, int> const& object_stream_data);
  525 + QPDFObjectHandle getUncompressedObject(QPDFObjectHandle&, QPDFWriter::ObjTable const& obj);
  526 + int lengthNextN(int first_object, int n);
  527 + void checkHPageOffset(
  528 + std::vector<QPDFObjectHandle> const& pages, std::map<int, int>& idx_to_obj);
  529 + void checkHSharedObject(
  530 + std::vector<QPDFObjectHandle> const& pages, std::map<int, int>& idx_to_obj);
  531 + void checkHOutlines();
  532 + void dumpHPageOffset();
  533 + void dumpHSharedObject();
  534 + void dumpHGeneric(HGeneric&);
  535 + qpdf_offset_t adjusted_offset(qpdf_offset_t offset);
  536 + template <typename T>
  537 + void calculateLinearizationData(T const& object_stream_data);
  538 + template <typename T>
  539 + void pushOutlinesToPart(
  540 + std::vector<QPDFObjectHandle>& part,
  541 + std::set<QPDFObjGen>& lc_outlines,
  542 + T const& object_stream_data);
  543 + int outputLengthNextN(
  544 + int in_object,
  545 + int n,
  546 + QPDFWriter::NewObjTable const& new_obj,
  547 + QPDFWriter::ObjTable const& obj);
  548 + void calculateHPageOffset(
  549 + QPDFWriter::NewObjTable const& new_obj, QPDFWriter::ObjTable const& obj);
  550 + void calculateHSharedObject(
  551 + QPDFWriter::NewObjTable const& new_obj, QPDFWriter::ObjTable const& obj);
  552 + void
  553 + calculateHOutline(QPDFWriter::NewObjTable const& new_obj, QPDFWriter::ObjTable const& obj);
  554 + void writeHPageOffset(BitWriter&);
  555 + void writeHSharedObject(BitWriter&);
  556 + void writeHGeneric(BitWriter&, HGeneric&);
  557 +
  558 + // Methods to support optimization
  559 +
  560 + void updateObjectMaps(
  561 + ObjUser const& ou,
  562 + QPDFObjectHandle oh,
  563 + std::function<int(QPDFObjectHandle&)> skip_stream_parameters);
  564 + void filterCompressedObjects(std::map<int, int> const& object_stream_data);
  565 + void filterCompressedObjects(QPDFWriter::ObjTable const& object_stream_data);
  566 +
  567 + private:
  568 + QPDF& qpdf;
  569 + QPDF::Members* m;
  570 + };
462 571
463 class Objects 572 class Objects
464 { 573 {
@@ -613,11 +722,18 @@ class QPDF::Doc @@ -613,11 +722,18 @@ class QPDF::Doc
613 Doc(QPDF& qpdf, QPDF::Members& m) : 722 Doc(QPDF& qpdf, QPDF::Members& m) :
614 qpdf(qpdf), 723 qpdf(qpdf),
615 m(m), 724 m(m),
  725 + lin_(qpdf, &m),
616 objects_(qpdf, &m), 726 objects_(qpdf, &m),
617 pages_(qpdf, &m) 727 pages_(qpdf, &m)
618 { 728 {
619 } 729 }
620 730
  731 + Linearization&
  732 + linearization()
  733 + {
  734 + return lin_;
  735 + };
  736 +
621 Objects& 737 Objects&
622 objects() 738 objects()
623 { 739 {
@@ -681,6 +797,7 @@ class QPDF::Doc @@ -681,6 +797,7 @@ class QPDF::Doc
681 QPDF& qpdf; 797 QPDF& qpdf;
682 QPDF::Members& m; 798 QPDF::Members& m;
683 799
  800 + Linearization lin_;
684 Objects objects_; 801 Objects objects_;
685 Pages pages_; 802 Pages pages_;
686 803
@@ -704,6 +821,7 @@ class QPDF::Members @@ -704,6 +821,7 @@ class QPDF::Members
704 821
705 private: 822 private:
706 Doc doc; 823 Doc doc;
  824 + Doc::Linearization& lin;
707 Doc::Objects& objects; 825 Doc::Objects& objects;
708 Doc::Pages& pages; 826 Doc::Pages& pages;
709 std::shared_ptr<QPDFLogger> log; 827 std::shared_ptr<QPDFLogger> log;