Commit 6324d43835f27ac3f8d53a9692582ef9a3e28836

Authored by m-holger
Committed by GitHub
2 parents afb09d2e a8e30477

Merge pull request #1555 from m-holger/qpdf_hh

Remove implementation detail from QPDF header file
include/qpdf/QPDF.hh
@@ -46,27 +46,7 @@ @@ -46,27 +46,7 @@
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 -  
53 - namespace is  
54 - {  
55 - class OffsetBuffer;  
56 - }  
57 -} // namespace qpdf  
58 -  
59 -class QPDF_Stream;  
60 -class BitStream;  
61 -class BitWriter;  
62 -class BufferInputSource;  
63 class QPDFLogger; 49 class QPDFLogger;
64 -class QPDFParser;  
65 -class QPDFAcroFormDocumentHelper;  
66 -class QPDFEmbeddedFileDocumentHelper;  
67 -class QPDFOutlineDocumentHelper;  
68 -class QPDFPageDocumentHelper;  
69 -class QPDFPageLabelDocumentHelper;  
70 50
71 class QPDF 51 class QPDF
72 { 52 {
@@ -477,7 +457,7 @@ class QPDF @@ -477,7 +457,7 @@ class QPDF
477 V(V), 457 V(V),
478 R(R), 458 R(R),
479 Length_bytes(Length_bytes), 459 Length_bytes(Length_bytes),
480 - P(static_cast<unsigned long long>(P)), 460 + P(P),
481 O(O), 461 O(O),
482 U(U), 462 U(U),
483 OE(OE), 463 OE(OE),
@@ -487,20 +467,11 @@ class QPDF @@ -487,20 +467,11 @@ class QPDF
487 encrypt_metadata(encrypt_metadata) 467 encrypt_metadata(encrypt_metadata)
488 { 468 {
489 } 469 }
490 - EncryptionData(int V, int R, int Length_bytes, bool encrypt_metadata) :  
491 - V(V),  
492 - R(R),  
493 - Length_bytes(Length_bytes),  
494 - encrypt_metadata(encrypt_metadata)  
495 - {  
496 - }  
497 470
498 int getV() const; 471 int getV() const;
499 int getR() const; 472 int getR() const;
500 int getLengthBytes() const; 473 int getLengthBytes() const;
501 int getP() const; 474 int getP() const;
502 - // Bits in P are numbered from 1 as in the PDF spec.  
503 - bool getP(size_t bit) const;  
504 std::string const& getO() const; 475 std::string const& getO() const;
505 std::string const& getU() const; 476 std::string const& getU() const;
506 std::string const& getOE() const; 477 std::string const& getOE() const;
@@ -508,12 +479,9 @@ class QPDF @@ -508,12 +479,9 @@ class QPDF
508 std::string const& getPerms() const; 479 std::string const& getPerms() const;
509 std::string const& getId1() const; 480 std::string const& getId1() const;
510 bool getEncryptMetadata() const; 481 bool getEncryptMetadata() const;
511 - // Bits in P are numbered from 1 as in the PDF spec.  
512 - void setP(size_t bit, bool val);  
513 - void setP(unsigned long val); 482 +
514 void setO(std::string const&); 483 void setO(std::string const&);
515 void setU(std::string const&); 484 void setU(std::string const&);
516 - void setId1(std::string const& val);  
517 void setV5EncryptionParameters( 485 void setV5EncryptionParameters(
518 std::string const& O, 486 std::string const& O,
519 std::string const& OE, 487 std::string const& OE,
@@ -521,51 +489,14 @@ class QPDF @@ -521,51 +489,14 @@ class QPDF
521 std::string const& UE, 489 std::string const& UE,
522 std::string const& Perms); 490 std::string const& Perms);
523 491
524 - std::string compute_encryption_key(std::string const& password) const;  
525 -  
526 - bool  
527 - check_owner_password(std::string& user_password, std::string const& owner_password) const;  
528 -  
529 - bool check_user_password(std::string const& user_password) const;  
530 -  
531 - std::string  
532 - recover_encryption_key_with_password(std::string const& password, bool& perms_valid) const;  
533 -  
534 - void compute_encryption_O_U(char const* user_password, char const* owner_password);  
535 -  
536 - std::string  
537 - compute_encryption_parameters_V5(char const* user_password, char const* owner_password);  
538 -  
539 - std::string compute_parameters(char const* user_password, char const* owner_password);  
540 -  
541 private: 492 private:
542 - static constexpr unsigned int OU_key_bytes_V4 = 16; // ( == sizeof(MD5::Digest)  
543 -  
544 EncryptionData(EncryptionData const&) = delete; 493 EncryptionData(EncryptionData const&) = delete;
545 EncryptionData& operator=(EncryptionData const&) = delete; 494 EncryptionData& operator=(EncryptionData const&) = delete;
546 495
547 - std::string hash_V5(  
548 - std::string const& password, std::string const& salt, std::string const& udata) const;  
549 - std::string  
550 - compute_O_value(std::string const& user_password, std::string const& owner_password) const;  
551 - std::string compute_U_value(std::string const& user_password) const;  
552 - std::string compute_encryption_key_from_password(std::string const& password) const;  
553 - std::string recover_encryption_key_with_password(std::string const& password) const;  
554 - bool check_owner_password_V4(  
555 - std::string& user_password, std::string const& owner_password) const;  
556 - bool check_owner_password_V5(std::string const& owner_passworda) const;  
557 - std::string compute_Perms_value_V5_clear() const;  
558 - std::string compute_O_rc4_key(  
559 - std::string const& user_password, std::string const& owner_password) const;  
560 - std::string compute_U_value_R2(std::string const& user_password) const;  
561 - std::string compute_U_value_R3(std::string const& user_password) const;  
562 - bool check_user_password_V4(std::string const& user_password) const;  
563 - bool check_user_password_V5(std::string const& user_password) const;  
564 -  
565 int V; 496 int V;
566 int R; 497 int R;
567 int Length_bytes; 498 int Length_bytes;
568 - std::bitset<32> P{0xfffffffc}; // Specification always requires bits 1 and 2 to be cleared. 499 + int P;
569 std::string O; 500 std::string O;
570 std::string U; 501 std::string U;
571 std::string OE; 502 std::string OE;
@@ -574,7 +505,6 @@ class QPDF @@ -574,7 +505,6 @@ class QPDF
574 std::string id1; 505 std::string id1;
575 bool encrypt_metadata; 506 bool encrypt_metadata;
576 }; 507 };
577 -  
578 QPDF_DLL 508 QPDF_DLL
579 bool isEncrypted() const; 509 bool isEncrypted() const;
580 510
@@ -794,19 +724,9 @@ class QPDF @@ -794,19 +724,9 @@ class QPDF
794 724
795 // End of the public API. The following classes and methods are for qpdf internal use only. 725 // End of the public API. The following classes and methods are for qpdf internal use only.
796 726
797 - class Writer;  
798 - class Resolver;  
799 - class StreamCopier;  
800 - class ParseGuard;  
801 - class Pipe;  
802 - class JobSetter; 727 + class Doc;
803 728
804 - inline bool reconstructed_xref() const;  
805 - inline QPDFAcroFormDocumentHelper& acroform();  
806 - inline QPDFEmbeddedFileDocumentHelper& embedded_files();  
807 - inline QPDFOutlineDocumentHelper& outlines();  
808 - inline QPDFPageDocumentHelper& pages();  
809 - inline QPDFPageLabelDocumentHelper& page_labels(); 729 + inline Doc& doc();
810 730
811 // For testing only -- do not add to DLL 731 // For testing only -- do not add to DLL
812 static bool test_json_validators(); 732 static bool test_json_validators();
@@ -830,66 +750,10 @@ class QPDF @@ -830,66 +750,10 @@ class QPDF
830 class ResolveRecorder; 750 class ResolveRecorder;
831 class JSONReactor; 751 class JSONReactor;
832 752
833 - void parse(char const* password);  
834 - void inParse(bool);  
835 - void setTrailer(QPDFObjectHandle obj);  
836 - void read_xref(qpdf_offset_t offset, bool in_stream_recovery = false);  
837 - bool resolveXRefTable();  
838 - void reconstruct_xref(QPDFExc& e, bool found_startxref = true);  
839 - bool parse_xrefFirst(std::string const& line, int& obj, int& num, int& bytes);  
840 - bool read_xrefEntry(qpdf_offset_t& f1, int& f2, char& type);  
841 - bool read_bad_xrefEntry(qpdf_offset_t& f1, int& f2, char& type);  
842 - qpdf_offset_t read_xrefTable(qpdf_offset_t offset);  
843 - qpdf_offset_t read_xrefStream(qpdf_offset_t offset, bool in_stream_recovery = false);  
844 - qpdf_offset_t processXRefStream(  
845 - qpdf_offset_t offset, QPDFObjectHandle& xref_stream, bool in_stream_recovery = false);  
846 - std::pair<int, std::array<int, 3>>  
847 - processXRefW(QPDFObjectHandle& dict, std::function<QPDFExc(std::string_view)> damaged);  
848 - int processXRefSize(  
849 - QPDFObjectHandle& dict, int entry_size, std::function<QPDFExc(std::string_view)> damaged);  
850 - std::pair<int, std::vector<std::pair<int, int>>> processXRefIndex(  
851 - QPDFObjectHandle& dict,  
852 - int max_num_entries,  
853 - std::function<QPDFExc(std::string_view)> damaged);  
854 - void insertXrefEntry(int obj, int f0, qpdf_offset_t f1, int f2);  
855 - void insertFreeXrefEntry(QPDFObjGen);  
856 - void setLastObjectDescription(std::string const& description, QPDFObjGen og);  
857 - QPDFObjectHandle readTrailer();  
858 - QPDFObjectHandle readObject(std::string const& description, QPDFObjGen og);  
859 - void readStream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset);  
860 - void validateStreamLineEnd(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset);  
861 - QPDFObjectHandle readObjectInStream(qpdf::is::OffsetBuffer& input, int stream_id, int obj_id);  
862 - size_t recoverStreamLength(  
863 - std::shared_ptr<InputSource> input, QPDFObjGen og, qpdf_offset_t stream_offset);  
864 - QPDFTokenizer::Token readToken(InputSource&, size_t max_len = 0);  
865 -  
866 - QPDFObjGen read_object_start(qpdf_offset_t offset);  
867 - void readObjectAtOffset(  
868 - bool attempt_recovery,  
869 - qpdf_offset_t offset,  
870 - std::string const& description,  
871 - QPDFObjGen exp_og);  
872 - QPDFObjectHandle readObjectAtOffset(  
873 - qpdf_offset_t offset, std::string const& description, bool skip_cache_if_in_xref);  
874 - std::shared_ptr<QPDFObject> const& resolve(QPDFObjGen og);  
875 - void resolveObjectsInStream(int obj_stream_number);  
876 void stopOnError(std::string const& message); 753 void stopOnError(std::string const& message);
877 inline void 754 inline void
878 no_ci_stop_if(bool condition, std::string const& message, std::string const& context = {}); 755 no_ci_stop_if(bool condition, std::string const& message, std::string const& context = {});
879 - QPDFObjGen nextObjGen();  
880 - QPDFObjectHandle newIndirect(QPDFObjGen, std::shared_ptr<QPDFObject> const&);  
881 - QPDFObjectHandle makeIndirectFromQPDFObject(std::shared_ptr<QPDFObject> const& obj);  
882 - bool isCached(QPDFObjGen og);  
883 - bool isUnresolved(QPDFObjGen og);  
884 - std::shared_ptr<QPDFObject> getObjectForParser(int id, int gen, bool parse_pdf);  
885 - std::shared_ptr<QPDFObject> getObjectForJSON(int id, int gen);  
886 void removeObject(QPDFObjGen og); 756 void removeObject(QPDFObjGen og);
887 - void updateCache(  
888 - QPDFObjGen og,  
889 - std::shared_ptr<QPDFObject> const& object,  
890 - qpdf_offset_t end_before_space,  
891 - qpdf_offset_t end_after_space,  
892 - bool destroy = true);  
893 static QPDFExc damagedPDF( 757 static QPDFExc damagedPDF(
894 InputSource& input, 758 InputSource& input,
895 std::string const& object, 759 std::string const& object,
@@ -929,34 +793,6 @@ class QPDF @@ -929,34 +793,6 @@ class QPDF
929 // For QPDFWriter: 793 // For QPDFWriter:
930 794
931 std::map<QPDFObjGen, QPDFXRefEntry> const& getXRefTableInternal(); 795 std::map<QPDFObjGen, QPDFXRefEntry> const& getXRefTableInternal();
932 - template <typename T>  
933 - void optimize_internal(  
934 - T const& object_stream_data,  
935 - bool allow_changes = true,  
936 - std::function<int(QPDFObjectHandle&)> skip_stream_parameters = nullptr);  
937 - void optimize(  
938 - QPDFWriter::ObjTable const& obj,  
939 - std::function<int(QPDFObjectHandle&)> skip_stream_parameters);  
940 - size_t tableSize();  
941 -  
942 - // Get lists of all objects in order according to the part of a linearized file that they belong  
943 - // to.  
944 - void getLinearizedParts(  
945 - QPDFWriter::ObjTable const& obj,  
946 - std::vector<QPDFObjectHandle>& part4,  
947 - std::vector<QPDFObjectHandle>& part6,  
948 - std::vector<QPDFObjectHandle>& part7,  
949 - std::vector<QPDFObjectHandle>& part8,  
950 - std::vector<QPDFObjectHandle>& part9);  
951 -  
952 - void generateHintStream(  
953 - QPDFWriter::NewObjTable const& new_obj,  
954 - QPDFWriter::ObjTable const& obj,  
955 - std::string& hint_stream,  
956 - int& S,  
957 - int& O,  
958 - bool compressed);  
959 -  
960 // 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.
961 template <typename T> 797 template <typename T>
962 std::vector<T> getCompressibleObjGens(); 798 std::vector<T> getCompressibleObjGens();
@@ -1016,66 +852,6 @@ class QPDF @@ -1016,66 +852,6 @@ class QPDF
1016 bool findStartxref(); 852 bool findStartxref();
1017 bool findEndstream(); 853 bool findEndstream();
1018 854
1019 - // methods to support linearization checking -- implemented in QPDF_linearization.cc  
1020 - void readLinearizationData();  
1021 - void checkLinearizationInternal();  
1022 - void dumpLinearizationDataInternal();  
1023 - void linearizationWarning(std::string_view);  
1024 - qpdf::Dictionary readHintStream(Pipeline&, qpdf_offset_t offset, size_t length);  
1025 - void readHPageOffset(BitStream);  
1026 - void readHSharedObject(BitStream);  
1027 - void readHGeneric(BitStream, HGeneric&);  
1028 - qpdf_offset_t maxEnd(ObjUser const& ou);  
1029 - qpdf_offset_t getLinearizationOffset(QPDFObjGen);  
1030 - QPDFObjectHandle  
1031 - getUncompressedObject(QPDFObjectHandle&, std::map<int, int> const& object_stream_data);  
1032 - QPDFObjectHandle getUncompressedObject(QPDFObjectHandle&, QPDFWriter::ObjTable const& obj);  
1033 - int lengthNextN(int first_object, int n);  
1034 - void  
1035 - checkHPageOffset(std::vector<QPDFObjectHandle> const& pages, std::map<int, int>& idx_to_obj);  
1036 - void  
1037 - checkHSharedObject(std::vector<QPDFObjectHandle> const& pages, std::map<int, int>& idx_to_obj);  
1038 - void checkHOutlines();  
1039 - void dumpHPageOffset();  
1040 - void dumpHSharedObject();  
1041 - void dumpHGeneric(HGeneric&);  
1042 - qpdf_offset_t adjusted_offset(qpdf_offset_t offset);  
1043 - template <typename T>  
1044 - void calculateLinearizationData(T const& object_stream_data);  
1045 - template <typename T>  
1046 - void pushOutlinesToPart(  
1047 - std::vector<QPDFObjectHandle>& part,  
1048 - std::set<QPDFObjGen>& lc_outlines,  
1049 - T const& object_stream_data);  
1050 - int outputLengthNextN(  
1051 - int in_object,  
1052 - int n,  
1053 - QPDFWriter::NewObjTable const& new_obj,  
1054 - QPDFWriter::ObjTable const& obj);  
1055 - void  
1056 - calculateHPageOffset(QPDFWriter::NewObjTable const& new_obj, QPDFWriter::ObjTable const& obj);  
1057 - void  
1058 - calculateHSharedObject(QPDFWriter::NewObjTable const& new_obj, QPDFWriter::ObjTable const& obj);  
1059 - void calculateHOutline(QPDFWriter::NewObjTable const& new_obj, QPDFWriter::ObjTable const& obj);  
1060 - void writeHPageOffset(BitWriter&);  
1061 - void writeHSharedObject(BitWriter&);  
1062 - void writeHGeneric(BitWriter&, HGeneric&);  
1063 -  
1064 - // Methods to support optimization  
1065 -  
1066 - void pushInheritedAttributesToPage(bool allow_changes, bool warn_skipped_keys);  
1067 - void pushInheritedAttributesToPageInternal(  
1068 - QPDFObjectHandle,  
1069 - std::map<std::string, std::vector<QPDFObjectHandle>>&,  
1070 - bool allow_changes,  
1071 - bool warn_skipped_keys);  
1072 - void updateObjectMaps(  
1073 - ObjUser const& ou,  
1074 - QPDFObjectHandle oh,  
1075 - std::function<int(QPDFObjectHandle&)> skip_stream_parameters);  
1076 - void filterCompressedObjects(std::map<int, int> const& object_stream_data);  
1077 - void filterCompressedObjects(QPDFWriter::ObjTable const& object_stream_data);  
1078 -  
1079 // JSON import 855 // JSON import
1080 void importJSON(std::shared_ptr<InputSource>, bool must_be_complete); 856 void importJSON(std::shared_ptr<InputSource>, bool must_be_complete);
1081 857
libqpdf/NNTree.cc
@@ -30,7 +30,7 @@ void @@ -30,7 +30,7 @@ void
30 NNTreeImpl::warn(QPDFObjectHandle const& node, std::string const& msg) 30 NNTreeImpl::warn(QPDFObjectHandle const& node, std::string const& msg)
31 { 31 {
32 qpdf.warn(qpdf_e_damaged_pdf, get_description(node), 0, msg); 32 qpdf.warn(qpdf_e_damaged_pdf, get_description(node), 0, msg);
33 - if (++error_count > 5 && qpdf.reconstructed_xref()) { 33 + if (++error_count > 5 && qpdf.doc().reconstructed_xref()) {
34 error(node, "too many errors - giving up"); 34 error(node, "too many errors - giving up");
35 } 35 }
36 } 36 }
libqpdf/QPDF.cc
@@ -178,7 +178,11 @@ QPDF::QPDFVersion() @@ -178,7 +178,11 @@ QPDF::QPDFVersion()
178 return QPDF::qpdf_version; 178 return QPDF::qpdf_version;
179 } 179 }
180 180
181 -QPDF::Members::Members() : 181 +QPDF::Members::Members(QPDF& qpdf) :
  182 + doc(qpdf, *this),
  183 + lin(doc.linearization()),
  184 + objects(doc.objects()),
  185 + pages(doc.pages()),
182 log(QPDFLogger::defaultLogger()), 186 log(QPDFLogger::defaultLogger()),
183 file(new InvalidInputSource()), 187 file(new InvalidInputSource()),
184 encp(new EncryptionParameters) 188 encp(new EncryptionParameters)
@@ -186,7 +190,7 @@ QPDF::Members::Members() : @@ -186,7 +190,7 @@ QPDF::Members::Members() :
186 } 190 }
187 191
188 QPDF::QPDF() : 192 QPDF::QPDF() :
189 - m(std::make_unique<Members>()) 193 + m(std::make_unique<Members>(*this))
190 { 194 {
191 m->tokenizer.allowEOF(); 195 m->tokenizer.allowEOF();
192 // Generate a unique ID. It just has to be unique among all QPDF objects allocated throughout 196 // Generate a unique ID. It just has to be unique among all QPDF objects allocated throughout
@@ -266,7 +270,7 @@ void @@ -266,7 +270,7 @@ void
266 QPDF::processInputSource(std::shared_ptr<InputSource> source, char const* password) 270 QPDF::processInputSource(std::shared_ptr<InputSource> source, char const* password)
267 { 271 {
268 m->file = source; 272 m->file = source;
269 - parse(password); 273 + m->objects.parse(password);
270 } 274 }
271 275
272 void 276 void
@@ -434,20 +438,20 @@ QPDF::warn( @@ -434,20 +438,20 @@ QPDF::warn(
434 QPDFObjectHandle 438 QPDFObjectHandle
435 QPDF::newReserved() 439 QPDF::newReserved()
436 { 440 {
437 - return makeIndirectFromQPDFObject(QPDFObject::create<QPDF_Reserved>()); 441 + return m->objects.makeIndirectFromQPDFObject(QPDFObject::create<QPDF_Reserved>());
438 } 442 }
439 443
440 QPDFObjectHandle 444 QPDFObjectHandle
441 QPDF::newIndirectNull() 445 QPDF::newIndirectNull()
442 { 446 {
443 - return makeIndirectFromQPDFObject(QPDFObject::create<QPDF_Null>()); 447 + return m->objects.makeIndirectFromQPDFObject(QPDFObject::create<QPDF_Null>());
444 } 448 }
445 449
446 QPDFObjectHandle 450 QPDFObjectHandle
447 QPDF::newStream() 451 QPDF::newStream()
448 { 452 {
449 return makeIndirectObject( 453 return makeIndirectObject(
450 - qpdf::Stream(*this, nextObjGen(), QPDFObjectHandle::newDictionary(), 0, 0)); 454 + qpdf::Stream(*this, m->objects.nextObjGen(), Dictionary::empty(), 0, 0));
451 } 455 }
452 456
453 QPDFObjectHandle 457 QPDFObjectHandle
libqpdf/QPDFAcroFormDocumentHelper.cc
@@ -45,7 +45,7 @@ QPDFAcroFormDocumentHelper::QPDFAcroFormDocumentHelper(QPDF&amp; qpdf) : @@ -45,7 +45,7 @@ QPDFAcroFormDocumentHelper::QPDFAcroFormDocumentHelper(QPDF&amp; qpdf) :
45 QPDFAcroFormDocumentHelper& 45 QPDFAcroFormDocumentHelper&
46 QPDFAcroFormDocumentHelper::get(QPDF& qpdf) 46 QPDFAcroFormDocumentHelper::get(QPDF& qpdf)
47 { 47 {
48 - return qpdf.acroform(); 48 + return qpdf.doc().acroform();
49 } 49 }
50 50
51 void 51 void
libqpdf/QPDFEmbeddedFileDocumentHelper.cc
@@ -53,7 +53,7 @@ QPDFEmbeddedFileDocumentHelper::QPDFEmbeddedFileDocumentHelper(QPDF&amp; qpdf) : @@ -53,7 +53,7 @@ QPDFEmbeddedFileDocumentHelper::QPDFEmbeddedFileDocumentHelper(QPDF&amp; qpdf) :
53 QPDFEmbeddedFileDocumentHelper& 53 QPDFEmbeddedFileDocumentHelper&
54 QPDFEmbeddedFileDocumentHelper::get(QPDF& qpdf) 54 QPDFEmbeddedFileDocumentHelper::get(QPDF& qpdf)
55 { 55 {
56 - return qpdf.embedded_files(); 56 + return qpdf.doc().embedded_files();
57 } 57 }
58 58
59 void 59 void
libqpdf/QPDFFormFieldObjectHelper.cc
@@ -337,7 +337,7 @@ QPDFFormFieldObjectHelper::setV(QPDFObjectHandle value, bool need_appearances) @@ -337,7 +337,7 @@ QPDFFormFieldObjectHelper::setV(QPDFObjectHandle value, bool need_appearances)
337 QPDF& qpdf = oh().getQPDF( 337 QPDF& qpdf = oh().getQPDF(
338 "QPDFFormFieldObjectHelper::setV called with need_appearances = " 338 "QPDFFormFieldObjectHelper::setV called with need_appearances = "
339 "true on an object that is not associated with an owning QPDF"); 339 "true on an object that is not associated with an owning QPDF");
340 - qpdf.acroform().setNeedAppearances(true); 340 + qpdf.doc().acroform().setNeedAppearances(true);
341 } 341 }
342 } 342 }
343 343
libqpdf/QPDFJob.cc
@@ -30,6 +30,18 @@ @@ -30,6 +30,18 @@
30 30
31 using namespace qpdf; 31 using namespace qpdf;
32 32
  33 +// JobSetter class is restricted to QPDFJob.
  34 +class QPDF::Doc::JobSetter
  35 +{
  36 + public:
  37 + // Enable enhanced warnings for pdf file checking.
  38 + static void
  39 + setCheckMode(QPDF& qpdf, bool val)
  40 + {
  41 + qpdf.m->check_mode = val;
  42 + }
  43 +};
  44 +
33 namespace 45 namespace
34 { 46 {
35 class ImageOptimizer final: public QPDFObjectHandle::StreamDataProvider 47 class ImageOptimizer final: public QPDFObjectHandle::StreamDataProvider
@@ -734,7 +746,7 @@ QPDFJob::doCheck(QPDF&amp; pdf) @@ -734,7 +746,7 @@ QPDFJob::doCheck(QPDF&amp; pdf)
734 bool okay = true; 746 bool okay = true;
735 auto& cout = *m->log->getInfo(); 747 auto& cout = *m->log->getInfo();
736 cout << "checking " << m->infile_name() << "\n"; 748 cout << "checking " << m->infile_name() << "\n";
737 - QPDF::JobSetter::setCheckMode(pdf, true); 749 + QPDF::Doc::JobSetter::setCheckMode(pdf, true);
738 try { 750 try {
739 int extension_level = pdf.getExtensionLevel(); 751 int extension_level = pdf.getExtensionLevel();
740 cout << "PDF Version: " << pdf.getPDFVersion(); 752 cout << "PDF Version: " << pdf.getPDFVersion();
@@ -751,12 +763,13 @@ QPDFJob::doCheck(QPDF&amp; pdf) @@ -751,12 +763,13 @@ QPDFJob::doCheck(QPDF&amp; pdf)
751 } 763 }
752 764
753 // Create all document helper to trigger any validations they carry out. 765 // Create all document helper to trigger any validations they carry out.
754 - auto& pages = pdf.pages();  
755 - (void)pdf.acroform();  
756 - (void)pdf.embedded_files();  
757 - (void)pdf.page_labels();  
758 - (void)pdf.outlines().resolveNamedDest(QPDFObjectHandle::newString("dummy"));  
759 - (void)pdf.outlines().getOutlinesForPage(pages.getAllPages().at(0)); 766 + auto& doc = pdf.doc();
  767 + auto& pages = doc.page_dh();
  768 + (void)doc.acroform();
  769 + (void)doc.embedded_files();
  770 + (void)doc.page_labels();
  771 + (void)doc.outlines().resolveNamedDest(QPDFObjectHandle::newString("dummy"));
  772 + (void)doc.outlines().getOutlinesForPage(pages.getAllPages().at(0));
760 773
761 // Write the file to nowhere, uncompressing streams. This causes full file traversal and 774 // Write the file to nowhere, uncompressing streams. This causes full file traversal and
762 // decoding of all streams we can decode. 775 // decoding of all streams we can decode.
@@ -839,8 +852,8 @@ QPDFJob::doShowPages(QPDF&amp; pdf) @@ -839,8 +852,8 @@ QPDFJob::doShowPages(QPDF&amp; pdf)
839 { 852 {
840 int pageno = 0; 853 int pageno = 0;
841 auto& cout = *m->log->getInfo(); 854 auto& cout = *m->log->getInfo();
842 - for (auto& ph: pdf.pages().getAllPages()) {  
843 - QPDFObjectHandle page = ph.getObjectHandle(); 855 + for (auto& page: pdf.getAllPages()) {
  856 + QPDFPageObjectHelper ph(page);
844 ++pageno; 857 ++pageno;
845 858
846 cout << "page " << pageno << ": " << page.getObjectID() << " " << page.getGeneration() 859 cout << "page " << pageno << ": " << page.getObjectID() << " " << page.getGeneration()
@@ -871,7 +884,7 @@ QPDFJob::doShowPages(QPDF&amp; pdf) @@ -871,7 +884,7 @@ QPDFJob::doShowPages(QPDF&amp; pdf)
871 void 884 void
872 QPDFJob::doListAttachments(QPDF& pdf) 885 QPDFJob::doListAttachments(QPDF& pdf)
873 { 886 {
874 - auto& efdh = pdf.embedded_files(); 887 + auto& efdh = pdf.doc().embedded_files();
875 if (efdh.hasEmbeddedFiles()) { 888 if (efdh.hasEmbeddedFiles()) {
876 for (auto const& i: efdh.getEmbeddedFiles()) { 889 for (auto const& i: efdh.getEmbeddedFiles()) {
877 std::string const& key = i.first; 890 std::string const& key = i.first;
@@ -911,7 +924,7 @@ QPDFJob::doListAttachments(QPDF&amp; pdf) @@ -911,7 +924,7 @@ QPDFJob::doListAttachments(QPDF&amp; pdf)
911 void 924 void
912 QPDFJob::doShowAttachment(QPDF& pdf) 925 QPDFJob::doShowAttachment(QPDF& pdf)
913 { 926 {
914 - auto& efdh = pdf.embedded_files(); 927 + auto& efdh = pdf.doc().embedded_files();
915 auto fs = efdh.getEmbeddedFile(m->attachment_to_show); 928 auto fs = efdh.getEmbeddedFile(m->attachment_to_show);
916 if (!fs) { 929 if (!fs) {
917 throw std::runtime_error("attachment " + m->attachment_to_show + " not found"); 930 throw std::runtime_error("attachment " + m->attachment_to_show + " not found");
@@ -1030,13 +1043,13 @@ QPDFJob::doJSONPages(Pipeline* p, bool&amp; first, QPDF&amp; pdf) @@ -1030,13 +1043,13 @@ QPDFJob::doJSONPages(Pipeline* p, bool&amp; first, QPDF&amp; pdf)
1030 JSON::writeDictionaryKey(p, first, "pages", 1); 1043 JSON::writeDictionaryKey(p, first, "pages", 1);
1031 bool first_page = true; 1044 bool first_page = true;
1032 JSON::writeArrayOpen(p, first_page, 2); 1045 JSON::writeArrayOpen(p, first_page, 2);
1033 - auto& pldh = pdf.page_labels();  
1034 - auto& odh = pdf.outlines(); 1046 + auto& pldh = pdf.doc().page_labels();
  1047 + auto& odh = pdf.doc().outlines();
1035 int pageno = -1; 1048 int pageno = -1;
1036 - for (auto& ph: pdf.pages().getAllPages()) { 1049 + for (auto& page: pdf.getAllPages()) {
1037 ++pageno; 1050 ++pageno;
1038 JSON j_page = JSON::makeDictionary(); 1051 JSON j_page = JSON::makeDictionary();
1039 - QPDFObjectHandle page = ph.getObjectHandle(); 1052 + QPDFPageObjectHelper ph(page);
1040 j_page.addDictionaryMember("object", page.getJSON(m->json_version)); 1053 j_page.addDictionaryMember("object", page.getJSON(m->json_version));
1041 JSON j_images = j_page.addDictionaryMember("images", JSON::makeArray()); 1054 JSON j_images = j_page.addDictionaryMember("images", JSON::makeArray());
1042 for (auto const& iter2: ph.getImages()) { 1055 for (auto const& iter2: ph.getImages()) {
@@ -1093,8 +1106,8 @@ void @@ -1093,8 +1106,8 @@ void
1093 QPDFJob::doJSONPageLabels(Pipeline* p, bool& first, QPDF& pdf) 1106 QPDFJob::doJSONPageLabels(Pipeline* p, bool& first, QPDF& pdf)
1094 { 1107 {
1095 JSON j_labels = JSON::makeArray(); 1108 JSON j_labels = JSON::makeArray();
1096 - auto& pldh = pdf.page_labels();  
1097 - long long npages = QIntC::to_longlong(pdf.pages().getAllPages().size()); 1109 + auto& pldh = pdf.doc().page_labels();
  1110 + long long npages = QIntC::to_longlong(pdf.getAllPages().size());
1098 if (pldh.hasPageLabels()) { 1111 if (pldh.hasPageLabels()) {
1099 std::vector<QPDFObjectHandle> labels; 1112 std::vector<QPDFObjectHandle> labels;
1100 pldh.getLabelsForPageRange(0, npages - 1, 0, labels); 1113 pldh.getLabelsForPageRange(0, npages - 1, 0, labels);
@@ -1142,13 +1155,12 @@ QPDFJob::doJSONOutlines(Pipeline* p, bool&amp; first, QPDF&amp; pdf) @@ -1142,13 +1155,12 @@ QPDFJob::doJSONOutlines(Pipeline* p, bool&amp; first, QPDF&amp; pdf)
1142 { 1155 {
1143 std::map<QPDFObjGen, int> page_numbers; 1156 std::map<QPDFObjGen, int> page_numbers;
1144 int n = 0; 1157 int n = 0;
1145 - for (auto const& ph: pdf.pages().getAllPages()) {  
1146 - QPDFObjectHandle oh = ph.getObjectHandle();  
1147 - page_numbers[oh.getObjGen()] = ++n; 1158 + for (auto const& oh: pdf.getAllPages()) {
  1159 + page_numbers[oh] = ++n;
1148 } 1160 }
1149 1161
1150 JSON j_outlines = JSON::makeArray(); 1162 JSON j_outlines = JSON::makeArray();
1151 - addOutlinesToJson(pdf.outlines().getTopLevelOutlines(), j_outlines, page_numbers); 1163 + addOutlinesToJson(pdf.doc().outlines().getTopLevelOutlines(), j_outlines, page_numbers);
1152 JSON::writeDictionaryItem(p, first, "outlines", j_outlines, 1); 1164 JSON::writeDictionaryItem(p, first, "outlines", j_outlines, 1);
1153 } 1165 }
1154 1166
@@ -1156,14 +1168,14 @@ void @@ -1156,14 +1168,14 @@ void
1156 QPDFJob::doJSONAcroform(Pipeline* p, bool& first, QPDF& pdf) 1168 QPDFJob::doJSONAcroform(Pipeline* p, bool& first, QPDF& pdf)
1157 { 1169 {
1158 JSON j_acroform = JSON::makeDictionary(); 1170 JSON j_acroform = JSON::makeDictionary();
1159 - auto& afdh = pdf.acroform(); 1171 + auto& afdh = pdf.doc().acroform();
1160 j_acroform.addDictionaryMember("hasacroform", JSON::makeBool(afdh.hasAcroForm())); 1172 j_acroform.addDictionaryMember("hasacroform", JSON::makeBool(afdh.hasAcroForm()));
1161 j_acroform.addDictionaryMember("needappearances", JSON::makeBool(afdh.getNeedAppearances())); 1173 j_acroform.addDictionaryMember("needappearances", JSON::makeBool(afdh.getNeedAppearances()));
1162 JSON j_fields = j_acroform.addDictionaryMember("fields", JSON::makeArray()); 1174 JSON j_fields = j_acroform.addDictionaryMember("fields", JSON::makeArray());
1163 int pagepos1 = 0; 1175 int pagepos1 = 0;
1164 - for (auto const& page: pdf.pages().getAllPages()) { 1176 + for (auto const& page: pdf.getAllPages()) {
1165 ++pagepos1; 1177 ++pagepos1;
1166 - for (auto& aoh: afdh.getWidgetAnnotationsForPage(page)) { 1178 + for (auto& aoh: afdh.getWidgetAnnotationsForPage({page})) {
1167 QPDFFormFieldObjectHelper ffh = afdh.getFieldForAnnotation(aoh); 1179 QPDFFormFieldObjectHelper ffh = afdh.getFieldForAnnotation(aoh);
1168 if (!ffh.getObjectHandle().isDictionary()) { 1180 if (!ffh.getObjectHandle().isDictionary()) {
1169 continue; 1181 continue;
@@ -1297,7 +1309,7 @@ QPDFJob::doJSONAttachments(Pipeline* p, bool&amp; first, QPDF&amp; pdf) @@ -1297,7 +1309,7 @@ QPDFJob::doJSONAttachments(Pipeline* p, bool&amp; first, QPDF&amp; pdf)
1297 }; 1309 };
1298 1310
1299 JSON j_attachments = JSON::makeDictionary(); 1311 JSON j_attachments = JSON::makeDictionary();
1300 - auto& efdh = pdf.embedded_files(); 1312 + auto& efdh = pdf.doc().embedded_files();
1301 for (auto const& iter: efdh.getEmbeddedFiles()) { 1313 for (auto const& iter: efdh.getEmbeddedFiles()) {
1302 std::string const& key = iter.first; 1314 std::string const& key = iter.first;
1303 auto fsoh = iter.second; 1315 auto fsoh = iter.second;
@@ -1873,7 +1885,7 @@ QPDFJob::doUnderOverlayForPage( @@ -1873,7 +1885,7 @@ QPDFJob::doUnderOverlayForPage(
1873 if (!(uo.pdf && pagenos[pageno.idx].contains(uo_idx))) { 1885 if (!(uo.pdf && pagenos[pageno.idx].contains(uo_idx))) {
1874 return ""; 1886 return "";
1875 } 1887 }
1876 - auto& dest_afdh = dest_page.qpdf()->acroform(); 1888 + auto& dest_afdh = dest_page.qpdf()->doc().acroform();
1877 1889
1878 auto const& pages = uo.pdf->getAllPages(); 1890 auto const& pages = uo.pdf->getAllPages();
1879 std::string content; 1891 std::string content;
@@ -1894,7 +1906,7 @@ QPDFJob::doUnderOverlayForPage( @@ -1894,7 +1906,7 @@ QPDFJob::doUnderOverlayForPage(
1894 QPDFMatrix cm; 1906 QPDFMatrix cm;
1895 std::string new_content = dest_page.placeFormXObject( 1907 std::string new_content = dest_page.placeFormXObject(
1896 fo[from_no.no][uo_idx], name, dest_page.getTrimBox().getArrayAsRectangle(), cm); 1908 fo[from_no.no][uo_idx], name, dest_page.getTrimBox().getArrayAsRectangle(), cm);
1897 - dest_page.copyAnnotations(from_page, cm, &dest_afdh, &from_page.qpdf()->acroform()); 1909 + dest_page.copyAnnotations(from_page, cm, &dest_afdh, &from_page.qpdf()->doc().acroform());
1898 if (!new_content.empty()) { 1910 if (!new_content.empty()) {
1899 resources.mergeResources("<< /XObject << >> >>"_qpdf); 1911 resources.mergeResources("<< /XObject << >> >>"_qpdf);
1900 auto xobject = resources.getKey("/XObject"); 1912 auto xobject = resources.getKey("/XObject");
@@ -2019,7 +2031,7 @@ void @@ -2019,7 +2031,7 @@ void
2019 QPDFJob::addAttachments(QPDF& pdf) 2031 QPDFJob::addAttachments(QPDF& pdf)
2020 { 2032 {
2021 maybe_set_pagemode(pdf, "/UseAttachments"); 2033 maybe_set_pagemode(pdf, "/UseAttachments");
2022 - auto& efdh = pdf.embedded_files(); 2034 + auto& efdh = pdf.doc().embedded_files();
2023 std::vector<std::string> duplicated_keys; 2035 std::vector<std::string> duplicated_keys;
2024 for (auto const& to_add: m->attachments_to_add) { 2036 for (auto const& to_add: m->attachments_to_add) {
2025 if ((!to_add.replace) && efdh.getEmbeddedFile(to_add.key)) { 2037 if ((!to_add.replace) && efdh.getEmbeddedFile(to_add.key)) {
@@ -2063,7 +2075,7 @@ void @@ -2063,7 +2075,7 @@ void
2063 QPDFJob::copyAttachments(QPDF& pdf) 2075 QPDFJob::copyAttachments(QPDF& pdf)
2064 { 2076 {
2065 maybe_set_pagemode(pdf, "/UseAttachments"); 2077 maybe_set_pagemode(pdf, "/UseAttachments");
2066 - auto& efdh = pdf.embedded_files(); 2078 + auto& efdh = pdf.doc().embedded_files();
2067 std::vector<std::string> duplicates; 2079 std::vector<std::string> duplicates;
2068 for (auto const& to_copy: m->attachments_to_copy) { 2080 for (auto const& to_copy: m->attachments_to_copy) {
2069 doIfVerbose([&](Pipeline& v, std::string const& prefix) { 2081 doIfVerbose([&](Pipeline& v, std::string const& prefix) {
@@ -2071,7 +2083,7 @@ QPDFJob::copyAttachments(QPDF&amp; pdf) @@ -2071,7 +2083,7 @@ QPDFJob::copyAttachments(QPDF&amp; pdf)
2071 }); 2083 });
2072 std::unique_ptr<QPDF> other; 2084 std::unique_ptr<QPDF> other;
2073 processFile(other, to_copy.path.c_str(), to_copy.password.c_str(), false, false); 2085 processFile(other, to_copy.path.c_str(), to_copy.password.c_str(), false, false);
2074 - auto& other_efdh = other->embedded_files(); 2086 + auto& other_efdh = other->doc().embedded_files();
2075 auto other_attachments = other_efdh.getEmbeddedFiles(); 2087 auto other_attachments = other_efdh.getEmbeddedFiles();
2076 for (auto const& iter: other_attachments) { 2088 for (auto const& iter: other_attachments) {
2077 std::string new_key = to_copy.prefix + iter.first; 2089 std::string new_key = to_copy.prefix + iter.first;
@@ -2114,7 +2126,7 @@ QPDFJob::handleTransformations(QPDF&amp; pdf) @@ -2114,7 +2126,7 @@ QPDFJob::handleTransformations(QPDF&amp; pdf)
2114 QPDFAcroFormDocumentHelper* afdh_ptr = nullptr; 2126 QPDFAcroFormDocumentHelper* afdh_ptr = nullptr;
2115 auto afdh = [&]() -> QPDFAcroFormDocumentHelper& { 2127 auto afdh = [&]() -> QPDFAcroFormDocumentHelper& {
2116 if (!afdh_ptr) { 2128 if (!afdh_ptr) {
2117 - afdh_ptr = &pdf.acroform(); 2129 + afdh_ptr = &pdf.doc().acroform();
2118 } 2130 }
2119 return *afdh_ptr; 2131 return *afdh_ptr;
2120 }; 2132 };
@@ -2205,7 +2217,7 @@ QPDFJob::handleTransformations(QPDF&amp; pdf) @@ -2205,7 +2217,7 @@ QPDFJob::handleTransformations(QPDF&amp; pdf)
2205 pdf.getRoot().replaceKey("/PageLabels", page_labels); 2217 pdf.getRoot().replaceKey("/PageLabels", page_labels);
2206 } 2218 }
2207 if (!m->attachments_to_remove.empty()) { 2219 if (!m->attachments_to_remove.empty()) {
2208 - auto& efdh = pdf.embedded_files(); 2220 + auto& efdh = pdf.doc().embedded_files();
2209 for (auto const& key: m->attachments_to_remove) { 2221 for (auto const& key: m->attachments_to_remove) {
2210 if (efdh.removeEmbeddedFile(key)) { 2222 if (efdh.removeEmbeddedFile(key)) {
2211 doIfVerbose([&](Pipeline& v, std::string const& prefix) { 2223 doIfVerbose([&](Pipeline& v, std::string const& prefix) {
@@ -2344,7 +2356,7 @@ QPDFJob::Input::initialize(Inputs&amp; in, QPDF* a_qpdf) @@ -2344,7 +2356,7 @@ QPDFJob::Input::initialize(Inputs&amp; in, QPDF* a_qpdf)
2344 if (in.job.m->remove_unreferenced_page_resources != QPDFJob::re_no) { 2356 if (in.job.m->remove_unreferenced_page_resources != QPDFJob::re_no) {
2345 remove_unreferenced = in.job.shouldRemoveUnreferencedResources(*qpdf); 2357 remove_unreferenced = in.job.shouldRemoveUnreferencedResources(*qpdf);
2346 } 2358 }
2347 - if (qpdf->page_labels().hasPageLabels()) { 2359 + if (qpdf->doc().page_labels().hasPageLabels()) {
2348 in.any_page_labels = true; 2360 in.any_page_labels = true;
2349 } 2361 }
2350 } 2362 }
@@ -2574,15 +2586,15 @@ QPDFJob::handlePageSpecs(QPDF&amp; pdf) @@ -2574,15 +2586,15 @@ QPDFJob::handlePageSpecs(QPDF&amp; pdf)
2574 // original file that we are selecting. 2586 // original file that we are selecting.
2575 std::vector<QPDFObjectHandle> new_labels; 2587 std::vector<QPDFObjectHandle> new_labels;
2576 int out_pageno = 0; 2588 int out_pageno = 0;
2577 - auto& this_afdh = pdf.acroform(); 2589 + auto& this_afdh = pdf.doc().acroform();
2578 std::set<QPDFObjGen> referenced_fields; 2590 std::set<QPDFObjGen> referenced_fields;
2579 for (auto& selection: new_specs.empty() ? m->inputs.selections : new_specs) { 2591 for (auto& selection: new_specs.empty() ? m->inputs.selections : new_specs) {
2580 auto& input = selection.input(); 2592 auto& input = selection.input();
2581 if (input.cfis) { 2593 if (input.cfis) {
2582 input.cfis->stayOpen(true); 2594 input.cfis->stayOpen(true);
2583 } 2595 }
2584 - auto* pldh = m->inputs.any_page_labels ? &input.qpdf->page_labels() : nullptr;  
2585 - auto& other_afdh = input.qpdf->acroform(); 2596 + auto* pldh = m->inputs.any_page_labels ? &input.qpdf->doc().page_labels() : nullptr;
  2597 + auto& other_afdh = input.qpdf->doc().acroform();
2586 doIfVerbose([&](Pipeline& v, std::string const& prefix) { 2598 doIfVerbose([&](Pipeline& v, std::string const& prefix) {
2587 v << prefix << ": adding pages from " << selection.filename() << "\n"; 2599 v << prefix << ": adding pages from " << selection.filename() << "\n";
2588 }); 2600 });
@@ -3012,8 +3024,8 @@ QPDFJob::doSplitPages(QPDF&amp; pdf) @@ -3012,8 +3024,8 @@ QPDFJob::doSplitPages(QPDF&amp; pdf)
3012 QPDFPageDocumentHelper dh(pdf); 3024 QPDFPageDocumentHelper dh(pdf);
3013 dh.removeUnreferencedResources(); 3025 dh.removeUnreferencedResources();
3014 } 3026 }
3015 - auto& pldh = pdf.page_labels();  
3016 - auto& afdh = pdf.acroform(); 3027 + auto& pldh = pdf.doc().page_labels();
  3028 + auto& afdh = pdf.doc().acroform();
3017 std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages(); 3029 std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages();
3018 size_t pageno_len = std::to_string(pages.size()).length(); 3030 size_t pageno_len = std::to_string(pages.size()).length();
3019 size_t num_pages = pages.size(); 3031 size_t num_pages = pages.size();
@@ -3025,7 +3037,8 @@ QPDFJob::doSplitPages(QPDF&amp; pdf) @@ -3025,7 +3037,8 @@ QPDFJob::doSplitPages(QPDF&amp; pdf)
3025 } 3037 }
3026 QPDF outpdf; 3038 QPDF outpdf;
3027 outpdf.emptyPDF(); 3039 outpdf.emptyPDF();
3028 - QPDFAcroFormDocumentHelper* out_afdh = afdh.hasAcroForm() ? &outpdf.acroform() : nullptr; 3040 + QPDFAcroFormDocumentHelper* out_afdh =
  3041 + afdh.hasAcroForm() ? &outpdf.doc().acroform() : nullptr;
3029 if (m->suppress_warnings) { 3042 if (m->suppress_warnings) {
3030 outpdf.setSuppressWarnings(true); 3043 outpdf.setSuppressWarnings(true);
3031 } 3044 }
libqpdf/QPDFObjectHandle.cc
@@ -1949,7 +1949,7 @@ QPDFObjectHandle::copyStream() @@ -1949,7 +1949,7 @@ QPDFObjectHandle::copyStream()
1949 dict.replaceKey(iter.first, iter.second.shallowCopy()); 1949 dict.replaceKey(iter.first, iter.second.shallowCopy());
1950 } 1950 }
1951 } 1951 }
1952 - QPDF::StreamCopier::copyStreamData(getOwningQPDF(), result, *this); 1952 + QPDF::Doc::StreamCopier::copyStreamData(getOwningQPDF(), result, *this);
1953 return result; 1953 return result;
1954 } 1954 }
1955 1955
libqpdf/QPDFOutlineDocumentHelper.cc
@@ -34,7 +34,7 @@ QPDFOutlineDocumentHelper::QPDFOutlineDocumentHelper(QPDF&amp; qpdf) : @@ -34,7 +34,7 @@ QPDFOutlineDocumentHelper::QPDFOutlineDocumentHelper(QPDF&amp; qpdf) :
34 QPDFOutlineDocumentHelper& 34 QPDFOutlineDocumentHelper&
35 QPDFOutlineDocumentHelper::get(QPDF& qpdf) 35 QPDFOutlineDocumentHelper::get(QPDF& qpdf)
36 { 36 {
37 - return qpdf.outlines(); 37 + return qpdf.doc().outlines();
38 } 38 }
39 39
40 void 40 void
libqpdf/QPDFPageDocumentHelper.cc
@@ -18,7 +18,7 @@ QPDFPageDocumentHelper::QPDFPageDocumentHelper(QPDF&amp; qpdf) : @@ -18,7 +18,7 @@ QPDFPageDocumentHelper::QPDFPageDocumentHelper(QPDF&amp; qpdf) :
18 QPDFPageDocumentHelper& 18 QPDFPageDocumentHelper&
19 QPDFPageDocumentHelper::get(QPDF& qpdf) 19 QPDFPageDocumentHelper::get(QPDF& qpdf)
20 { 20 {
21 - return qpdf.pages(); 21 + return qpdf.doc().page_dh();
22 } 22 }
23 23
24 void 24 void
@@ -72,7 +72,7 @@ QPDFPageDocumentHelper::removePage(QPDFPageObjectHelper page) @@ -72,7 +72,7 @@ QPDFPageDocumentHelper::removePage(QPDFPageObjectHelper page)
72 void 72 void
73 QPDFPageDocumentHelper::flattenAnnotations(int required_flags, int forbidden_flags) 73 QPDFPageDocumentHelper::flattenAnnotations(int required_flags, int forbidden_flags)
74 { 74 {
75 - auto& afdh = qpdf.acroform(); 75 + auto& afdh = qpdf.doc().acroform();
76 if (afdh.getNeedAppearances()) { 76 if (afdh.getNeedAppearances()) {
77 qpdf.getRoot() 77 qpdf.getRoot()
78 .getKey("/AcroForm") 78 .getKey("/AcroForm")
libqpdf/QPDFPageLabelDocumentHelper.cc
@@ -26,7 +26,7 @@ QPDFPageLabelDocumentHelper::QPDFPageLabelDocumentHelper(QPDF&amp; qpdf) : @@ -26,7 +26,7 @@ QPDFPageLabelDocumentHelper::QPDFPageLabelDocumentHelper(QPDF&amp; qpdf) :
26 QPDFPageLabelDocumentHelper& 26 QPDFPageLabelDocumentHelper&
27 QPDFPageLabelDocumentHelper::get(QPDF& qpdf) 27 QPDFPageLabelDocumentHelper::get(QPDF& qpdf)
28 { 28 {
29 - return qpdf.page_labels(); 29 + return qpdf.doc().page_labels();
30 } 30 }
31 31
32 void 32 void
libqpdf/QPDFParser.cc
@@ -15,6 +15,36 @@ using namespace qpdf; @@ -15,6 +15,36 @@ using namespace qpdf;
15 15
16 using ObjectPtr = std::shared_ptr<QPDFObject>; 16 using ObjectPtr = std::shared_ptr<QPDFObject>;
17 17
  18 +// The ParseGuard class allows QPDFParser to detect re-entrant parsing. It also provides
  19 +// special access to allow the parser to create unresolved objects and dangling references.
  20 +class QPDF::Doc::ParseGuard
  21 +{
  22 + public:
  23 + ParseGuard(QPDF* qpdf) :
  24 + objects(qpdf ? &qpdf->m->objects : nullptr)
  25 + {
  26 + if (objects) {
  27 + objects->inParse(true);
  28 + }
  29 + }
  30 +
  31 + static std::shared_ptr<QPDFObject>
  32 + getObject(QPDF* qpdf, int id, int gen, bool parse_pdf)
  33 + {
  34 + return qpdf->m->objects.getObjectForParser(id, gen, parse_pdf);
  35 + }
  36 +
  37 + ~ParseGuard()
  38 + {
  39 + if (objects) {
  40 + objects->inParse(false);
  41 + }
  42 + }
  43 + QPDF::Doc::Objects* objects;
  44 +};
  45 +
  46 +using ParseGuard = QPDF::Doc::ParseGuard;
  47 +
18 QPDFObjectHandle 48 QPDFObjectHandle
19 QPDFParser::parse(InputSource& input, std::string const& object_description, QPDF* context) 49 QPDFParser::parse(InputSource& input, std::string const& object_description, QPDF* context)
20 { 50 {
@@ -49,7 +79,7 @@ QPDFParser::parse_content( @@ -49,7 +79,7 @@ QPDFParser::parse_content(
49 true, 79 true,
50 0, 80 0,
51 0, 81 0,
52 - context && context->reconstructed_xref()) 82 + context && context->doc().reconstructed_xref())
53 .parse(empty, true); 83 .parse(empty, true);
54 } 84 }
55 85
@@ -126,7 +156,7 @@ QPDFParser::parse(bool&amp; empty, bool content_stream) @@ -126,7 +156,7 @@ QPDFParser::parse(bool&amp; empty, bool content_stream)
126 // effect of reading the object and changing the file pointer. If you do this, it will cause a 156 // effect of reading the object and changing the file pointer. If you do this, it will cause a
127 // logic error to be thrown from QPDF::inParse(). 157 // logic error to be thrown from QPDF::inParse().
128 158
129 - QPDF::ParseGuard pg(context); 159 + ParseGuard pg(context);
130 empty = false; 160 empty = false;
131 start = input.tell(); 161 start = input.tell();
132 162
@@ -262,7 +292,7 @@ QPDFParser::parseRemainder(bool content_stream) @@ -262,7 +292,7 @@ QPDFParser::parseRemainder(bool content_stream)
262 auto id = QIntC::to_int(int_buffer[(int_count - 1) % 2]); 292 auto id = QIntC::to_int(int_buffer[(int_count - 1) % 2]);
263 auto gen = QIntC::to_int(int_buffer[(int_count) % 2]); 293 auto gen = QIntC::to_int(int_buffer[(int_count) % 2]);
264 if (!(id < 1 || gen < 0 || gen >= 65535)) { 294 if (!(id < 1 || gen < 0 || gen >= 65535)) {
265 - add(QPDF::ParseGuard::getObject(context, id, gen, parse_pdf)); 295 + add(ParseGuard::getObject(context, id, gen, parse_pdf));
266 } else { 296 } else {
267 QTC::TC("qpdf", "QPDFParser invalid objgen"); 297 QTC::TC("qpdf", "QPDFParser invalid objgen");
268 addNull(); 298 addNull();
@@ -392,7 +422,6 @@ QPDFParser::parseRemainder(bool content_stream) @@ -392,7 +422,6 @@ QPDFParser::parseRemainder(bool content_stream)
392 frame = &stack.back(); 422 frame = &stack.back();
393 add(std::move(object)); 423 add(std::move(object));
394 } else { 424 } else {
395 - QTC::TC("qpdf", "QPDFParser bad dictionary close in parseRemainder");  
396 if (sanity_checks) { 425 if (sanity_checks) {
397 // During sanity checks, assume nesting of containers is corrupt and object is 426 // During sanity checks, assume nesting of containers is corrupt and object is
398 // unusable. 427 // unusable.
libqpdf/QPDFWriter.cc
@@ -27,6 +27,8 @@ @@ -27,6 +27,8 @@
27 using namespace std::literals; 27 using namespace std::literals;
28 using namespace qpdf; 28 using namespace qpdf;
29 29
  30 +using Encryption = QPDF::Doc::Encryption;
  31 +
30 QPDFWriter::ProgressReporter::~ProgressReporter() // NOLINT (modernize-use-equals-default) 32 QPDFWriter::ProgressReporter::~ProgressReporter() // NOLINT (modernize-use-equals-default)
31 { 33 {
32 // Must be explicit and not inline -- see QPDF_DLL_CLASS in README-maintainer 34 // Must be explicit and not inline -- see QPDF_DLL_CLASS in README-maintainer
@@ -260,11 +262,13 @@ Pl_stack::Popper::pop() @@ -260,11 +262,13 @@ Pl_stack::Popper::pop()
260 } 262 }
261 263
262 // Writer class is restricted to QPDFWriter so that only it can call certain methods. 264 // Writer class is restricted to QPDFWriter so that only it can call certain methods.
263 -class QPDF::Writer 265 +class QPDF::Doc::Writer
264 { 266 {
265 friend class QPDFWriter; 267 friend class QPDFWriter;
266 Writer(QPDF& pdf) : 268 Writer(QPDF& pdf) :
267 - pdf(pdf) 269 + pdf(pdf),
  270 + lin(pdf.m->lin),
  271 + objects(pdf.m->objects)
268 { 272 {
269 } 273 }
270 274
@@ -274,7 +278,7 @@ class QPDF::Writer @@ -274,7 +278,7 @@ class QPDF::Writer
274 QPDFWriter::ObjTable const& obj, 278 QPDFWriter::ObjTable const& obj,
275 std::function<int(QPDFObjectHandle&)> skip_stream_parameters) 279 std::function<int(QPDFObjectHandle&)> skip_stream_parameters)
276 { 280 {
277 - pdf.optimize(obj, skip_stream_parameters); 281 + lin.optimize(obj, skip_stream_parameters);
278 } 282 }
279 283
280 void 284 void
@@ -286,7 +290,7 @@ class QPDF::Writer @@ -286,7 +290,7 @@ class QPDF::Writer
286 std::vector<QPDFObjectHandle>& part8, 290 std::vector<QPDFObjectHandle>& part8,
287 std::vector<QPDFObjectHandle>& part9) 291 std::vector<QPDFObjectHandle>& part9)
288 { 292 {
289 - pdf.getLinearizedParts(obj, part4, part6, part7, part8, part9); 293 + lin.getLinearizedParts(obj, part4, part6, part7, part8, part9);
290 } 294 }
291 295
292 void 296 void
@@ -298,19 +302,19 @@ class QPDF::Writer @@ -298,19 +302,19 @@ class QPDF::Writer
298 int& O, 302 int& O,
299 bool compressed) 303 bool compressed)
300 { 304 {
301 - pdf.generateHintStream(new_obj, obj, hint_stream, S, O, compressed); 305 + lin.generateHintStream(new_obj, obj, hint_stream, S, O, compressed);
302 } 306 }
303 307
304 std::vector<QPDFObjGen> 308 std::vector<QPDFObjGen>
305 getCompressibleObjGens() 309 getCompressibleObjGens()
306 { 310 {
307 - return pdf.getCompressibleObjVector(); 311 + return objects.getCompressibleObjVector();
308 } 312 }
309 313
310 std::vector<bool> 314 std::vector<bool>
311 getCompressibleObjSet() 315 getCompressibleObjSet()
312 { 316 {
313 - return pdf.getCompressibleObjSet(); 317 + return objects.getCompressibleObjSet();
314 } 318 }
315 319
316 std::map<QPDFObjGen, QPDFXRefEntry> const& 320 std::map<QPDFObjGen, QPDFXRefEntry> const&
@@ -322,13 +326,15 @@ class QPDF::Writer @@ -322,13 +326,15 @@ class QPDF::Writer
322 size_t 326 size_t
323 tableSize() 327 tableSize()
324 { 328 {
325 - return pdf.tableSize(); 329 + return pdf.m->objects.tableSize();
326 } 330 }
327 331
328 QPDF& pdf; 332 QPDF& pdf;
  333 + QPDF::Doc::Linearization& lin;
  334 + QPDF::Doc::Objects& objects;
329 }; 335 };
330 336
331 -class QPDFWriter::Members: QPDF::Writer 337 +class QPDFWriter::Members: QPDF::Doc::Writer
332 { 338 {
333 friend class QPDFWriter; 339 friend class QPDFWriter;
334 340
@@ -343,7 +349,7 @@ class QPDFWriter::Members: QPDF::Writer @@ -343,7 +349,7 @@ class QPDFWriter::Members: QPDF::Writer
343 enum trailer_e { t_normal, t_lin_first, t_lin_second }; 349 enum trailer_e { t_normal, t_lin_first, t_lin_second };
344 350
345 Members(QPDFWriter& w, QPDF& pdf) : 351 Members(QPDFWriter& w, QPDF& pdf) :
346 - QPDF::Writer(pdf), 352 + QPDF::Doc::Writer(pdf),
347 w(w), 353 w(w),
348 root_og( 354 root_og(
349 pdf.getRoot().getObjGen().isIndirect() ? pdf.getRoot().getObjGen() : QPDFObjGen(-1, 0)), 355 pdf.getRoot().getObjGen().isIndirect() ? pdf.getRoot().getObjGen() : QPDFObjGen(-1, 0)),
@@ -504,7 +510,7 @@ class QPDFWriter::Members: QPDF::Writer @@ -504,7 +510,7 @@ class QPDFWriter::Members: QPDF::Writer
504 bool pclm{false}; 510 bool pclm{false};
505 qpdf_object_stream_e object_stream_mode{qpdf_o_preserve}; 511 qpdf_object_stream_e object_stream_mode{qpdf_o_preserve};
506 512
507 - std::unique_ptr<QPDF::EncryptionData> encryption; 513 + std::unique_ptr<QPDF::Doc::Encryption> encryption;
508 std::string encryption_key; 514 std::string encryption_key;
509 bool encrypt_use_aes{false}; 515 bool encrypt_use_aes{false};
510 516
@@ -829,7 +835,7 @@ QPDFWriter::setR2EncryptionParametersInsecure( @@ -829,7 +835,7 @@ QPDFWriter::setR2EncryptionParametersInsecure(
829 bool allow_extract, 835 bool allow_extract,
830 bool allow_annotate) 836 bool allow_annotate)
831 { 837 {
832 - m->encryption = std::make_unique<QPDF::EncryptionData>(1, 2, 5, true); 838 + m->encryption = std::make_unique<Encryption>(1, 2, 5, true);
833 if (!allow_print) { 839 if (!allow_print) {
834 m->encryption->setP(3, false); 840 m->encryption->setP(3, false);
835 } 841 }
@@ -857,7 +863,7 @@ QPDFWriter::setR3EncryptionParametersInsecure( @@ -857,7 +863,7 @@ QPDFWriter::setR3EncryptionParametersInsecure(
857 bool allow_modify_other, 863 bool allow_modify_other,
858 qpdf_r3_print_e print) 864 qpdf_r3_print_e print)
859 { 865 {
860 - m->encryption = std::make_unique<QPDF::EncryptionData>(2, 3, 16, true); 866 + m->encryption = std::make_unique<Encryption>(2, 3, 16, true);
861 m->interpretR3EncryptionParameters( 867 m->interpretR3EncryptionParameters(
862 allow_accessibility, 868 allow_accessibility,
863 allow_extract, 869 allow_extract,
@@ -884,7 +890,7 @@ QPDFWriter::setR4EncryptionParametersInsecure( @@ -884,7 +890,7 @@ QPDFWriter::setR4EncryptionParametersInsecure(
884 bool encrypt_metadata, 890 bool encrypt_metadata,
885 bool use_aes) 891 bool use_aes)
886 { 892 {
887 - m->encryption = std::make_unique<QPDF::EncryptionData>(4, 4, 16, encrypt_metadata); 893 + m->encryption = std::make_unique<Encryption>(4, 4, 16, encrypt_metadata);
888 m->encrypt_use_aes = use_aes; 894 m->encrypt_use_aes = use_aes;
889 m->interpretR3EncryptionParameters( 895 m->interpretR3EncryptionParameters(
890 allow_accessibility, 896 allow_accessibility,
@@ -911,7 +917,7 @@ QPDFWriter::setR5EncryptionParameters( @@ -911,7 +917,7 @@ QPDFWriter::setR5EncryptionParameters(
911 qpdf_r3_print_e print, 917 qpdf_r3_print_e print,
912 bool encrypt_metadata) 918 bool encrypt_metadata)
913 { 919 {
914 - m->encryption = std::make_unique<QPDF::EncryptionData>(5, 5, 32, encrypt_metadata); 920 + m->encryption = std::make_unique<Encryption>(5, 5, 32, encrypt_metadata);
915 m->encrypt_use_aes = true; 921 m->encrypt_use_aes = true;
916 m->interpretR3EncryptionParameters( 922 m->interpretR3EncryptionParameters(
917 allow_accessibility, 923 allow_accessibility,
@@ -938,7 +944,7 @@ QPDFWriter::setR6EncryptionParameters( @@ -938,7 +944,7 @@ QPDFWriter::setR6EncryptionParameters(
938 qpdf_r3_print_e print, 944 qpdf_r3_print_e print,
939 bool encrypt_metadata) 945 bool encrypt_metadata)
940 { 946 {
941 - m->encryption = std::make_unique<QPDF::EncryptionData>(5, 6, 32, encrypt_metadata); 947 + m->encryption = std::make_unique<Encryption>(5, 6, 32, encrypt_metadata);
942 m->interpretR3EncryptionParameters( 948 m->interpretR3EncryptionParameters(
943 allow_accessibility, 949 allow_accessibility,
944 allow_extract, 950 allow_extract,
@@ -1094,7 +1100,7 @@ QPDFWriter::Members::copyEncryptionParameters(QPDF&amp; qpdf) @@ -1094,7 +1100,7 @@ QPDFWriter::Members::copyEncryptionParameters(QPDF&amp; qpdf)
1094 QTC::TC("qpdf", "QPDFWriter copy encrypt metadata", encrypt_metadata ? 0 : 1); 1100 QTC::TC("qpdf", "QPDFWriter copy encrypt metadata", encrypt_metadata ? 0 : 1);
1095 QTC::TC("qpdf", "QPDFWriter copy use_aes", encrypt_use_aes ? 0 : 1); 1101 QTC::TC("qpdf", "QPDFWriter copy use_aes", encrypt_use_aes ? 0 : 1);
1096 1102
1097 - encryption = std::make_unique<QPDF::EncryptionData>( 1103 + encryption = std::make_unique<Encryption>(
1098 V, 1104 V,
1099 encrypt.getKey("/R").getIntValueAsInt(), 1105 encrypt.getKey("/R").getIntValueAsInt(),
1100 key_len, 1106 key_len,
libqpdf/QPDF_Stream.cc
@@ -26,6 +26,27 @@ @@ -26,6 +26,27 @@
26 using namespace std::literals; 26 using namespace std::literals;
27 using namespace qpdf; 27 using namespace qpdf;
28 28
  29 +// Pipe class is restricted to QPDF_Stream.
  30 +class QPDF::Doc::Streams
  31 +{
  32 + public:
  33 + static bool
  34 + pipeStreamData(
  35 + QPDF* qpdf,
  36 + QPDFObjGen og,
  37 + qpdf_offset_t offset,
  38 + size_t length,
  39 + QPDFObjectHandle dict,
  40 + bool is_root_metadata,
  41 + Pipeline* pipeline,
  42 + bool suppress_warnings,
  43 + bool will_retry)
  44 + {
  45 + return qpdf->pipeStreamData(
  46 + og, offset, length, dict, is_root_metadata, pipeline, suppress_warnings, will_retry);
  47 + }
  48 +};
  49 +
29 namespace 50 namespace
30 { 51 {
31 class SF_Crypt final: public QPDFStreamFilter 52 class SF_Crypt final: public QPDFStreamFilter
@@ -563,7 +584,7 @@ Stream::pipeStreamData( @@ -563,7 +584,7 @@ Stream::pipeStreamData(
563 throw std::logic_error("pipeStreamData called for stream with no data"); 584 throw std::logic_error("pipeStreamData called for stream with no data");
564 } 585 }
565 QTC::TC("qpdf", "QPDF_Stream pipe original stream data"); 586 QTC::TC("qpdf", "QPDF_Stream pipe original stream data");
566 - if (!QPDF::Pipe::pipeStreamData( 587 + if (!QPDF::Doc::Streams::pipeStreamData(
567 obj->getQPDF(), 588 obj->getQPDF(),
568 obj->getObjGen(), 589 obj->getObjGen(),
569 obj->getParsedOffset(), 590 obj->getParsedOffset(),
libqpdf/QPDF_encryption.cc
@@ -21,6 +21,8 @@ @@ -21,6 +21,8 @@
21 using namespace qpdf; 21 using namespace qpdf;
22 using namespace std::literals; 22 using namespace std::literals;
23 23
  24 +using Encryption = QPDF::Doc::Encryption;
  25 +
24 static std::string padding_string = 26 static std::string padding_string =
25 "\x28\xbf\x4e\x5e\x4e\x75\x8a\x41\x64\x00\x4e\x56\xff\xfa\x01\x08" 27 "\x28\xbf\x4e\x5e\x4e\x75\x8a\x41\x64\x00\x4e\x56\xff\xfa\x01\x08"
26 "\x2e\x2e\x00\xb6\xd0\x68\x3e\x80\x2f\x0c\xa9\xfe\x64\x53\x69\x7a"s; 28 "\x2e\x2e\x00\xb6\xd0\x68\x3e\x80\x2f\x0c\xa9\xfe\x64\x53\x69\x7a"s;
@@ -52,11 +54,104 @@ QPDF::EncryptionData::getLengthBytes() const @@ -52,11 +54,104 @@ QPDF::EncryptionData::getLengthBytes() const
52 int 54 int
53 QPDF::EncryptionData::getP() const 55 QPDF::EncryptionData::getP() const
54 { 56 {
  57 + return this->P;
  58 +}
  59 +
  60 +std::string const&
  61 +QPDF::EncryptionData::getO() const
  62 +{
  63 + return this->O;
  64 +}
  65 +
  66 +std::string const&
  67 +QPDF::EncryptionData::getU() const
  68 +{
  69 + return this->U;
  70 +}
  71 +
  72 +std::string const&
  73 +QPDF::EncryptionData::getOE() const
  74 +{
  75 + return this->OE;
  76 +}
  77 +
  78 +std::string const&
  79 +QPDF::EncryptionData::getUE() const
  80 +{
  81 + return this->UE;
  82 +}
  83 +
  84 +std::string const&
  85 +QPDF::EncryptionData::getPerms() const
  86 +{
  87 + return this->Perms;
  88 +}
  89 +
  90 +std::string const&
  91 +QPDF::EncryptionData::getId1() const
  92 +{
  93 + return this->id1;
  94 +}
  95 +
  96 +bool
  97 +QPDF::EncryptionData::getEncryptMetadata() const
  98 +{
  99 + return this->encrypt_metadata;
  100 +}
  101 +
  102 +void
  103 +QPDF::EncryptionData::setO(std::string const& O)
  104 +{
  105 + this->O = O;
  106 +}
  107 +
  108 +void
  109 +QPDF::EncryptionData::setU(std::string const& U)
  110 +{
  111 + this->U = U;
  112 +}
  113 +
  114 +void
  115 +QPDF::EncryptionData::setV5EncryptionParameters(
  116 + std::string const& O,
  117 + std::string const& OE,
  118 + std::string const& U,
  119 + std::string const& UE,
  120 + std::string const& Perms)
  121 +{
  122 + this->O = O;
  123 + this->OE = OE;
  124 + this->U = U;
  125 + this->UE = UE;
  126 + this->Perms = Perms;
  127 +}
  128 +
  129 +int
  130 +Encryption::getV() const
  131 +{
  132 + return this->V;
  133 +}
  134 +
  135 +int
  136 +Encryption::getR() const
  137 +{
  138 + return this->R;
  139 +}
  140 +
  141 +int
  142 +Encryption::getLengthBytes() const
  143 +{
  144 + return this->Length_bytes;
  145 +}
  146 +
  147 +int
  148 +Encryption::getP() const
  149 +{
55 return static_cast<int>(P.to_ulong()); 150 return static_cast<int>(P.to_ulong());
56 } 151 }
57 152
58 bool 153 bool
59 -QPDF::EncryptionData::getP(size_t bit) const 154 +Encryption::getP(size_t bit) const
60 { 155 {
61 qpdf_assert_debug(bit); 156 qpdf_assert_debug(bit);
62 return P.test(bit - 1); 157 return P.test(bit - 1);
@@ -70,80 +165,80 @@ QPDF::EncryptionParameters::P(size_t bit) const @@ -70,80 +165,80 @@ QPDF::EncryptionParameters::P(size_t bit) const
70 } 165 }
71 166
72 std::string const& 167 std::string const&
73 -QPDF::EncryptionData::getO() const 168 +Encryption::getO() const
74 { 169 {
75 return this->O; 170 return this->O;
76 } 171 }
77 172
78 std::string const& 173 std::string const&
79 -QPDF::EncryptionData::getU() const 174 +Encryption::getU() const
80 { 175 {
81 return this->U; 176 return this->U;
82 } 177 }
83 178
84 std::string const& 179 std::string const&
85 -QPDF::EncryptionData::getOE() const 180 +Encryption::getOE() const
86 { 181 {
87 return this->OE; 182 return this->OE;
88 } 183 }
89 184
90 std::string const& 185 std::string const&
91 -QPDF::EncryptionData::getUE() const 186 +Encryption::getUE() const
92 { 187 {
93 return this->UE; 188 return this->UE;
94 } 189 }
95 190
96 std::string const& 191 std::string const&
97 -QPDF::EncryptionData::getPerms() const 192 +Encryption::getPerms() const
98 { 193 {
99 return this->Perms; 194 return this->Perms;
100 } 195 }
101 196
102 std::string const& 197 std::string const&
103 -QPDF::EncryptionData::getId1() const 198 +Encryption::getId1() const
104 { 199 {
105 return this->id1; 200 return this->id1;
106 } 201 }
107 202
108 bool 203 bool
109 -QPDF::EncryptionData::getEncryptMetadata() const 204 +Encryption::getEncryptMetadata() const
110 { 205 {
111 return this->encrypt_metadata; 206 return this->encrypt_metadata;
112 } 207 }
113 208
114 void 209 void
115 -QPDF::EncryptionData::setO(std::string const& O) 210 +Encryption::setO(std::string const& O)
116 { 211 {
117 this->O = O; 212 this->O = O;
118 } 213 }
119 214
120 void 215 void
121 -QPDF::EncryptionData::setU(std::string const& U) 216 +Encryption::setU(std::string const& U)
122 { 217 {
123 this->U = U; 218 this->U = U;
124 } 219 }
125 220
126 void 221 void
127 -QPDF::EncryptionData::setP(size_t bit, bool val) 222 +Encryption::setP(size_t bit, bool val)
128 { 223 {
129 qpdf_assert_debug(bit); 224 qpdf_assert_debug(bit);
130 P.set(bit - 1, val); 225 P.set(bit - 1, val);
131 } 226 }
132 227
133 void 228 void
134 -QPDF::EncryptionData::setP(unsigned long val) 229 +Encryption::setP(unsigned long val)
135 { 230 {
136 P = std::bitset<32>(val); 231 P = std::bitset<32>(val);
137 } 232 }
138 233
139 void 234 void
140 -QPDF::EncryptionData::setId1(std::string const& val) 235 +Encryption::setId1(std::string const& val)
141 { 236 {
142 id1 = val; 237 id1 = val;
143 } 238 }
144 239
145 void 240 void
146 -QPDF::EncryptionData::setV5EncryptionParameters( 241 +Encryption::setV5EncryptionParameters(
147 std::string const& O, 242 std::string const& O,
148 std::string const& OE, 243 std::string const& OE,
149 std::string const& U, 244 std::string const& U,
@@ -246,7 +341,7 @@ process_with_aes( @@ -246,7 +341,7 @@ process_with_aes(
246 } 341 }
247 342
248 std::string 343 std::string
249 -QPDF::EncryptionData::hash_V5( 344 +Encryption::hash_V5(
250 std::string const& password, std::string const& salt, std::string const& udata) const 345 std::string const& password, std::string const& salt, std::string const& udata) const
251 { 346 {
252 Pl_SHA2 hash(256); 347 Pl_SHA2 hash(256);
@@ -358,13 +453,25 @@ QPDF::compute_data_key( @@ -358,13 +453,25 @@ QPDF::compute_data_key(
358 } 453 }
359 454
360 std::string 455 std::string
361 -QPDF::compute_encryption_key(std::string const& password, EncryptionData const& data)  
362 -{  
363 - return data.compute_encryption_key(password); 456 +QPDF::compute_encryption_key(std::string const& password, EncryptionData const& ed)
  457 +{
  458 + return Encryption(
  459 + ed.getV(),
  460 + ed.getR(),
  461 + ed.getLengthBytes(),
  462 + ed.getP(),
  463 + ed.getO(),
  464 + ed.getU(),
  465 + ed.getOE(),
  466 + ed.getUE(),
  467 + ed.getPerms(),
  468 + ed.getId1(),
  469 + ed.getEncryptMetadata())
  470 + .compute_encryption_key(password);
364 } 471 }
365 472
366 std::string 473 std::string
367 -QPDF::EncryptionData::compute_encryption_key(std::string const& password) const 474 +Encryption::compute_encryption_key(std::string const& password) const
368 { 475 {
369 if (getV() >= 5) { 476 if (getV() >= 5) {
370 // For V >= 5, the encryption key is generated and stored in the file, encrypted separately 477 // For V >= 5, the encryption key is generated and stored in the file, encrypted separately
@@ -378,7 +485,7 @@ QPDF::EncryptionData::compute_encryption_key(std::string const&amp; password) const @@ -378,7 +485,7 @@ QPDF::EncryptionData::compute_encryption_key(std::string const&amp; password) const
378 } 485 }
379 486
380 std::string 487 std::string
381 -QPDF::EncryptionData::compute_encryption_key_from_password(std::string const& password) const 488 +Encryption::compute_encryption_key_from_password(std::string const& password) const
382 { 489 {
383 // Algorithm 3.2 from the PDF 1.7 Reference Manual 490 // Algorithm 3.2 from the PDF 1.7 Reference Manual
384 491
@@ -405,7 +512,7 @@ QPDF::EncryptionData::compute_encryption_key_from_password(std::string const&amp; pa @@ -405,7 +512,7 @@ QPDF::EncryptionData::compute_encryption_key_from_password(std::string const&amp; pa
405 } 512 }
406 513
407 std::string 514 std::string
408 -QPDF::EncryptionData::compute_O_rc4_key( 515 +Encryption::compute_O_rc4_key(
409 std::string const& user_password, std::string const& owner_password) const 516 std::string const& user_password, std::string const& owner_password) const
410 { 517 {
411 if (getV() >= 5) { 518 if (getV() >= 5) {
@@ -418,7 +525,7 @@ QPDF::EncryptionData::compute_O_rc4_key( @@ -418,7 +525,7 @@ QPDF::EncryptionData::compute_O_rc4_key(
418 } 525 }
419 526
420 std::string 527 std::string
421 -QPDF::EncryptionData::compute_O_value( 528 +Encryption::compute_O_value(
422 std::string const& user_password, std::string const& owner_password) const 529 std::string const& user_password, std::string const& owner_password) const
423 { 530 {
424 // Algorithm 3.3 from the PDF 1.7 Reference Manual 531 // Algorithm 3.3 from the PDF 1.7 Reference Manual
@@ -431,7 +538,7 @@ QPDF::EncryptionData::compute_O_value( @@ -431,7 +538,7 @@ QPDF::EncryptionData::compute_O_value(
431 } 538 }
432 539
433 std::string 540 std::string
434 -QPDF::EncryptionData::compute_U_value_R2(std::string const& user_password) const 541 +Encryption::compute_U_value_R2(std::string const& user_password) const
435 { 542 {
436 // Algorithm 3.4 from the PDF 1.7 Reference Manual 543 // Algorithm 3.4 from the PDF 1.7 Reference Manual
437 544
@@ -443,7 +550,7 @@ QPDF::EncryptionData::compute_U_value_R2(std::string const&amp; user_password) const @@ -443,7 +550,7 @@ QPDF::EncryptionData::compute_U_value_R2(std::string const&amp; user_password) const
443 } 550 }
444 551
445 std::string 552 std::string
446 -QPDF::EncryptionData::compute_U_value_R3(std::string const& user_password) const 553 +Encryption::compute_U_value_R3(std::string const& user_password) const
447 { 554 {
448 // Algorithm 3.5 from the PDF 1.7 Reference Manual 555 // Algorithm 3.5 from the PDF 1.7 Reference Manual
449 556
@@ -460,7 +567,7 @@ QPDF::EncryptionData::compute_U_value_R3(std::string const&amp; user_password) const @@ -460,7 +567,7 @@ QPDF::EncryptionData::compute_U_value_R3(std::string const&amp; user_password) const
460 } 567 }
461 568
462 std::string 569 std::string
463 -QPDF::EncryptionData::compute_U_value(std::string const& user_password) const 570 +Encryption::compute_U_value(std::string const& user_password) const
464 { 571 {
465 if (getR() >= 3) { 572 if (getR() >= 3) {
466 return compute_U_value_R3(user_password); 573 return compute_U_value_R3(user_password);
@@ -470,7 +577,7 @@ QPDF::EncryptionData::compute_U_value(std::string const&amp; user_password) const @@ -470,7 +577,7 @@ QPDF::EncryptionData::compute_U_value(std::string const&amp; user_password) const
470 } 577 }
471 578
472 bool 579 bool
473 -QPDF::EncryptionData::check_user_password_V4(std::string const& user_password) const 580 +Encryption::check_user_password_V4(std::string const& user_password) const
474 { 581 {
475 // Algorithm 3.6 from the PDF 1.7 Reference Manual 582 // Algorithm 3.6 from the PDF 1.7 Reference Manual
476 583
@@ -480,7 +587,7 @@ QPDF::EncryptionData::check_user_password_V4(std::string const&amp; user_password) c @@ -480,7 +587,7 @@ QPDF::EncryptionData::check_user_password_V4(std::string const&amp; user_password) c
480 } 587 }
481 588
482 bool 589 bool
483 -QPDF::EncryptionData::check_user_password_V5(std::string const& user_password) const 590 +Encryption::check_user_password_V5(std::string const& user_password) const
484 { 591 {
485 // Algorithm 3.11 from the PDF 1.7 extension level 3 592 // Algorithm 3.11 from the PDF 1.7 extension level 3
486 593
@@ -491,7 +598,7 @@ QPDF::EncryptionData::check_user_password_V5(std::string const&amp; user_password) c @@ -491,7 +598,7 @@ QPDF::EncryptionData::check_user_password_V5(std::string const&amp; user_password) c
491 } 598 }
492 599
493 bool 600 bool
494 -QPDF::EncryptionData::check_user_password(std::string const& user_password) const 601 +Encryption::check_user_password(std::string const& user_password) const
495 { 602 {
496 if (getV() < 5) { 603 if (getV() < 5) {
497 return check_user_password_V4(user_password); 604 return check_user_password_V4(user_password);
@@ -501,7 +608,7 @@ QPDF::EncryptionData::check_user_password(std::string const&amp; user_password) cons @@ -501,7 +608,7 @@ QPDF::EncryptionData::check_user_password(std::string const&amp; user_password) cons
501 } 608 }
502 609
503 bool 610 bool
504 -QPDF::EncryptionData::check_owner_password_V4( 611 +Encryption::check_owner_password_V4(
505 std::string& user_password, std::string const& owner_password) const 612 std::string& user_password, std::string const& owner_password) const
506 { 613 {
507 // Algorithm 3.7 from the PDF 1.7 Reference Manual 614 // Algorithm 3.7 from the PDF 1.7 Reference Manual
@@ -518,7 +625,7 @@ QPDF::EncryptionData::check_owner_password_V4( @@ -518,7 +625,7 @@ QPDF::EncryptionData::check_owner_password_V4(
518 } 625 }
519 626
520 bool 627 bool
521 -QPDF::EncryptionData::check_owner_password_V5(std::string const& owner_password) const 628 +Encryption::check_owner_password_V5(std::string const& owner_password) const
522 { 629 {
523 // Algorithm 3.12 from the PDF 1.7 extension level 3 630 // Algorithm 3.12 from the PDF 1.7 extension level 3
524 631
@@ -529,7 +636,7 @@ QPDF::EncryptionData::check_owner_password_V5(std::string const&amp; owner_password) @@ -529,7 +636,7 @@ QPDF::EncryptionData::check_owner_password_V5(std::string const&amp; owner_password)
529 } 636 }
530 637
531 bool 638 bool
532 -QPDF::EncryptionData::check_owner_password( 639 +Encryption::check_owner_password(
533 std::string& user_password, std::string const& owner_password) const 640 std::string& user_password, std::string const& owner_password) const
534 { 641 {
535 if (getV() < 5) { 642 if (getV() < 5) {
@@ -540,7 +647,7 @@ QPDF::EncryptionData::check_owner_password( @@ -540,7 +647,7 @@ QPDF::EncryptionData::check_owner_password(
540 } 647 }
541 648
542 std::string 649 std::string
543 -QPDF::EncryptionData::recover_encryption_key_with_password(std::string const& password) const 650 +Encryption::recover_encryption_key_with_password(std::string const& password) const
544 { 651 {
545 // Disregard whether Perms is valid. 652 // Disregard whether Perms is valid.
546 bool disregard; 653 bool disregard;
@@ -548,7 +655,7 @@ QPDF::EncryptionData::recover_encryption_key_with_password(std::string const&amp; pa @@ -548,7 +655,7 @@ QPDF::EncryptionData::recover_encryption_key_with_password(std::string const&amp; pa
548 } 655 }
549 656
550 std::string 657 std::string
551 -QPDF::EncryptionData::compute_Perms_value_V5_clear() const 658 +Encryption::compute_Perms_value_V5_clear() const
552 { 659 {
553 // From algorithm 3.10 from the PDF 1.7 extension level 3 660 // From algorithm 3.10 from the PDF 1.7 extension level 3
554 std::string k = " \xff\xff\xff\xffTadb "; 661 std::string k = " \xff\xff\xff\xffTadb ";
@@ -565,7 +672,7 @@ QPDF::EncryptionData::compute_Perms_value_V5_clear() const @@ -565,7 +672,7 @@ QPDF::EncryptionData::compute_Perms_value_V5_clear() const
565 } 672 }
566 673
567 std::string 674 std::string
568 -QPDF::EncryptionData::recover_encryption_key_with_password( 675 +Encryption::recover_encryption_key_with_password(
569 std::string const& password, bool& perms_valid) const 676 std::string const& password, bool& perms_valid) const
570 { 677 {
571 // Algorithm 3.2a from the PDF 1.7 extension level 3 678 // Algorithm 3.2a from the PDF 1.7 extension level 3
@@ -795,7 +902,7 @@ QPDF::EncryptionParameters::initialize(QPDF&amp; qpdf) @@ -795,7 +902,7 @@ QPDF::EncryptionParameters::initialize(QPDF&amp; qpdf)
795 } 902 }
796 } 903 }
797 904
798 - EncryptionData data(V, R, Length / 8, p, O, U, OE, UE, Perms, id1, encrypt_metadata); 905 + Encryption data(V, R, Length / 8, p, O, U, OE, UE, Perms, id1, encrypt_metadata);
799 if (qm.provided_password_is_hex_key) { 906 if (qm.provided_password_is_hex_key) {
800 // ignore passwords in file 907 // ignore passwords in file
801 encryption_key = QUtil::hex_decode(provided_password); 908 encryption_key = QUtil::hex_decode(provided_password);
@@ -1023,14 +1130,14 @@ QPDF::compute_encryption_O_U( @@ -1023,14 +1130,14 @@ QPDF::compute_encryption_O_U(
1023 std::string& out_O, 1130 std::string& out_O,
1024 std::string& out_U) 1131 std::string& out_U)
1025 { 1132 {
1026 - EncryptionData data(V, R, key_len, P, "", "", "", "", "", id1, encrypt_metadata); 1133 + Encryption data(V, R, key_len, P, "", "", "", "", "", id1, encrypt_metadata);
1027 data.compute_encryption_O_U(user_password, owner_password); 1134 data.compute_encryption_O_U(user_password, owner_password);
1028 out_O = data.getO(); 1135 out_O = data.getO();
1029 out_U = data.getU(); 1136 out_U = data.getU();
1030 } 1137 }
1031 1138
1032 void 1139 void
1033 -QPDF::EncryptionData::compute_encryption_O_U(char const* user_password, char const* owner_password) 1140 +Encryption::compute_encryption_O_U(char const* user_password, char const* owner_password)
1034 { 1141 {
1035 if (V >= 5) { 1142 if (V >= 5) {
1036 throw std::logic_error("compute_encryption_O_U called for file with V >= 5"); 1143 throw std::logic_error("compute_encryption_O_U called for file with V >= 5");
@@ -1056,7 +1163,7 @@ QPDF::compute_encryption_parameters_V5( @@ -1056,7 +1163,7 @@ QPDF::compute_encryption_parameters_V5(
1056 std::string& out_UE, 1163 std::string& out_UE,
1057 std::string& out_Perms) 1164 std::string& out_Perms)
1058 { 1165 {
1059 - EncryptionData data(V, R, key_len, P, "", "", "", "", "", id1, encrypt_metadata); 1166 + Encryption data(V, R, key_len, P, "", "", "", "", "", id1, encrypt_metadata);
1060 encryption_key = data.compute_encryption_parameters_V5(user_password, owner_password); 1167 encryption_key = data.compute_encryption_parameters_V5(user_password, owner_password);
1061 1168
1062 out_O = data.getO(); 1169 out_O = data.getO();
@@ -1067,8 +1174,7 @@ QPDF::compute_encryption_parameters_V5( @@ -1067,8 +1174,7 @@ QPDF::compute_encryption_parameters_V5(
1067 } 1174 }
1068 1175
1069 std::string 1176 std::string
1070 -QPDF::EncryptionData::compute_encryption_parameters_V5(  
1071 - char const* user_password, char const* owner_password) 1177 +Encryption::compute_encryption_parameters_V5(char const* user_password, char const* owner_password)
1072 { 1178 {
1073 auto out_encryption_key = util::random_string(key_bytes); 1179 auto out_encryption_key = util::random_string(key_bytes);
1074 // Algorithm 8 from the PDF 2.0 1180 // Algorithm 8 from the PDF 2.0
@@ -1089,7 +1195,7 @@ QPDF::EncryptionData::compute_encryption_parameters_V5( @@ -1089,7 +1195,7 @@ QPDF::EncryptionData::compute_encryption_parameters_V5(
1089 } 1195 }
1090 1196
1091 std::string 1197 std::string
1092 -QPDF::EncryptionData::compute_parameters(char const* user_password, char const* owner_password) 1198 +Encryption::compute_parameters(char const* user_password, char const* owner_password)
1093 { 1199 {
1094 if (V < 5) { 1200 if (V < 5) {
1095 compute_encryption_O_U(user_password, owner_password); 1201 compute_encryption_O_U(user_password, owner_password);
libqpdf/QPDF_json.cc
@@ -277,6 +277,7 @@ class QPDF::JSONReactor: public JSON::Reactor @@ -277,6 +277,7 @@ class QPDF::JSONReactor: public JSON::Reactor
277 void replaceObject(QPDFObjectHandle&& replacement, JSON const& value); 277 void replaceObject(QPDFObjectHandle&& replacement, JSON const& value);
278 278
279 QPDF& pdf; 279 QPDF& pdf;
  280 + QPDF::Doc::Objects& objects = pdf.m->objects;
280 std::shared_ptr<InputSource> is; 281 std::shared_ptr<InputSource> is;
281 bool must_be_complete{true}; 282 bool must_be_complete{true};
282 std::shared_ptr<QPDFObject::Description> descr; 283 std::shared_ptr<QPDFObject::Description> descr;
@@ -541,7 +542,7 @@ QPDF::JSONReactor::dictionaryItem(std::string const&amp; key, JSON const&amp; value) @@ -541,7 +542,7 @@ QPDF::JSONReactor::dictionaryItem(std::string const&amp; key, JSON const&amp; value)
541 } else if (is_obj_key(key, obj, gen)) { 542 } else if (is_obj_key(key, obj, gen)) {
542 this->cur_object = key; 543 this->cur_object = key;
543 if (setNextStateIfDictionary(key, value, st_object_top)) { 544 if (setNextStateIfDictionary(key, value, st_object_top)) {
544 - next_obj = pdf.getObjectForJSON(obj, gen); 545 + next_obj = objects.getObjectForJSON(obj, gen);
545 } 546 }
546 } else { 547 } else {
547 QTC::TC("qpdf", "QPDF_json bad object key"); 548 QTC::TC("qpdf", "QPDF_json bad object key");
@@ -743,7 +744,7 @@ QPDF::JSONReactor::makeObject(JSON const&amp; value) @@ -743,7 +744,7 @@ QPDF::JSONReactor::makeObject(JSON const&amp; value)
743 int gen = 0; 744 int gen = 0;
744 std::string str; 745 std::string str;
745 if (is_indirect_object(str_v, obj, gen)) { 746 if (is_indirect_object(str_v, obj, gen)) {
746 - result = pdf.getObjectForJSON(obj, gen); 747 + result = objects.getObjectForJSON(obj, gen);
747 } else if (is_unicode_string(str_v, str)) { 748 } else if (is_unicode_string(str_v, str)) {
748 result = QPDFObjectHandle::newUnicodeString(str); 749 result = QPDFObjectHandle::newUnicodeString(str);
749 } else if (is_binary_string(str_v, str)) { 750 } else if (is_binary_string(str_v, str)) {
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 }
@@ -112,9 +114,9 @@ QPDF::isLinearized() @@ -112,9 +114,9 @@ QPDF::isLinearized()
112 // next iteration. 114 // next iteration.
113 m->file->seek(toO(pos), SEEK_SET); 115 m->file->seek(toO(pos), SEEK_SET);
114 116
115 - auto t1 = readToken(*m->file, 20);  
116 - if (!(t1.isInteger() && readToken(*m->file, 6).isInteger() &&  
117 - readToken(*m->file, 4).isWord("obj"))) { 117 + auto t1 = m->objects.readToken(*m->file, 20);
  118 + if (!(t1.isInteger() && m->objects.readToken(*m->file, 6).isInteger() &&
  119 + m->objects.readToken(*m->file, 4).isWord("obj"))) {
118 pos = buffer.find_first_not_of("0123456789"sv, pos); 120 pos = buffer.find_first_not_of("0123456789"sv, pos);
119 if (pos == std::string::npos) { 121 if (pos == std::string::npos) {
120 return false; 122 return false;
@@ -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 = 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_objects.cc
@@ -23,6 +23,8 @@ @@ -23,6 +23,8 @@
23 using namespace qpdf; 23 using namespace qpdf;
24 using namespace std::literals; 24 using namespace std::literals;
25 25
  26 +using Objects = QPDF::Doc::Objects;
  27 +
26 namespace 28 namespace
27 { 29 {
28 class InvalidInputSource: public InputSource 30 class InvalidInputSource: public InputSource
@@ -102,7 +104,8 @@ class QPDF::ResolveRecorder final @@ -102,7 +104,8 @@ class QPDF::ResolveRecorder final
102 bool 104 bool
103 QPDF::findStartxref() 105 QPDF::findStartxref()
104 { 106 {
105 - if (readToken(*m->file).isWord("startxref") && readToken(*m->file).isInteger()) { 107 + if (m->objects.readToken(*m->file).isWord("startxref") &&
  108 + m->objects.readToken(*m->file).isInteger()) {
106 // Position in front of offset token 109 // Position in front of offset token
107 m->file->seek(m->file->getLastOffset(), SEEK_SET); 110 m->file->seek(m->file->getLastOffset(), SEEK_SET);
108 return true; 111 return true;
@@ -111,17 +114,16 @@ QPDF::findStartxref() @@ -111,17 +114,16 @@ QPDF::findStartxref()
111 } 114 }
112 115
113 void 116 void
114 -QPDF::parse(char const* password) 117 +Objects::parse(char const* password)
115 { 118 {
116 if (password) { 119 if (password) {
117 m->encp->provided_password = password; 120 m->encp->provided_password = password;
118 } 121 }
119 122
120 // Find the header anywhere in the first 1024 bytes of the file. 123 // Find the header anywhere in the first 1024 bytes of the file.
121 - PatternFinder hf(*this, &QPDF::findHeader); 124 + PatternFinder hf(qpdf, &QPDF::findHeader);
122 if (!m->file->findFirst("%PDF-", 0, 1024, hf)) { 125 if (!m->file->findFirst("%PDF-", 0, 1024, hf)) {
123 - QTC::TC("qpdf", "QPDF not a pdf file");  
124 - warn(damagedPDF("", -1, "can't find PDF header")); 126 + qpdf.warn(qpdf.damagedPDF("", -1, "can't find PDF header"));
125 // QPDFWriter writes files that usually require at least version 1.2 for /FlateDecode 127 // QPDFWriter writes files that usually require at least version 1.2 for /FlateDecode
126 m->pdf_version = "1.2"; 128 m->pdf_version = "1.2";
127 } 129 }
@@ -137,7 +139,7 @@ QPDF::parse(char const* password) @@ -137,7 +139,7 @@ QPDF::parse(char const* password)
137 m->xref_table_max_id = static_cast<int>(m->xref_table_max_offset / 3); 139 m->xref_table_max_id = static_cast<int>(m->xref_table_max_offset / 3);
138 } 140 }
139 qpdf_offset_t start_offset = (end_offset > 1054 ? end_offset - 1054 : 0); 141 qpdf_offset_t start_offset = (end_offset > 1054 ? end_offset - 1054 : 0);
140 - PatternFinder sf(*this, &QPDF::findStartxref); 142 + PatternFinder sf(qpdf, &QPDF::findStartxref);
141 qpdf_offset_t xref_offset = 0; 143 qpdf_offset_t xref_offset = 0;
142 if (m->file->findLast("startxref", start_offset, 0, sf)) { 144 if (m->file->findLast("startxref", start_offset, 0, sf)) {
143 xref_offset = QUtil::string_to_ll(readToken(*m->file).getValue().c_str()); 145 xref_offset = QUtil::string_to_ll(readToken(*m->file).getValue().c_str());
@@ -145,35 +147,33 @@ QPDF::parse(char const* password) @@ -145,35 +147,33 @@ QPDF::parse(char const* password)
145 147
146 try { 148 try {
147 if (xref_offset == 0) { 149 if (xref_offset == 0) {
148 - QTC::TC("qpdf", "QPDF can't find startxref");  
149 - throw damagedPDF("", -1, "can't find startxref"); 150 + throw qpdf.damagedPDF("", -1, "can't find startxref");
150 } 151 }
151 try { 152 try {
152 read_xref(xref_offset); 153 read_xref(xref_offset);
153 } catch (QPDFExc&) { 154 } catch (QPDFExc&) {
154 throw; 155 throw;
155 } catch (std::exception& e) { 156 } catch (std::exception& e) {
156 - throw damagedPDF("", -1, std::string("error reading xref: ") + e.what()); 157 + throw qpdf.damagedPDF("", -1, std::string("error reading xref: ") + e.what());
157 } 158 }
158 } catch (QPDFExc& e) { 159 } catch (QPDFExc& e) {
159 if (m->attempt_recovery) { 160 if (m->attempt_recovery) {
160 reconstruct_xref(e, xref_offset > 0); 161 reconstruct_xref(e, xref_offset > 0);
161 - QTC::TC("qpdf", "QPDF reconstructed xref table");  
162 } else { 162 } else {
163 throw; 163 throw;
164 } 164 }
165 } 165 }
166 166
167 - initializeEncryption(); 167 + qpdf.initializeEncryption();
168 m->parsed = true; 168 m->parsed = true;
169 - if (!m->xref_table.empty() && !getRoot().getKey("/Pages").isDictionary()) { 169 + if (!m->xref_table.empty() && !qpdf.getRoot().getKey("/Pages").isDictionary()) {
170 // QPDFs created from JSON have an empty xref table and no root object yet. 170 // QPDFs created from JSON have an empty xref table and no root object yet.
171 - throw damagedPDF("", -1, "unable to find page tree"); 171 + throw qpdf.damagedPDF("", -1, "unable to find page tree");
172 } 172 }
173 } 173 }
174 174
175 void 175 void
176 -QPDF::inParse(bool v) 176 +Objects::inParse(bool v)
177 { 177 {
178 if (m->in_parse == v) { 178 if (m->in_parse == v) {
179 // This happens if QPDFParser::parse tries to resolve an indirect object while it is 179 // This happens if QPDFParser::parse tries to resolve an indirect object while it is
@@ -186,7 +186,7 @@ QPDF::inParse(bool v) @@ -186,7 +186,7 @@ QPDF::inParse(bool v)
186 } 186 }
187 187
188 void 188 void
189 -QPDF::setTrailer(QPDFObjectHandle obj) 189 +Objects::setTrailer(QPDFObjectHandle obj)
190 { 190 {
191 if (m->trailer) { 191 if (m->trailer) {
192 return; 192 return;
@@ -195,7 +195,7 @@ QPDF::setTrailer(QPDFObjectHandle obj) @@ -195,7 +195,7 @@ QPDF::setTrailer(QPDFObjectHandle obj)
195 } 195 }
196 196
197 void 197 void
198 -QPDF::reconstruct_xref(QPDFExc& e, bool found_startxref) 198 +Objects::reconstruct_xref(QPDFExc& e, bool found_startxref)
199 { 199 {
200 if (m->reconstructed_xref) { 200 if (m->reconstructed_xref) {
201 // Avoid xref reconstruction infinite loops. This is getting very hard to reproduce because 201 // Avoid xref reconstruction infinite loops. This is getting very hard to reproduce because
@@ -208,7 +208,8 @@ QPDF::reconstruct_xref(QPDFExc&amp; e, bool found_startxref) @@ -208,7 +208,8 @@ QPDF::reconstruct_xref(QPDFExc&amp; e, bool found_startxref)
208 const auto max_warnings = m->warnings.size() + 1000U; 208 const auto max_warnings = m->warnings.size() + 1000U;
209 auto check_warnings = [this, max_warnings]() { 209 auto check_warnings = [this, max_warnings]() {
210 if (m->warnings.size() > max_warnings) { 210 if (m->warnings.size() > max_warnings) {
211 - throw damagedPDF("", -1, "too many errors while reconstructing cross-reference table"); 211 + throw qpdf.damagedPDF(
  212 + "", -1, "too many errors while reconstructing cross-reference table");
212 } 213 }
213 }; 214 };
214 215
@@ -216,9 +217,9 @@ QPDF::reconstruct_xref(QPDFExc&amp; e, bool found_startxref) @@ -216,9 +217,9 @@ QPDF::reconstruct_xref(QPDFExc&amp; e, bool found_startxref)
216 // We may find more objects, which may contain dangling references. 217 // We may find more objects, which may contain dangling references.
217 m->fixed_dangling_refs = false; 218 m->fixed_dangling_refs = false;
218 219
219 - warn(damagedPDF("", -1, "file is damaged"));  
220 - warn(e);  
221 - warn(damagedPDF("", -1, "Attempting to reconstruct cross-reference table")); 220 + qpdf.warn(qpdf.damagedPDF("", -1, "file is damaged"));
  221 + qpdf.warn(e);
  222 + qpdf.warn(qpdf.damagedPDF("", -1, "Attempting to reconstruct cross-reference table"));
222 223
223 // Delete all references to type 1 (uncompressed) objects 224 // Delete all references to type 1 (uncompressed) objects
224 std::vector<QPDFObjGen> to_delete; 225 std::vector<QPDFObjGen> to_delete;
@@ -241,18 +242,18 @@ QPDF::reconstruct_xref(QPDFExc&amp; e, bool found_startxref) @@ -241,18 +242,18 @@ QPDF::reconstruct_xref(QPDFExc&amp; e, bool found_startxref)
241 // Don't allow very long tokens here during recovery. All the interesting tokens are covered. 242 // Don't allow very long tokens here during recovery. All the interesting tokens are covered.
242 static size_t const MAX_LEN = 10; 243 static size_t const MAX_LEN = 10;
243 while (m->file->tell() < eof) { 244 while (m->file->tell() < eof) {
244 - QPDFTokenizer::Token t1 = readToken(*m->file, MAX_LEN); 245 + QPDFTokenizer::Token t1 = m->objects.readToken(*m->file, MAX_LEN);
245 qpdf_offset_t token_start = m->file->tell() - toO(t1.getValue().length()); 246 qpdf_offset_t token_start = m->file->tell() - toO(t1.getValue().length());
246 if (t1.isInteger()) { 247 if (t1.isInteger()) {
247 auto pos = m->file->tell(); 248 auto pos = m->file->tell();
248 - auto t2 = readToken(*m->file, MAX_LEN);  
249 - if (t2.isInteger() && readToken(*m->file, MAX_LEN).isWord("obj")) { 249 + auto t2 = m->objects.readToken(*m->file, MAX_LEN);
  250 + if (t2.isInteger() && m->objects.readToken(*m->file, MAX_LEN).isWord("obj")) {
250 int obj = QUtil::string_to_int(t1.getValue().c_str()); 251 int obj = QUtil::string_to_int(t1.getValue().c_str());
251 int gen = QUtil::string_to_int(t2.getValue().c_str()); 252 int gen = QUtil::string_to_int(t2.getValue().c_str());
252 if (obj <= m->xref_table_max_id) { 253 if (obj <= m->xref_table_max_id) {
253 found_objects.emplace_back(obj, gen, token_start); 254 found_objects.emplace_back(obj, gen, token_start);
254 } else { 255 } else {
255 - warn(damagedPDF( 256 + qpdf.warn(qpdf.damagedPDF(
256 "", -1, "ignoring object with impossibly large id " + std::to_string(obj))); 257 "", -1, "ignoring object with impossibly large id " + std::to_string(obj)));
257 } 258 }
258 } 259 }
@@ -271,14 +272,15 @@ QPDF::reconstruct_xref(QPDFExc&amp; e, bool found_startxref) @@ -271,14 +272,15 @@ QPDF::reconstruct_xref(QPDFExc&amp; e, bool found_startxref)
271 auto xref_backup{m->xref_table}; 272 auto xref_backup{m->xref_table};
272 try { 273 try {
273 m->file->seek(startxrefs.back(), SEEK_SET); 274 m->file->seek(startxrefs.back(), SEEK_SET);
274 - if (auto offset = QUtil::string_to_ll(readToken(*m->file).getValue().data())) {  
275 - read_xref(offset); 275 + if (auto offset =
  276 + QUtil::string_to_ll(m->objects.readToken(*m->file).getValue().data())) {
  277 + m->objects.read_xref(offset);
276 278
277 - if (getRoot().getKey("/Pages").isDictionary()) { 279 + if (qpdf.getRoot().getKey("/Pages").isDictionary()) {
278 QTC::TC("qpdf", "QPDF startxref more than 1024 before end"); 280 QTC::TC("qpdf", "QPDF startxref more than 1024 before end");
279 - warn(damagedPDF( 281 + qpdf.warn(qpdf.damagedPDF(
280 "", -1, "startxref was more than 1024 bytes before end of file")); 282 "", -1, "startxref was more than 1024 bytes before end of file"));
281 - initializeEncryption(); 283 + qpdf.initializeEncryption();
282 m->parsed = true; 284 m->parsed = true;
283 m->reconstructed_xref = false; 285 m->reconstructed_xref = false;
284 return; 286 return;
@@ -311,7 +313,7 @@ QPDF::reconstruct_xref(QPDFExc&amp; e, bool found_startxref) @@ -311,7 +313,7 @@ QPDF::reconstruct_xref(QPDFExc&amp; e, bool found_startxref)
311 m->trailer = t; 313 m->trailer = t;
312 break; 314 break;
313 } 315 }
314 - warn(damagedPDF("trailer", *it, "recovered trailer has no /Root entry")); 316 + qpdf.warn(qpdf.damagedPDF("trailer", *it, "recovered trailer has no /Root entry"));
315 } 317 }
316 check_warnings(); 318 check_warnings();
317 } 319 }
@@ -325,7 +327,7 @@ QPDF::reconstruct_xref(QPDFExc&amp; e, bool found_startxref) @@ -325,7 +327,7 @@ QPDF::reconstruct_xref(QPDFExc&amp; e, bool found_startxref)
325 if (entry.getType() != 1) { 327 if (entry.getType() != 1) {
326 continue; 328 continue;
327 } 329 }
328 - auto oh = getObject(iter.first); 330 + auto oh = qpdf.getObject(iter.first);
329 try { 331 try {
330 if (!oh.isStreamOfType("/XRef")) { 332 if (!oh.isStreamOfType("/XRef")) {
331 continue; 333 continue;
@@ -345,7 +347,7 @@ QPDF::reconstruct_xref(QPDFExc&amp; e, bool found_startxref) @@ -345,7 +347,7 @@ QPDF::reconstruct_xref(QPDFExc&amp; e, bool found_startxref)
345 try { 347 try {
346 read_xref(max_offset, true); 348 read_xref(max_offset, true);
347 } catch (std::exception&) { 349 } catch (std::exception&) {
348 - warn(damagedPDF( 350 + qpdf.warn(qpdf.damagedPDF(
349 "", -1, "error decoding candidate xref stream while recovering damaged file")); 351 "", -1, "error decoding candidate xref stream while recovering damaged file"));
350 } 352 }
351 QTC::TC("qpdf", "QPDF recover xref stream"); 353 QTC::TC("qpdf", "QPDF recover xref stream");
@@ -366,7 +368,7 @@ QPDF::reconstruct_xref(QPDFExc&amp; e, bool found_startxref) @@ -366,7 +368,7 @@ QPDF::reconstruct_xref(QPDFExc&amp; e, bool found_startxref)
366 } 368 }
367 if (root) { 369 if (root) {
368 if (!m->trailer) { 370 if (!m->trailer) {
369 - warn(damagedPDF( 371 + qpdf.warn(qpdf.damagedPDF(
370 "", -1, "unable to find trailer dictionary while recovering damaged file")); 372 "", -1, "unable to find trailer dictionary while recovering damaged file"));
371 m->trailer = QPDFObjectHandle::newDictionary(); 373 m->trailer = QPDFObjectHandle::newDictionary();
372 } 374 }
@@ -379,21 +381,22 @@ QPDF::reconstruct_xref(QPDFExc&amp; e, bool found_startxref) @@ -379,21 +381,22 @@ QPDF::reconstruct_xref(QPDFExc&amp; e, bool found_startxref)
379 // could try to get the trailer from there. This may make it possible to recover files with 381 // could try to get the trailer from there. This may make it possible to recover files with
380 // bad startxref pointers even when they have object streams. 382 // bad startxref pointers even when they have object streams.
381 383
382 - throw damagedPDF("", -1, "unable to find trailer dictionary while recovering damaged file"); 384 + throw qpdf.damagedPDF(
  385 + "", -1, "unable to find trailer dictionary while recovering damaged file");
383 } 386 }
384 if (m->xref_table.empty()) { 387 if (m->xref_table.empty()) {
385 // We cannot check for an empty xref table in parse because empty tables are valid when 388 // We cannot check for an empty xref table in parse because empty tables are valid when
386 // creating QPDF objects from JSON. 389 // creating QPDF objects from JSON.
387 - throw damagedPDF("", -1, "unable to find objects while recovering damaged file"); 390 + throw qpdf.damagedPDF("", -1, "unable to find objects while recovering damaged file");
388 } 391 }
389 check_warnings(); 392 check_warnings();
390 if (!m->parsed) { 393 if (!m->parsed) {
391 m->parsed = true; 394 m->parsed = true;
392 - getAllPages(); 395 + qpdf.getAllPages();
393 check_warnings(); 396 check_warnings();
394 if (m->all_pages.empty()) { 397 if (m->all_pages.empty()) {
395 m->parsed = false; 398 m->parsed = false;
396 - throw damagedPDF("", -1, "unable to find any pages while recovering damaged file"); 399 + throw qpdf.damagedPDF("", -1, "unable to find any pages while recovering damaged file");
397 } 400 }
398 } 401 }
399 402
@@ -405,7 +408,7 @@ QPDF::reconstruct_xref(QPDFExc&amp; e, bool found_startxref) @@ -405,7 +408,7 @@ QPDF::reconstruct_xref(QPDFExc&amp; e, bool found_startxref)
405 } 408 }
406 409
407 void 410 void
408 -QPDF::read_xref(qpdf_offset_t xref_offset, bool in_stream_recovery) 411 +Objects::read_xref(qpdf_offset_t xref_offset, bool in_stream_recovery)
409 { 412 {
410 std::map<int, int> free_table; 413 std::map<int, int> free_table;
411 std::set<qpdf_offset_t> visited; 414 std::set<qpdf_offset_t> visited;
@@ -440,8 +443,7 @@ QPDF::read_xref(qpdf_offset_t xref_offset, bool in_stream_recovery) @@ -440,8 +443,7 @@ QPDF::read_xref(qpdf_offset_t xref_offset, bool in_stream_recovery)
440 // where it is terminated by arbitrary whitespace. 443 // where it is terminated by arbitrary whitespace.
441 if ((strncmp(buf, "xref", 4) == 0) && util::is_space(buf[4])) { 444 if ((strncmp(buf, "xref", 4) == 0) && util::is_space(buf[4])) {
442 if (skipped_space) { 445 if (skipped_space) {
443 - QTC::TC("qpdf", "QPDF xref skipped space");  
444 - warn(damagedPDF("", -1, "extraneous whitespace seen before xref")); 446 + qpdf.warn(qpdf.damagedPDF("", -1, "extraneous whitespace seen before xref"));
445 } 447 }
446 QTC::TC( 448 QTC::TC(
447 "qpdf", 449 "qpdf",
@@ -460,13 +462,12 @@ QPDF::read_xref(qpdf_offset_t xref_offset, bool in_stream_recovery) @@ -460,13 +462,12 @@ QPDF::read_xref(qpdf_offset_t xref_offset, bool in_stream_recovery)
460 xref_offset = read_xrefStream(xref_offset, in_stream_recovery); 462 xref_offset = read_xrefStream(xref_offset, in_stream_recovery);
461 } 463 }
462 if (visited.contains(xref_offset)) { 464 if (visited.contains(xref_offset)) {
463 - QTC::TC("qpdf", "QPDF xref loop");  
464 - throw damagedPDF("", -1, "loop detected following xref tables"); 465 + throw qpdf.damagedPDF("", -1, "loop detected following xref tables");
465 } 466 }
466 } 467 }
467 468
468 if (!m->trailer) { 469 if (!m->trailer) {
469 - throw damagedPDF("", -1, "unable to find trailer while reading xref"); 470 + throw qpdf.damagedPDF("", -1, "unable to find trailer while reading xref");
470 } 471 }
471 int size = m->trailer.getKey("/Size").getIntValueAsInt(); 472 int size = m->trailer.getKey("/Size").getIntValueAsInt();
472 int max_obj = 0; 473 int max_obj = 0;
@@ -477,8 +478,7 @@ QPDF::read_xref(qpdf_offset_t xref_offset, bool in_stream_recovery) @@ -477,8 +478,7 @@ QPDF::read_xref(qpdf_offset_t xref_offset, bool in_stream_recovery)
477 max_obj = std::max(max_obj, *(m->deleted_objects.rbegin())); 478 max_obj = std::max(max_obj, *(m->deleted_objects.rbegin()));
478 } 479 }
479 if ((size < 1) || (size - 1 != max_obj)) { 480 if ((size < 1) || (size - 1 != max_obj)) {
480 - QTC::TC("qpdf", "QPDF xref size mismatch");  
481 - warn(damagedPDF( 481 + qpdf.warn(qpdf.damagedPDF(
482 "", 482 "",
483 -1, 483 -1,
484 ("reported number of objects (" + std::to_string(size) + 484 ("reported number of objects (" + std::to_string(size) +
@@ -494,14 +494,14 @@ QPDF::read_xref(qpdf_offset_t xref_offset, bool in_stream_recovery) @@ -494,14 +494,14 @@ QPDF::read_xref(qpdf_offset_t xref_offset, bool in_stream_recovery)
494 for (auto const& item: m->xref_table) { 494 for (auto const& item: m->xref_table) {
495 auto id = item.first.getObj(); 495 auto id = item.first.getObj();
496 if (id == last_og.getObj() && id > 0) { 496 if (id == last_og.getObj() && id > 0) {
497 - removeObject(last_og); 497 + qpdf.removeObject(last_og);
498 } 498 }
499 last_og = item.first; 499 last_og = item.first;
500 } 500 }
501 } 501 }
502 502
503 bool 503 bool
504 -QPDF::parse_xrefFirst(std::string const& line, int& obj, int& num, int& bytes) 504 +Objects::parse_xrefFirst(std::string const& line, int& obj, int& num, int& bytes)
505 { 505 {
506 // is_space and is_digit both return false on '\0', so this will not overrun the null-terminated 506 // is_space and is_digit both return false on '\0', so this will not overrun the null-terminated
507 // buffer. 507 // buffer.
@@ -549,7 +549,7 @@ QPDF::parse_xrefFirst(std::string const&amp; line, int&amp; obj, int&amp; num, int&amp; bytes) @@ -549,7 +549,7 @@ QPDF::parse_xrefFirst(std::string const&amp; line, int&amp; obj, int&amp; num, int&amp; bytes)
549 } 549 }
550 550
551 bool 551 bool
552 -QPDF::read_bad_xrefEntry(qpdf_offset_t& f1, int& f2, char& type) 552 +Objects::read_bad_xrefEntry(qpdf_offset_t& f1, int& f2, char& type)
553 { 553 {
554 // Reposition after initial read attempt and reread. 554 // Reposition after initial read attempt and reread.
555 m->file->seek(m->file->getLastOffset(), SEEK_SET); 555 m->file->seek(m->file->getLastOffset(), SEEK_SET);
@@ -563,7 +563,6 @@ QPDF::read_bad_xrefEntry(qpdf_offset_t&amp; f1, int&amp; f2, char&amp; type) @@ -563,7 +563,6 @@ QPDF::read_bad_xrefEntry(qpdf_offset_t&amp; f1, int&amp; f2, char&amp; type)
563 bool invalid = false; 563 bool invalid = false;
564 while (util::is_space(*p)) { 564 while (util::is_space(*p)) {
565 ++p; 565 ++p;
566 - QTC::TC("qpdf", "QPDF ignore first space in xref entry");  
567 invalid = true; 566 invalid = true;
568 } 567 }
569 // Require digit 568 // Require digit
@@ -580,7 +579,6 @@ QPDF::read_bad_xrefEntry(qpdf_offset_t&amp; f1, int&amp; f2, char&amp; type) @@ -580,7 +579,6 @@ QPDF::read_bad_xrefEntry(qpdf_offset_t&amp; f1, int&amp; f2, char&amp; type)
580 return false; 579 return false;
581 } 580 }
582 if (util::is_space(*(p + 1))) { 581 if (util::is_space(*(p + 1))) {
583 - QTC::TC("qpdf", "QPDF ignore first extra space in xref entry");  
584 invalid = true; 582 invalid = true;
585 } 583 }
586 // Skip spaces 584 // Skip spaces
@@ -601,7 +599,6 @@ QPDF::read_bad_xrefEntry(qpdf_offset_t&amp; f1, int&amp; f2, char&amp; type) @@ -601,7 +599,6 @@ QPDF::read_bad_xrefEntry(qpdf_offset_t&amp; f1, int&amp; f2, char&amp; type)
601 return false; 599 return false;
602 } 600 }
603 if (util::is_space(*(p + 1))) { 601 if (util::is_space(*(p + 1))) {
604 - QTC::TC("qpdf", "QPDF ignore second extra space in xref entry");  
605 invalid = true; 602 invalid = true;
606 } 603 }
607 // Skip spaces 604 // Skip spaces
@@ -614,12 +611,11 @@ QPDF::read_bad_xrefEntry(qpdf_offset_t&amp; f1, int&amp; f2, char&amp; type) @@ -614,12 +611,11 @@ QPDF::read_bad_xrefEntry(qpdf_offset_t&amp; f1, int&amp; f2, char&amp; type)
614 return false; 611 return false;
615 } 612 }
616 if ((f1_str.length() != 10) || (f2_str.length() != 5)) { 613 if ((f1_str.length() != 10) || (f2_str.length() != 5)) {
617 - QTC::TC("qpdf", "QPDF ignore length error xref entry");  
618 invalid = true; 614 invalid = true;
619 } 615 }
620 616
621 if (invalid) { 617 if (invalid) {
622 - warn(damagedPDF("xref table", "accepting invalid xref table entry")); 618 + qpdf.warn(qpdf.damagedPDF("xref table", "accepting invalid xref table entry"));
623 } 619 }
624 620
625 f1 = QUtil::string_to_ll(f1_str.c_str()); 621 f1 = QUtil::string_to_ll(f1_str.c_str());
@@ -631,7 +627,7 @@ QPDF::read_bad_xrefEntry(qpdf_offset_t&amp; f1, int&amp; f2, char&amp; type) @@ -631,7 +627,7 @@ QPDF::read_bad_xrefEntry(qpdf_offset_t&amp; f1, int&amp; f2, char&amp; type)
631 // Optimistically read and parse xref entry. If entry is bad, call read_bad_xrefEntry and return 627 // Optimistically read and parse xref entry. If entry is bad, call read_bad_xrefEntry and return
632 // result. 628 // result.
633 bool 629 bool
634 -QPDF::read_xrefEntry(qpdf_offset_t& f1, int& f2, char& type) 630 +Objects::read_xrefEntry(qpdf_offset_t& f1, int& f2, char& type)
635 { 631 {
636 std::array<char, 21> line; 632 std::array<char, 21> line;
637 if (m->file->read(line.data(), 20) != 20) { 633 if (m->file->read(line.data(), 20) != 20) {
@@ -685,7 +681,7 @@ QPDF::read_xrefEntry(qpdf_offset_t&amp; f1, int&amp; f2, char&amp; type) @@ -685,7 +681,7 @@ QPDF::read_xrefEntry(qpdf_offset_t&amp; f1, int&amp; f2, char&amp; type)
685 681
686 // Read a single cross-reference table section and associated trailer. 682 // Read a single cross-reference table section and associated trailer.
687 qpdf_offset_t 683 qpdf_offset_t
688 -QPDF::read_xrefTable(qpdf_offset_t xref_offset) 684 +Objects::read_xrefTable(qpdf_offset_t xref_offset)
689 { 685 {
690 m->file->seek(xref_offset, SEEK_SET); 686 m->file->seek(xref_offset, SEEK_SET);
691 std::string line; 687 std::string line;
@@ -696,8 +692,7 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset) @@ -696,8 +692,7 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset)
696 int num = 0; 692 int num = 0;
697 int bytes = 0; 693 int bytes = 0;
698 if (!parse_xrefFirst(line, obj, num, bytes)) { 694 if (!parse_xrefFirst(line, obj, num, bytes)) {
699 - QTC::TC("qpdf", "QPDF invalid xref");  
700 - throw damagedPDF("xref table", "xref syntax invalid"); 695 + throw qpdf.damagedPDF("xref table", "xref syntax invalid");
701 } 696 }
702 m->file->seek(m->file->getLastOffset() + bytes, SEEK_SET); 697 m->file->seek(m->file->getLastOffset() + bytes, SEEK_SET);
703 for (qpdf_offset_t i = obj; i - num < obj; ++i) { 698 for (qpdf_offset_t i = obj; i - num < obj; ++i) {
@@ -710,8 +705,7 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset) @@ -710,8 +705,7 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset)
710 int f2 = 0; 705 int f2 = 0;
711 char type = '\0'; 706 char type = '\0';
712 if (!read_xrefEntry(f1, f2, type)) { 707 if (!read_xrefEntry(f1, f2, type)) {
713 - QTC::TC("qpdf", "QPDF invalid xref entry");  
714 - throw damagedPDF( 708 + throw qpdf.damagedPDF(
715 "xref table", "invalid xref entry (obj=" + std::to_string(i) + ")"); 709 "xref table", "invalid xref entry (obj=" + std::to_string(i) + ")");
716 } 710 }
717 if (type == 'f') { 711 if (type == 'f') {
@@ -729,22 +723,19 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset) @@ -729,22 +723,19 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset)
729 } 723 }
730 724
731 // Set offset to previous xref table if any 725 // Set offset to previous xref table if any
732 - QPDFObjectHandle cur_trailer = readTrailer(); 726 + QPDFObjectHandle cur_trailer = m->objects.readTrailer();
733 if (!cur_trailer.isDictionary()) { 727 if (!cur_trailer.isDictionary()) {
734 - QTC::TC("qpdf", "QPDF missing trailer");  
735 - throw damagedPDF("", "expected trailer dictionary"); 728 + throw qpdf.damagedPDF("", "expected trailer dictionary");
736 } 729 }
737 730
738 if (!m->trailer) { 731 if (!m->trailer) {
739 setTrailer(cur_trailer); 732 setTrailer(cur_trailer);
740 733
741 if (!m->trailer.hasKey("/Size")) { 734 if (!m->trailer.hasKey("/Size")) {
742 - QTC::TC("qpdf", "QPDF trailer lacks size");  
743 - throw damagedPDF("trailer", "trailer dictionary lacks /Size key"); 735 + throw qpdf.damagedPDF("trailer", "trailer dictionary lacks /Size key");
744 } 736 }
745 if (!m->trailer.getKey("/Size").isInteger()) { 737 if (!m->trailer.getKey("/Size").isInteger()) {
746 - QTC::TC("qpdf", "QPDF trailer size not integer");  
747 - throw damagedPDF("trailer", "/Size key in trailer dictionary is not an integer"); 738 + throw qpdf.damagedPDF("trailer", "/Size key in trailer dictionary is not an integer");
748 } 739 }
749 } 740 }
750 741
@@ -757,17 +748,15 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset) @@ -757,17 +748,15 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset)
757 // /Prev key instead of the xref stream's. 748 // /Prev key instead of the xref stream's.
758 (void)read_xrefStream(cur_trailer.getKey("/XRefStm").getIntValue()); 749 (void)read_xrefStream(cur_trailer.getKey("/XRefStm").getIntValue());
759 } else { 750 } else {
760 - throw damagedPDF("xref stream", xref_offset, "invalid /XRefStm"); 751 + throw qpdf.damagedPDF("xref stream", xref_offset, "invalid /XRefStm");
761 } 752 }
762 } 753 }
763 } 754 }
764 755
765 if (cur_trailer.hasKey("/Prev")) { 756 if (cur_trailer.hasKey("/Prev")) {
766 if (!cur_trailer.getKey("/Prev").isInteger()) { 757 if (!cur_trailer.getKey("/Prev").isInteger()) {
767 - QTC::TC("qpdf", "QPDF trailer prev not integer");  
768 - throw damagedPDF("trailer", "/Prev key in trailer dictionary is not an integer"); 758 + throw qpdf.damagedPDF("trailer", "/Prev key in trailer dictionary is not an integer");
769 } 759 }
770 - QTC::TC("qpdf", "QPDF prev key in trailer dictionary");  
771 return cur_trailer.getKey("/Prev").getIntValue(); 760 return cur_trailer.getKey("/Prev").getIntValue();
772 } 761 }
773 762
@@ -776,7 +765,7 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset) @@ -776,7 +765,7 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset)
776 765
777 // Read a single cross-reference stream. 766 // Read a single cross-reference stream.
778 qpdf_offset_t 767 qpdf_offset_t
779 -QPDF::read_xrefStream(qpdf_offset_t xref_offset, bool in_stream_recovery) 768 +Objects::read_xrefStream(qpdf_offset_t xref_offset, bool in_stream_recovery)
780 { 769 {
781 if (!m->ignore_xref_streams) { 770 if (!m->ignore_xref_streams) {
782 QPDFObjectHandle xref_obj; 771 QPDFObjectHandle xref_obj;
@@ -788,19 +777,17 @@ QPDF::read_xrefStream(qpdf_offset_t xref_offset, bool in_stream_recovery) @@ -788,19 +777,17 @@ QPDF::read_xrefStream(qpdf_offset_t xref_offset, bool in_stream_recovery)
788 } 777 }
789 m->in_read_xref_stream = false; 778 m->in_read_xref_stream = false;
790 if (xref_obj.isStreamOfType("/XRef")) { 779 if (xref_obj.isStreamOfType("/XRef")) {
791 - QTC::TC("qpdf", "QPDF found xref stream");  
792 return processXRefStream(xref_offset, xref_obj, in_stream_recovery); 780 return processXRefStream(xref_offset, xref_obj, in_stream_recovery);
793 } 781 }
794 } 782 }
795 783
796 - QTC::TC("qpdf", "QPDF can't find xref");  
797 - throw damagedPDF("", xref_offset, "xref not found"); 784 + throw qpdf.damagedPDF("", xref_offset, "xref not found");
798 return 0; // unreachable 785 return 0; // unreachable
799 } 786 }
800 787
801 // Return the entry size of the xref stream and the processed W array. 788 // Return the entry size of the xref stream and the processed W array.
802 std::pair<int, std::array<int, 3>> 789 std::pair<int, std::array<int, 3>>
803 -QPDF::processXRefW(QPDFObjectHandle& dict, std::function<QPDFExc(std::string_view)> damaged) 790 +Objects::processXRefW(QPDFObjectHandle& dict, std::function<QPDFExc(std::string_view)> damaged)
804 { 791 {
805 auto W_obj = dict.getKey("/W"); 792 auto W_obj = dict.getKey("/W");
806 if (!(W_obj.size() >= 3 && W_obj.getArrayItem(0).isInteger() && 793 if (!(W_obj.size() >= 3 && W_obj.getArrayItem(0).isInteger() &&
@@ -830,7 +817,7 @@ QPDF::processXRefW(QPDFObjectHandle&amp; dict, std::function&lt;QPDFExc(std::string_vie @@ -830,7 +817,7 @@ QPDF::processXRefW(QPDFObjectHandle&amp; dict, std::function&lt;QPDFExc(std::string_vie
830 817
831 // Validate Size key and return the maximum number of entries that the xref stream can contain. 818 // Validate Size key and return the maximum number of entries that the xref stream can contain.
832 int 819 int
833 -QPDF::processXRefSize( 820 +Objects::processXRefSize(
834 QPDFObjectHandle& dict, int entry_size, std::function<QPDFExc(std::string_view)> damaged) 821 QPDFObjectHandle& dict, int entry_size, std::function<QPDFExc(std::string_view)> damaged)
835 { 822 {
836 // Number of entries is limited by the highest possible object id and stream size. 823 // Number of entries is limited by the highest possible object id and stream size.
@@ -854,7 +841,7 @@ QPDF::processXRefSize( @@ -854,7 +841,7 @@ QPDF::processXRefSize(
854 841
855 // Return the number of entries of the xref stream and the processed Index array. 842 // Return the number of entries of the xref stream and the processed Index array.
856 std::pair<int, std::vector<std::pair<int, int>>> 843 std::pair<int, std::vector<std::pair<int, int>>>
857 -QPDF::processXRefIndex( 844 +Objects::processXRefIndex(
858 QPDFObjectHandle& dict, int max_num_entries, std::function<QPDFExc(std::string_view)> damaged) 845 QPDFObjectHandle& dict, int max_num_entries, std::function<QPDFExc(std::string_view)> damaged)
859 { 846 {
860 auto size = dict.getKey("/Size").getIntValueAsInt(); 847 auto size = dict.getKey("/Size").getIntValueAsInt();
@@ -921,11 +908,11 @@ QPDF::processXRefIndex( @@ -921,11 +908,11 @@ QPDF::processXRefIndex(
921 } 908 }
922 909
923 qpdf_offset_t 910 qpdf_offset_t
924 -QPDF::processXRefStream( 911 +Objects::processXRefStream(
925 qpdf_offset_t xref_offset, QPDFObjectHandle& xref_obj, bool in_stream_recovery) 912 qpdf_offset_t xref_offset, QPDFObjectHandle& xref_obj, bool in_stream_recovery)
926 { 913 {
927 auto damaged = [this, xref_offset](std::string_view msg) -> QPDFExc { 914 auto damaged = [this, xref_offset](std::string_view msg) -> QPDFExc {
928 - return damagedPDF("xref stream", xref_offset, msg.data()); 915 + return qpdf.damagedPDF("xref stream", xref_offset, msg.data());
929 }; 916 };
930 917
931 auto dict = xref_obj.getDict(); 918 auto dict = xref_obj.getDict();
@@ -945,7 +932,7 @@ QPDF::processXRefStream( @@ -945,7 +932,7 @@ QPDF::processXRefStream(
945 if (expected_size > actual_size) { 932 if (expected_size > actual_size) {
946 throw x; 933 throw x;
947 } else { 934 } else {
948 - warn(x); 935 + qpdf.warn(x);
949 } 936 }
950 } 937 }
951 938
@@ -960,7 +947,6 @@ QPDF::processXRefStream( @@ -960,7 +947,6 @@ QPDF::processXRefStream(
960 // Read this entry 947 // Read this entry
961 std::array<qpdf_offset_t, 3> fields{}; 948 std::array<qpdf_offset_t, 3> fields{};
962 if (W[0] == 0) { 949 if (W[0] == 0) {
963 - QTC::TC("qpdf", "QPDF default for xref stream field 0");  
964 fields[0] = 1; 950 fields[0] = 1;
965 } 951 }
966 for (size_t j = 0; j < 3; ++j) { 952 for (size_t j = 0; j < 3; ++j) {
@@ -1006,10 +992,9 @@ QPDF::processXRefStream( @@ -1006,10 +992,9 @@ QPDF::processXRefStream(
1006 992
1007 if (dict.hasKey("/Prev")) { 993 if (dict.hasKey("/Prev")) {
1008 if (!dict.getKey("/Prev").isInteger()) { 994 if (!dict.getKey("/Prev").isInteger()) {
1009 - throw damagedPDF( 995 + throw qpdf.damagedPDF(
1010 "xref stream", "/Prev key in xref stream dictionary is not an integer"); 996 "xref stream", "/Prev key in xref stream dictionary is not an integer");
1011 } 997 }
1012 - QTC::TC("qpdf", "QPDF prev key in xref stream dictionary");  
1013 return dict.getKey("/Prev").getIntValue(); 998 return dict.getKey("/Prev").getIntValue();
1014 } else { 999 } else {
1015 return 0; 1000 return 0;
@@ -1017,7 +1002,7 @@ QPDF::processXRefStream( @@ -1017,7 +1002,7 @@ QPDF::processXRefStream(
1017 } 1002 }
1018 1003
1019 void 1004 void
1020 -QPDF::insertXrefEntry(int obj, int f0, qpdf_offset_t f1, int f2) 1005 +Objects::insertXrefEntry(int obj, int f0, qpdf_offset_t f1, int f2)
1021 { 1006 {
1022 // Populate the xref table in such a way that the first reference to an object that we see, 1007 // Populate the xref table in such a way that the first reference to an object that we see,
1023 // which is the one in the latest xref table in which it appears, is the one that gets stored. 1008 // which is the one in the latest xref table in which it appears, is the one that gets stored.
@@ -1035,25 +1020,23 @@ QPDF::insertXrefEntry(int obj, int f0, qpdf_offset_t f1, int f2) @@ -1035,25 +1020,23 @@ QPDF::insertXrefEntry(int obj, int f0, qpdf_offset_t f1, int f2)
1035 // We are ignoring invalid objgens. Most will arrive here from xref reconstruction. There 1020 // We are ignoring invalid objgens. Most will arrive here from xref reconstruction. There
1036 // is probably no point having another warning but we could count invalid items in order to 1021 // is probably no point having another warning but we could count invalid items in order to
1037 // decide when to give up. 1022 // decide when to give up.
1038 - QTC::TC("qpdf", "QPDF xref overwrite invalid objgen");  
1039 // ignore impossibly large object ids or object ids > Size. 1023 // ignore impossibly large object ids or object ids > Size.
1040 return; 1024 return;
1041 } 1025 }
1042 1026
1043 if (m->deleted_objects.contains(obj)) { 1027 if (m->deleted_objects.contains(obj)) {
1044 - QTC::TC("qpdf", "QPDF xref deleted object");  
1045 return; 1028 return;
1046 } 1029 }
1047 1030
1048 if (f0 == 2) { 1031 if (f0 == 2) {
1049 if (f1 == obj) { 1032 if (f1 == obj) {
1050 - warn(  
1051 - damagedPDF("xref stream", "self-referential object stream " + std::to_string(obj))); 1033 + qpdf.warn(qpdf.damagedPDF(
  1034 + "xref stream", "self-referential object stream " + std::to_string(obj)));
1052 return; 1035 return;
1053 } 1036 }
1054 if (f1 > m->xref_table_max_id) { 1037 if (f1 > m->xref_table_max_id) {
1055 // ignore impossibly large object stream ids 1038 // ignore impossibly large object stream ids
1056 - warn(damagedPDF( 1039 + qpdf.warn(qpdf.damagedPDF(
1057 "xref stream", 1040 "xref stream",
1058 "object stream id " + std::to_string(f1) + " for object " + std::to_string(obj) + 1041 "object stream id " + std::to_string(f1) + " for object " + std::to_string(obj) +
1059 " is impossibly large")); 1042 " is impossibly large"));
@@ -1063,7 +1046,6 @@ QPDF::insertXrefEntry(int obj, int f0, qpdf_offset_t f1, int f2) @@ -1063,7 +1046,6 @@ QPDF::insertXrefEntry(int obj, int f0, qpdf_offset_t f1, int f2)
1063 1046
1064 auto [iter, created] = m->xref_table.try_emplace(QPDFObjGen(obj, (f0 == 2 ? 0 : f2))); 1047 auto [iter, created] = m->xref_table.try_emplace(QPDFObjGen(obj, (f0 == 2 ? 0 : f2)));
1065 if (!created) { 1048 if (!created) {
1066 - QTC::TC("qpdf", "QPDF xref reused object");  
1067 return; 1049 return;
1068 } 1050 }
1069 1051
@@ -1079,13 +1061,14 @@ QPDF::insertXrefEntry(int obj, int f0, qpdf_offset_t f1, int f2) @@ -1079,13 +1061,14 @@ QPDF::insertXrefEntry(int obj, int f0, qpdf_offset_t f1, int f2)
1079 break; 1061 break;
1080 1062
1081 default: 1063 default:
1082 - throw damagedPDF("xref stream", "unknown xref stream entry type " + std::to_string(f0)); 1064 + throw qpdf.damagedPDF(
  1065 + "xref stream", "unknown xref stream entry type " + std::to_string(f0));
1083 break; 1066 break;
1084 } 1067 }
1085 } 1068 }
1086 1069
1087 void 1070 void
1088 -QPDF::insertFreeXrefEntry(QPDFObjGen og) 1071 +Objects::insertFreeXrefEntry(QPDFObjGen og)
1089 { 1072 {
1090 if (!m->xref_table.contains(og)) { 1073 if (!m->xref_table.contains(og)) {
1091 m->deleted_objects.insert(og.getObj()); 1074 m->deleted_objects.insert(og.getObj());
@@ -1121,7 +1104,7 @@ QPDF::showXRefTable() @@ -1121,7 +1104,7 @@ QPDF::showXRefTable()
1121 // Resolve all objects in the xref table. If this triggers a xref table reconstruction abort and 1104 // Resolve all objects in the xref table. If this triggers a xref table reconstruction abort and
1122 // return false. Otherwise return true. 1105 // return false. Otherwise return true.
1123 bool 1106 bool
1124 -QPDF::resolveXRefTable() 1107 +Objects::resolveXRefTable()
1125 { 1108 {
1126 bool may_change = !m->reconstructed_xref; 1109 bool may_change = !m->reconstructed_xref;
1127 for (auto& iter: m->xref_table) { 1110 for (auto& iter: m->xref_table) {
@@ -1143,9 +1126,8 @@ QPDF::fixDanglingReferences(bool force) @@ -1143,9 +1126,8 @@ QPDF::fixDanglingReferences(bool force)
1143 if (m->fixed_dangling_refs) { 1126 if (m->fixed_dangling_refs) {
1144 return; 1127 return;
1145 } 1128 }
1146 - if (!resolveXRefTable()) {  
1147 - QTC::TC("qpdf", "QPDF fix dangling triggered xref reconstruction");  
1148 - resolveXRefTable(); 1129 + if (!m->objects.resolveXRefTable()) {
  1130 + m->objects.resolveXRefTable();
1149 } 1131 }
1150 m->fixed_dangling_refs = true; 1132 m->fixed_dangling_refs = true;
1151 } 1133 }
@@ -1171,13 +1153,13 @@ QPDF::getAllObjects() @@ -1171,13 +1153,13 @@ QPDF::getAllObjects()
1171 fixDanglingReferences(); 1153 fixDanglingReferences();
1172 std::vector<QPDFObjectHandle> result; 1154 std::vector<QPDFObjectHandle> result;
1173 for (auto const& iter: m->obj_cache) { 1155 for (auto const& iter: m->obj_cache) {
1174 - result.push_back(newIndirect(iter.first, iter.second.object)); 1156 + result.emplace_back(m->objects.newIndirect(iter.first, iter.second.object));
1175 } 1157 }
1176 return result; 1158 return result;
1177 } 1159 }
1178 1160
1179 void 1161 void
1180 -QPDF::setLastObjectDescription(std::string const& description, QPDFObjGen og) 1162 +Objects::setLastObjectDescription(std::string const& description, QPDFObjGen og)
1181 { 1163 {
1182 m->last_object_description.clear(); 1164 m->last_object_description.clear();
1183 if (!description.empty()) { 1165 if (!description.empty()) {
@@ -1192,17 +1174,17 @@ QPDF::setLastObjectDescription(std::string const&amp; description, QPDFObjGen og) @@ -1192,17 +1174,17 @@ QPDF::setLastObjectDescription(std::string const&amp; description, QPDFObjGen og)
1192 } 1174 }
1193 1175
1194 QPDFObjectHandle 1176 QPDFObjectHandle
1195 -QPDF::readTrailer() 1177 +Objects::readTrailer()
1196 { 1178 {
1197 qpdf_offset_t offset = m->file->tell(); 1179 qpdf_offset_t offset = m->file->tell();
1198 auto [object, empty] = 1180 auto [object, empty] =
1199 - QPDFParser::parse(*m->file, "trailer", m->tokenizer, nullptr, *this, m->reconstructed_xref); 1181 + QPDFParser::parse(*m->file, "trailer", m->tokenizer, nullptr, qpdf, m->reconstructed_xref);
1200 if (empty) { 1182 if (empty) {
1201 // Nothing in the PDF spec appears to allow empty objects, but they have been encountered in 1183 // Nothing in the PDF spec appears to allow empty objects, but they have been encountered in
1202 // actual PDF files and Adobe Reader appears to ignore them. 1184 // actual PDF files and Adobe Reader appears to ignore them.
1203 - warn(damagedPDF("trailer", "empty object treated as null"));  
1204 - } else if (object.isDictionary() && readToken(*m->file).isWord("stream")) {  
1205 - warn(damagedPDF("trailer", m->file->tell(), "stream keyword found in trailer")); 1185 + qpdf.warn(qpdf.damagedPDF("trailer", "empty object treated as null"));
  1186 + } else if (object.isDictionary() && m->objects.readToken(*m->file).isWord("stream")) {
  1187 + qpdf.warn(qpdf.damagedPDF("trailer", m->file->tell(), "stream keyword found in trailer"));
1206 } 1188 }
1207 // Override last_offset so that it points to the beginning of the object we just read 1189 // Override last_offset so that it points to the beginning of the object we just read
1208 m->file->setLastOffset(offset); 1190 m->file->setLastOffset(offset);
@@ -1210,25 +1192,26 @@ QPDF::readTrailer() @@ -1210,25 +1192,26 @@ QPDF::readTrailer()
1210 } 1192 }
1211 1193
1212 QPDFObjectHandle 1194 QPDFObjectHandle
1213 -QPDF::readObject(std::string const& description, QPDFObjGen og) 1195 +Objects::readObject(std::string const& description, QPDFObjGen og)
1214 { 1196 {
1215 setLastObjectDescription(description, og); 1197 setLastObjectDescription(description, og);
1216 qpdf_offset_t offset = m->file->tell(); 1198 qpdf_offset_t offset = m->file->tell();
1217 1199
1218 - StringDecrypter decrypter{this, og}; 1200 + StringDecrypter decrypter{&qpdf, og};
1219 StringDecrypter* decrypter_ptr = m->encp->encrypted ? &decrypter : nullptr; 1201 StringDecrypter* decrypter_ptr = m->encp->encrypted ? &decrypter : nullptr;
1220 auto [object, empty] = QPDFParser::parse( 1202 auto [object, empty] = QPDFParser::parse(
1221 *m->file, 1203 *m->file,
1222 m->last_object_description, 1204 m->last_object_description,
1223 m->tokenizer, 1205 m->tokenizer,
1224 decrypter_ptr, 1206 decrypter_ptr,
1225 - *this, 1207 + qpdf,
1226 m->reconstructed_xref || m->in_read_xref_stream); 1208 m->reconstructed_xref || m->in_read_xref_stream);
1227 ; 1209 ;
1228 if (empty) { 1210 if (empty) {
1229 // Nothing in the PDF spec appears to allow empty objects, but they have been encountered in 1211 // Nothing in the PDF spec appears to allow empty objects, but they have been encountered in
1230 // actual PDF files and Adobe Reader appears to ignore them. 1212 // actual PDF files and Adobe Reader appears to ignore them.
1231 - warn(damagedPDF(*m->file, m->file->getLastOffset(), "empty object treated as null")); 1213 + qpdf.warn(
  1214 + qpdf.damagedPDF(*m->file, m->file->getLastOffset(), "empty object treated as null"));
1232 return object; 1215 return object;
1233 } 1216 }
1234 auto token = readToken(*m->file); 1217 auto token = readToken(*m->file);
@@ -1237,15 +1220,14 @@ QPDF::readObject(std::string const&amp; description, QPDFObjGen og) @@ -1237,15 +1220,14 @@ QPDF::readObject(std::string const&amp; description, QPDFObjGen og)
1237 token = readToken(*m->file); 1220 token = readToken(*m->file);
1238 } 1221 }
1239 if (!token.isWord("endobj")) { 1222 if (!token.isWord("endobj")) {
1240 - QTC::TC("qpdf", "QPDF err expected endobj");  
1241 - warn(damagedPDF("expected endobj")); 1223 + qpdf.warn(qpdf.damagedPDF("expected endobj"));
1242 } 1224 }
1243 return object; 1225 return object;
1244 } 1226 }
1245 1227
1246 // After reading stream dictionary and stream keyword, read rest of stream. 1228 // After reading stream dictionary and stream keyword, read rest of stream.
1247 void 1229 void
1248 -QPDF::readStream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset) 1230 +Objects::readStream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset)
1249 { 1231 {
1250 validateStreamLineEnd(object, og, offset); 1232 validateStreamLineEnd(object, og, offset);
1251 1233
@@ -1259,9 +1241,9 @@ QPDF::readStream(QPDFObjectHandle&amp; object, QPDFObjGen og, qpdf_offset_t offset) @@ -1259,9 +1241,9 @@ QPDF::readStream(QPDFObjectHandle&amp; object, QPDFObjGen og, qpdf_offset_t offset)
1259 1241
1260 if (!length_obj.isInteger()) { 1242 if (!length_obj.isInteger()) {
1261 if (length_obj.null()) { 1243 if (length_obj.null()) {
1262 - throw damagedPDF(offset, "stream dictionary lacks /Length key"); 1244 + throw qpdf.damagedPDF(offset, "stream dictionary lacks /Length key");
1263 } 1245 }
1264 - throw damagedPDF(offset, "/Length key in stream dictionary is not an integer"); 1246 + throw qpdf.damagedPDF(offset, "/Length key in stream dictionary is not an integer");
1265 } 1247 }
1266 1248
1267 length = toS(length_obj.getUIntValue()); 1249 length = toS(length_obj.getUIntValue());
@@ -1269,21 +1251,21 @@ QPDF::readStream(QPDFObjectHandle&amp; object, QPDFObjGen og, qpdf_offset_t offset) @@ -1269,21 +1251,21 @@ QPDF::readStream(QPDFObjectHandle&amp; object, QPDFObjGen og, qpdf_offset_t offset)
1269 m->file->seek(stream_offset, SEEK_SET); 1251 m->file->seek(stream_offset, SEEK_SET);
1270 m->file->seek(toO(length), SEEK_CUR); 1252 m->file->seek(toO(length), SEEK_CUR);
1271 if (!readToken(*m->file).isWord("endstream")) { 1253 if (!readToken(*m->file).isWord("endstream")) {
1272 - throw damagedPDF("expected endstream"); 1254 + throw qpdf.damagedPDF("expected endstream");
1273 } 1255 }
1274 } catch (QPDFExc& e) { 1256 } catch (QPDFExc& e) {
1275 if (m->attempt_recovery) { 1257 if (m->attempt_recovery) {
1276 - warn(e); 1258 + qpdf.warn(e);
1277 length = recoverStreamLength(m->file, og, stream_offset); 1259 length = recoverStreamLength(m->file, og, stream_offset);
1278 } else { 1260 } else {
1279 throw; 1261 throw;
1280 } 1262 }
1281 } 1263 }
1282 - object = QPDFObjectHandle(qpdf::Stream(*this, og, object, stream_offset, length)); 1264 + object = QPDFObjectHandle(qpdf::Stream(qpdf, og, object, stream_offset, length));
1283 } 1265 }
1284 1266
1285 void 1267 void
1286 -QPDF::validateStreamLineEnd(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset) 1268 +Objects::validateStreamLineEnd(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset)
1287 { 1269 {
1288 // The PDF specification states that the word "stream" should be followed by either a carriage 1270 // The PDF specification states that the word "stream" should be followed by either a carriage
1289 // return and a newline or by a newline alone. It specifically disallowed following it by a 1271 // return and a newline or by a newline alone. It specifically disallowed following it by a
@@ -1301,7 +1283,6 @@ QPDF::validateStreamLineEnd(QPDFObjectHandle&amp; object, QPDFObjGen og, qpdf_offset @@ -1301,7 +1283,6 @@ QPDF::validateStreamLineEnd(QPDFObjectHandle&amp; object, QPDFObjGen og, qpdf_offset
1301 } 1283 }
1302 if (ch == '\n') { 1284 if (ch == '\n') {
1303 // ready to read stream data 1285 // ready to read stream data
1304 - QTC::TC("qpdf", "QPDF stream with NL only");  
1305 return; 1286 return;
1306 } 1287 }
1307 if (ch == '\r') { 1288 if (ch == '\r') {
@@ -1313,33 +1294,32 @@ QPDF::validateStreamLineEnd(QPDFObjectHandle&amp; object, QPDFObjGen og, qpdf_offset @@ -1313,33 +1294,32 @@ QPDF::validateStreamLineEnd(QPDFObjectHandle&amp; object, QPDFObjGen og, qpdf_offset
1313 } else { 1294 } else {
1314 // Treat the \r by itself as the whitespace after endstream and start reading 1295 // Treat the \r by itself as the whitespace after endstream and start reading
1315 // stream data in spite of not having seen a newline. 1296 // stream data in spite of not having seen a newline.
1316 - QTC::TC("qpdf", "QPDF stream with CR only");  
1317 m->file->unreadCh(ch); 1297 m->file->unreadCh(ch);
1318 - warn(damagedPDF( 1298 + qpdf.warn(qpdf.damagedPDF(
1319 m->file->tell(), "stream keyword followed by carriage return only")); 1299 m->file->tell(), "stream keyword followed by carriage return only"));
1320 } 1300 }
1321 } 1301 }
1322 return; 1302 return;
1323 } 1303 }
1324 if (!util::is_space(ch)) { 1304 if (!util::is_space(ch)) {
1325 - QTC::TC("qpdf", "QPDF stream without newline");  
1326 m->file->unreadCh(ch); 1305 m->file->unreadCh(ch);
1327 - warn(damagedPDF( 1306 + qpdf.warn(qpdf.damagedPDF(
1328 m->file->tell(), "stream keyword not followed by proper line terminator")); 1307 m->file->tell(), "stream keyword not followed by proper line terminator"));
1329 return; 1308 return;
1330 } 1309 }
1331 - warn(damagedPDF(m->file->tell(), "stream keyword followed by extraneous whitespace")); 1310 + qpdf.warn(
  1311 + qpdf.damagedPDF(m->file->tell(), "stream keyword followed by extraneous whitespace"));
1332 } 1312 }
1333 } 1313 }
1334 1314
1335 QPDFObjectHandle 1315 QPDFObjectHandle
1336 -QPDF::readObjectInStream(is::OffsetBuffer& input, int stream_id, int obj_id) 1316 +Objects::readObjectInStream(is::OffsetBuffer& input, int stream_id, int obj_id)
1337 { 1317 {
1338 - auto [object, empty] = QPDFParser::parse(input, stream_id, obj_id, m->tokenizer, *this); 1318 + auto [object, empty] = QPDFParser::parse(input, stream_id, obj_id, m->tokenizer, qpdf);
1339 if (empty) { 1319 if (empty) {
1340 // Nothing in the PDF spec appears to allow empty objects, but they have been encountered in 1320 // Nothing in the PDF spec appears to allow empty objects, but they have been encountered in
1341 // actual PDF files and Adobe Reader appears to ignore them. 1321 // actual PDF files and Adobe Reader appears to ignore them.
1342 - warn(QPDFExc( 1322 + qpdf.warn(QPDFExc(
1343 qpdf_e_damaged_pdf, 1323 qpdf_e_damaged_pdf,
1344 m->file->getName() + " object stream " + std::to_string(stream_id), 1324 m->file->getName() + " object stream " + std::to_string(stream_id),
1345 +"object " + std::to_string(obj_id) + " 0, offset " + 1325 +"object " + std::to_string(obj_id) + " 0, offset " +
@@ -1354,7 +1334,7 @@ bool @@ -1354,7 +1334,7 @@ bool
1354 QPDF::findEndstream() 1334 QPDF::findEndstream()
1355 { 1335 {
1356 // Find endstream or endobj. Position the input at that token. 1336 // Find endstream or endobj. Position the input at that token.
1357 - auto t = readToken(*m->file, 20); 1337 + auto t = m->objects.readToken(*m->file, 20);
1358 if (t.isWord("endobj") || t.isWord("endstream")) { 1338 if (t.isWord("endobj") || t.isWord("endstream")) {
1359 m->file->seek(m->file->getLastOffset(), SEEK_SET); 1339 m->file->seek(m->file->getLastOffset(), SEEK_SET);
1360 return true; 1340 return true;
@@ -1363,13 +1343,13 @@ QPDF::findEndstream() @@ -1363,13 +1343,13 @@ QPDF::findEndstream()
1363 } 1343 }
1364 1344
1365 size_t 1345 size_t
1366 -QPDF::recoverStreamLength( 1346 +Objects::recoverStreamLength(
1367 std::shared_ptr<InputSource> input, QPDFObjGen og, qpdf_offset_t stream_offset) 1347 std::shared_ptr<InputSource> input, QPDFObjGen og, qpdf_offset_t stream_offset)
1368 { 1348 {
1369 // Try to reconstruct stream length by looking for endstream or endobj 1349 // Try to reconstruct stream length by looking for endstream or endobj
1370 - warn(damagedPDF(*input, stream_offset, "attempting to recover stream length")); 1350 + qpdf.warn(qpdf.damagedPDF(*input, stream_offset, "attempting to recover stream length"));
1371 1351
1372 - PatternFinder ef(*this, &QPDF::findEndstream); 1352 + PatternFinder ef(qpdf, &QPDF::findEndstream);
1373 size_t length = 0; 1353 size_t length = 0;
1374 if (m->file->findFirst("end", stream_offset, 0, ef)) { 1354 if (m->file->findFirst("end", stream_offset, 0, ef)) {
1375 length = toS(m->file->tell() - stream_offset); 1355 length = toS(m->file->tell() - stream_offset);
@@ -1401,65 +1381,58 @@ QPDF::recoverStreamLength( @@ -1401,65 +1381,58 @@ QPDF::recoverStreamLength(
1401 // found endstream\nendobj within the space allowed for this object, so we're probably 1381 // found endstream\nendobj within the space allowed for this object, so we're probably
1402 // in good shape. 1382 // in good shape.
1403 } else { 1383 } else {
1404 - QTC::TC("qpdf", "QPDF found wrong endstream in recovery");  
1405 length = 0; 1384 length = 0;
1406 } 1385 }
1407 } 1386 }
1408 1387
1409 if (length == 0) { 1388 if (length == 0) {
1410 - warn(damagedPDF( 1389 + qpdf.warn(qpdf.damagedPDF(
1411 *input, stream_offset, "unable to recover stream data; treating stream as empty")); 1390 *input, stream_offset, "unable to recover stream data; treating stream as empty"));
1412 } else { 1391 } else {
1413 - warn(damagedPDF( 1392 + qpdf.warn(qpdf.damagedPDF(
1414 *input, stream_offset, "recovered stream length: " + std::to_string(length))); 1393 *input, stream_offset, "recovered stream length: " + std::to_string(length)));
1415 } 1394 }
1416 1395
1417 - QTC::TC("qpdf", "QPDF recovered stream length");  
1418 return length; 1396 return length;
1419 } 1397 }
1420 1398
1421 QPDFTokenizer::Token 1399 QPDFTokenizer::Token
1422 -QPDF::readToken(InputSource& input, size_t max_len) 1400 +Objects::readToken(InputSource& input, size_t max_len)
1423 { 1401 {
1424 return m->tokenizer.readToken(input, m->last_object_description, true, max_len); 1402 return m->tokenizer.readToken(input, m->last_object_description, true, max_len);
1425 } 1403 }
1426 1404
1427 QPDFObjGen 1405 QPDFObjGen
1428 -QPDF::read_object_start(qpdf_offset_t offset) 1406 +Objects::read_object_start(qpdf_offset_t offset)
1429 { 1407 {
1430 m->file->seek(offset, SEEK_SET); 1408 m->file->seek(offset, SEEK_SET);
1431 QPDFTokenizer::Token tobjid = readToken(*m->file); 1409 QPDFTokenizer::Token tobjid = readToken(*m->file);
1432 bool objidok = tobjid.isInteger(); 1410 bool objidok = tobjid.isInteger();
1433 - QTC::TC("qpdf", "QPDF check objid", objidok ? 1 : 0);  
1434 if (!objidok) { 1411 if (!objidok) {
1435 - QTC::TC("qpdf", "QPDF expected n n obj");  
1436 - throw damagedPDF(offset, "expected n n obj"); 1412 + throw qpdf.damagedPDF(offset, "expected n n obj");
1437 } 1413 }
1438 QPDFTokenizer::Token tgen = readToken(*m->file); 1414 QPDFTokenizer::Token tgen = readToken(*m->file);
1439 bool genok = tgen.isInteger(); 1415 bool genok = tgen.isInteger();
1440 - QTC::TC("qpdf", "QPDF check generation", genok ? 1 : 0);  
1441 if (!genok) { 1416 if (!genok) {
1442 - throw damagedPDF(offset, "expected n n obj"); 1417 + throw qpdf.damagedPDF(offset, "expected n n obj");
1443 } 1418 }
1444 QPDFTokenizer::Token tobj = readToken(*m->file); 1419 QPDFTokenizer::Token tobj = readToken(*m->file);
1445 1420
1446 bool objok = tobj.isWord("obj"); 1421 bool objok = tobj.isWord("obj");
1447 - QTC::TC("qpdf", "QPDF check obj", objok ? 1 : 0);  
1448 1422
1449 if (!objok) { 1423 if (!objok) {
1450 - throw damagedPDF(offset, "expected n n obj"); 1424 + throw qpdf.damagedPDF(offset, "expected n n obj");
1451 } 1425 }
1452 int objid = QUtil::string_to_int(tobjid.getValue().c_str()); 1426 int objid = QUtil::string_to_int(tobjid.getValue().c_str());
1453 int generation = QUtil::string_to_int(tgen.getValue().c_str()); 1427 int generation = QUtil::string_to_int(tgen.getValue().c_str());
1454 if (objid == 0) { 1428 if (objid == 0) {
1455 - QTC::TC("qpdf", "QPDF object id 0");  
1456 - throw damagedPDF(offset, "object with ID 0"); 1429 + throw qpdf.damagedPDF(offset, "object with ID 0");
1457 } 1430 }
1458 return {objid, generation}; 1431 return {objid, generation};
1459 } 1432 }
1460 1433
1461 void 1434 void
1462 -QPDF::readObjectAtOffset( 1435 +Objects::readObjectAtOffset(
1463 bool try_recovery, qpdf_offset_t offset, std::string const& description, QPDFObjGen exp_og) 1436 bool try_recovery, qpdf_offset_t offset, std::string const& description, QPDFObjGen exp_og)
1464 { 1437 {
1465 QPDFObjGen og; 1438 QPDFObjGen og;
@@ -1474,22 +1447,20 @@ QPDF::readObjectAtOffset( @@ -1474,22 +1447,20 @@ QPDF::readObjectAtOffset(
1474 // "0000000000 00000 n", which is not correct, but it won't hurt anything for us to ignore 1447 // "0000000000 00000 n", which is not correct, but it won't hurt anything for us to ignore
1475 // these. 1448 // these.
1476 if (offset == 0) { 1449 if (offset == 0) {
1477 - QTC::TC("qpdf", "QPDF bogus 0 offset", 0);  
1478 - warn(damagedPDF(-1, "object has offset 0")); 1450 + qpdf.warn(qpdf.damagedPDF(-1, "object has offset 0"));
1479 return; 1451 return;
1480 } 1452 }
1481 1453
1482 try { 1454 try {
1483 og = read_object_start(offset); 1455 og = read_object_start(offset);
1484 if (exp_og != og) { 1456 if (exp_og != og) {
1485 - QTC::TC("qpdf", "QPDF err wrong objid/generation");  
1486 - QPDFExc e = damagedPDF(offset, "expected " + exp_og.unparse(' ') + " obj"); 1457 + QPDFExc e = qpdf.damagedPDF(offset, "expected " + exp_og.unparse(' ') + " obj");
1487 if (try_recovery) { 1458 if (try_recovery) {
1488 // Will be retried below 1459 // Will be retried below
1489 throw e; 1460 throw e;
1490 } else { 1461 } else {
1491 // We can try reading the object anyway even if the ID doesn't match. 1462 // We can try reading the object anyway even if the ID doesn't match.
1492 - warn(e); 1463 + qpdf.warn(e);
1493 } 1464 }
1494 } 1465 }
1495 } catch (QPDFExc& e) { 1466 } catch (QPDFExc& e) {
@@ -1501,11 +1472,9 @@ QPDF::readObjectAtOffset( @@ -1501,11 +1472,9 @@ QPDF::readObjectAtOffset(
1501 if (m->xref_table.contains(exp_og) && m->xref_table[exp_og].getType() == 1) { 1472 if (m->xref_table.contains(exp_og) && m->xref_table[exp_og].getType() == 1) {
1502 qpdf_offset_t new_offset = m->xref_table[exp_og].getOffset(); 1473 qpdf_offset_t new_offset = m->xref_table[exp_og].getOffset();
1503 readObjectAtOffset(false, new_offset, description, exp_og); 1474 readObjectAtOffset(false, new_offset, description, exp_og);
1504 - QTC::TC("qpdf", "QPDF recovered in readObjectAtOffset");  
1505 return; 1475 return;
1506 } 1476 }
1507 - QTC::TC("qpdf", "QPDF object gone after xref reconstruction");  
1508 - warn(damagedPDF( 1477 + qpdf.warn(qpdf.damagedPDF(
1509 "", 1478 "",
1510 -1, 1479 -1,
1511 ("object " + exp_og.unparse(' ') + 1480 ("object " + exp_og.unparse(' ') +
@@ -1524,24 +1493,24 @@ QPDF::readObjectAtOffset( @@ -1524,24 +1493,24 @@ QPDF::readObjectAtOffset(
1524 while (true) { 1493 while (true) {
1525 char ch; 1494 char ch;
1526 if (!m->file->read(&ch, 1)) { 1495 if (!m->file->read(&ch, 1)) {
1527 - throw damagedPDF(m->file->tell(), "EOF after endobj"); 1496 + throw qpdf.damagedPDF(m->file->tell(), "EOF after endobj");
1528 } 1497 }
1529 if (!isspace(static_cast<unsigned char>(ch))) { 1498 if (!isspace(static_cast<unsigned char>(ch))) {
1530 m->file->seek(-1, SEEK_CUR); 1499 m->file->seek(-1, SEEK_CUR);
1531 break; 1500 break;
1532 } 1501 }
1533 } 1502 }
1534 - updateCache(og, oh.getObj(), end_before_space, m->file->tell()); 1503 + m->objects.updateCache(og, oh.getObj(), end_before_space, m->file->tell());
1535 } 1504 }
1536 1505
1537 QPDFObjectHandle 1506 QPDFObjectHandle
1538 -QPDF::readObjectAtOffset( 1507 +Objects::readObjectAtOffset(
1539 qpdf_offset_t offset, std::string const& description, bool skip_cache_if_in_xref) 1508 qpdf_offset_t offset, std::string const& description, bool skip_cache_if_in_xref)
1540 { 1509 {
1541 auto og = read_object_start(offset); 1510 auto og = read_object_start(offset);
1542 auto oh = readObject(description, og); 1511 auto oh = readObject(description, og);
1543 1512
1544 - if (!isUnresolved(og)) { 1513 + if (!m->objects.isUnresolved(og)) {
1545 return oh; 1514 return oh;
1546 } 1515 }
1547 1516
@@ -1583,20 +1552,20 @@ QPDF::readObjectAtOffset( @@ -1583,20 +1552,20 @@ QPDF::readObjectAtOffset(
1583 while (true) { 1552 while (true) {
1584 char ch; 1553 char ch;
1585 if (!m->file->read(&ch, 1)) { 1554 if (!m->file->read(&ch, 1)) {
1586 - throw damagedPDF(m->file->tell(), "EOF after endobj"); 1555 + throw qpdf.damagedPDF(m->file->tell(), "EOF after endobj");
1587 } 1556 }
1588 if (!isspace(static_cast<unsigned char>(ch))) { 1557 if (!isspace(static_cast<unsigned char>(ch))) {
1589 m->file->seek(-1, SEEK_CUR); 1558 m->file->seek(-1, SEEK_CUR);
1590 break; 1559 break;
1591 } 1560 }
1592 } 1561 }
1593 - updateCache(og, oh.getObj(), end_before_space, m->file->tell()); 1562 + m->objects.updateCache(og, oh.getObj(), end_before_space, m->file->tell());
1594 1563
1595 return oh; 1564 return oh;
1596 } 1565 }
1597 1566
1598 std::shared_ptr<QPDFObject> const& 1567 std::shared_ptr<QPDFObject> const&
1599 -QPDF::resolve(QPDFObjGen og) 1568 +Objects::resolve(QPDFObjGen og)
1600 { 1569 {
1601 if (!isUnresolved(og)) { 1570 if (!isUnresolved(og)) {
1602 return m->obj_cache[og].object; 1571 return m->obj_cache[og].object;
@@ -1605,12 +1574,11 @@ QPDF::resolve(QPDFObjGen og) @@ -1605,12 +1574,11 @@ QPDF::resolve(QPDFObjGen og)
1605 if (m->resolving.contains(og)) { 1574 if (m->resolving.contains(og)) {
1606 // This can happen if an object references itself directly or indirectly in some key that 1575 // This can happen if an object references itself directly or indirectly in some key that
1607 // has to be resolved during object parsing, such as stream length. 1576 // has to be resolved during object parsing, such as stream length.
1608 - QTC::TC("qpdf", "QPDF recursion loop in resolve");  
1609 - warn(damagedPDF("", "loop detected resolving object " + og.unparse(' '))); 1577 + qpdf.warn(qpdf.damagedPDF("", "loop detected resolving object " + og.unparse(' ')));
1610 updateCache(og, QPDFObject::create<QPDF_Null>(), -1, -1); 1578 updateCache(og, QPDFObject::create<QPDF_Null>(), -1, -1);
1611 return m->obj_cache[og].object; 1579 return m->obj_cache[og].object;
1612 } 1580 }
1613 - ResolveRecorder rr(*this, og); 1581 + ResolveRecorder rr(qpdf, og);
1614 1582
1615 if (m->xref_table.contains(og)) { 1583 if (m->xref_table.contains(og)) {
1616 QPDFXRefEntry const& entry = m->xref_table[og]; 1584 QPDFXRefEntry const& entry = m->xref_table[og];
@@ -1626,30 +1594,29 @@ QPDF::resolve(QPDFObjGen og) @@ -1626,30 +1594,29 @@ QPDF::resolve(QPDFObjGen og)
1626 break; 1594 break;
1627 1595
1628 default: 1596 default:
1629 - throw damagedPDF( 1597 + throw qpdf.damagedPDF(
1630 "", -1, ("object " + og.unparse('/') + " has unexpected xref entry type")); 1598 "", -1, ("object " + og.unparse('/') + " has unexpected xref entry type"));
1631 } 1599 }
1632 } catch (QPDFExc& e) { 1600 } catch (QPDFExc& e) {
1633 - warn(e); 1601 + qpdf.warn(e);
1634 } catch (std::exception& e) { 1602 } catch (std::exception& e) {
1635 - warn(damagedPDF( 1603 + qpdf.warn(qpdf.damagedPDF(
1636 "", -1, ("object " + og.unparse('/') + ": error reading object: " + e.what()))); 1604 "", -1, ("object " + og.unparse('/') + ": error reading object: " + e.what())));
1637 } 1605 }
1638 } 1606 }
1639 1607
1640 if (isUnresolved(og)) { 1608 if (isUnresolved(og)) {
1641 // PDF spec says unknown objects resolve to the null object. 1609 // PDF spec says unknown objects resolve to the null object.
1642 - QTC::TC("qpdf", "QPDF resolve failure to null");  
1643 updateCache(og, QPDFObject::create<QPDF_Null>(), -1, -1); 1610 updateCache(og, QPDFObject::create<QPDF_Null>(), -1, -1);
1644 } 1611 }
1645 1612
1646 auto& result(m->obj_cache[og].object); 1613 auto& result(m->obj_cache[og].object);
1647 - result->setDefaultDescription(this, og); 1614 + result->setDefaultDescription(&qpdf, og);
1648 return result; 1615 return result;
1649 } 1616 }
1650 1617
1651 void 1618 void
1652 -QPDF::resolveObjectsInStream(int obj_stream_number) 1619 +Objects::resolveObjectsInStream(int obj_stream_number)
1653 { 1620 {
1654 auto damaged = 1621 auto damaged =
1655 [this, obj_stream_number](int id, qpdf_offset_t offset, std::string const& msg) -> QPDFExc { 1622 [this, obj_stream_number](int id, qpdf_offset_t offset, std::string const& msg) -> QPDFExc {
@@ -1667,9 +1634,9 @@ QPDF::resolveObjectsInStream(int obj_stream_number) @@ -1667,9 +1634,9 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
1667 } 1634 }
1668 m->resolved_object_streams.insert(obj_stream_number); 1635 m->resolved_object_streams.insert(obj_stream_number);
1669 // Force resolution of object stream 1636 // Force resolution of object stream
1670 - auto obj_stream = getObject(obj_stream_number, 0).as_stream(); 1637 + auto obj_stream = qpdf.getObject(obj_stream_number, 0).as_stream();
1671 if (!obj_stream) { 1638 if (!obj_stream) {
1672 - throw damagedPDF( 1639 + throw qpdf.damagedPDF(
1673 "object " + std::to_string(obj_stream_number) + " 0", 1640 "object " + std::to_string(obj_stream_number) + " 0",
1674 "supposed object stream " + std::to_string(obj_stream_number) + " is not a stream"); 1641 "supposed object stream " + std::to_string(obj_stream_number) + " is not a stream");
1675 } 1642 }
@@ -1682,8 +1649,7 @@ QPDF::resolveObjectsInStream(int obj_stream_number) @@ -1682,8 +1649,7 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
1682 1649
1683 QPDFObjectHandle dict = obj_stream.getDict(); 1650 QPDFObjectHandle dict = obj_stream.getDict();
1684 if (!dict.isDictionaryOfType("/ObjStm")) { 1651 if (!dict.isDictionaryOfType("/ObjStm")) {
1685 - QTC::TC("qpdf", "QPDF ERR object stream with wrong type");  
1686 - warn(damagedPDF( 1652 + qpdf.warn(qpdf.damagedPDF(
1687 "object " + std::to_string(obj_stream_number) + " 0", 1653 "object " + std::to_string(obj_stream_number) + " 0",
1688 "supposed object stream " + std::to_string(obj_stream_number) + " has wrong type")); 1654 "supposed object stream " + std::to_string(obj_stream_number) + " has wrong type"));
1689 } 1655 }
@@ -1691,7 +1657,7 @@ QPDF::resolveObjectsInStream(int obj_stream_number) @@ -1691,7 +1657,7 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
1691 unsigned int n{0}; 1657 unsigned int n{0};
1692 int first{0}; 1658 int first{0};
1693 if (!(dict.getKey("/N").getValueAsUInt(n) && dict.getKey("/First").getValueAsInt(first))) { 1659 if (!(dict.getKey("/N").getValueAsUInt(n) && dict.getKey("/First").getValueAsInt(first))) {
1694 - throw damagedPDF( 1660 + throw qpdf.damagedPDF(
1695 "object " + std::to_string(obj_stream_number) + " 0", 1661 "object " + std::to_string(obj_stream_number) + " 0",
1696 "object stream " + std::to_string(obj_stream_number) + " has incorrect keys"); 1662 "object stream " + std::to_string(obj_stream_number) + " has incorrect keys");
1697 } 1663 }
@@ -1708,7 +1674,7 @@ QPDF::resolveObjectsInStream(int obj_stream_number) @@ -1708,7 +1674,7 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
1708 auto b_start = stream_data.data(); 1674 auto b_start = stream_data.data();
1709 1675
1710 if (first >= end_offset) { 1676 if (first >= end_offset) {
1711 - throw damagedPDF( 1677 + throw qpdf.damagedPDF(
1712 "object " + std::to_string(obj_stream_number) + " 0", 1678 "object " + std::to_string(obj_stream_number) + " 0",
1713 "object stream " + std::to_string(obj_stream_number) + " has invalid /First entry"); 1679 "object stream " + std::to_string(obj_stream_number) + " has invalid /First entry");
1714 } 1680 }
@@ -1728,20 +1694,17 @@ QPDF::resolveObjectsInStream(int obj_stream_number) @@ -1728,20 +1694,17 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
1728 long long offset = QUtil::string_to_int(toffset.getValue().c_str()); 1694 long long offset = QUtil::string_to_int(toffset.getValue().c_str());
1729 1695
1730 if (num == obj_stream_number) { 1696 if (num == obj_stream_number) {
1731 - QTC::TC("qpdf", "QPDF ignore self-referential object stream");  
1732 - warn(damaged(num, id_offset, "object stream claims to contain itself")); 1697 + qpdf.warn(damaged(num, id_offset, "object stream claims to contain itself"));
1733 continue; 1698 continue;
1734 } 1699 }
1735 1700
1736 if (num < 1) { 1701 if (num < 1) {
1737 - QTC::TC("qpdf", "QPDF object stream contains id < 1");  
1738 - warn(damaged(num, id_offset, "object id is invalid"s)); 1702 + qpdf.warn(damaged(num, id_offset, "object id is invalid"s));
1739 continue; 1703 continue;
1740 } 1704 }
1741 1705
1742 if (offset <= last_offset) { 1706 if (offset <= last_offset) {
1743 - QTC::TC("qpdf", "QPDF object stream offsets not increasing");  
1744 - warn(damaged( 1707 + qpdf.warn(damaged(
1745 num, 1708 num,
1746 input.getLastOffset(), 1709 input.getLastOffset(),
1747 "offset " + std::to_string(offset) + 1710 "offset " + std::to_string(offset) +
@@ -1755,7 +1718,7 @@ QPDF::resolveObjectsInStream(int obj_stream_number) @@ -1755,7 +1718,7 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
1755 } 1718 }
1756 1719
1757 if (first + offset >= end_offset) { 1720 if (first + offset >= end_offset) {
1758 - warn(damaged( 1721 + qpdf.warn(damaged(
1759 num, input.getLastOffset(), "offset " + std::to_string(offset) + " is too large")); 1722 num, input.getLastOffset(), "offset " + std::to_string(offset) + " is too large"));
1760 continue; 1723 continue;
1761 } 1724 }
@@ -1796,21 +1759,21 @@ QPDF::resolveObjectsInStream(int obj_stream_number) @@ -1796,21 +1759,21 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
1796 } 1759 }
1797 1760
1798 QPDFObjectHandle 1761 QPDFObjectHandle
1799 -QPDF::newIndirect(QPDFObjGen og, std::shared_ptr<QPDFObject> const& obj) 1762 +Objects::newIndirect(QPDFObjGen og, std::shared_ptr<QPDFObject> const& obj)
1800 { 1763 {
1801 - obj->setDefaultDescription(this, og); 1764 + obj->setDefaultDescription(&qpdf, og);
1802 return {obj}; 1765 return {obj};
1803 } 1766 }
1804 1767
1805 void 1768 void
1806 -QPDF::updateCache( 1769 +Objects::updateCache(
1807 QPDFObjGen og, 1770 QPDFObjGen og,
1808 std::shared_ptr<QPDFObject> const& object, 1771 std::shared_ptr<QPDFObject> const& object,
1809 qpdf_offset_t end_before_space, 1772 qpdf_offset_t end_before_space,
1810 qpdf_offset_t end_after_space, 1773 qpdf_offset_t end_after_space,
1811 bool destroy) 1774 bool destroy)
1812 { 1775 {
1813 - object->setObjGen(this, og); 1776 + object->setObjGen(&qpdf, og);
1814 if (isCached(og)) { 1777 if (isCached(og)) {
1815 auto& cache = m->obj_cache[og]; 1778 auto& cache = m->obj_cache[og];
1816 object->move_to(cache.object, destroy); 1779 object->move_to(cache.object, destroy);
@@ -1822,21 +1785,21 @@ QPDF::updateCache( @@ -1822,21 +1785,21 @@ QPDF::updateCache(
1822 } 1785 }
1823 1786
1824 bool 1787 bool
1825 -QPDF::isCached(QPDFObjGen og) 1788 +Objects::isCached(QPDFObjGen og)
1826 { 1789 {
1827 return m->obj_cache.contains(og); 1790 return m->obj_cache.contains(og);
1828 } 1791 }
1829 1792
1830 bool 1793 bool
1831 -QPDF::isUnresolved(QPDFObjGen og) 1794 +Objects::isUnresolved(QPDFObjGen og)
1832 { 1795 {
1833 return !isCached(og) || m->obj_cache[og].object->isUnresolved(); 1796 return !isCached(og) || m->obj_cache[og].object->isUnresolved();
1834 } 1797 }
1835 1798
1836 QPDFObjGen 1799 QPDFObjGen
1837 -QPDF::nextObjGen() 1800 +Objects::nextObjGen()
1838 { 1801 {
1839 - int max_objid = toI(getObjectCount()); 1802 + int max_objid = toI(qpdf.getObjectCount());
1840 if (max_objid == std::numeric_limits<int>::max()) { 1803 if (max_objid == std::numeric_limits<int>::max()) {
1841 throw std::range_error("max object id is too high to create new objects"); 1804 throw std::range_error("max object id is too high to create new objects");
1842 } 1805 }
@@ -1844,7 +1807,7 @@ QPDF::nextObjGen() @@ -1844,7 +1807,7 @@ QPDF::nextObjGen()
1844 } 1807 }
1845 1808
1846 QPDFObjectHandle 1809 QPDFObjectHandle
1847 -QPDF::makeIndirectFromQPDFObject(std::shared_ptr<QPDFObject> const& obj) 1810 +Objects::makeIndirectFromQPDFObject(std::shared_ptr<QPDFObject> const& obj)
1848 { 1811 {
1849 QPDFObjGen next{nextObjGen()}; 1812 QPDFObjGen next{nextObjGen()};
1850 m->obj_cache[next] = ObjCache(obj, -1, -1); 1813 m->obj_cache[next] = ObjCache(obj, -1, -1);
@@ -1857,11 +1820,11 @@ QPDF::makeIndirectObject(QPDFObjectHandle oh) @@ -1857,11 +1820,11 @@ QPDF::makeIndirectObject(QPDFObjectHandle oh)
1857 if (!oh) { 1820 if (!oh) {
1858 throw std::logic_error("attempted to make an uninitialized QPDFObjectHandle indirect"); 1821 throw std::logic_error("attempted to make an uninitialized QPDFObjectHandle indirect");
1859 } 1822 }
1860 - return makeIndirectFromQPDFObject(oh.getObj()); 1823 + return m->objects.makeIndirectFromQPDFObject(oh.getObj());
1861 } 1824 }
1862 1825
1863 std::shared_ptr<QPDFObject> 1826 std::shared_ptr<QPDFObject>
1864 -QPDF::getObjectForParser(int id, int gen, bool parse_pdf) 1827 +Objects::getObjectForParser(int id, int gen, bool parse_pdf)
1865 { 1828 {
1866 // This method is called by the parser and therefore must not resolve any objects. 1829 // This method is called by the parser and therefore must not resolve any objects.
1867 auto og = QPDFObjGen(id, gen); 1830 auto og = QPDFObjGen(id, gen);
@@ -1869,25 +1832,25 @@ QPDF::getObjectForParser(int id, int gen, bool parse_pdf) @@ -1869,25 +1832,25 @@ QPDF::getObjectForParser(int id, int gen, bool parse_pdf)
1869 return iter->second.object; 1832 return iter->second.object;
1870 } 1833 }
1871 if (m->xref_table.contains(og) || (!m->parsed && og.getObj() < m->xref_table_max_id)) { 1834 if (m->xref_table.contains(og) || (!m->parsed && og.getObj() < m->xref_table_max_id)) {
1872 - return m->obj_cache.insert({og, QPDFObject::create<QPDF_Unresolved>(this, og)}) 1835 + return m->obj_cache.insert({og, QPDFObject::create<QPDF_Unresolved>(&qpdf, og)})
1873 .first->second.object; 1836 .first->second.object;
1874 } 1837 }
1875 if (parse_pdf) { 1838 if (parse_pdf) {
1876 return QPDFObject::create<QPDF_Null>(); 1839 return QPDFObject::create<QPDF_Null>();
1877 } 1840 }
1878 - return m->obj_cache.insert({og, QPDFObject::create<QPDF_Null>(this, og)}).first->second.object; 1841 + return m->obj_cache.insert({og, QPDFObject::create<QPDF_Null>(&qpdf, og)}).first->second.object;
1879 } 1842 }
1880 1843
1881 std::shared_ptr<QPDFObject> 1844 std::shared_ptr<QPDFObject>
1882 -QPDF::getObjectForJSON(int id, int gen) 1845 +Objects::getObjectForJSON(int id, int gen)
1883 { 1846 {
1884 auto og = QPDFObjGen(id, gen); 1847 auto og = QPDFObjGen(id, gen);
1885 auto [it, inserted] = m->obj_cache.try_emplace(og); 1848 auto [it, inserted] = m->obj_cache.try_emplace(og);
1886 auto& obj = it->second.object; 1849 auto& obj = it->second.object;
1887 if (inserted) { 1850 if (inserted) {
1888 obj = (m->parsed && !m->xref_table.contains(og)) 1851 obj = (m->parsed && !m->xref_table.contains(og))
1889 - ? QPDFObject::create<QPDF_Null>(this, og)  
1890 - : QPDFObject::create<QPDF_Unresolved>(this, og); 1852 + ? QPDFObject::create<QPDF_Null>(&qpdf, og)
  1853 + : QPDFObject::create<QPDF_Unresolved>(&qpdf, og);
1891 } 1854 }
1892 return obj; 1855 return obj;
1893 } 1856 }
@@ -1916,10 +1879,9 @@ void @@ -1916,10 +1879,9 @@ void
1916 QPDF::replaceObject(QPDFObjGen og, QPDFObjectHandle oh) 1879 QPDF::replaceObject(QPDFObjGen og, QPDFObjectHandle oh)
1917 { 1880 {
1918 if (!oh || (oh.isIndirect() && !(oh.isStream() && oh.getObjGen() == og))) { 1881 if (!oh || (oh.isIndirect() && !(oh.isStream() && oh.getObjGen() == og))) {
1919 - QTC::TC("qpdf", "QPDF replaceObject called with indirect object");  
1920 throw std::logic_error("QPDF::replaceObject called with indirect object handle"); 1882 throw std::logic_error("QPDF::replaceObject called with indirect object handle");
1921 } 1883 }
1922 - updateCache(og, oh.getObj(), -1, -1, false); 1884 + m->objects.updateCache(og, oh.getObj(), -1, -1, false);
1923 } 1885 }
1924 1886
1925 void 1887 void
@@ -1955,13 +1917,13 @@ void @@ -1955,13 +1917,13 @@ void
1955 QPDF::swapObjects(QPDFObjGen og1, QPDFObjGen og2) 1917 QPDF::swapObjects(QPDFObjGen og1, QPDFObjGen og2)
1956 { 1918 {
1957 // Force objects to be read from the input source if needed, then swap them in the cache. 1919 // Force objects to be read from the input source if needed, then swap them in the cache.
1958 - resolve(og1);  
1959 - resolve(og2); 1920 + m->objects.resolve(og1);
  1921 + m->objects.resolve(og2);
1960 m->obj_cache[og1].object->swapWith(m->obj_cache[og2].object); 1922 m->obj_cache[og1].object->swapWith(m->obj_cache[og2].object);
1961 } 1923 }
1962 1924
1963 size_t 1925 size_t
1964 -QPDF::tableSize() 1926 +Objects::tableSize()
1965 { 1927 {
1966 // If obj_cache is dense, accommodate all object in tables,else accommodate only original 1928 // If obj_cache is dense, accommodate all object in tables,else accommodate only original
1967 // objects. 1929 // objects.
@@ -1972,7 +1934,7 @@ QPDF::tableSize() @@ -1972,7 +1934,7 @@ QPDF::tableSize()
1972 // Temporary fix. Long-term solution is 1934 // Temporary fix. Long-term solution is
1973 // - QPDFObjGen to enforce objgens are valid and sensible 1935 // - QPDFObjGen to enforce objgens are valid and sensible
1974 // - xref table and obj cache to protect against insertion of impossibly large obj ids 1936 // - xref table and obj cache to protect against insertion of impossibly large obj ids
1975 - stopOnError("Impossibly large object id encountered."); 1937 + qpdf.stopOnError("Impossibly large object id encountered.");
1976 } 1938 }
1977 if (max_obj < 1.1 * std::max(toI(m->obj_cache.size()), max_xref)) { 1939 if (max_obj < 1.1 * std::max(toI(m->obj_cache.size()), max_xref)) {
1978 return toS(++max_obj); 1940 return toS(++max_obj);
@@ -1981,20 +1943,20 @@ QPDF::tableSize() @@ -1981,20 +1943,20 @@ QPDF::tableSize()
1981 } 1943 }
1982 1944
1983 std::vector<QPDFObjGen> 1945 std::vector<QPDFObjGen>
1984 -QPDF::getCompressibleObjVector() 1946 +Objects::getCompressibleObjVector()
1985 { 1947 {
1986 return getCompressibleObjGens<QPDFObjGen>(); 1948 return getCompressibleObjGens<QPDFObjGen>();
1987 } 1949 }
1988 1950
1989 std::vector<bool> 1951 std::vector<bool>
1990 -QPDF::getCompressibleObjSet() 1952 +Objects::getCompressibleObjSet()
1991 { 1953 {
1992 return getCompressibleObjGens<bool>(); 1954 return getCompressibleObjGens<bool>();
1993 } 1955 }
1994 1956
1995 template <typename T> 1957 template <typename T>
1996 std::vector<T> 1958 std::vector<T>
1997 -QPDF::getCompressibleObjGens() 1959 +Objects::getCompressibleObjGens()
1998 { 1960 {
1999 // Return a list of objects that are allowed to be in object streams. Walk through the objects 1961 // Return a list of objects that are allowed to be in object streams. Walk through the objects
2000 // by traversing the document from the root, including a traversal of the pages tree. This 1962 // by traversing the document from the root, including a traversal of the pages tree. This
@@ -2006,11 +1968,11 @@ QPDF::getCompressibleObjGens() @@ -2006,11 +1968,11 @@ QPDF::getCompressibleObjGens()
2006 QPDFObjectHandle encryption_dict = m->trailer.getKey("/Encrypt"); 1968 QPDFObjectHandle encryption_dict = m->trailer.getKey("/Encrypt");
2007 QPDFObjGen encryption_dict_og = encryption_dict.getObjGen(); 1969 QPDFObjGen encryption_dict_og = encryption_dict.getObjGen();
2008 1970
2009 - const size_t max_obj = getObjectCount(); 1971 + const size_t max_obj = qpdf.getObjectCount();
2010 std::vector<bool> visited(max_obj, false); 1972 std::vector<bool> visited(max_obj, false);
2011 std::vector<QPDFObjectHandle> queue; 1973 std::vector<QPDFObjectHandle> queue;
2012 queue.reserve(512); 1974 queue.reserve(512);
2013 - queue.push_back(m->trailer); 1975 + queue.emplace_back(m->trailer);
2014 std::vector<T> result; 1976 std::vector<T> result;
2015 if constexpr (std::is_same_v<T, QPDFObjGen>) { 1977 if constexpr (std::is_same_v<T, QPDFObjGen>) {
2016 result.reserve(m->obj_cache.size()); 1978 result.reserve(m->obj_cache.size());
@@ -2030,7 +1992,6 @@ QPDF::getCompressibleObjGens() @@ -2030,7 +1992,6 @@ QPDF::getCompressibleObjGens()
2030 "unexpected object id encountered in getCompressibleObjGens"); 1992 "unexpected object id encountered in getCompressibleObjGens");
2031 } 1993 }
2032 if (visited[id]) { 1994 if (visited[id]) {
2033 - QTC::TC("qpdf", "QPDF loop detected traversing objects");  
2034 continue; 1995 continue;
2035 } 1996 }
2036 1997
@@ -2039,7 +2000,7 @@ QPDF::getCompressibleObjGens() @@ -2039,7 +2000,7 @@ QPDF::getCompressibleObjGens()
2039 // in the queue. 2000 // in the queue.
2040 auto upper = m->obj_cache.upper_bound(og); 2001 auto upper = m->obj_cache.upper_bound(og);
2041 if (upper != m->obj_cache.end() && upper->first.getObj() == og.getObj()) { 2002 if (upper != m->obj_cache.end() && upper->first.getObj() == og.getObj()) {
2042 - removeObject(og); 2003 + qpdf.removeObject(og);
2043 continue; 2004 continue;
2044 } 2005 }
2045 2006
libqpdf/QPDF_optimization.cc
@@ -7,6 +7,9 @@ @@ -7,6 +7,9 @@
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;
  11 +using Pages = QPDF::Doc::Pages;
  12 +
10 QPDF::ObjUser::ObjUser(user_e type) : 13 QPDF::ObjUser::ObjUser(user_e type) :
11 ou_type(type) 14 ou_type(type)
12 { 15 {
@@ -58,11 +61,11 @@ QPDF::optimize( @@ -58,11 +61,11 @@ QPDF::optimize(
58 bool allow_changes, 61 bool allow_changes,
59 std::function<int(QPDFObjectHandle&)> skip_stream_parameters) 62 std::function<int(QPDFObjectHandle&)> skip_stream_parameters)
60 { 63 {
61 - optimize_internal(object_stream_data, allow_changes, skip_stream_parameters); 64 + m->lin.optimize_internal(object_stream_data, allow_changes, skip_stream_parameters);
62 } 65 }
63 66
64 void 67 void
65 -QPDF::optimize( 68 +Lin::optimize(
66 QPDFWriter::ObjTable const& obj, std::function<int(QPDFObjectHandle&)> skip_stream_parameters) 69 QPDFWriter::ObjTable const& obj, std::function<int(QPDFObjectHandle&)> skip_stream_parameters)
67 { 70 {
68 optimize_internal(obj, true, skip_stream_parameters); 71 optimize_internal(obj, true, skip_stream_parameters);
@@ -70,7 +73,7 @@ QPDF::optimize( @@ -70,7 +73,7 @@ QPDF::optimize(
70 73
71 template <typename T> 74 template <typename T>
72 void 75 void
73 -QPDF::optimize_internal( 76 +Lin::optimize_internal(
74 T const& object_stream_data, 77 T const& object_stream_data,
75 bool allow_changes, 78 bool allow_changes,
76 std::function<int(QPDFObjectHandle&)> skip_stream_parameters) 79 std::function<int(QPDFObjectHandle&)> skip_stream_parameters)
@@ -82,18 +85,17 @@ QPDF::optimize_internal( @@ -82,18 +85,17 @@ QPDF::optimize_internal(
82 85
83 // 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
84 // 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.)
85 - QPDFObjectHandle root = getRoot(); 88 + QPDFObjectHandle root = qpdf.getRoot();
86 if (root.getKey("/Outlines").isDictionary()) { 89 if (root.getKey("/Outlines").isDictionary()) {
87 QPDFObjectHandle outlines = root.getKey("/Outlines"); 90 QPDFObjectHandle outlines = root.getKey("/Outlines");
88 if (!outlines.isIndirect()) { 91 if (!outlines.isIndirect()) {
89 - QTC::TC("qpdf", "QPDF_optimization indirect outlines");  
90 - root.replaceKey("/Outlines", makeIndirectObject(outlines)); 92 + root.replaceKey("/Outlines", qpdf.makeIndirectObject(outlines));
91 } 93 }
92 } 94 }
93 95
94 // Traverse pages tree pushing all inherited resources down to the page level. This also 96 // Traverse pages tree pushing all inherited resources down to the page level. This also
95 // initializes m->all_pages. 97 // initializes m->all_pages.
96 - pushInheritedAttributesToPage(allow_changes, false); 98 + m->pages.pushInheritedAttributesToPage(allow_changes, false);
97 99
98 // Traverse pages 100 // Traverse pages
99 size_t n = m->all_pages.size(); 101 size_t n = m->all_pages.size();
@@ -136,11 +138,11 @@ void @@ -136,11 +138,11 @@ void
136 QPDF::pushInheritedAttributesToPage() 138 QPDF::pushInheritedAttributesToPage()
137 { 139 {
138 // Public API should not have access to allow_changes. 140 // Public API should not have access to allow_changes.
139 - pushInheritedAttributesToPage(true, false); 141 + m->pages.pushInheritedAttributesToPage(true, false);
140 } 142 }
141 143
142 void 144 void
143 -QPDF::pushInheritedAttributesToPage(bool allow_changes, bool warn_skipped_keys) 145 +Pages::pushInheritedAttributesToPage(bool allow_changes, bool warn_skipped_keys)
144 { 146 {
145 // Traverse pages tree pushing all inherited resources down to the page level. 147 // Traverse pages tree pushing all inherited resources down to the page level.
146 148
@@ -152,7 +154,7 @@ QPDF::pushInheritedAttributesToPage(bool allow_changes, bool warn_skipped_keys) @@ -152,7 +154,7 @@ QPDF::pushInheritedAttributesToPage(bool allow_changes, bool warn_skipped_keys)
152 154
153 // Calling getAllPages() resolves any duplicated page objects, repairs broken nodes, and detects 155 // Calling getAllPages() resolves any duplicated page objects, repairs broken nodes, and detects
154 // loops, so we don't have to do those activities here. 156 // loops, so we don't have to do those activities here.
155 - getAllPages(); 157 + qpdf.getAllPages();
156 158
157 // key_ancestors is a mapping of page attribute keys to a stack of Pages nodes that contain 159 // key_ancestors is a mapping of page attribute keys to a stack of Pages nodes that contain
158 // values for them. 160 // values for them.
@@ -171,7 +173,7 @@ QPDF::pushInheritedAttributesToPage(bool allow_changes, bool warn_skipped_keys) @@ -171,7 +173,7 @@ QPDF::pushInheritedAttributesToPage(bool allow_changes, bool warn_skipped_keys)
171 } 173 }
172 174
173 void 175 void
174 -QPDF::pushInheritedAttributesToPageInternal( 176 +Pages ::pushInheritedAttributesToPageInternal(
175 QPDFObjectHandle cur_pages, 177 QPDFObjectHandle cur_pages,
176 std::map<std::string, std::vector<QPDFObjectHandle>>& key_ancestors, 178 std::map<std::string, std::vector<QPDFObjectHandle>>& key_ancestors,
177 bool allow_changes, 179 bool allow_changes,
@@ -183,8 +185,7 @@ QPDF::pushInheritedAttributesToPageInternal( @@ -183,8 +185,7 @@ QPDF::pushInheritedAttributesToPageInternal(
183 185
184 std::set<std::string> inheritable_keys; 186 std::set<std::string> inheritable_keys;
185 for (auto const& key: cur_pages.getKeys()) { 187 for (auto const& key: cur_pages.getKeys()) {
186 - if ((key == "/MediaBox") || (key == "/CropBox") || (key == "/Resources") ||  
187 - (key == "/Rotate")) { 188 + if (key == "/MediaBox" || key == "/CropBox" || key == "/Resources" || key == "/Rotate") {
188 if (!allow_changes) { 189 if (!allow_changes) {
189 throw QPDFExc( 190 throw QPDFExc(
190 qpdf_e_internal, 191 qpdf_e_internal,
@@ -197,34 +198,29 @@ QPDF::pushInheritedAttributesToPageInternal( @@ -197,34 +198,29 @@ QPDF::pushInheritedAttributesToPageInternal(
197 // This is an inheritable resource 198 // This is an inheritable resource
198 inheritable_keys.insert(key); 199 inheritable_keys.insert(key);
199 QPDFObjectHandle oh = cur_pages.getKey(key); 200 QPDFObjectHandle oh = cur_pages.getKey(key);
200 - QTC::TC("qpdf", "QPDF opt direct pages resource", oh.isIndirect() ? 0 : 1);  
201 - if (!oh.isIndirect()) { 201 + QTC::TC("qpdf", "QPDF opt direct pages resource", oh.indirect() ? 0 : 1);
  202 + if (!oh.indirect()) {
202 if (!oh.isScalar()) { 203 if (!oh.isScalar()) {
203 // Replace shared direct object non-scalar resources with indirect objects to 204 // Replace shared direct object non-scalar resources with indirect objects to
204 // avoid copying large structures around. 205 // avoid copying large structures around.
205 - cur_pages.replaceKey(key, makeIndirectObject(oh)); 206 + cur_pages.replaceKey(key, qpdf.makeIndirectObject(oh));
206 oh = cur_pages.getKey(key); 207 oh = cur_pages.getKey(key);
207 } else { 208 } else {
208 // It's okay to copy scalars. 209 // It's okay to copy scalars.
209 - QTC::TC("qpdf", "QPDF opt inherited scalar");  
210 } 210 }
211 } 211 }
212 key_ancestors[key].push_back(oh); 212 key_ancestors[key].push_back(oh);
213 if (key_ancestors[key].size() > 1) { 213 if (key_ancestors[key].size() > 1) {
214 - QTC::TC("qpdf", "QPDF opt key ancestors depth > 1");  
215 } 214 }
216 // Remove this resource from this node. It will be reattached at the page level. 215 // Remove this resource from this node. It will be reattached at the page level.
217 cur_pages.removeKey(key); 216 cur_pages.removeKey(key);
218 - } else if (!((key == "/Type") || (key == "/Parent") || (key == "/Kids") ||  
219 - (key == "/Count"))) { 217 + } else if (!(key == "/Type" || key == "/Parent" || key == "/Kids" || key == "/Count")) {
220 // Warn when flattening, but not if the key is at the top level (i.e. "/Parent" not 218 // Warn when flattening, but not if the key is at the top level (i.e. "/Parent" not
221 // set), as we don't change these; but flattening removes intermediate /Pages nodes. 219 // set), as we don't change these; but flattening removes intermediate /Pages nodes.
222 - if ((warn_skipped_keys) && (cur_pages.hasKey("/Parent"))) {  
223 - QTC::TC("qpdf", "QPDF unknown key not inherited");  
224 - setLastObjectDescription("Pages object", cur_pages.getObjGen());  
225 - warn( 220 + if (warn_skipped_keys && cur_pages.hasKey("/Parent")) {
  221 + qpdf.warn(
226 qpdf_e_pages, 222 qpdf_e_pages,
227 - m->last_object_description, 223 + "Pages object: object " + cur_pages.id_gen().unparse(' '),
228 0, 224 0,
229 ("Unknown key " + key + 225 ("Unknown key " + key +
230 " in /Pages object is being discarded as a result of flattening the /Pages " 226 " in /Pages object is being discarded as a result of flattening the /Pages "
@@ -245,7 +241,6 @@ QPDF::pushInheritedAttributesToPageInternal( @@ -245,7 +241,6 @@ QPDF::pushInheritedAttributesToPageInternal(
245 for (auto const& iter: key_ancestors) { 241 for (auto const& iter: key_ancestors) {
246 std::string const& key = iter.first; 242 std::string const& key = iter.first;
247 if (!kid.hasKey(key)) { 243 if (!kid.hasKey(key)) {
248 - QTC::TC("qpdf", "QPDF opt resource inherited");  
249 kid.replaceKey(key, iter.second.back()); 244 kid.replaceKey(key, iter.second.back());
250 } else { 245 } else {
251 QTC::TC("qpdf", "QPDF opt page resource hides ancestor"); 246 QTC::TC("qpdf", "QPDF opt page resource hides ancestor");
@@ -259,11 +254,9 @@ QPDF::pushInheritedAttributesToPageInternal( @@ -259,11 +254,9 @@ QPDF::pushInheritedAttributesToPageInternal(
259 // which inheritable attributes are available. 254 // which inheritable attributes are available.
260 255
261 if (!inheritable_keys.empty()) { 256 if (!inheritable_keys.empty()) {
262 - QTC::TC("qpdf", "QPDF opt inheritable keys");  
263 for (auto const& key: inheritable_keys) { 257 for (auto const& key: inheritable_keys) {
264 key_ancestors[key].pop_back(); 258 key_ancestors[key].pop_back();
265 if (key_ancestors[key].empty()) { 259 if (key_ancestors[key].empty()) {
266 - QTC::TC("qpdf", "QPDF opt erase empty key ancestor");  
267 key_ancestors.erase(key); 260 key_ancestors.erase(key);
268 } 261 }
269 } 262 }
@@ -273,7 +266,7 @@ QPDF::pushInheritedAttributesToPageInternal( @@ -273,7 +266,7 @@ QPDF::pushInheritedAttributesToPageInternal(
273 } 266 }
274 267
275 void 268 void
276 -QPDF::updateObjectMaps( 269 +Lin::updateObjectMaps(
277 ObjUser const& first_ou, 270 ObjUser const& first_ou,
278 QPDFObjectHandle first_oh, 271 QPDFObjectHandle first_oh,
279 std::function<int(QPDFObjectHandle&)> skip_stream_parameters) 272 std::function<int(QPDFObjectHandle&)> skip_stream_parameters)
@@ -346,7 +339,7 @@ QPDF::updateObjectMaps( @@ -346,7 +339,7 @@ QPDF::updateObjectMaps(
346 } 339 }
347 340
348 void 341 void
349 -QPDF::filterCompressedObjects(std::map<int, int> const& object_stream_data) 342 +Lin::filterCompressedObjects(std::map<int, int> const& object_stream_data)
350 { 343 {
351 if (object_stream_data.empty()) { 344 if (object_stream_data.empty()) {
352 return; 345 return;
@@ -390,7 +383,7 @@ QPDF::filterCompressedObjects(std::map&lt;int, int&gt; const&amp; object_stream_data) @@ -390,7 +383,7 @@ QPDF::filterCompressedObjects(std::map&lt;int, int&gt; const&amp; object_stream_data)
390 } 383 }
391 384
392 void 385 void
393 -QPDF::filterCompressedObjects(QPDFWriter::ObjTable const& obj) 386 +Lin::filterCompressedObjects(QPDFWriter::ObjTable const& obj)
394 { 387 {
395 if (obj.getStreamsEmpty()) { 388 if (obj.getStreamsEmpty()) {
396 return; 389 return;
libqpdf/QPDF_pages.cc
@@ -37,6 +37,8 @@ @@ -37,6 +37,8 @@
37 // insertPage, and removePage, along with methods they call, are concerned with it. Everything else 37 // insertPage, and removePage, along with methods they call, are concerned with it. Everything else
38 // goes through one of those methods. 38 // goes through one of those methods.
39 39
  40 +using Pages = QPDF::Doc::Pages;
  41 +
40 std::vector<QPDFObjectHandle> const& 42 std::vector<QPDFObjectHandle> const&
41 QPDF::getAllPages() 43 QPDF::getAllPages()
42 { 44 {
@@ -75,14 +77,14 @@ QPDF::getAllPages() @@ -75,14 +77,14 @@ QPDF::getAllPages()
75 qpdf_e_pages, m->file->getName(), "", 0, "root of pages tree has no /Kids array"); 77 qpdf_e_pages, m->file->getName(), "", 0, "root of pages tree has no /Kids array");
76 } 78 }
77 try { 79 try {
78 - getAllPagesInternal(pages, visited, seen, false, false); 80 + m->pages.getAllPagesInternal(pages, visited, seen, false, false);
79 } catch (...) { 81 } catch (...) {
80 m->all_pages.clear(); 82 m->all_pages.clear();
81 m->invalid_page_found = false; 83 m->invalid_page_found = false;
82 throw; 84 throw;
83 } 85 }
84 if (m->invalid_page_found) { 86 if (m->invalid_page_found) {
85 - flattenPagesTree(); 87 + m->pages.flattenPagesTree();
86 m->invalid_page_found = false; 88 m->invalid_page_found = false;
87 } 89 }
88 } 90 }
@@ -90,7 +92,7 @@ QPDF::getAllPages() @@ -90,7 +92,7 @@ QPDF::getAllPages()
90 } 92 }
91 93
92 void 94 void
93 -QPDF::getAllPagesInternal( 95 +Pages::getAllPagesInternal(
94 QPDFObjectHandle cur_node, 96 QPDFObjectHandle cur_node,
95 QPDFObjGen::set& visited, 97 QPDFObjGen::set& visited,
96 QPDFObjGen::set& seen, 98 QPDFObjGen::set& seen,
@@ -139,17 +141,15 @@ QPDF::getAllPagesInternal( @@ -139,17 +141,15 @@ QPDF::getAllPagesInternal(
139 continue; 141 continue;
140 } 142 }
141 if (!kid.isIndirect()) { 143 if (!kid.isIndirect()) {
142 - QTC::TC("qpdf", "QPDF handle direct page object");  
143 cur_node.warn( 144 cur_node.warn(
144 "kid " + std::to_string(i) + " (from 0) is direct; converting to indirect"); 145 "kid " + std::to_string(i) + " (from 0) is direct; converting to indirect");
145 - kid = makeIndirectObject(kid); 146 + kid = qpdf.makeIndirectObject(kid);
146 ++errors; 147 ++errors;
147 } 148 }
148 if (kid.hasKey("/Kids")) { 149 if (kid.hasKey("/Kids")) {
149 getAllPagesInternal(kid, visited, seen, media_box, resources); 150 getAllPagesInternal(kid, visited, seen, media_box, resources);
150 } else { 151 } else {
151 if (!media_box && !kid.getKey("/MediaBox").isRectangle()) { 152 if (!media_box && !kid.getKey("/MediaBox").isRectangle()) {
152 - QTC::TC("qpdf", "QPDF missing mediabox");  
153 kid.warn( 153 kid.warn(
154 "kid " + std::to_string(i) + 154 "kid " + std::to_string(i) +
155 " (from 0) MediaBox is undefined; setting to letter / ANSI A"); 155 " (from 0) MediaBox is undefined; setting to letter / ANSI A");
@@ -193,7 +193,6 @@ QPDF::getAllPagesInternal( @@ -193,7 +193,6 @@ QPDF::getAllPagesInternal(
193 if (!seen.add(kid)) { 193 if (!seen.add(kid)) {
194 // Make a copy of the page. This does the same as shallowCopyPage in 194 // Make a copy of the page. This does the same as shallowCopyPage in
195 // QPDFPageObjectHelper. 195 // QPDFPageObjectHelper.
196 - QTC::TC("qpdf", "QPDF resolve duplicated page object");  
197 if (!m->reconstructed_xref) { 196 if (!m->reconstructed_xref) {
198 cur_node.warn( 197 cur_node.warn(
199 "kid " + std::to_string(i) + 198 "kid " + std::to_string(i) +
@@ -201,7 +200,7 @@ QPDF::getAllPagesInternal( @@ -201,7 +200,7 @@ QPDF::getAllPagesInternal(
201 " creating a new page object as a copy"); 200 " creating a new page object as a copy");
202 // This needs to be fixed. shallowCopy does not necessarily produce a valid 201 // This needs to be fixed. shallowCopy does not necessarily produce a valid
203 // page. 202 // page.
204 - kid = makeIndirectObject(QPDFObjectHandle(kid).shallowCopy()); 203 + kid = qpdf.makeIndirectObject(QPDFObjectHandle(kid).shallowCopy());
205 seen.add(kid); 204 seen.add(kid);
206 } else { 205 } else {
207 cur_node.warn( 206 cur_node.warn(
@@ -239,7 +238,6 @@ QPDF::updateAllPagesCache() @@ -239,7 +238,6 @@ QPDF::updateAllPagesCache()
239 // Force regeneration of the pages cache. We force immediate recalculation of all_pages since 238 // Force regeneration of the pages cache. We force immediate recalculation of all_pages since
240 // users may have references to it that they got from calls to getAllPages(). We can defer 239 // users may have references to it that they got from calls to getAllPages(). We can defer
241 // recalculation of pageobj_to_pages_pos until needed. 240 // recalculation of pageobj_to_pages_pos until needed.
242 - QTC::TC("qpdf", "QPDF updateAllPagesCache");  
243 m->all_pages.clear(); 241 m->all_pages.clear();
244 m->pageobj_to_pages_pos.clear(); 242 m->pageobj_to_pages_pos.clear();
245 m->pushed_inherited_attributes_to_pages = false; 243 m->pushed_inherited_attributes_to_pages = false;
@@ -247,7 +245,7 @@ QPDF::updateAllPagesCache() @@ -247,7 +245,7 @@ QPDF::updateAllPagesCache()
247 } 245 }
248 246
249 void 247 void
250 -QPDF::flattenPagesTree() 248 +Pages::flattenPagesTree()
251 { 249 {
252 // If not already done, flatten the /Pages structure and initialize pageobj_to_pages_pos. 250 // If not already done, flatten the /Pages structure and initialize pageobj_to_pages_pos.
253 251
@@ -259,7 +257,7 @@ QPDF::flattenPagesTree() @@ -259,7 +257,7 @@ QPDF::flattenPagesTree()
259 // generated. 257 // generated.
260 pushInheritedAttributesToPage(true, true); 258 pushInheritedAttributesToPage(true, true);
261 259
262 - QPDFObjectHandle pages = getRoot().getKey("/Pages"); 260 + QPDFObjectHandle pages = qpdf.getRoot().getKey("/Pages");
263 261
264 size_t const len = m->all_pages.size(); 262 size_t const len = m->all_pages.size();
265 for (size_t pos = 0; pos < len; ++pos) { 263 for (size_t pos = 0; pos < len; ++pos) {
@@ -282,17 +280,16 @@ QPDF::flattenPagesTree() @@ -282,17 +280,16 @@ QPDF::flattenPagesTree()
282 } 280 }
283 281
284 void 282 void
285 -QPDF::insertPageobjToPage(QPDFObjectHandle const& obj, int pos, bool check_duplicate) 283 +Pages::insertPageobjToPage(QPDFObjectHandle const& obj, int pos, bool check_duplicate)
286 { 284 {
287 QPDFObjGen og(obj.getObjGen()); 285 QPDFObjGen og(obj.getObjGen());
288 if (check_duplicate) { 286 if (check_duplicate) {
289 if (!m->pageobj_to_pages_pos.insert(std::make_pair(og, pos)).second) { 287 if (!m->pageobj_to_pages_pos.insert(std::make_pair(og, pos)).second) {
290 // The library never calls insertPageobjToPage in a way that causes this to happen. 288 // The library never calls insertPageobjToPage in a way that causes this to happen.
291 - setLastObjectDescription("page " + std::to_string(pos) + " (numbered from zero)", og);  
292 throw QPDFExc( 289 throw QPDFExc(
293 qpdf_e_pages, 290 qpdf_e_pages,
294 m->file->getName(), 291 m->file->getName(),
295 - m->last_object_description, 292 + "page " + std::to_string(pos) + " (numbered from zero): object " + og.unparse(' '),
296 0, 293 0,
297 "duplicate page reference found; this would cause loss of data"); 294 "duplicate page reference found; this would cause loss of data");
298 } 295 }
@@ -302,24 +299,22 @@ QPDF::insertPageobjToPage(QPDFObjectHandle const&amp; obj, int pos, bool check_dupli @@ -302,24 +299,22 @@ QPDF::insertPageobjToPage(QPDFObjectHandle const&amp; obj, int pos, bool check_dupli
302 } 299 }
303 300
304 void 301 void
305 -QPDF::insertPage(QPDFObjectHandle newpage, int pos) 302 +Pages::insertPage(QPDFObjectHandle newpage, int pos)
306 { 303 {
307 // pos is numbered from 0, so pos = 0 inserts at the beginning and pos = npages adds to the end. 304 // pos is numbered from 0, so pos = 0 inserts at the beginning and pos = npages adds to the end.
308 305
309 flattenPagesTree(); 306 flattenPagesTree();
310 307
311 if (!newpage.isIndirect()) { 308 if (!newpage.isIndirect()) {
312 - QTC::TC("qpdf", "QPDF insert non-indirect page");  
313 - newpage = makeIndirectObject(newpage);  
314 - } else if (newpage.getOwningQPDF() != this) {  
315 - QTC::TC("qpdf", "QPDF insert foreign page"); 309 + newpage = qpdf.makeIndirectObject(newpage);
  310 + } else if (newpage.getOwningQPDF() != &qpdf) {
316 newpage.getQPDF().pushInheritedAttributesToPage(); 311 newpage.getQPDF().pushInheritedAttributesToPage();
317 - newpage = copyForeignObject(newpage); 312 + newpage = qpdf.copyForeignObject(newpage);
318 } else { 313 } else {
319 QTC::TC("qpdf", "QPDF insert indirect page"); 314 QTC::TC("qpdf", "QPDF insert indirect page");
320 } 315 }
321 316
322 - if ((pos < 0) || (toS(pos) > m->all_pages.size())) { 317 + if (pos < 0 || toS(pos) > m->all_pages.size()) {
323 throw std::runtime_error("QPDF::insertPage called with pos out of range"); 318 throw std::runtime_error("QPDF::insertPage called with pos out of range");
324 } 319 }
325 320
@@ -332,11 +327,10 @@ QPDF::insertPage(QPDFObjectHandle newpage, int pos) @@ -332,11 +327,10 @@ QPDF::insertPage(QPDFObjectHandle newpage, int pos)
332 327
333 auto og = newpage.getObjGen(); 328 auto og = newpage.getObjGen();
334 if (m->pageobj_to_pages_pos.contains(og)) { 329 if (m->pageobj_to_pages_pos.contains(og)) {
335 - QTC::TC("qpdf", "QPDF resolve duplicated page in insert");  
336 - newpage = makeIndirectObject(QPDFObjectHandle(newpage).shallowCopy()); 330 + newpage = qpdf.makeIndirectObject(QPDFObjectHandle(newpage).shallowCopy());
337 } 331 }
338 332
339 - QPDFObjectHandle pages = getRoot().getKey("/Pages"); 333 + QPDFObjectHandle pages = qpdf.getRoot().getKey("/Pages");
340 QPDFObjectHandle kids = pages.getKey("/Kids"); 334 QPDFObjectHandle kids = pages.getKey("/Kids");
341 335
342 newpage.replaceKey("/Parent", pages); 336 newpage.replaceKey("/Parent", pages);
@@ -370,7 +364,7 @@ QPDF::removePage(QPDFObjectHandle page) @@ -370,7 +364,7 @@ QPDF::removePage(QPDFObjectHandle page)
370 m->all_pages.erase(m->all_pages.begin() + pos); 364 m->all_pages.erase(m->all_pages.begin() + pos);
371 m->pageobj_to_pages_pos.erase(page.getObjGen()); 365 m->pageobj_to_pages_pos.erase(page.getObjGen());
372 for (int i = pos; i < npages; ++i) { 366 for (int i = pos; i < npages; ++i) {
373 - insertPageobjToPage(m->all_pages.at(toS(i)), i, false); 367 + m->pages.insertPageobjToPage(m->all_pages.at(toS(i)), i, false);
374 } 368 }
375 } 369 }
376 370
@@ -381,16 +375,17 @@ QPDF::addPageAt(QPDFObjectHandle newpage, bool before, QPDFObjectHandle refpage) @@ -381,16 +375,17 @@ QPDF::addPageAt(QPDFObjectHandle newpage, bool before, QPDFObjectHandle refpage)
381 if (!before) { 375 if (!before) {
382 ++refpos; 376 ++refpos;
383 } 377 }
384 - insertPage(newpage, refpos); 378 + m->pages.insertPage(newpage, refpos);
385 } 379 }
386 380
387 void 381 void
388 QPDF::addPage(QPDFObjectHandle newpage, bool first) 382 QPDF::addPage(QPDFObjectHandle newpage, bool first)
389 { 383 {
390 if (first) { 384 if (first) {
391 - insertPage(newpage, 0); 385 + m->pages.insertPage(newpage, 0);
392 } else { 386 } else {
393 - insertPage(newpage, getRoot().getKey("/Pages").getKey("/Count").getIntValueAsInt()); 387 + m->pages.insertPage(
  388 + newpage, getRoot().getKey("/Pages").getKey("/Count").getIntValueAsInt());
394 } 389 }
395 } 390 }
396 391
@@ -403,15 +398,13 @@ QPDF::findPage(QPDFObjectHandle&amp; page) @@ -403,15 +398,13 @@ QPDF::findPage(QPDFObjectHandle&amp; page)
403 int 398 int
404 QPDF::findPage(QPDFObjGen og) 399 QPDF::findPage(QPDFObjGen og)
405 { 400 {
406 - flattenPagesTree(); 401 + m->pages.flattenPagesTree();
407 auto it = m->pageobj_to_pages_pos.find(og); 402 auto it = m->pageobj_to_pages_pos.find(og);
408 if (it == m->pageobj_to_pages_pos.end()) { 403 if (it == m->pageobj_to_pages_pos.end()) {
409 - QTC::TC("qpdf", "QPDF_pages findPage not found");  
410 - setLastObjectDescription("page object", og);  
411 throw QPDFExc( 404 throw QPDFExc(
412 qpdf_e_pages, 405 qpdf_e_pages,
413 m->file->getName(), 406 m->file->getName(),
414 - m->last_object_description, 407 + "page object: object " + og.unparse(' '),
415 0, 408 0,
416 "page object not referenced in /Pages tree"); 409 "page object not referenced in /Pages tree");
417 } 410 }
libqpdf/qpdf/QPDFObjectHandle_private.hh
@@ -577,7 +577,7 @@ namespace qpdf @@ -577,7 +577,7 @@ namespace qpdf
577 return &std::get<T>(obj->value); 577 return &std::get<T>(obj->value);
578 } 578 }
579 if (std::holds_alternative<QPDF_Unresolved>(obj->value)) { 579 if (std::holds_alternative<QPDF_Unresolved>(obj->value)) {
580 - return BaseHandle(QPDF::Resolver::resolved(obj->qpdf, obj->og)).as<T>(); 580 + return BaseHandle(QPDF::Doc::Resolver::resolved(obj->qpdf, obj->og)).as<T>();
581 } 581 }
582 if (std::holds_alternative<QPDF_Reference>(obj->value)) { 582 if (std::holds_alternative<QPDF_Reference>(obj->value)) {
583 // see comment in QPDF_Reference. 583 // see comment in QPDF_Reference.
@@ -676,7 +676,7 @@ namespace qpdf @@ -676,7 +676,7 @@ namespace qpdf
676 return ::ot_uninitialized; 676 return ::ot_uninitialized;
677 } 677 }
678 if (raw_type_code() == ::ot_unresolved) { 678 if (raw_type_code() == ::ot_unresolved) {
679 - return QPDF::Resolver::resolved(obj->qpdf, obj->og)->getTypeCode(); 679 + return QPDF::Doc::Resolver::resolved(obj->qpdf, obj->og)->getTypeCode();
680 } 680 }
681 return raw_type_code(); 681 return raw_type_code();
682 } 682 }
@@ -688,7 +688,7 @@ namespace qpdf @@ -688,7 +688,7 @@ namespace qpdf
688 return ::ot_uninitialized; 688 return ::ot_uninitialized;
689 } 689 }
690 if (raw_type_code() == ::ot_unresolved) { 690 if (raw_type_code() == ::ot_unresolved) {
691 - return QPDF::Resolver::resolved(obj->qpdf, obj->og)->getTypeCode(); 691 + return QPDF::Doc::Resolver::resolved(obj->qpdf, obj->og)->getTypeCode();
692 } 692 }
693 if (raw_type_code() == ::ot_reference) { 693 if (raw_type_code() == ::ot_reference) {
694 return std::get<QPDF_Reference>(obj->value).obj->getTypeCode(); 694 return std::get<QPDF_Reference>(obj->value).obj->getTypeCode();
@@ -728,7 +728,7 @@ inline qpdf_object_type_e @@ -728,7 +728,7 @@ inline qpdf_object_type_e
728 QPDFObject::getResolvedTypeCode() const 728 QPDFObject::getResolvedTypeCode() const
729 { 729 {
730 if (getTypeCode() == ::ot_unresolved) { 730 if (getTypeCode() == ::ot_unresolved) {
731 - return QPDF::Resolver::resolved(qpdf, og)->getTypeCode(); 731 + return QPDF::Doc::Resolver::resolved(qpdf, og)->getTypeCode();
732 } 732 }
733 if (getTypeCode() == ::ot_reference) { 733 if (getTypeCode() == ::ot_reference) {
734 return std::get<QPDF_Reference>(value).obj->getTypeCode(); 734 return std::get<QPDF_Reference>(value).obj->getTypeCode();
libqpdf/qpdf/QPDF_private.hh
@@ -13,86 +13,13 @@ @@ -13,86 +13,13 @@
13 13
14 using namespace qpdf; 14 using namespace qpdf;
15 15
16 -// The Resolver class is restricted to QPDFObject so that only it can resolve indirect  
17 -// references.  
18 -class QPDF::Resolver 16 +namespace qpdf::is
19 { 17 {
20 - friend class QPDFObject;  
21 - friend class qpdf::BaseHandle;  
22 -  
23 - private:  
24 - static std::shared_ptr<QPDFObject> const&  
25 - resolved(QPDF* qpdf, QPDFObjGen og)  
26 - {  
27 - return qpdf->resolve(og);  
28 - }  
29 -};  
30 -  
31 -// StreamCopier class is restricted to QPDFObjectHandle so it can copy stream data.  
32 -class QPDF::StreamCopier  
33 -{  
34 - friend class QPDFObjectHandle;  
35 -  
36 - private:  
37 - static void  
38 - copyStreamData(QPDF* qpdf, QPDFObjectHandle const& dest, QPDFObjectHandle const& src)  
39 - {  
40 - qpdf->copyStreamData(dest, src);  
41 - }  
42 -}; 18 + class OffsetBuffer;
  19 +} // namespace qpdf::is
43 20
44 -// The ParseGuard class allows QPDFParser to detect re-entrant parsing. It also provides  
45 -// special access to allow the parser to create unresolved objects and dangling references.  
46 -class QPDF::ParseGuard  
47 -{  
48 - friend class QPDFParser;  
49 -  
50 - private:  
51 - ParseGuard(QPDF* qpdf) :  
52 - qpdf(qpdf)  
53 - {  
54 - if (qpdf) {  
55 - qpdf->inParse(true);  
56 - }  
57 - }  
58 -  
59 - static std::shared_ptr<QPDFObject>  
60 - getObject(QPDF* qpdf, int id, int gen, bool parse_pdf)  
61 - {  
62 - return qpdf->getObjectForParser(id, gen, parse_pdf);  
63 - }  
64 -  
65 - ~ParseGuard()  
66 - {  
67 - if (qpdf) {  
68 - qpdf->inParse(false);  
69 - }  
70 - }  
71 - QPDF* qpdf;  
72 -};  
73 -  
74 -// Pipe class is restricted to QPDF_Stream.  
75 -class QPDF::Pipe  
76 -{  
77 - friend class qpdf::Stream;  
78 -  
79 - private:  
80 - static bool  
81 - pipeStreamData(  
82 - QPDF* qpdf,  
83 - QPDFObjGen og,  
84 - qpdf_offset_t offset,  
85 - size_t length,  
86 - QPDFObjectHandle dict,  
87 - bool is_root_metadata,  
88 - Pipeline* pipeline,  
89 - bool suppress_warnings,  
90 - bool will_retry)  
91 - {  
92 - return qpdf->pipeStreamData(  
93 - og, offset, length, dict, is_root_metadata, pipeline, suppress_warnings, will_retry);  
94 - }  
95 -}; 21 +class BitStream;
  22 +class BitWriter;
96 23
97 class QPDF::ObjCache 24 class QPDF::ObjCache
98 { 25 {
@@ -405,17 +332,498 @@ class QPDF::PatternFinder final: public InputSource::Finder @@ -405,17 +332,498 @@ class QPDF::PatternFinder final: public InputSource::Finder
405 bool (QPDF::*checker)(); 332 bool (QPDF::*checker)();
406 }; 333 };
407 334
  335 +// This class is used to represent a PDF document.
  336 +//
  337 +// The main function of the QPDF class is to represent a PDF document. Doc is the implementation
  338 +// class for this aspect of QPDF.
  339 +class QPDF::Doc
  340 +{
  341 + public:
  342 + class JobSetter;
  343 + class ParseGuard;
  344 + class Resolver;
  345 + class StreamCopier;
  346 + class Streams;
  347 + class Writer;
  348 +
  349 + class Encryption
  350 + {
  351 + public:
  352 + // This class holds data read from the encryption dictionary.
  353 + Encryption(
  354 + int V,
  355 + int R,
  356 + int Length_bytes,
  357 + int P,
  358 + std::string const& O,
  359 + std::string const& U,
  360 + std::string const& OE,
  361 + std::string const& UE,
  362 + std::string const& Perms,
  363 + std::string const& id1,
  364 + bool encrypt_metadata) :
  365 + V(V),
  366 + R(R),
  367 + Length_bytes(Length_bytes),
  368 + P(static_cast<unsigned long long>(P)),
  369 + O(O),
  370 + U(U),
  371 + OE(OE),
  372 + UE(UE),
  373 + Perms(Perms),
  374 + id1(id1),
  375 + encrypt_metadata(encrypt_metadata)
  376 + {
  377 + }
  378 + Encryption(int V, int R, int Length_bytes, bool encrypt_metadata) :
  379 + V(V),
  380 + R(R),
  381 + Length_bytes(Length_bytes),
  382 + encrypt_metadata(encrypt_metadata)
  383 + {
  384 + }
  385 +
  386 + int getV() const;
  387 + int getR() const;
  388 + int getLengthBytes() const;
  389 + int getP() const;
  390 + // Bits in P are numbered from 1 as in the PDF spec.
  391 + bool getP(size_t bit) const;
  392 + std::string const& getO() const;
  393 + std::string const& getU() const;
  394 + std::string const& getOE() const;
  395 + std::string const& getUE() const;
  396 + std::string const& getPerms() const;
  397 + std::string const& getId1() const;
  398 + bool getEncryptMetadata() const;
  399 + // Bits in P are numbered from 1 as in the PDF spec.
  400 + void setP(size_t bit, bool val);
  401 + void setP(unsigned long val);
  402 + void setO(std::string const&);
  403 + void setU(std::string const&);
  404 + void setId1(std::string const& val);
  405 + void setV5EncryptionParameters(
  406 + std::string const& O,
  407 + std::string const& OE,
  408 + std::string const& U,
  409 + std::string const& UE,
  410 + std::string const& Perms);
  411 +
  412 + std::string compute_encryption_key(std::string const& password) const;
  413 +
  414 + bool
  415 + check_owner_password(std::string& user_password, std::string const& owner_password) const;
  416 +
  417 + bool check_user_password(std::string const& user_password) const;
  418 +
  419 + std::string
  420 + recover_encryption_key_with_password(std::string const& password, bool& perms_valid) const;
  421 +
  422 + void compute_encryption_O_U(char const* user_password, char const* owner_password);
  423 +
  424 + std::string
  425 + compute_encryption_parameters_V5(char const* user_password, char const* owner_password);
  426 +
  427 + std::string compute_parameters(char const* user_password, char const* owner_password);
  428 +
  429 + private:
  430 + static constexpr unsigned int OU_key_bytes_V4 = 16; // ( == sizeof(MD5::Digest)
  431 +
  432 + Encryption(Encryption const&) = delete;
  433 + Encryption& operator=(Encryption const&) = delete;
  434 +
  435 + std::string hash_V5(
  436 + std::string const& password, std::string const& salt, std::string const& udata) const;
  437 + std::string
  438 + compute_O_value(std::string const& user_password, std::string const& owner_password) const;
  439 + std::string compute_U_value(std::string const& user_password) const;
  440 + std::string compute_encryption_key_from_password(std::string const& password) const;
  441 + std::string recover_encryption_key_with_password(std::string const& password) const;
  442 + bool check_owner_password_V4(
  443 + std::string& user_password, std::string const& owner_password) const;
  444 + bool check_owner_password_V5(std::string const& owner_passworda) const;
  445 + std::string compute_Perms_value_V5_clear() const;
  446 + std::string compute_O_rc4_key(
  447 + std::string const& user_password, std::string const& owner_password) const;
  448 + std::string compute_U_value_R2(std::string const& user_password) const;
  449 + std::string compute_U_value_R3(std::string const& user_password) const;
  450 + bool check_user_password_V4(std::string const& user_password) const;
  451 + bool check_user_password_V5(std::string const& user_password) const;
  452 +
  453 + int V;
  454 + int R;
  455 + int Length_bytes;
  456 + std::bitset<32> P{0xfffffffc}; // Specification always requires bits 1 and 2 to be cleared.
  457 + std::string O;
  458 + std::string U;
  459 + std::string OE;
  460 + std::string UE;
  461 + std::string Perms;
  462 + std::string id1;
  463 + bool encrypt_metadata;
  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 + };
  571 +
  572 + class Objects
  573 + {
  574 + public:
  575 + Objects() = delete;
  576 + Objects(Objects const&) = delete;
  577 + Objects(Objects&&) = delete;
  578 + Objects& operator=(Objects const&) = delete;
  579 + Objects& operator=(Objects&&) = delete;
  580 + ~Objects() = default;
  581 +
  582 + Objects(QPDF& qpdf, QPDF::Members* m) :
  583 + qpdf(qpdf),
  584 + m(m)
  585 + {
  586 + }
  587 +
  588 + void parse(char const* password);
  589 + std::shared_ptr<QPDFObject> const& resolve(QPDFObjGen og);
  590 + void inParse(bool);
  591 + QPDFObjGen nextObjGen();
  592 + QPDFObjectHandle newIndirect(QPDFObjGen, std::shared_ptr<QPDFObject> const&);
  593 + void updateCache(
  594 + QPDFObjGen og,
  595 + std::shared_ptr<QPDFObject> const& object,
  596 + qpdf_offset_t end_before_space,
  597 + qpdf_offset_t end_after_space,
  598 + bool destroy = true);
  599 + bool resolveXRefTable();
  600 + QPDFObjectHandle readObjectAtOffset(
  601 + qpdf_offset_t offset, std::string const& description, bool skip_cache_if_in_xref);
  602 + QPDFTokenizer::Token readToken(InputSource& input, size_t max_len = 0);
  603 + QPDFObjectHandle makeIndirectFromQPDFObject(std::shared_ptr<QPDFObject> const& obj);
  604 + std::shared_ptr<QPDFObject> getObjectForParser(int id, int gen, bool parse_pdf);
  605 + std::shared_ptr<QPDFObject> getObjectForJSON(int id, int gen);
  606 + size_t tableSize();
  607 +
  608 + // For QPDFWriter:
  609 +
  610 + // Get a list of objects that would be permitted in an object stream.
  611 + template <typename T>
  612 + std::vector<T> getCompressibleObjGens();
  613 + std::vector<QPDFObjGen> getCompressibleObjVector();
  614 + std::vector<bool> getCompressibleObjSet();
  615 +
  616 + private:
  617 + void setTrailer(QPDFObjectHandle obj);
  618 + void reconstruct_xref(QPDFExc& e, bool found_startxref = true);
  619 + void read_xref(qpdf_offset_t offset, bool in_stream_recovery = false);
  620 + bool parse_xrefFirst(std::string const& line, int& obj, int& num, int& bytes);
  621 + bool read_xrefEntry(qpdf_offset_t& f1, int& f2, char& type);
  622 + bool read_bad_xrefEntry(qpdf_offset_t& f1, int& f2, char& type);
  623 + qpdf_offset_t read_xrefTable(qpdf_offset_t offset);
  624 + qpdf_offset_t read_xrefStream(qpdf_offset_t offset, bool in_stream_recovery = false);
  625 + qpdf_offset_t processXRefStream(
  626 + qpdf_offset_t offset, QPDFObjectHandle& xref_stream, bool in_stream_recovery = false);
  627 + std::pair<int, std::array<int, 3>>
  628 + processXRefW(QPDFObjectHandle& dict, std::function<QPDFExc(std::string_view)> damaged);
  629 + int processXRefSize(
  630 + QPDFObjectHandle& dict,
  631 + int entry_size,
  632 + std::function<QPDFExc(std::string_view)> damaged);
  633 + std::pair<int, std::vector<std::pair<int, int>>> processXRefIndex(
  634 + QPDFObjectHandle& dict,
  635 + int max_num_entries,
  636 + std::function<QPDFExc(std::string_view)> damaged);
  637 + void insertXrefEntry(int obj, int f0, qpdf_offset_t f1, int f2);
  638 + void insertFreeXrefEntry(QPDFObjGen);
  639 + QPDFObjectHandle readTrailer();
  640 + QPDFObjectHandle readObject(std::string const& description, QPDFObjGen og);
  641 + void readStream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset);
  642 + void validateStreamLineEnd(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset);
  643 + QPDFObjectHandle
  644 + readObjectInStream(qpdf::is::OffsetBuffer& input, int stream_id, int obj_id);
  645 + size_t recoverStreamLength(
  646 + std::shared_ptr<InputSource> input, QPDFObjGen og, qpdf_offset_t stream_offset);
  647 +
  648 + QPDFObjGen read_object_start(qpdf_offset_t offset);
  649 + void readObjectAtOffset(
  650 + bool attempt_recovery,
  651 + qpdf_offset_t offset,
  652 + std::string const& description,
  653 + QPDFObjGen exp_og);
  654 + void resolveObjectsInStream(int obj_stream_number);
  655 + bool isCached(QPDFObjGen og);
  656 + bool isUnresolved(QPDFObjGen og);
  657 + void setLastObjectDescription(std::string const& description, QPDFObjGen og);
  658 +
  659 + private:
  660 + QPDF& qpdf;
  661 + QPDF::Members* m;
  662 + }; // class QPDF::Doc::Objects
  663 +
  664 + // This class is used to represent a PDF Pages tree.
  665 + class Pages
  666 + {
  667 + public:
  668 + Pages() = delete;
  669 + Pages(Pages const&) = delete;
  670 + Pages(Pages&&) = delete;
  671 + Pages& operator=(Pages const&) = delete;
  672 + Pages& operator=(Pages&&) = delete;
  673 + ~Pages() = default;
  674 +
  675 + Pages(QPDF& qpdf, QPDF::Members* m) :
  676 + qpdf(qpdf),
  677 + m(m)
  678 + {
  679 + }
  680 +
  681 + void getAllPagesInternal(
  682 + QPDFObjectHandle cur_pages,
  683 + QPDFObjGen::set& visited,
  684 + QPDFObjGen::set& seen,
  685 + bool media_box,
  686 + bool resources);
  687 + void insertPage(QPDFObjectHandle newpage, int pos);
  688 + void flattenPagesTree();
  689 + void insertPageobjToPage(QPDFObjectHandle const& obj, int pos, bool check_duplicate);
  690 + void pushInheritedAttributesToPage(bool allow_changes, bool warn_skipped_keys);
  691 + void pushInheritedAttributesToPageInternal(
  692 + QPDFObjectHandle,
  693 + std::map<std::string, std::vector<QPDFObjectHandle>>&,
  694 + bool allow_changes,
  695 + bool warn_skipped_keys);
  696 +
  697 + private:
  698 + QPDF& qpdf;
  699 + QPDF::Members* m;
  700 + }; // class QPDF::Doc::Pages
  701 +
  702 + // StreamCopier class is restricted to QPDFObjectHandle so it can copy stream data.
  703 + class StreamCopier
  704 + {
  705 + friend class QPDFObjectHandle;
  706 +
  707 + private:
  708 + static void
  709 + copyStreamData(QPDF* qpdf, QPDFObjectHandle const& dest, QPDFObjectHandle const& src)
  710 + {
  711 + qpdf->copyStreamData(dest, src);
  712 + }
  713 + };
  714 +
  715 + Doc() = delete;
  716 + Doc(Doc const&) = delete;
  717 + Doc(Doc&&) = delete;
  718 + Doc& operator=(Doc const&) = delete;
  719 + Doc& operator=(Doc&&) = delete;
  720 + ~Doc() = default;
  721 +
  722 + Doc(QPDF& qpdf, QPDF::Members& m) :
  723 + qpdf(qpdf),
  724 + m(m),
  725 + lin_(qpdf, &m),
  726 + objects_(qpdf, &m),
  727 + pages_(qpdf, &m)
  728 + {
  729 + }
  730 +
  731 + Linearization&
  732 + linearization()
  733 + {
  734 + return lin_;
  735 + };
  736 +
  737 + Objects&
  738 + objects()
  739 + {
  740 + return objects_;
  741 + };
  742 +
  743 + Pages&
  744 + pages()
  745 + {
  746 + return pages_;
  747 + }
  748 +
  749 + bool reconstructed_xref() const;
  750 +
  751 + QPDFAcroFormDocumentHelper&
  752 + acroform()
  753 + {
  754 + if (!acroform_) {
  755 + acroform_ = std::make_unique<QPDFAcroFormDocumentHelper>(qpdf);
  756 + }
  757 + return *acroform_;
  758 + }
  759 +
  760 + QPDFEmbeddedFileDocumentHelper&
  761 + embedded_files()
  762 + {
  763 + if (!embedded_files_) {
  764 + embedded_files_ = std::make_unique<QPDFEmbeddedFileDocumentHelper>(qpdf);
  765 + }
  766 + return *embedded_files_;
  767 + }
  768 +
  769 + QPDFOutlineDocumentHelper&
  770 + outlines()
  771 + {
  772 + if (!outlines_) {
  773 + outlines_ = std::make_unique<QPDFOutlineDocumentHelper>(qpdf);
  774 + }
  775 + return *outlines_;
  776 + }
  777 +
  778 + QPDFPageDocumentHelper&
  779 + page_dh()
  780 + {
  781 + if (!page_dh_) {
  782 + page_dh_ = std::make_unique<QPDFPageDocumentHelper>(qpdf);
  783 + }
  784 + return *page_dh_;
  785 + }
  786 +
  787 + QPDFPageLabelDocumentHelper&
  788 + page_labels()
  789 + {
  790 + if (!page_labels_) {
  791 + page_labels_ = std::make_unique<QPDFPageLabelDocumentHelper>(qpdf);
  792 + }
  793 + return *page_labels_;
  794 + }
  795 +
  796 + private:
  797 + QPDF& qpdf;
  798 + QPDF::Members& m;
  799 +
  800 + Linearization lin_;
  801 + Objects objects_;
  802 + Pages pages_;
  803 +
  804 + // Document Helpers;
  805 + std::unique_ptr<QPDFAcroFormDocumentHelper> acroform_;
  806 + std::unique_ptr<QPDFEmbeddedFileDocumentHelper> embedded_files_;
  807 + std::unique_ptr<QPDFOutlineDocumentHelper> outlines_;
  808 + std::unique_ptr<QPDFPageDocumentHelper> page_dh_;
  809 + std::unique_ptr<QPDFPageLabelDocumentHelper> page_labels_;
  810 +};
  811 +
408 class QPDF::Members 812 class QPDF::Members
409 { 813 {
410 friend class QPDF; 814 friend class QPDF;
411 friend class ResolveRecorder; 815 friend class ResolveRecorder;
412 816
413 public: 817 public:
414 - Members(); 818 + Members(QPDF& qpdf);
415 Members(Members const&) = delete; 819 Members(Members const&) = delete;
416 ~Members() = default; 820 ~Members() = default;
417 821
418 private: 822 private:
  823 + Doc doc;
  824 + Doc::Linearization& lin;
  825 + Doc::Objects& objects;
  826 + Doc::Pages& pages;
419 std::shared_ptr<QPDFLogger> log; 827 std::shared_ptr<QPDFLogger> log;
420 unsigned long long unique_id{0}; 828 unsigned long long unique_id{0};
421 qpdf::Tokenizer tokenizer; 829 qpdf::Tokenizer tokenizer;
@@ -488,78 +896,33 @@ class QPDF::Members @@ -488,78 +896,33 @@ class QPDF::Members
488 // Optimization data 896 // Optimization data
489 std::map<ObjUser, std::set<QPDFObjGen>> obj_user_to_objects; 897 std::map<ObjUser, std::set<QPDFObjGen>> obj_user_to_objects;
490 std::map<QPDFObjGen, std::set<ObjUser>> object_to_obj_users; 898 std::map<QPDFObjGen, std::set<ObjUser>> object_to_obj_users;
491 -  
492 - // Document Helpers;  
493 - std::unique_ptr<QPDFAcroFormDocumentHelper> acroform;  
494 - std::unique_ptr<QPDFEmbeddedFileDocumentHelper> embedded_files;  
495 - std::unique_ptr<QPDFOutlineDocumentHelper> outlines;  
496 - std::unique_ptr<QPDFPageDocumentHelper> pages;  
497 - std::unique_ptr<QPDFPageLabelDocumentHelper> page_labels;  
498 }; 899 };
499 900
500 -// JobSetter class is restricted to QPDFJob.  
501 -class QPDF::JobSetter 901 +// The Resolver class is restricted to QPDFObject and BaseHandle so that only it can resolve
  902 +// indirect references.
  903 +class QPDF::Doc::Resolver
502 { 904 {
503 - friend class QPDFJob; 905 + friend class QPDFObject;
  906 + friend class qpdf::BaseHandle;
504 907
505 private: 908 private:
506 - // Enable enhanced warnings for pdf file checking.  
507 - static void  
508 - setCheckMode(QPDF& qpdf, bool val) 909 + static std::shared_ptr<QPDFObject> const&
  910 + resolved(QPDF* qpdf, QPDFObjGen og)
509 { 911 {
510 - qpdf.m->check_mode = val; 912 + return qpdf->m->objects.resolve(og);
511 } 913 }
512 }; 914 };
513 915
514 inline bool 916 inline bool
515 -QPDF::reconstructed_xref() const  
516 -{  
517 - return m->reconstructed_xref;  
518 -}  
519 -  
520 -inline QPDFAcroFormDocumentHelper&  
521 -QPDF::acroform()  
522 -{  
523 - if (!m->acroform) {  
524 - m->acroform = std::make_unique<QPDFAcroFormDocumentHelper>(*this);  
525 - }  
526 - return *m->acroform;  
527 -}  
528 -  
529 -inline QPDFEmbeddedFileDocumentHelper&  
530 -QPDF::embedded_files() 917 +QPDF::Doc::reconstructed_xref() const
531 { 918 {
532 - if (!m->embedded_files) {  
533 - m->embedded_files = std::make_unique<QPDFEmbeddedFileDocumentHelper>(*this);  
534 - }  
535 - return *m->embedded_files; 919 + return m.reconstructed_xref;
536 } 920 }
537 921
538 -inline QPDFOutlineDocumentHelper&  
539 -QPDF::outlines() 922 +inline QPDF::Doc&
  923 +QPDF::doc()
540 { 924 {
541 - if (!m->outlines) {  
542 - m->outlines = std::make_unique<QPDFOutlineDocumentHelper>(*this);  
543 - }  
544 - return *m->outlines;  
545 -}  
546 -  
547 -inline QPDFPageDocumentHelper&  
548 -QPDF::pages()  
549 -{  
550 - if (!m->pages) {  
551 - m->pages = std::make_unique<QPDFPageDocumentHelper>(*this);  
552 - }  
553 - return *m->pages;  
554 -}  
555 -  
556 -inline QPDFPageLabelDocumentHelper&  
557 -QPDF::page_labels()  
558 -{  
559 - if (!m->page_labels) {  
560 - m->page_labels = std::make_unique<QPDFPageLabelDocumentHelper>(*this);  
561 - }  
562 - return *m->page_labels; 925 + return m->doc;
563 } 926 }
564 927
565 // Throw a generic exception for unusual error conditions that do not be covered during CI testing. 928 // Throw a generic exception for unusual error conditions that do not be covered during CI testing.
qpdf/qpdf.testcov
1 ignored-scope: libtests 1 ignored-scope: libtests
2 -QPDF err expected endobj 0  
3 -QPDF err wrong objid/generation 0  
4 -QPDF check objid 1  
5 -QPDF check generation 1  
6 -QPDF check obj 1  
7 -QPDF object stream offsets not increasing 0  
8 -QPDF ignore self-referential object stream 0  
9 -QPDF object stream contains id < 1 0  
10 QPDF hint table length direct 0 2 QPDF hint table length direct 0
11 QPDF P absent in lindict 1 3 QPDF P absent in lindict 1
12 -QPDF expected n n obj 0  
13 QPDF opt direct pages resource 1 4 QPDF opt direct pages resource 1
14 -QPDF opt inheritable keys 0  
15 QPDF opt no inheritable keys 0 5 QPDF opt no inheritable keys 0
16 -QPDF opt erase empty key ancestor 0  
17 -QPDF opt resource inherited 0  
18 QPDF opt page resource hides ancestor 0 6 QPDF opt page resource hides ancestor 0
19 -QPDF opt key ancestors depth > 1 0  
20 QPDF opt loop detected 0 7 QPDF opt loop detected 0
21 QPDF categorize pagemode present 1 8 QPDF categorize pagemode present 1
22 QPDF categorize pagemode outlines 1 9 QPDF categorize pagemode outlines 1
@@ -39,26 +26,13 @@ main QTest dictionary indirect 1 @@ -39,26 +26,13 @@ main QTest dictionary indirect 1
39 main QTest stream 0 26 main QTest stream 0
40 QPDF lin write nshared_total > nshared_first_page 1 27 QPDF lin write nshared_total > nshared_first_page 1
41 QPDFWriter encrypted hint stream 0 28 QPDFWriter encrypted hint stream 0
42 -QPDF opt inherited scalar 0  
43 -QPDF xref reused object 0  
44 QPDF xref gen > 0 1 29 QPDF xref gen > 0 1
45 -QPDF xref size mismatch 0  
46 -QPDF not a pdf file 0  
47 -QPDF can't find startxref 0  
48 QPDF startxref more than 1024 before end 0 30 QPDF startxref more than 1024 before end 0
49 -QPDF invalid xref 0  
50 -QPDF invalid xref entry 0  
51 -QPDF missing trailer 0  
52 -QPDF trailer lacks size 0  
53 -QPDF trailer size not integer 0  
54 -QPDF trailer prev not integer 0  
55 QPDFParser bad brace 0 31 QPDFParser bad brace 0
56 QPDFParser bad brace in parseRemainder 0 32 QPDFParser bad brace in parseRemainder 0
57 QPDFParser bad array close 0 33 QPDFParser bad array close 0
58 QPDFParser bad array close in parseRemainder 0 34 QPDFParser bad array close in parseRemainder 0
59 QPDFParser bad dictionary close 0 35 QPDFParser bad dictionary close 0
60 -QPDFParser bad dictionary close in parseRemainder 0  
61 -QPDF can't find xref 0  
62 QPDFTokenizer bad ) 0 36 QPDFTokenizer bad ) 0
63 QPDFTokenizer bad > 0 37 QPDFTokenizer bad > 0
64 QPDFTokenizer bad hexstring character 0 38 QPDFTokenizer bad hexstring character 0
@@ -69,26 +43,15 @@ QPDFTokenizer bad name 2 0 @@ -69,26 +43,15 @@ QPDFTokenizer bad name 2 0
69 QPDF UseOutlines but no Outlines 0 43 QPDF UseOutlines but no Outlines 0
70 QPDFObjectHandle makeDirect loop 0 44 QPDFObjectHandle makeDirect loop 0
71 QPDFObjectHandle copy stream 1 45 QPDFObjectHandle copy stream 1
72 -QPDF default for xref stream field 0 0  
73 -QPDF prev key in xref stream dictionary 0  
74 -QPDF prev key in trailer dictionary 0  
75 -QPDF found xref stream 0  
76 QPDF ignoring XRefStm in trailer 0 46 QPDF ignoring XRefStm in trailer 0
77 -QPDF xref deleted object 0  
78 SF_FlateLzwDecode PNG filter 0 47 SF_FlateLzwDecode PNG filter 0
79 QPDF xref /Index is array 1 48 QPDF xref /Index is array 1
80 QPDFWriter encrypt object stream 0 49 QPDFWriter encrypt object stream 0
81 QPDF exclude indirect length 0 50 QPDF exclude indirect length 0
82 QPDF exclude encryption dictionary 0 51 QPDF exclude encryption dictionary 0
83 -QPDF loop detected traversing objects 0  
84 -QPDF reconstructed xref table 0  
85 -QPDF recovered in readObjectAtOffset 0  
86 -QPDF recovered stream length 0  
87 -QPDF found wrong endstream in recovery 0  
88 QPDF_Stream pipeStreamData with null pipeline 0 52 QPDF_Stream pipeStreamData with null pipeline 0
89 QPDFJob unable to filter 0 53 QPDFJob unable to filter 0
90 QUtil non-trivial UTF-16 0 54 QUtil non-trivial UTF-16 0
91 -QPDF xref overwrite invalid objgen 0  
92 QPDF decoding error warning 0 55 QPDF decoding error warning 0
93 qpdf-c called qpdf_init 0 56 qpdf-c called qpdf_init 0
94 qpdf-c called qpdf_cleanup 0 57 qpdf-c called qpdf_cleanup 0
@@ -133,8 +96,6 @@ QPDF_encryption aes decode string 0 @@ -133,8 +96,6 @@ QPDF_encryption aes decode string 0
133 QPDFWriter forced version disabled encryption 0 96 QPDFWriter forced version disabled encryption 0
134 qpdf-c called qpdf_set_r4_encryption_parameters_insecure 0 97 qpdf-c called qpdf_set_r4_encryption_parameters_insecure 0
135 qpdf-c called qpdf_set_static_aes_IV 0 98 qpdf-c called qpdf_set_static_aes_IV 0
136 -QPDF ERR object stream with wrong type 0  
137 -QPDF object gone after xref reconstruction 0  
138 qpdf-c called qpdf_has_error 0 99 qpdf-c called qpdf_has_error 0
139 qpdf-c called qpdf_get_qpdf_version 0 100 qpdf-c called qpdf_get_qpdf_version 0
140 QPDF_Stream pipe original stream data 0 101 QPDF_Stream pipe original stream data 0
@@ -148,11 +109,7 @@ QPDFObjectHandle append page contents 0 @@ -148,11 +109,7 @@ QPDFObjectHandle append page contents 0
148 QPDF_Stream getRawStreamData 0 109 QPDF_Stream getRawStreamData 0
149 QPDF_Stream getStreamData 0 110 QPDF_Stream getStreamData 0
150 qpdf-c called qpdf_read_memory 0 111 qpdf-c called qpdf_read_memory 0
151 -QPDF stream without newline 0  
152 -QPDF stream with CR only 0  
153 QPDF stream with CRNL 0 112 QPDF stream with CRNL 0
154 -QPDF stream with NL only 0  
155 -QPDF replaceObject called with indirect object 0  
156 QPDFWriter copy encrypt metadata 1 113 QPDFWriter copy encrypt metadata 1
157 qpdf-c get_info_key 1 114 qpdf-c get_info_key 1
158 qpdf-c set_info_key to value 0 115 qpdf-c set_info_key to value 0
@@ -165,12 +122,9 @@ exercise processFile(FILE*) 0 @@ -165,12 +122,9 @@ exercise processFile(FILE*) 0
165 exercise processMemoryFile 0 122 exercise processMemoryFile 0
166 QPDF remove page 2 123 QPDF remove page 2
167 QPDF insert page 2 124 QPDF insert page 2
168 -QPDF updateAllPagesCache 0  
169 -QPDF insert non-indirect page 0  
170 QPDF insert indirect page 0 125 QPDF insert indirect page 0
171 QPDF_Stream ERR shallow copy stream 0 126 QPDF_Stream ERR shallow copy stream 0
172 QPDFObjectHandle newStream with string 0 127 QPDFObjectHandle newStream with string 0
173 -QPDF unknown key not inherited 0  
174 QPDF_Stream provider length not provided 0 128 QPDF_Stream provider length not provided 0
175 QPDF_Stream unknown stream length 0 129 QPDF_Stream unknown stream length 0
176 QPDF replaceReserved 0 130 QPDF replaceReserved 0
@@ -181,14 +135,12 @@ QPDF replace array 0 @@ -181,14 +135,12 @@ QPDF replace array 0
181 QPDF replace dictionary 0 135 QPDF replace dictionary 0
182 QPDF replace stream 0 136 QPDF replace stream 0
183 QPDF replace foreign indirect with null 0 137 QPDF replace foreign indirect with null 0
184 -QPDF insert foreign page 0  
185 QPDFWriter copy use_aes 1 138 QPDFWriter copy use_aes 1
186 QPDFParser indirect without context 0 139 QPDFParser indirect without context 0
187 QPDFObjectHandle trailing data in parse 0 140 QPDFObjectHandle trailing data in parse 0
188 QPDFTokenizer EOF reading token 0 141 QPDFTokenizer EOF reading token 0
189 QPDFTokenizer EOF reading appendable token 0 142 QPDFTokenizer EOF reading appendable token 0
190 QPDFWriter extra header text no newline 0 143 QPDFWriter extra header text no newline 0
191 -QPDF bogus 0 offset 0  
192 QPDF global offset 0 144 QPDF global offset 0
193 QPDFWriter make Extensions direct 0 145 QPDFWriter make Extensions direct 0
194 QPDFWriter make ADBE direct 1 146 QPDFWriter make ADBE direct 1
@@ -202,29 +154,21 @@ qpdf-c called qpdf_set_r6_encryption_parameters 0 @@ -202,29 +154,21 @@ qpdf-c called qpdf_set_r6_encryption_parameters 0
202 QPDFObjectHandle EOF in inline image 0 154 QPDFObjectHandle EOF in inline image 0
203 QPDFObjectHandle inline image token 0 155 QPDFObjectHandle inline image token 0
204 QPDF not caching overridden objstm object 0 156 QPDF not caching overridden objstm object 0
205 -QPDF_optimization indirect outlines 0  
206 QPDF xref space 2 157 QPDF xref space 2
207 QPDFJob pages range omitted in middle 0 158 QPDFJob pages range omitted in middle 0
208 QPDFWriter standard deterministic ID 1 159 QPDFWriter standard deterministic ID 1
209 QPDFWriter linearized deterministic ID 1 160 QPDFWriter linearized deterministic ID 1
210 qpdf-c called qpdf_set_deterministic_ID 0 161 qpdf-c called qpdf_set_deterministic_ID 0
211 QPDFParser invalid objgen 0 162 QPDFParser invalid objgen 0
212 -QPDF object id 0 0  
213 -QPDF recursion loop in resolve 0  
214 QPDFParser treat word as string 0 163 QPDFParser treat word as string 0
215 QPDFParser treat word as string in parseRemainder 0 164 QPDFParser treat word as string in parseRemainder 0
216 QPDFParser found fake 1 165 QPDFParser found fake 1
217 QPDFParser no val for last key 0 166 QPDFParser no val for last key 0
218 -QPDF resolve failure to null 0  
219 QPDFObjectHandle errors in parsecontent 0 167 QPDFObjectHandle errors in parsecontent 0
220 QPDFJob split-pages %d 0 168 QPDFJob split-pages %d 0
221 QPDFJob split-pages .pdf 0 169 QPDFJob split-pages .pdf 0
222 QPDFJob split-pages other 0 170 QPDFJob split-pages other 0
223 QPDFTokenizer allowing bad token 0 171 QPDFTokenizer allowing bad token 0
224 -QPDF ignore first space in xref entry 0  
225 -QPDF ignore first extra space in xref entry 0  
226 -QPDF ignore second extra space in xref entry 0  
227 -QPDF ignore length error xref entry 0  
228 QPDF_encryption pad short parameter 0 172 QPDF_encryption pad short parameter 0
229 QPDFObjectHandle found old angle 1 173 QPDFObjectHandle found old angle 1
230 QPDFTokenizer block long token 0 174 QPDFTokenizer block long token 0
@@ -261,7 +205,6 @@ QPDFObjectHandle dictionary ignoring replaceKey 0 @@ -261,7 +205,6 @@ QPDFObjectHandle dictionary ignoring replaceKey 0
261 QPDFObjectHandle numeric non-numeric 0 205 QPDFObjectHandle numeric non-numeric 0
262 QPDFObjectHandle erase array bounds 0 206 QPDFObjectHandle erase array bounds 0
263 qpdf-c called qpdf_check_pdf 0 207 qpdf-c called qpdf_check_pdf 0
264 -QPDF xref loop 0  
265 QPDFParser too deep 0 208 QPDFParser too deep 0
266 QPDFFormFieldObjectHelper TU present 0 209 QPDFFormFieldObjectHelper TU present 0
267 QPDFFormFieldObjectHelper TM present 0 210 QPDFFormFieldObjectHelper TM present 0
@@ -330,9 +273,6 @@ QPDFPageDocumentHelper ignore annotation with no appearance 0 @@ -330,9 +273,6 @@ QPDFPageDocumentHelper ignore annotation with no appearance 0
330 QPDFFormFieldObjectHelper replaced BMC at EOF 0 273 QPDFFormFieldObjectHelper replaced BMC at EOF 0
331 QPDFFormFieldObjectHelper fallback Tf 0 274 QPDFFormFieldObjectHelper fallback Tf 0
332 QPDFPageObjectHelper copy shared attribute 1 275 QPDFPageObjectHelper copy shared attribute 1
333 -QPDF resolve duplicated page object 0  
334 -QPDF handle direct page object 0  
335 -QPDF missing mediabox 0  
336 QPDF inherit mediabox 1 276 QPDF inherit mediabox 1
337 QPDFTokenizer finder found wrong word 0 277 QPDFTokenizer finder found wrong word 0
338 QPDFTokenizer found EI by byte count 0 278 QPDFTokenizer found EI by byte count 0
@@ -341,7 +281,6 @@ QPDFPageObjectHelper externalize inline image 0 @@ -341,7 +281,6 @@ QPDFPageObjectHelper externalize inline image 0
341 QPDFPageObjectHelper keep inline image 0 281 QPDFPageObjectHelper keep inline image 0
342 QPDFJob image optimize colorspace 0 282 QPDFJob image optimize colorspace 0
343 QPDFJob image optimize bits per component 0 283 QPDFJob image optimize bits per component 0
344 -QPDF xref skipped space 0  
345 QPDF eof skipping spaces before xref 1 284 QPDF eof skipping spaces before xref 1
346 QPDF_encryption user matches owner V < 5 0 285 QPDF_encryption user matches owner V < 5 0
347 QPDF_encryption same password 1 286 QPDF_encryption same password 1
@@ -451,9 +390,7 @@ QPDFAcroFormDocumentHelper /DA parse error 0 @@ -451,9 +390,7 @@ QPDFAcroFormDocumentHelper /DA parse error 0
451 QPDFAcroFormDocumentHelper AP parse error 1 390 QPDFAcroFormDocumentHelper AP parse error 1
452 QPDFJob copy fields not this file 0 391 QPDFJob copy fields not this file 0
453 QPDFJob copy fields non-first from orig 0 392 QPDFJob copy fields non-first from orig 0
454 -QPDF resolve duplicated page in insert 0  
455 QPDFWriter exclude from object stream 0 393 QPDFWriter exclude from object stream 0
456 -QPDF_pages findPage not found 0  
457 QPDFJob weak crypto error 0 394 QPDFJob weak crypto error 0
458 qpdf-c called qpdf_oh_is_initialized 0 395 qpdf-c called qpdf_oh_is_initialized 0
459 qpdf-c registered progress reporter 0 396 qpdf-c registered progress reporter 0
@@ -533,7 +470,6 @@ QPDF_json bad calledgetallpages 0 @@ -533,7 +470,6 @@ QPDF_json bad calledgetallpages 0
533 QPDF_json bad pushedinheritedpageresources 0 470 QPDF_json bad pushedinheritedpageresources 0
534 QPDFPageObjectHelper used fallback without copying 0 471 QPDFPageObjectHelper used fallback without copying 0
535 QPDF skipping cache for known unchecked object 0 472 QPDF skipping cache for known unchecked object 0
536 -QPDF fix dangling triggered xref reconstruction 0  
537 QPDF recover xref stream 0 473 QPDF recover xref stream 0
538 QPDFJob json over/under no file 0 474 QPDFJob json over/under no file 0
539 QPDF_Array copy 1 475 QPDF_Array copy 1