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