Commit 5a8ba44bea776212d5b9f79fa7c826e6a9a944fc

Authored by m-holger
Committed by GitHub
2 parents 4ba6377f 306e8093

Merge pull request #1558 from m-holger/qpdf_hh

Remove implementation detail from QPDF header file
include/qpdf/QPDF.hh
@@ -787,18 +787,6 @@ class QPDF @@ -787,18 +787,6 @@ class QPDF
787 bool is_root_metadata, 787 bool is_root_metadata,
788 std::unique_ptr<Pipeline>& heap); 788 std::unique_ptr<Pipeline>& heap);
789 789
790 - struct HPageOffsetEntry;  
791 - struct HPageOffset;  
792 - struct HSharedObjectEntry;  
793 - struct HSharedObject;  
794 - struct HGeneric;  
795 - struct LinParameters;  
796 - struct CHPageOffsetEntry;  
797 - struct CHPageOffset;  
798 - struct CHSharedObjectEntry;  
799 - struct CHSharedObject;  
800 - class ObjUser;  
801 - struct UpdateObjectMapsFrame;  
802 class PatternFinder; 790 class PatternFinder;
803 791
804 // Methods to support pattern finding 792 // Methods to support pattern finding
include/qpdf/QPDFJob.hh
@@ -460,7 +460,6 @@ class QPDFJob @@ -460,7 +460,6 @@ class QPDFJob
460 bool main_input); 460 bool main_input);
461 461
462 // Transformations 462 // Transformations
463 - void setQPDFOptions(QPDF& pdf);  
464 void handlePageSpecs(QPDF& pdf); 463 void handlePageSpecs(QPDF& pdf);
465 bool shouldRemoveUnreferencedResources(QPDF& pdf); 464 bool shouldRemoveUnreferencedResources(QPDF& pdf);
466 void handleRotations(QPDF& pdf); 465 void handleRotations(QPDF& pdf);
libqpdf/QPDF.cc
@@ -27,9 +27,9 @@ @@ -27,9 +27,9 @@
27 using namespace qpdf; 27 using namespace qpdf;
28 using namespace std::literals; 28 using namespace std::literals;
29 29
30 -using Doc = QPDF::Doc;  
31 -using Common = Doc::Common;  
32 -using Objects = Doc::Objects; 30 +using QDoc = QPDF::Doc;
  31 +using Common = QDoc::Common;
  32 +using Objects = QDoc::Objects;
33 using Foreign = Objects::Foreign; 33 using Foreign = Objects::Foreign;
34 using Streams = Objects::Streams; 34 using Streams = Objects::Streams;
35 35
@@ -133,7 +133,6 @@ QPDF::Members::Members(QPDF&amp; qpdf) : @@ -133,7 +133,6 @@ QPDF::Members::Members(QPDF&amp; qpdf) :
133 lin(*this), 133 lin(*this),
134 objects(*this), 134 objects(*this),
135 pages(*this), 135 pages(*this),
136 - log(QPDFLogger::defaultLogger()),  
137 file(std::make_shared<InvalidInputSource>()), 136 file(std::make_shared<InvalidInputSource>()),
138 encp(std::make_shared<EncryptionParameters>()) 137 encp(std::make_shared<EncryptionParameters>())
139 { 138 {
@@ -232,7 +231,7 @@ QPDF::closeInputSource() @@ -232,7 +231,7 @@ QPDF::closeInputSource()
232 void 231 void
233 QPDF::setPasswordIsHexKey(bool val) 232 QPDF::setPasswordIsHexKey(bool val)
234 { 233 {
235 - m->provided_password_is_hex_key = val; 234 + m->cf.password_is_hex_key(val);
236 } 235 }
237 236
238 void 237 void
@@ -251,56 +250,56 @@ QPDF::registerStreamFilter( @@ -251,56 +250,56 @@ QPDF::registerStreamFilter(
251 void 250 void
252 QPDF::setIgnoreXRefStreams(bool val) 251 QPDF::setIgnoreXRefStreams(bool val)
253 { 252 {
254 - m->ignore_xref_streams = val; 253 + (void)m->cf.ignore_xref_streams(val);
255 } 254 }
256 255
257 std::shared_ptr<QPDFLogger> 256 std::shared_ptr<QPDFLogger>
258 QPDF::getLogger() 257 QPDF::getLogger()
259 { 258 {
260 - return m->log; 259 + return m->cf.log();
261 } 260 }
262 261
263 void 262 void
264 QPDF::setLogger(std::shared_ptr<QPDFLogger> l) 263 QPDF::setLogger(std::shared_ptr<QPDFLogger> l)
265 { 264 {
266 - m->log = l; 265 + m->cf.log(l);
267 } 266 }
268 267
269 void 268 void
270 QPDF::setOutputStreams(std::ostream* out, std::ostream* err) 269 QPDF::setOutputStreams(std::ostream* out, std::ostream* err)
271 { 270 {
272 setLogger(QPDFLogger::create()); 271 setLogger(QPDFLogger::create());
273 - m->log->setOutputStreams(out, err); 272 + m->cf.log()->setOutputStreams(out, err);
274 } 273 }
275 274
276 void 275 void
277 QPDF::setSuppressWarnings(bool val) 276 QPDF::setSuppressWarnings(bool val)
278 { 277 {
279 - m->suppress_warnings = val; 278 + (void)m->cf.suppress_warnings(val);
280 } 279 }
281 280
282 void 281 void
283 QPDF::setMaxWarnings(size_t val) 282 QPDF::setMaxWarnings(size_t val)
284 { 283 {
285 - m->max_warnings = val; 284 + (void)m->cf.max_warnings(val);
286 } 285 }
287 286
288 void 287 void
289 QPDF::setAttemptRecovery(bool val) 288 QPDF::setAttemptRecovery(bool val)
290 { 289 {
291 - m->attempt_recovery = val; 290 + (void)m->cf.surpress_recovery(!val);
292 } 291 }
293 292
294 void 293 void
295 QPDF::setImmediateCopyFrom(bool val) 294 QPDF::setImmediateCopyFrom(bool val)
296 { 295 {
297 - m->immediate_copy_from = val; 296 + (void)m->cf.immediate_copy_from(val);
298 } 297 }
299 298
300 std::vector<QPDFExc> 299 std::vector<QPDFExc>
301 QPDF::getWarnings() 300 QPDF::getWarnings()
302 { 301 {
303 - std::vector<QPDFExc> result = m->warnings; 302 + std::vector<QPDFExc> result = std::move(m->warnings);
304 m->warnings.clear(); 303 m->warnings.clear();
305 return result; 304 return result;
306 } 305 }
@@ -372,12 +371,12 @@ QPDF::warn(QPDFExc const&amp; e) @@ -372,12 +371,12 @@ QPDF::warn(QPDFExc const&amp; e)
372 void 371 void
373 Common::warn(QPDFExc const& e) 372 Common::warn(QPDFExc const& e)
374 { 373 {
375 - if (m->max_warnings > 0 && m->warnings.size() >= m->max_warnings) { 374 + if (cf.max_warnings() > 0 && m->warnings.size() >= cf.max_warnings()) {
376 stopOnError("Too many warnings - file is too badly damaged"); 375 stopOnError("Too many warnings - file is too badly damaged");
377 } 376 }
378 - m->warnings.push_back(e);  
379 - if (!m->suppress_warnings) {  
380 - *m->log->getWarn() << "WARNING: " << m->warnings.back().what() << "\n"; 377 + m->warnings.emplace_back(e);
  378 + if (!cf.suppress_warnings()) {
  379 + *cf.log()->getWarn() << "WARNING: " << m->warnings.back().what() << "\n";
381 } 380 }
382 } 381 }
383 382
@@ -715,7 +714,7 @@ QPDF::getRoot() @@ -715,7 +714,7 @@ QPDF::getRoot()
715 } else if ( 714 } else if (
716 // Check_mode is an interim solution to request #810 pending a more comprehensive review of 715 // Check_mode is an interim solution to request #810 pending a more comprehensive review of
717 // the approach to more extensive checks and warning levels. 716 // the approach to more extensive checks and warning levels.
718 - m->check_mode && !root.getKey("/Type").isNameAndEquals("/Catalog")) { 717 + m->cf.check_mode() && !root.getKey("/Type").isNameAndEquals("/Catalog")) {
719 warn(m->c.damagedPDF("", -1, "catalog /Type entry missing or invalid")); 718 warn(m->c.damagedPDF("", -1, "catalog /Type entry missing or invalid"));
720 root.replaceKey("/Type", "/Catalog"_qpdf); 719 root.replaceKey("/Type", "/Catalog"_qpdf);
721 } 720 }
libqpdf/QPDFJob.cc
@@ -30,20 +30,8 @@ @@ -30,20 +30,8 @@
30 30
31 using namespace qpdf; 31 using namespace qpdf;
32 32
33 -using Doc = QPDF::Doc;  
34 -using Pages = Doc::Pages;  
35 -  
36 -// JobSetter class is restricted to QPDFJob.  
37 -class Doc::JobSetter  
38 -{  
39 - public:  
40 - // Enable enhanced warnings for pdf file checking.  
41 - static void  
42 - setCheckMode(QPDF& qpdf, bool val)  
43 - {  
44 - qpdf.m->check_mode = val;  
45 - }  
46 -}; 33 +using QDoc = QPDF::Doc;
  34 +using Pages = QDoc::Pages;
47 35
48 namespace 36 namespace
49 { 37 {
@@ -489,7 +477,7 @@ QPDFJob::writeQPDF(QPDF&amp; pdf) @@ -489,7 +477,7 @@ QPDFJob::writeQPDF(QPDF&amp; pdf)
489 if (!pdf.getWarnings().empty()) { 477 if (!pdf.getWarnings().empty()) {
490 m->warnings = true; 478 m->warnings = true;
491 } 479 }
492 - if (m->warnings && (!m->suppress_warnings)) { 480 + if (m->warnings && !m->qcf.suppress_warnings()) {
493 if (createsOutput()) { 481 if (createsOutput()) {
494 *m->log->getWarn() 482 *m->log->getWarn()
495 << m->message_prefix 483 << m->message_prefix
@@ -647,24 +635,6 @@ QPDFJob::getEncryptionStatus() @@ -647,24 +635,6 @@ QPDFJob::getEncryptionStatus()
647 return m->encryption_status; 635 return m->encryption_status;
648 } 636 }
649 637
650 -void  
651 -QPDFJob::setQPDFOptions(QPDF& pdf)  
652 -{  
653 - pdf.setLogger(m->log);  
654 - if (m->ignore_xref_streams) {  
655 - pdf.setIgnoreXRefStreams(true);  
656 - }  
657 - if (m->suppress_recovery) {  
658 - pdf.setAttemptRecovery(false);  
659 - }  
660 - if (m->password_is_hex_key) {  
661 - pdf.setPasswordIsHexKey(true);  
662 - }  
663 - if (m->suppress_warnings) {  
664 - pdf.setSuppressWarnings(true);  
665 - }  
666 -}  
667 -  
668 static std::string 638 static std::string
669 show_bool(bool v) 639 show_bool(bool v)
670 { 640 {
@@ -749,7 +719,6 @@ QPDFJob::doCheck(QPDF&amp; pdf) @@ -749,7 +719,6 @@ QPDFJob::doCheck(QPDF&amp; pdf)
749 bool okay = true; 719 bool okay = true;
750 auto& cout = *m->log->getInfo(); 720 auto& cout = *m->log->getInfo();
751 cout << "checking " << m->infile_name() << "\n"; 721 cout << "checking " << m->infile_name() << "\n";
752 - Doc::JobSetter::setCheckMode(pdf, true);  
753 try { 722 try {
754 int extension_level = pdf.getExtensionLevel(); 723 int extension_level = pdf.getExtensionLevel();
755 cout << "PDF Version: " << pdf.getPDFVersion(); 724 cout << "PDF Version: " << pdf.getPDFVersion();
@@ -1740,7 +1709,7 @@ QPDFJob::doProcessOnce( @@ -1740,7 +1709,7 @@ QPDFJob::doProcessOnce(
1740 bool main_input) 1709 bool main_input)
1741 { 1710 {
1742 pdf = std::make_unique<QPDF>(); 1711 pdf = std::make_unique<QPDF>();
1743 - setQPDFOptions(*pdf); 1712 + pdf->doc().config(m->qcf.log(m->log));
1744 if (empty) { 1713 if (empty) {
1745 pdf->emptyPDF(); 1714 pdf->emptyPDF();
1746 } else if (main_input && m->json_input) { 1715 } else if (main_input && m->json_input) {
@@ -1770,16 +1739,15 @@ QPDFJob::doProcess( @@ -1770,16 +1739,15 @@ QPDFJob::doProcess(
1770 // was incorrectly encoded, there's a good chance we'd succeed here. 1739 // was incorrectly encoded, there's a good chance we'd succeed here.
1771 1740
1772 std::string ptemp; 1741 std::string ptemp;
1773 - if (password && (!m->password_is_hex_key)) { 1742 + if (password && !m->qcf.password_is_hex_key()) {
1774 if (m->password_mode == QPDFJob::pm_hex_bytes) { 1743 if (m->password_mode == QPDFJob::pm_hex_bytes) {
1775 // Special case: handle --password-mode=hex-bytes for input password as well as output 1744 // Special case: handle --password-mode=hex-bytes for input password as well as output
1776 // password 1745 // password
1777 - QTC::TC("qpdf", "QPDFJob input password hex-bytes");  
1778 ptemp = QUtil::hex_decode(password); 1746 ptemp = QUtil::hex_decode(password);
1779 password = ptemp.c_str(); 1747 password = ptemp.c_str();
1780 } 1748 }
1781 } 1749 }
1782 - if ((password == nullptr) || empty || m->password_is_hex_key || m->suppress_password_recovery) { 1750 + if (!password || empty || m->qcf.password_is_hex_key() || m->suppress_password_recovery) {
1783 // There is no password, or we're not doing recovery, so just do the normal processing with 1751 // There is no password, or we're not doing recovery, so just do the normal processing with
1784 // the supplied password. 1752 // the supplied password.
1785 doProcessOnce(pdf, fn, password, empty, used_for_input, main_input); 1753 doProcessOnce(pdf, fn, password, empty, used_for_input, main_input);
@@ -3046,12 +3014,10 @@ QPDFJob::doSplitPages(QPDF&amp; pdf) @@ -3046,12 +3014,10 @@ QPDFJob::doSplitPages(QPDF&amp; pdf)
3046 last = num_pages; 3014 last = num_pages;
3047 } 3015 }
3048 QPDF outpdf; 3016 QPDF outpdf;
  3017 + outpdf.doc().config(m->qcf);
3049 outpdf.emptyPDF(); 3018 outpdf.emptyPDF();
3050 QPDFAcroFormDocumentHelper* out_afdh = 3019 QPDFAcroFormDocumentHelper* out_afdh =
3051 afdh.hasAcroForm() ? &outpdf.doc().acroform() : nullptr; 3020 afdh.hasAcroForm() ? &outpdf.doc().acroform() : nullptr;
3052 - if (m->suppress_warnings) {  
3053 - outpdf.setSuppressWarnings(true);  
3054 - }  
3055 for (size_t pageno = first; pageno <= last; ++pageno) { 3021 for (size_t pageno = first; pageno <= last; ++pageno) {
3056 QPDFObjectHandle page = pages.at(pageno - 1); 3022 QPDFObjectHandle page = pages.at(pageno - 1);
3057 outpdf.addPage(page, false); 3023 outpdf.addPage(page, false);
libqpdf/QPDFJob_config.cc
@@ -68,6 +68,7 @@ QPDFJob::Config* @@ -68,6 +68,7 @@ QPDFJob::Config*
68 QPDFJob::Config::check() 68 QPDFJob::Config::check()
69 { 69 {
70 o.m->check = true; 70 o.m->check = true;
  71 + o.m->qcf.check_mode(true);
71 o.m->require_outfile = false; 72 o.m->require_outfile = false;
72 return this; 73 return this;
73 } 74 }
@@ -233,7 +234,7 @@ QPDFJob::Config::generateAppearances() @@ -233,7 +234,7 @@ QPDFJob::Config::generateAppearances()
233 QPDFJob::Config* 234 QPDFJob::Config*
234 QPDFJob::Config::ignoreXrefStreams() 235 QPDFJob::Config::ignoreXrefStreams()
235 { 236 {
236 - o.m->ignore_xref_streams = true; 237 + o.m->qcf.ignore_xref_streams(true);
237 return this; 238 return this;
238 } 239 }
239 240
@@ -415,7 +416,7 @@ QPDFJob::Config::noOriginalObjectIds() @@ -415,7 +416,7 @@ QPDFJob::Config::noOriginalObjectIds()
415 QPDFJob::Config* 416 QPDFJob::Config*
416 QPDFJob::Config::noWarn() 417 QPDFJob::Config::noWarn()
417 { 418 {
418 - o.m->suppress_warnings = true; 419 + o.m->qcf.suppress_warnings(true);
419 return this; 420 return this;
420 } 421 }
421 422
@@ -465,7 +466,7 @@ QPDFJob::Config::password(std::string const&amp; parameter) @@ -465,7 +466,7 @@ QPDFJob::Config::password(std::string const&amp; parameter)
465 QPDFJob::Config* 466 QPDFJob::Config*
466 QPDFJob::Config::passwordIsHexKey() 467 QPDFJob::Config::passwordIsHexKey()
467 { 468 {
468 - o.m->password_is_hex_key = true; 469 + o.m->qcf.password_is_hex_key(true);
469 return this; 470 return this;
470 } 471 }
471 472
@@ -662,7 +663,7 @@ QPDFJob::Config::suppressPasswordRecovery() @@ -662,7 +663,7 @@ QPDFJob::Config::suppressPasswordRecovery()
662 QPDFJob::Config* 663 QPDFJob::Config*
663 QPDFJob::Config::suppressRecovery() 664 QPDFJob::Config::suppressRecovery()
664 { 665 {
665 - o.m->suppress_recovery = true; 666 + o.m->qcf.surpress_recovery(true);
666 return this; 667 return this;
667 } 668 }
668 669
libqpdf/QPDFWriter.cc
@@ -27,8 +27,8 @@ @@ -27,8 +27,8 @@
27 using namespace std::literals; 27 using namespace std::literals;
28 using namespace qpdf; 28 using namespace qpdf;
29 29
30 -using Doc = QPDF::Doc;  
31 -using Encryption = Doc::Encryption; 30 +using QDoc = QPDF::Doc;
  31 +using Encryption = QDoc::Encryption;
32 32
33 QPDFWriter::ProgressReporter::~ProgressReporter() // NOLINT (modernize-use-equals-default) 33 QPDFWriter::ProgressReporter::~ProgressReporter() // NOLINT (modernize-use-equals-default)
34 { 34 {
@@ -263,7 +263,7 @@ Pl_stack::Popper::pop() @@ -263,7 +263,7 @@ Pl_stack::Popper::pop()
263 } 263 }
264 264
265 // Writer class is restricted to QPDFWriter so that only it can call certain methods. 265 // Writer class is restricted to QPDFWriter so that only it can call certain methods.
266 -class Doc::Writer: Doc::Common 266 +class QPDF::Doc::Writer: QPDF::Doc::Common
267 { 267 {
268 friend class QPDFWriter; 268 friend class QPDFWriter;
269 Writer(QPDF& qpdf) : 269 Writer(QPDF& qpdf) :
libqpdf/QPDF_Stream.cc
@@ -27,12 +27,6 @@ using namespace qpdf; @@ -27,12 +27,6 @@ using namespace qpdf;
27 27
28 using Streams = QPDF::Doc::Objects::Streams; 28 using Streams = QPDF::Doc::Objects::Streams;
29 29
30 -bool  
31 -Streams::immediate_copy_from() const  
32 -{  
33 - return qpdf.m->immediate_copy_from;  
34 -}  
35 -  
36 class Streams::Copier final: public QPDFObjectHandle::StreamDataProvider 30 class Streams::Copier final: public QPDFObjectHandle::StreamDataProvider
37 { 31 {
38 class Data 32 class Data
@@ -308,14 +302,13 @@ Stream::copy_data_to(Stream&amp; dest) @@ -308,14 +302,13 @@ Stream::copy_data_to(Stream&amp; dest)
308 { 302 {
309 qpdf_expect(dest); 303 qpdf_expect(dest);
310 auto s = stream(); 304 auto s = stream();
311 - auto& streams = qpdf()->doc().objects().streams();  
312 auto& d_streams = dest.qpdf()->doc().objects().streams(); 305 auto& d_streams = dest.qpdf()->doc().objects().streams();
313 306
314 auto dict = dest.getDict(); 307 auto dict = dest.getDict();
315 308
316 // Copy information from the foreign stream so we can pipe its data later without keeping the 309 // Copy information from the foreign stream so we can pipe its data later without keeping the
317 // original QPDF object around. 310 // original QPDF object around.
318 - if (streams.immediate_copy_from() && !s->stream_data) { 311 + if (qpdf()->doc().config().immediate_copy_from() && !s->stream_data) {
319 // Pull the stream data into a buffer before attempting the copy operation. Do it on the 312 // Pull the stream data into a buffer before attempting the copy operation. Do it on the
320 // source stream so that if the source stream is copied multiple times, we don't have to 313 // source stream so that if the source stream is copied multiple times, we don't have to
321 // keep duplicating the memory. 314 // keep duplicating the memory.
libqpdf/QPDF_encryption.cc
@@ -904,7 +904,7 @@ QPDF::EncryptionParameters::initialize(QPDF&amp; qpdf) @@ -904,7 +904,7 @@ QPDF::EncryptionParameters::initialize(QPDF&amp; qpdf)
904 } 904 }
905 905
906 Encryption data(V, R, Length / 8, p, O, U, OE, UE, Perms, id1, encrypt_metadata); 906 Encryption data(V, R, Length / 8, p, O, U, OE, UE, Perms, id1, encrypt_metadata);
907 - if (qm.provided_password_is_hex_key) { 907 + if (qm.cf.password_is_hex_key()) {
908 // ignore passwords in file 908 // ignore passwords in file
909 encryption_key = QUtil::hex_decode(provided_password); 909 encryption_key = QUtil::hex_decode(provided_password);
910 return; 910 return;
libqpdf/QPDF_linearization.cc
@@ -69,20 +69,20 @@ load_vector_vector( @@ -69,20 +69,20 @@ load_vector_vector(
69 bit_stream.skipToNextByte(); 69 bit_stream.skipToNextByte();
70 } 70 }
71 71
72 -QPDF::ObjUser::ObjUser(user_e type) : 72 +Lin::ObjUser::ObjUser(user_e type) :
73 ou_type(type) 73 ou_type(type)
74 { 74 {
75 qpdf_expect(type == ou_root); 75 qpdf_expect(type == ou_root);
76 } 76 }
77 77
78 -QPDF::ObjUser::ObjUser(user_e type, size_t pageno) : 78 +Lin::ObjUser::ObjUser(user_e type, size_t pageno) :
79 ou_type(type), 79 ou_type(type),
80 pageno(pageno) 80 pageno(pageno)
81 { 81 {
82 qpdf_expect(type == ou_page || type == ou_thumb); 82 qpdf_expect(type == ou_page || type == ou_thumb);
83 } 83 }
84 84
85 -QPDF::ObjUser::ObjUser(user_e type, std::string const& key) : 85 +Lin::ObjUser::ObjUser(user_e type, std::string const& key) :
86 ou_type(type), 86 ou_type(type),
87 key(key) 87 key(key)
88 { 88 {
@@ -90,7 +90,7 @@ QPDF::ObjUser::ObjUser(user_e type, std::string const&amp; key) : @@ -90,7 +90,7 @@ QPDF::ObjUser::ObjUser(user_e type, std::string const&amp; key) :
90 } 90 }
91 91
92 bool 92 bool
93 -QPDF::ObjUser::operator<(ObjUser const& rhs) const 93 +Lin::ObjUser::operator<(ObjUser const& rhs) const
94 { 94 {
95 if (ou_type < rhs.ou_type) { 95 if (ou_type < rhs.ou_type) {
96 return true; 96 return true;
@@ -106,8 +106,8 @@ QPDF::ObjUser::operator&lt;(ObjUser const&amp; rhs) const @@ -106,8 +106,8 @@ QPDF::ObjUser::operator&lt;(ObjUser const&amp; rhs) const
106 return false; 106 return false;
107 } 107 }
108 108
109 -QPDF::UpdateObjectMapsFrame::UpdateObjectMapsFrame(  
110 - QPDF::ObjUser const& ou, QPDFObjectHandle oh, bool top) : 109 +Lin::UpdateObjectMapsFrame::UpdateObjectMapsFrame(
  110 + ObjUser const& ou, QPDFObjectHandle oh, bool top) :
111 ou(ou), 111 ou(ou),
112 oh(oh), 112 oh(oh),
113 top(top) 113 top(top)
@@ -137,7 +137,7 @@ Lin::optimize_internal( @@ -137,7 +137,7 @@ Lin::optimize_internal(
137 bool allow_changes, 137 bool allow_changes,
138 std::function<int(QPDFObjectHandle&)> skip_stream_parameters) 138 std::function<int(QPDFObjectHandle&)> skip_stream_parameters)
139 { 139 {
140 - if (!m->obj_user_to_objects.empty()) { 140 + if (!obj_user_to_objects_.empty()) {
141 // already optimized 141 // already optimized
142 return; 142 return;
143 } 143 }
@@ -186,9 +186,9 @@ Lin::optimize_internal( @@ -186,9 +186,9 @@ Lin::optimize_internal(
186 } 186 }
187 187
188 ObjUser root_ou = ObjUser(ObjUser::ou_root); 188 ObjUser root_ou = ObjUser(ObjUser::ou_root);
189 - auto root_og = QPDFObjGen(root.getObjGen());  
190 - m->obj_user_to_objects[root_ou].insert(root_og);  
191 - m->object_to_obj_users[root_og].insert(root_ou); 189 + auto root_og = root.id_gen();
  190 + obj_user_to_objects_[root_ou].insert(root_og);
  191 + object_to_obj_users_[root_og].insert(root_ou);
192 192
193 filterCompressedObjects(object_stream_data); 193 filterCompressedObjects(object_stream_data);
194 } 194 }
@@ -217,14 +217,14 @@ Lin::updateObjectMaps( @@ -217,14 +217,14 @@ Lin::updateObjectMaps(
217 } 217 }
218 } 218 }
219 219
220 - if (cur.oh.isIndirect()) { 220 + if (cur.oh.indirect()) {
221 QPDFObjGen og(cur.oh.getObjGen()); 221 QPDFObjGen og(cur.oh.getObjGen());
222 if (!visited.add(og)) { 222 if (!visited.add(og)) {
223 QTC::TC("qpdf", "QPDF opt loop detected"); 223 QTC::TC("qpdf", "QPDF opt loop detected");
224 continue; 224 continue;
225 } 225 }
226 - m->obj_user_to_objects[cur.ou].insert(og);  
227 - m->object_to_obj_users[og].insert(cur.ou); 226 + obj_user_to_objects_[cur.ou].insert(og);
  227 + object_to_obj_users_[og].insert(cur.ou);
228 } 228 }
229 229
230 if (cur.oh.isArray()) { 230 if (cur.oh.isArray()) {
@@ -280,34 +280,30 @@ Lin::filterCompressedObjects(std::map&lt;int, int&gt; const&amp; object_stream_data) @@ -280,34 +280,30 @@ Lin::filterCompressedObjects(std::map&lt;int, int&gt; const&amp; object_stream_data)
280 std::map<ObjUser, std::set<QPDFObjGen>> t_obj_user_to_objects; 280 std::map<ObjUser, std::set<QPDFObjGen>> t_obj_user_to_objects;
281 std::map<QPDFObjGen, std::set<ObjUser>> t_object_to_obj_users; 281 std::map<QPDFObjGen, std::set<ObjUser>> t_object_to_obj_users;
282 282
283 - for (auto const& i1: m->obj_user_to_objects) {  
284 - ObjUser const& ou = i1.first;  
285 - // Loop over objects.  
286 - for (auto const& og: i1.second) { 283 + for (auto const& [ou, ogs]: obj_user_to_objects_) {
  284 + for (auto const& og: ogs) {
287 auto i2 = object_stream_data.find(og.getObj()); 285 auto i2 = object_stream_data.find(og.getObj());
288 if (i2 == object_stream_data.end()) { 286 if (i2 == object_stream_data.end()) {
289 t_obj_user_to_objects[ou].insert(og); 287 t_obj_user_to_objects[ou].insert(og);
290 } else { 288 } else {
291 - t_obj_user_to_objects[ou].insert(QPDFObjGen(i2->second, 0)); 289 + t_obj_user_to_objects[ou].insert({i2->second, 0});
292 } 290 }
293 } 291 }
294 } 292 }
295 293
296 - for (auto const& i1: m->object_to_obj_users) {  
297 - QPDFObjGen const& og = i1.first;  
298 - // Loop over obj_users.  
299 - for (auto const& ou: i1.second) { 294 + for (auto const& [og, ous]: object_to_obj_users_) {
  295 + for (auto const& ou: ous) {
300 auto i2 = object_stream_data.find(og.getObj()); 296 auto i2 = object_stream_data.find(og.getObj());
301 if (i2 == object_stream_data.end()) { 297 if (i2 == object_stream_data.end()) {
302 t_object_to_obj_users[og].insert(ou); 298 t_object_to_obj_users[og].insert(ou);
303 } else { 299 } else {
304 - t_object_to_obj_users[QPDFObjGen(i2->second, 0)].insert(ou); 300 + t_object_to_obj_users[{i2->second, 0}].insert(ou);
305 } 301 }
306 } 302 }
307 } 303 }
308 304
309 - m->obj_user_to_objects = t_obj_user_to_objects;  
310 - m->object_to_obj_users = t_object_to_obj_users; 305 + obj_user_to_objects_ = std::move(t_obj_user_to_objects);
  306 + object_to_obj_users_ = std::move(t_object_to_obj_users);
311 } 307 }
312 308
313 void 309 void
@@ -324,10 +320,8 @@ Lin::filterCompressedObjects(QPDFWriter::ObjTable const&amp; obj) @@ -324,10 +320,8 @@ Lin::filterCompressedObjects(QPDFWriter::ObjTable const&amp; obj)
324 std::map<ObjUser, std::set<QPDFObjGen>> t_obj_user_to_objects; 320 std::map<ObjUser, std::set<QPDFObjGen>> t_obj_user_to_objects;
325 std::map<QPDFObjGen, std::set<ObjUser>> t_object_to_obj_users; 321 std::map<QPDFObjGen, std::set<ObjUser>> t_object_to_obj_users;
326 322
327 - for (auto const& i1: m->obj_user_to_objects) {  
328 - ObjUser const& ou = i1.first;  
329 - // Loop over objects.  
330 - for (auto const& og: i1.second) { 323 + for (auto const& [ou, ogs]: obj_user_to_objects_) {
  324 + for (auto const& og: ogs) {
331 if (obj.contains(og)) { 325 if (obj.contains(og)) {
332 if (auto const& i2 = obj[og].object_stream; i2 <= 0) { 326 if (auto const& i2 = obj[og].object_stream; i2 <= 0) {
333 t_obj_user_to_objects[ou].insert(og); 327 t_obj_user_to_objects[ou].insert(og);
@@ -338,40 +332,45 @@ Lin::filterCompressedObjects(QPDFWriter::ObjTable const&amp; obj) @@ -338,40 +332,45 @@ Lin::filterCompressedObjects(QPDFWriter::ObjTable const&amp; obj)
338 } 332 }
339 } 333 }
340 334
341 - for (auto const& i1: m->object_to_obj_users) {  
342 - QPDFObjGen const& og = i1.first; 335 + for (auto const& [og, ous]: object_to_obj_users_) {
343 if (obj.contains(og)) { 336 if (obj.contains(og)) {
344 // Loop over obj_users. 337 // Loop over obj_users.
345 - for (auto const& ou: i1.second) { 338 + for (auto const& ou: ous) {
346 if (auto i2 = obj[og].object_stream; i2 <= 0) { 339 if (auto i2 = obj[og].object_stream; i2 <= 0) {
347 t_object_to_obj_users[og].insert(ou); 340 t_object_to_obj_users[og].insert(ou);
348 } else { 341 } else {
349 - t_object_to_obj_users[QPDFObjGen(i2, 0)].insert(ou); 342 + t_object_to_obj_users[{i2, 0}].insert(ou);
350 } 343 }
351 } 344 }
352 } 345 }
353 } 346 }
354 347
355 - m->obj_user_to_objects = t_obj_user_to_objects;  
356 - m->object_to_obj_users = t_object_to_obj_users; 348 + obj_user_to_objects_ = std::move(t_obj_user_to_objects);
  349 + object_to_obj_users_ = std::move(t_object_to_obj_users);
357 } 350 }
358 351
359 void 352 void
360 Lin::linearizationWarning(std::string_view msg) 353 Lin::linearizationWarning(std::string_view msg)
361 { 354 {
362 - m->linearization_warnings = true; 355 + linearization_warnings_ = true;
363 warn(qpdf_e_linearization, "", 0, std::string(msg)); 356 warn(qpdf_e_linearization, "", 0, std::string(msg));
364 } 357 }
365 358
366 bool 359 bool
367 QPDF::checkLinearization() 360 QPDF::checkLinearization()
368 { 361 {
  362 + return m->lin.check();
  363 +}
  364 +
  365 +bool
  366 +Lin::check()
  367 +{
369 try { 368 try {
370 - m->lin.readLinearizationData();  
371 - m->lin.checkLinearizationInternal();  
372 - return !m->linearization_warnings; 369 + readLinearizationData();
  370 + checkLinearizationInternal();
  371 + return !linearization_warnings_;
373 } catch (std::runtime_error& e) { 372 } catch (std::runtime_error& e) {
374 - m->lin.linearizationWarning( 373 + linearizationWarning(
375 "error encountered while checking linearization data: " + std::string(e.what())); 374 "error encountered while checking linearization data: " + std::string(e.what()));
376 return false; 375 return false;
377 } 376 }
@@ -380,6 +379,12 @@ QPDF::checkLinearization() @@ -380,6 +379,12 @@ QPDF::checkLinearization()
380 bool 379 bool
381 QPDF::isLinearized() 380 QPDF::isLinearized()
382 { 381 {
  382 + return m->lin.linearized();
  383 +}
  384 +
  385 +bool
  386 +Lin::linearized()
  387 +{
383 // If the first object in the file is a dictionary with a suitable /Linearized key and has an /L 388 // If the first object in the file is a dictionary with a suitable /Linearized key and has an /L
384 // key that accurately indicates the file size, initialize m->lindict and return true. 389 // key that accurately indicates the file size, initialize m->lindict and return true.
385 390
@@ -411,7 +416,7 @@ QPDF::isLinearized() @@ -411,7 +416,7 @@ QPDF::isLinearized()
411 continue; 416 continue;
412 } 417 }
413 418
414 - Dictionary candidate = getObject(toI(QUtil::string_to_ll(t1.getValue().data())), 0); 419 + Dictionary candidate = qpdf.getObject(toI(QUtil::string_to_ll(t1.getValue().data())), 0);
415 auto linkey = candidate["/Linearized"]; 420 auto linkey = candidate["/Linearized"];
416 if (!(linkey.isNumber() && toI(floor(linkey.getNumericValue())) == 1)) { 421 if (!(linkey.isNumber() && toI(floor(linkey.getNumericValue())) == 1)) {
417 return false; 422 return false;
@@ -422,8 +427,8 @@ QPDF::isLinearized() @@ -422,8 +427,8 @@ QPDF::isLinearized()
422 if (L != m->file->tell()) { 427 if (L != m->file->tell()) {
423 return false; 428 return false;
424 } 429 }
425 - m->linp.file_size = L;  
426 - m->lindict = candidate; 430 + linp_.file_size = L;
  431 + lindict_ = candidate;
427 return true; 432 return true;
428 } 433 }
429 } 434 }
@@ -432,24 +437,24 @@ void @@ -432,24 +437,24 @@ void
432 Lin::readLinearizationData() 437 Lin::readLinearizationData()
433 { 438 {
434 util::assertion( 439 util::assertion(
435 - qpdf.isLinearized(), "called readLinearizationData for file that is not linearized" // 440 + linearized(), "called readLinearizationData for file that is not linearized" //
436 ); 441 );
437 442
438 // This function throws an exception (which is trapped by checkLinearization()) for any errors 443 // This function throws an exception (which is trapped by checkLinearization()) for any errors
439 // that prevent loading. 444 // that prevent loading.
440 445
441 // /L is read and stored in linp by isLinearized() 446 // /L is read and stored in linp by isLinearized()
442 - Array H = m->lindict["/H"]; // hint table offset/length for primary and overflow hint tables 447 + Array H = lindict_["/H"]; // hint table offset/length for primary and overflow hint tables
443 auto H_size = H.size(); 448 auto H_size = H.size();
444 Integer H_0 = H[0]; // hint table offset 449 Integer H_0 = H[0]; // hint table offset
445 Integer H_1 = H[1]; // hint table length 450 Integer H_1 = H[1]; // hint table length
446 Integer H_2 = H[2]; // hint table offset for overflow hint table 451 Integer H_2 = H[2]; // hint table offset for overflow hint table
447 Integer H_3 = H[3]; // hint table length for overflow hint table 452 Integer H_3 = H[3]; // hint table length for overflow hint table
448 - Integer O = m->lindict["/O"];  
449 - Integer E = m->lindict["/E"];  
450 - Integer N = m->lindict["/N"];  
451 - Integer T = m->lindict["/T"];  
452 - auto P_oh = m->lindict["/P"]; 453 + Integer O = lindict_["/O"];
  454 + Integer E = lindict_["/E"];
  455 + Integer N = lindict_["/N"];
  456 + Integer T = lindict_["/T"];
  457 + auto P_oh = lindict_["/P"];
453 Integer P = P_oh; // first page number 458 Integer P = P_oh; // first page number
454 QTC::TC("qpdf", "QPDF P absent in lindict", P ? 0 : 1); 459 QTC::TC("qpdf", "QPDF P absent in lindict", P ? 0 : 1);
455 460
@@ -482,13 +487,13 @@ Lin::readLinearizationData() @@ -482,13 +487,13 @@ Lin::readLinearizationData()
482 ); 487 );
483 488
484 // file_size initialized by isLinearized() 489 // file_size initialized by isLinearized()
485 - m->linp.first_page_object = O;  
486 - m->linp.first_page_end = E;  
487 - m->linp.npages = N;  
488 - m->linp.xref_zero_offset = T;  
489 - m->linp.first_page = P ? P : 0;  
490 - m->linp.H_offset = H_0;  
491 - m->linp.H_length = H_1; 490 + linp_.first_page_object = O;
  491 + linp_.first_page_end = E;
  492 + linp_.npages = N;
  493 + linp_.xref_zero_offset = T;
  494 + linp_.first_page = P ? P : 0;
  495 + linp_.H_offset = H_0;
  496 + linp_.H_length = H_1;
492 497
493 // Read hint streams 498 // Read hint streams
494 499
@@ -532,7 +537,7 @@ Lin::readLinearizationData() @@ -532,7 +537,7 @@ Lin::readLinearizationData()
532 "linearization dictionary" // 537 "linearization dictionary" //
533 ); 538 );
534 size_t HOi = HO; 539 size_t HOi = HO;
535 - readHGeneric(BitStream(h_buf + HO, h_size - HOi), m->outline_hints); 540 + readHGeneric(BitStream(h_buf + HO, h_size - HOi), outline_hints_);
536 } 541 }
537 } 542 }
538 543
@@ -576,7 +581,7 @@ Lin::readHPageOffset(BitStream h) @@ -576,7 +581,7 @@ Lin::readHPageOffset(BitStream h)
576 { 581 {
577 // All comments referring to the PDF spec refer to the spec for version 1.4. 582 // All comments referring to the PDF spec refer to the spec for version 1.4.
578 583
579 - HPageOffset& t = m->page_offset_hints; 584 + HPageOffset& t = page_offset_hints_;
580 585
581 t.min_nobjects = h.getBitsInt(32); // 1 586 t.min_nobjects = h.getBitsInt(32); // 1
582 t.first_page_offset = h.getBitsInt(32); // 2 587 t.first_page_offset = h.getBitsInt(32); // 2
@@ -594,7 +599,7 @@ Lin::readHPageOffset(BitStream h) @@ -594,7 +599,7 @@ Lin::readHPageOffset(BitStream h)
594 599
595 std::vector<HPageOffsetEntry>& entries = t.entries; 600 std::vector<HPageOffsetEntry>& entries = t.entries;
596 entries.clear(); 601 entries.clear();
597 - int nitems = toI(m->linp.npages); 602 + int nitems = toI(linp_.npages);
598 load_vector_int(h, nitems, entries, t.nbits_delta_nobjects, &HPageOffsetEntry::delta_nobjects); 603 load_vector_int(h, nitems, entries, t.nbits_delta_nobjects, &HPageOffsetEntry::delta_nobjects);
599 load_vector_int( 604 load_vector_int(
600 h, nitems, entries, t.nbits_delta_page_length, &HPageOffsetEntry::delta_page_length); 605 h, nitems, entries, t.nbits_delta_page_length, &HPageOffsetEntry::delta_page_length);
@@ -623,7 +628,7 @@ Lin::readHPageOffset(BitStream h) @@ -623,7 +628,7 @@ Lin::readHPageOffset(BitStream h)
623 void 628 void
624 Lin::readHSharedObject(BitStream h) 629 Lin::readHSharedObject(BitStream h)
625 { 630 {
626 - HSharedObject& t = m->shared_object_hints; 631 + HSharedObject& t = shared_object_hints_;
627 632
628 t.first_shared_obj = h.getBitsInt(32); // 1 633 t.first_shared_obj = h.getBitsInt(32); // 1
629 t.first_shared_offset = h.getBitsInt(32); // 2 634 t.first_shared_offset = h.getBitsInt(32); // 2
@@ -672,7 +677,7 @@ Lin::checkLinearizationInternal() @@ -672,7 +677,7 @@ Lin::checkLinearizationInternal()
672 677
673 // Check all values in linearization parameter dictionary 678 // Check all values in linearization parameter dictionary
674 679
675 - LinParameters& p = m->linp; 680 + LinParameters& p = linp_;
676 681
677 // L: file size in bytes -- checked by isLinearized 682 // L: file size in bytes -- checked by isLinearized
678 683
@@ -708,10 +713,10 @@ Lin::checkLinearizationInternal() @@ -708,10 +713,10 @@ Lin::checkLinearizationInternal()
708 break; 713 break;
709 } 714 }
710 } 715 }
711 - if (m->file->tell() != m->first_xref_item_offset) { 716 + if (m->file->tell() != objects.first_xref_item_offset()) {
712 linearizationWarning( 717 linearizationWarning(
713 "space before first xref item (/T) mismatch (computed = " + 718 "space before first xref item (/T) mismatch (computed = " +
714 - std::to_string(m->first_xref_item_offset) + 719 + std::to_string(objects.first_xref_item_offset()) +
715 "; file = " + std::to_string(m->file->tell())); 720 "; file = " + std::to_string(m->file->tell()));
716 } 721 }
717 722
@@ -722,7 +727,7 @@ Lin::checkLinearizationInternal() @@ -722,7 +727,7 @@ Lin::checkLinearizationInternal()
722 // compressed objects are supposed to be at the end of the containing xref section if any object 727 // compressed objects are supposed to be at the end of the containing xref section if any object
723 // streams are in use. 728 // streams are in use.
724 729
725 - if (m->uncompressed_after_compressed) { 730 + if (objects.uncompressed_after_compressed()) {
726 linearizationWarning( 731 linearizationWarning(
727 "linearized file contains an uncompressed object after a compressed " 732 "linearized file contains an uncompressed object after a compressed "
728 "one in a cross-reference stream"); 733 "one in a cross-reference stream");
@@ -751,11 +756,11 @@ Lin::checkLinearizationInternal() @@ -751,11 +756,11 @@ Lin::checkLinearizationInternal()
751 // suite doesn't contain any files with threads. 756 // suite doesn't contain any files with threads.
752 757
753 no_ci_stop_if( 758 no_ci_stop_if(
754 - m->part6.empty(), "linearization part 6 unexpectedly empty" // 759 + part6_.empty(), "linearization part 6 unexpectedly empty" //
755 ); 760 );
756 qpdf_offset_t min_E = -1; 761 qpdf_offset_t min_E = -1;
757 qpdf_offset_t max_E = -1; 762 qpdf_offset_t max_E = -1;
758 - for (auto const& oh: m->part6) { 763 + for (auto const& oh: part6_) {
759 QPDFObjGen og(oh.getObjGen()); 764 QPDFObjGen og(oh.getObjGen());
760 // All objects have to have been dereferenced to be classified. 765 // All objects have to have been dereferenced to be classified.
761 util::assertion(m->obj_cache.contains(og), "linearization part6 object not in cache"); 766 util::assertion(m->obj_cache.contains(og), "linearization part6 object not in cache");
@@ -781,12 +786,12 @@ qpdf_offset_t @@ -781,12 +786,12 @@ qpdf_offset_t
781 Lin::maxEnd(ObjUser const& ou) 786 Lin::maxEnd(ObjUser const& ou)
782 { 787 {
783 no_ci_stop_if( 788 no_ci_stop_if(
784 - !m->obj_user_to_objects.contains(ou), 789 + !obj_user_to_objects_.contains(ou),
785 "no entry in object user table for requested object user" // 790 "no entry in object user table for requested object user" //
786 ); 791 );
787 792
788 qpdf_offset_t end = 0; 793 qpdf_offset_t end = 0;
789 - for (auto const& og: m->obj_user_to_objects[ou]) { 794 + for (auto const& og: obj_user_to_objects_[ou]) {
790 no_ci_stop_if( 795 no_ci_stop_if(
791 !m->obj_cache.contains(og), "unknown object referenced in object user table" // 796 !m->obj_cache.contains(og), "unknown object referenced in object user table" //
792 ); 797 );
@@ -868,7 +873,7 @@ Lin::checkHPageOffset( @@ -868,7 +873,7 @@ Lin::checkHPageOffset(
868 // dictionary in with shared objects even when they are private. 873 // dictionary in with shared objects even when they are private.
869 874
870 size_t npages = pages.size(); 875 size_t npages = pages.size();
871 - qpdf_offset_t table_offset = adjusted_offset(m->page_offset_hints.first_page_offset); 876 + qpdf_offset_t table_offset = adjusted_offset(page_offset_hints_.first_page_offset);
872 QPDFObjGen first_page_og(pages.at(0).getObjGen()); 877 QPDFObjGen first_page_og(pages.at(0).getObjGen());
873 if (!m->xref_table.contains(first_page_og)) { 878 if (!m->xref_table.contains(first_page_og)) {
874 stopOnError("supposed first page object is not known"); 879 stopOnError("supposed first page object is not known");
@@ -886,9 +891,9 @@ Lin::checkHPageOffset( @@ -886,9 +891,9 @@ Lin::checkHPageOffset(
886 } 891 }
887 offset = getLinearizationOffset(page_og); 892 offset = getLinearizationOffset(page_og);
888 893
889 - HPageOffsetEntry& he = m->page_offset_hints.entries.at(toS(pageno));  
890 - CHPageOffsetEntry& ce = m->c_page_offset_data.entries.at(toS(pageno));  
891 - int h_nobjects = he.delta_nobjects + m->page_offset_hints.min_nobjects; 894 + HPageOffsetEntry& he = page_offset_hints_.entries.at(pageno);
  895 + CHPageOffsetEntry& ce = c_page_offset_data_.entries.at(pageno);
  896 + int h_nobjects = he.delta_nobjects + page_offset_hints_.min_nobjects;
892 if (h_nobjects != ce.nobjects) { 897 if (h_nobjects != ce.nobjects) {
893 // This happens with pdlin when there are thumbnails. 898 // This happens with pdlin when there are thumbnails.
894 linearizationWarning( 899 linearizationWarning(
@@ -899,7 +904,7 @@ Lin::checkHPageOffset( @@ -899,7 +904,7 @@ Lin::checkHPageOffset(
899 // Use value for number of objects in hint table rather than computed value if there is a 904 // Use value for number of objects in hint table rather than computed value if there is a
900 // discrepancy. 905 // discrepancy.
901 int length = lengthNextN(first_object, h_nobjects); 906 int length = lengthNextN(first_object, h_nobjects);
902 - int h_length = toI(he.delta_page_length + m->page_offset_hints.min_page_length); 907 + int h_length = toI(he.delta_page_length + page_offset_hints_.min_page_length);
903 if (length != h_length) { 908 if (length != h_length) {
904 // This condition almost certainly indicates a bad hint table or a bug in this code. 909 // This condition almost certainly indicates a bad hint table or a bug in this code.
905 linearizationWarning( 910 linearizationWarning(
@@ -932,11 +937,11 @@ Lin::checkHPageOffset( @@ -932,11 +937,11 @@ Lin::checkHPageOffset(
932 for (size_t i = 0; i < toS(ce.nshared_objects); ++i) { 937 for (size_t i = 0; i < toS(ce.nshared_objects); ++i) {
933 int idx = ce.shared_identifiers.at(i); 938 int idx = ce.shared_identifiers.at(i);
934 no_ci_stop_if( 939 no_ci_stop_if(
935 - idx >= m->c_shared_object_data.nshared_total, 940 + idx >= c_shared_object_data_.nshared_total,
936 "index out of bounds for shared object hint table" // 941 "index out of bounds for shared object hint table" //
937 ); 942 );
938 943
939 - int obj = m->c_shared_object_data.entries.at(toS(idx)).object; 944 + int obj = c_shared_object_data_.entries.at(toS(idx)).object;
940 computed_shared.insert(obj); 945 computed_shared.insert(obj);
941 } 946 }
942 947
@@ -978,7 +983,7 @@ Lin::checkHSharedObject(std::vector&lt;QPDFObjectHandle&gt; const&amp; pages, std::map&lt;int @@ -978,7 +983,7 @@ Lin::checkHSharedObject(std::vector&lt;QPDFObjectHandle&gt; const&amp; pages, std::map&lt;int
978 // Empirically, Acrobat and pdlin generate incorrect values for these whenever there are no 983 // Empirically, Acrobat and pdlin generate incorrect values for these whenever there are no
979 // shared objects not referenced by the first page (i.e., nshared_total == nshared_first_page). 984 // shared objects not referenced by the first page (i.e., nshared_total == nshared_first_page).
980 985
981 - HSharedObject& so = m->shared_object_hints; 986 + HSharedObject& so = shared_object_hints_;
982 if (so.nshared_total < so.nshared_first_page) { 987 if (so.nshared_total < so.nshared_first_page) {
983 linearizationWarning("shared object hint table: ntotal < nfirst_page"); 988 linearizationWarning("shared object hint table: ntotal < nfirst_page");
984 } else { 989 } else {
@@ -988,10 +993,10 @@ Lin::checkHSharedObject(std::vector&lt;QPDFObjectHandle&gt; const&amp; pages, std::map&lt;int @@ -988,10 +993,10 @@ Lin::checkHSharedObject(std::vector&lt;QPDFObjectHandle&gt; const&amp; pages, std::map&lt;int
988 for (int i = 0; i < so.nshared_total; ++i) { 993 for (int i = 0; i < so.nshared_total; ++i) {
989 if (i == so.nshared_first_page) { 994 if (i == so.nshared_first_page) {
990 QTC::TC("qpdf", "QPDF lin check shared past first page"); 995 QTC::TC("qpdf", "QPDF lin check shared past first page");
991 - if (m->part8.empty()) { 996 + if (part8_.empty()) {
992 linearizationWarning("part 8 is empty but nshared_total > nshared_first_page"); 997 linearizationWarning("part 8 is empty but nshared_total > nshared_first_page");
993 } else { 998 } else {
994 - int obj = m->part8.at(0).getObjectID(); 999 + int obj = part8_.at(0).getObjectID();
995 if (obj != so.first_shared_obj) { 1000 if (obj != so.first_shared_obj) {
996 linearizationWarning( 1001 linearizationWarning(
997 "first shared object number mismatch: hint table = " + 1002 "first shared object number mismatch: hint table = " +
@@ -1039,12 +1044,12 @@ Lin::checkHOutlines() @@ -1039,12 +1044,12 @@ Lin::checkHOutlines()
1039 // correct number of objects from the wrong starting place). pdlin appears to generate correct 1044 // correct number of objects from the wrong starting place). pdlin appears to generate correct
1040 // values in those cases. 1045 // values in those cases.
1041 1046
1042 - if (m->c_outline_data.nobjects == m->outline_hints.nobjects) {  
1043 - if (m->c_outline_data.nobjects == 0) { 1047 + if (c_outline_data_.nobjects == outline_hints_.nobjects) {
  1048 + if (c_outline_data_.nobjects == 0) {
1044 return; 1049 return;
1045 } 1050 }
1046 1051
1047 - if (m->c_outline_data.first_object == m->outline_hints.first_object) { 1052 + if (c_outline_data_.first_object == outline_hints_.first_object) {
1048 // Check length and offset. Acrobat gets these wrong. 1053 // Check length and offset. Acrobat gets these wrong.
1049 QPDFObjectHandle outlines = qpdf.getRoot().getKey("/Outlines"); 1054 QPDFObjectHandle outlines = qpdf.getRoot().getKey("/Outlines");
1050 if (!outlines.isIndirect()) { 1055 if (!outlines.isIndirect()) {
@@ -1060,13 +1065,13 @@ Lin::checkHOutlines() @@ -1060,13 +1065,13 @@ Lin::checkHOutlines()
1060 qpdf_offset_t offset = getLinearizationOffset(og); 1065 qpdf_offset_t offset = getLinearizationOffset(og);
1061 ObjUser ou(ObjUser::ou_root_key, "/Outlines"); 1066 ObjUser ou(ObjUser::ou_root_key, "/Outlines");
1062 int length = toI(maxEnd(ou) - offset); 1067 int length = toI(maxEnd(ou) - offset);
1063 - qpdf_offset_t table_offset = adjusted_offset(m->outline_hints.first_object_offset); 1068 + qpdf_offset_t table_offset = adjusted_offset(outline_hints_.first_object_offset);
1064 if (offset != table_offset) { 1069 if (offset != table_offset) {
1065 linearizationWarning( 1070 linearizationWarning(
1066 "incorrect offset in outlines table: hint table = " + 1071 "incorrect offset in outlines table: hint table = " +
1067 std::to_string(table_offset) + "; computed = " + std::to_string(offset)); 1072 std::to_string(table_offset) + "; computed = " + std::to_string(offset));
1068 } 1073 }
1069 - int table_length = m->outline_hints.group_length; 1074 + int table_length = outline_hints_.group_length;
1070 if (length != table_length) { 1075 if (length != table_length) {
1071 linearizationWarning( 1076 linearizationWarning(
1072 "incorrect length in outlines table: hint table = " + 1077 "incorrect length in outlines table: hint table = " +
@@ -1083,38 +1088,46 @@ Lin::checkHOutlines() @@ -1083,38 +1088,46 @@ Lin::checkHOutlines()
1083 void 1088 void
1084 QPDF::showLinearizationData() 1089 QPDF::showLinearizationData()
1085 { 1090 {
  1091 + m->lin.show_data();
  1092 +}
  1093 +
  1094 +void
  1095 +Lin::show_data()
  1096 +{
1086 try { 1097 try {
1087 - m->lin.readLinearizationData();  
1088 - m->lin.checkLinearizationInternal();  
1089 - m->lin.dumpLinearizationDataInternal(); 1098 + readLinearizationData();
  1099 + checkLinearizationInternal();
  1100 + dumpLinearizationDataInternal();
1090 } catch (QPDFExc& e) { 1101 } catch (QPDFExc& e) {
1091 - m->lin.linearizationWarning(e.what()); 1102 + linearizationWarning(e.what());
1092 } 1103 }
1093 } 1104 }
1094 1105
1095 void 1106 void
1096 Lin::dumpLinearizationDataInternal() 1107 Lin::dumpLinearizationDataInternal()
1097 { 1108 {
1098 - *m->log->getInfo() << m->file->getName() << ": linearization data:\n\n";  
1099 -  
1100 - *m->log->getInfo() << "file_size: " << m->linp.file_size << "\n"  
1101 - << "first_page_object: " << m->linp.first_page_object << "\n"  
1102 - << "first_page_end: " << m->linp.first_page_end << "\n"  
1103 - << "npages: " << m->linp.npages << "\n"  
1104 - << "xref_zero_offset: " << m->linp.xref_zero_offset << "\n"  
1105 - << "first_page: " << m->linp.first_page << "\n"  
1106 - << "H_offset: " << m->linp.H_offset << "\n"  
1107 - << "H_length: " << m->linp.H_length << "\n"  
1108 - << "\n";  
1109 -  
1110 - *m->log->getInfo() << "Page Offsets Hint Table\n\n"; 1109 + auto& info = *cf.log()->getInfo();
  1110 +
  1111 + info << m->file->getName() << ": linearization data:\n\n";
  1112 +
  1113 + info << "file_size: " << linp_.file_size << "\n"
  1114 + << "first_page_object: " << linp_.first_page_object << "\n"
  1115 + << "first_page_end: " << linp_.first_page_end << "\n"
  1116 + << "npages: " << linp_.npages << "\n"
  1117 + << "xref_zero_offset: " << linp_.xref_zero_offset << "\n"
  1118 + << "first_page: " << linp_.first_page << "\n"
  1119 + << "H_offset: " << linp_.H_offset << "\n"
  1120 + << "H_length: " << linp_.H_length << "\n"
  1121 + << "\n";
  1122 +
  1123 + info << "Page Offsets Hint Table\n\n";
1111 dumpHPageOffset(); 1124 dumpHPageOffset();
1112 - *m->log->getInfo() << "\nShared Objects Hint Table\n\n"; 1125 + info << "\nShared Objects Hint Table\n\n";
1113 dumpHSharedObject(); 1126 dumpHSharedObject();
1114 1127
1115 - if (m->outline_hints.nobjects > 0) {  
1116 - *m->log->getInfo() << "\nOutlines Hint Table\n\n";  
1117 - dumpHGeneric(m->outline_hints); 1128 + if (outline_hints_.nobjects > 0) {
  1129 + info << "\nOutlines Hint Table\n\n";
  1130 + dumpHGeneric(outline_hints_);
1118 } 1131 }
1119 } 1132 }
1120 1133
@@ -1123,8 +1136,8 @@ Lin::adjusted_offset(qpdf_offset_t offset) @@ -1123,8 +1136,8 @@ Lin::adjusted_offset(qpdf_offset_t offset)
1123 { 1136 {
1124 // All offsets >= H_offset have to be increased by H_length since all hint table location values 1137 // All offsets >= H_offset have to be increased by H_length since all hint table location values
1125 // disregard the hint table itself. 1138 // disregard the hint table itself.
1126 - if (offset >= m->linp.H_offset) {  
1127 - return offset + m->linp.H_length; 1139 + if (offset >= linp_.H_offset) {
  1140 + return offset + linp_.H_length;
1128 } 1141 }
1129 return offset; 1142 return offset;
1130 } 1143 }
@@ -1132,38 +1145,35 @@ Lin::adjusted_offset(qpdf_offset_t offset) @@ -1132,38 +1145,35 @@ Lin::adjusted_offset(qpdf_offset_t offset)
1132 void 1145 void
1133 Lin::dumpHPageOffset() 1146 Lin::dumpHPageOffset()
1134 { 1147 {
1135 - HPageOffset& t = m->page_offset_hints;  
1136 - *m->log->getInfo() << "min_nobjects: " << t.min_nobjects << "\n"  
1137 - << "first_page_offset: " << adjusted_offset(t.first_page_offset) << "\n"  
1138 - << "nbits_delta_nobjects: " << t.nbits_delta_nobjects << "\n"  
1139 - << "min_page_length: " << t.min_page_length << "\n"  
1140 - << "nbits_delta_page_length: " << t.nbits_delta_page_length << "\n"  
1141 - << "min_content_offset: " << t.min_content_offset << "\n"  
1142 - << "nbits_delta_content_offset: " << t.nbits_delta_content_offset << "\n"  
1143 - << "min_content_length: " << t.min_content_length << "\n"  
1144 - << "nbits_delta_content_length: " << t.nbits_delta_content_length << "\n"  
1145 - << "nbits_nshared_objects: " << t.nbits_nshared_objects << "\n"  
1146 - << "nbits_shared_identifier: " << t.nbits_shared_identifier << "\n"  
1147 - << "nbits_shared_numerator: " << t.nbits_shared_numerator << "\n"  
1148 - << "shared_denominator: " << t.shared_denominator << "\n";  
1149 -  
1150 - for (size_t i1 = 0; i1 < m->linp.npages; ++i1) { 1148 + auto& info = *cf.log()->getInfo();
  1149 + HPageOffset& t = page_offset_hints_;
  1150 + info << "min_nobjects: " << t.min_nobjects << "\n"
  1151 + << "first_page_offset: " << adjusted_offset(t.first_page_offset) << "\n"
  1152 + << "nbits_delta_nobjects: " << t.nbits_delta_nobjects << "\n"
  1153 + << "min_page_length: " << t.min_page_length << "\n"
  1154 + << "nbits_delta_page_length: " << t.nbits_delta_page_length << "\n"
  1155 + << "min_content_offset: " << t.min_content_offset << "\n"
  1156 + << "nbits_delta_content_offset: " << t.nbits_delta_content_offset << "\n"
  1157 + << "min_content_length: " << t.min_content_length << "\n"
  1158 + << "nbits_delta_content_length: " << t.nbits_delta_content_length << "\n"
  1159 + << "nbits_nshared_objects: " << t.nbits_nshared_objects << "\n"
  1160 + << "nbits_shared_identifier: " << t.nbits_shared_identifier << "\n"
  1161 + << "nbits_shared_numerator: " << t.nbits_shared_numerator << "\n"
  1162 + << "shared_denominator: " << t.shared_denominator << "\n";
  1163 +
  1164 + for (size_t i1 = 0; i1 < linp_.npages; ++i1) {
1151 HPageOffsetEntry& pe = t.entries.at(i1); 1165 HPageOffsetEntry& pe = t.entries.at(i1);
1152 - *m->log->getInfo() << "Page " << i1 << ":\n"  
1153 - << " nobjects: " << pe.delta_nobjects + t.min_nobjects << "\n"  
1154 - << " length: " << pe.delta_page_length + t.min_page_length  
1155 - << "\n"  
1156 - // content offset is relative to page, not file  
1157 - << " content_offset: " << pe.delta_content_offset + t.min_content_offset  
1158 - << "\n"  
1159 - << " content_length: " << pe.delta_content_length + t.min_content_length  
1160 - << "\n"  
1161 - << " nshared_objects: " << pe.nshared_objects << "\n"; 1166 + info << "Page " << i1 << ":\n"
  1167 + << " nobjects: " << pe.delta_nobjects + t.min_nobjects << "\n"
  1168 + << " length: " << pe.delta_page_length + t.min_page_length
  1169 + << "\n"
  1170 + // content offset is relative to page, not file
  1171 + << " content_offset: " << pe.delta_content_offset + t.min_content_offset << "\n"
  1172 + << " content_length: " << pe.delta_content_length + t.min_content_length << "\n"
  1173 + << " nshared_objects: " << pe.nshared_objects << "\n";
1162 for (size_t i2 = 0; i2 < toS(pe.nshared_objects); ++i2) { 1174 for (size_t i2 = 0; i2 < toS(pe.nshared_objects); ++i2) {
1163 - *m->log->getInfo() << " identifier " << i2 << ": " << pe.shared_identifiers.at(i2)  
1164 - << "\n";  
1165 - *m->log->getInfo() << " numerator " << i2 << ": " << pe.shared_numerators.at(i2)  
1166 - << "\n"; 1175 + info << " identifier " << i2 << ": " << pe.shared_identifiers.at(i2) << "\n";
  1176 + info << " numerator " << i2 << ": " << pe.shared_numerators.at(i2) << "\n";
1167 } 1177 }
1168 } 1178 }
1169 } 1179 }
@@ -1171,27 +1181,27 @@ Lin::dumpHPageOffset() @@ -1171,27 +1181,27 @@ Lin::dumpHPageOffset()
1171 void 1181 void
1172 Lin::dumpHSharedObject() 1182 Lin::dumpHSharedObject()
1173 { 1183 {
1174 - HSharedObject& t = m->shared_object_hints;  
1175 - *m->log->getInfo() << "first_shared_obj: " << t.first_shared_obj << "\n"  
1176 - << "first_shared_offset: " << adjusted_offset(t.first_shared_offset) << "\n"  
1177 - << "nshared_first_page: " << t.nshared_first_page << "\n"  
1178 - << "nshared_total: " << t.nshared_total << "\n"  
1179 - << "nbits_nobjects: " << t.nbits_nobjects << "\n"  
1180 - << "min_group_length: " << t.min_group_length << "\n"  
1181 - << "nbits_delta_group_length: " << t.nbits_delta_group_length << "\n"; 1184 + auto& info = *cf.log()->getInfo();
  1185 + HSharedObject& t = shared_object_hints_;
  1186 + info << "first_shared_obj: " << t.first_shared_obj << "\n"
  1187 + << "first_shared_offset: " << adjusted_offset(t.first_shared_offset) << "\n"
  1188 + << "nshared_first_page: " << t.nshared_first_page << "\n"
  1189 + << "nshared_total: " << t.nshared_total << "\n"
  1190 + << "nbits_nobjects: " << t.nbits_nobjects << "\n"
  1191 + << "min_group_length: " << t.min_group_length << "\n"
  1192 + << "nbits_delta_group_length: " << t.nbits_delta_group_length << "\n";
1182 1193
1183 for (size_t i = 0; i < toS(t.nshared_total); ++i) { 1194 for (size_t i = 0; i < toS(t.nshared_total); ++i) {
1184 HSharedObjectEntry& se = t.entries.at(i); 1195 HSharedObjectEntry& se = t.entries.at(i);
1185 - *m->log->getInfo() << "Shared Object " << i << ":\n"  
1186 - << " group length: " << se.delta_group_length + t.min_group_length  
1187 - << "\n"; 1196 + info << "Shared Object " << i << ":\n"
  1197 + << " group length: " << se.delta_group_length + t.min_group_length << "\n";
1188 // PDF spec says signature present nobjects_minus_one are always 0, so print them only if 1198 // PDF spec says signature present nobjects_minus_one are always 0, so print them only if
1189 // they have a non-zero value. 1199 // they have a non-zero value.
1190 if (se.signature_present) { 1200 if (se.signature_present) {
1191 - *m->log->getInfo() << " signature present\n"; 1201 + info << " signature present\n";
1192 } 1202 }
1193 if (se.nobjects_minus_one != 0) { 1203 if (se.nobjects_minus_one != 0) {
1194 - *m->log->getInfo() << " nobjects: " << se.nobjects_minus_one + 1 << "\n"; 1204 + info << " nobjects: " << se.nobjects_minus_one + 1 << "\n";
1195 } 1205 }
1196 } 1206 }
1197 } 1207 }
@@ -1199,10 +1209,11 @@ Lin::dumpHSharedObject() @@ -1199,10 +1209,11 @@ Lin::dumpHSharedObject()
1199 void 1209 void
1200 Lin::dumpHGeneric(HGeneric& t) 1210 Lin::dumpHGeneric(HGeneric& t)
1201 { 1211 {
1202 - *m->log->getInfo() << "first_object: " << t.first_object << "\n"  
1203 - << "first_object_offset: " << adjusted_offset(t.first_object_offset) << "\n"  
1204 - << "nobjects: " << t.nobjects << "\n"  
1205 - << "group_length: " << t.group_length << "\n"; 1212 + *cf.log()->getInfo() << "first_object: " << t.first_object << "\n"
  1213 + << "first_object_offset: " << adjusted_offset(t.first_object_offset)
  1214 + << "\n"
  1215 + << "nobjects: " << t.nobjects << "\n"
  1216 + << "group_length: " << t.group_length << "\n";
1206 } 1217 }
1207 1218
1208 template <typename T> 1219 template <typename T>
@@ -1215,7 +1226,7 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data) @@ -1215,7 +1226,7 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data)
1215 // actual offsets and lengths are not computed here, but anything related to object ordering is. 1226 // actual offsets and lengths are not computed here, but anything related to object ordering is.
1216 1227
1217 util::assertion( 1228 util::assertion(
1218 - !m->object_to_obj_users.empty(), 1229 + !object_to_obj_users_.empty(),
1219 "INTERNAL ERROR: QPDF::calculateLinearizationData called before optimize()" // 1230 "INTERNAL ERROR: QPDF::calculateLinearizationData called before optimize()" //
1220 ); 1231 );
1221 // Note that we can't call optimize here because we don't know whether it should be called 1232 // Note that we can't call optimize here because we don't know whether it should be called
@@ -1264,15 +1275,15 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data) @@ -1264,15 +1275,15 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data)
1264 1275
1265 // * outlines: part 6 or 9 1276 // * outlines: part 6 or 9
1266 1277
1267 - m->part4.clear();  
1268 - m->part6.clear();  
1269 - m->part7.clear();  
1270 - m->part8.clear();  
1271 - m->part9.clear();  
1272 - m->c_linp = LinParameters();  
1273 - m->c_page_offset_data = CHPageOffset();  
1274 - m->c_shared_object_data = CHSharedObject();  
1275 - m->c_outline_data = HGeneric(); 1278 + part4_.clear();
  1279 + part6_.clear();
  1280 + part7_.clear();
  1281 + part8_.clear();
  1282 + part9_.clear();
  1283 + c_linp_ = LinParameters();
  1284 + c_page_offset_data_ = CHPageOffset();
  1285 + c_shared_object_data_ = CHSharedObject();
  1286 + c_outline_data_ = HGeneric();
1276 1287
1277 QPDFObjectHandle root = qpdf.getRoot(); 1288 QPDFObjectHandle root = qpdf.getRoot();
1278 bool outlines_in_first_page = false; 1289 bool outlines_in_first_page = false;
@@ -1307,10 +1318,7 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data) @@ -1307,10 +1318,7 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data)
1307 std::set<QPDFObjGen> lc_outlines; 1318 std::set<QPDFObjGen> lc_outlines;
1308 std::set<QPDFObjGen> lc_root; 1319 std::set<QPDFObjGen> lc_root;
1309 1320
1310 - for (auto& oiter: m->object_to_obj_users) {  
1311 - QPDFObjGen const& og = oiter.first;  
1312 - std::set<ObjUser>& ous = oiter.second;  
1313 - 1321 + for (auto& [og, ous]: object_to_obj_users_) {
1314 bool in_open_document = false; 1322 bool in_open_document = false;
1315 bool in_first_page = false; 1323 bool in_first_page = false;
1316 int other_pages = 0; 1324 int other_pages = 0;
@@ -1409,8 +1417,8 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data) @@ -1409,8 +1417,8 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data)
1409 1417
1410 // npages is the size of the existing pages vector, which has been created by traversing the 1418 // npages is the size of the existing pages vector, which has been created by traversing the
1411 // pages tree, and as such is a reasonable size. 1419 // pages tree, and as such is a reasonable size.
1412 - m->c_linp.npages = npages;  
1413 - m->c_page_offset_data.entries = std::vector<CHPageOffsetEntry>(npages); 1420 + c_linp_.npages = npages;
  1421 + c_page_offset_data_.entries = std::vector<CHPageOffsetEntry>(npages);
1414 1422
1415 // Part 4: open document objects. We don't care about the order. 1423 // Part 4: open document objects. We don't care about the order.
1416 1424
@@ -1418,9 +1426,9 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data) @@ -1418,9 +1426,9 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data)
1418 lc_root.size() != 1, "found other than one root while calculating linearization data" // 1426 lc_root.size() != 1, "found other than one root while calculating linearization data" //
1419 ); 1427 );
1420 1428
1421 - m->part4.emplace_back(qpdf.getObject(*(lc_root.begin()))); 1429 + part4_.emplace_back(qpdf.getObject(*(lc_root.begin())));
1422 for (auto const& og: lc_open_document) { 1430 for (auto const& og: lc_open_document) {
1423 - m->part4.emplace_back(qpdf.getObject(og)); 1431 + part4_.emplace_back(qpdf.getObject(og));
1424 } 1432 }
1425 1433
1426 // Part 6: first page objects. Note: implementation note 124 states that Acrobat always treats 1434 // Part 6: first page objects. Note: implementation note 124 states that Acrobat always treats
@@ -1435,31 +1443,31 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data) @@ -1435,31 +1443,31 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data)
1435 no_ci_stop_if( 1443 no_ci_stop_if(
1436 !lc_first_page_private.erase(first_page_og), "unable to linearize first page" // 1444 !lc_first_page_private.erase(first_page_og), "unable to linearize first page" //
1437 ); 1445 );
1438 - m->c_linp.first_page_object = uc_pages.at(0).getObjectID();  
1439 - m->part6.emplace_back(uc_pages.at(0)); 1446 + c_linp_.first_page_object = uc_pages.at(0).getObjectID();
  1447 + part6_.emplace_back(uc_pages.at(0));
1440 1448
1441 // The PDF spec "recommends" an order for the rest of the objects, but we are going to disregard 1449 // The PDF spec "recommends" an order for the rest of the objects, but we are going to disregard
1442 // it except to the extent that it groups private and shared objects contiguously for the sake 1450 // it except to the extent that it groups private and shared objects contiguously for the sake
1443 // of hint tables. 1451 // of hint tables.
1444 1452
1445 for (auto const& og: lc_first_page_private) { 1453 for (auto const& og: lc_first_page_private) {
1446 - m->part6.emplace_back(qpdf.getObject(og)); 1454 + part6_.emplace_back(qpdf.getObject(og));
1447 } 1455 }
1448 1456
1449 for (auto const& og: lc_first_page_shared) { 1457 for (auto const& og: lc_first_page_shared) {
1450 - m->part6.emplace_back(qpdf.getObject(og)); 1458 + part6_.emplace_back(qpdf.getObject(og));
1451 } 1459 }
1452 1460
1453 // Place the outline dictionary if it goes in the first page section. 1461 // Place the outline dictionary if it goes in the first page section.
1454 if (outlines_in_first_page) { 1462 if (outlines_in_first_page) {
1455 - pushOutlinesToPart(m->part6, lc_outlines, object_stream_data); 1463 + pushOutlinesToPart(part6_, lc_outlines, object_stream_data);
1456 } 1464 }
1457 1465
1458 // Fill in page offset hint table information for the first page. The PDF spec says that 1466 // Fill in page offset hint table information for the first page. The PDF spec says that
1459 // nshared_objects should be zero for the first page. pdlin does not appear to obey this, but 1467 // nshared_objects should be zero for the first page. pdlin does not appear to obey this, but
1460 // it fills in garbage values for all the shared object identifiers on the first page. 1468 // it fills in garbage values for all the shared object identifiers on the first page.
1461 1469
1462 - m->c_page_offset_data.entries.at(0).nobjects = toI(m->part6.size()); 1470 + c_page_offset_data_.entries.at(0).nobjects = toI(part6_.size());
1463 1471
1464 // Part 7: other pages' private objects 1472 // Part 7: other pages' private objects
1465 1473
@@ -1473,23 +1481,23 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data) @@ -1473,23 +1481,23 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data)
1473 "unable to linearize page " + std::to_string(i) // 1481 "unable to linearize page " + std::to_string(i) //
1474 ); 1482 );
1475 1483
1476 - m->part7.emplace_back(uc_pages.at(i)); 1484 + part7_.emplace_back(uc_pages.at(i));
1477 1485
1478 // Place all non-shared objects referenced by this page, updating the page object count for 1486 // Place all non-shared objects referenced by this page, updating the page object count for
1479 // the hint table. 1487 // the hint table.
1480 1488
1481 - m->c_page_offset_data.entries.at(i).nobjects = 1; 1489 + c_page_offset_data_.entries.at(i).nobjects = 1;
1482 1490
1483 ObjUser ou(ObjUser::ou_page, i); 1491 ObjUser ou(ObjUser::ou_page, i);
1484 no_ci_stop_if( 1492 no_ci_stop_if(
1485 - !m->obj_user_to_objects.contains(ou), 1493 + !obj_user_to_objects_.contains(ou),
1486 "found unreferenced page while calculating linearization data" // 1494 "found unreferenced page while calculating linearization data" //
1487 ); 1495 );
1488 1496
1489 - for (auto const& og: m->obj_user_to_objects[ou]) { 1497 + for (auto const& og: obj_user_to_objects_[ou]) {
1490 if (lc_other_page_private.erase(og)) { 1498 if (lc_other_page_private.erase(og)) {
1491 - m->part7.emplace_back(qpdf.getObject(og));  
1492 - ++m->c_page_offset_data.entries.at(i).nobjects; 1499 + part7_.emplace_back(qpdf.getObject(og));
  1500 + ++c_page_offset_data_.entries.at(i).nobjects;
1493 } 1501 }
1494 } 1502 }
1495 } 1503 }
@@ -1504,7 +1512,7 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data) @@ -1504,7 +1512,7 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data)
1504 1512
1505 // Order is unimportant. 1513 // Order is unimportant.
1506 for (auto const& og: lc_other_page_shared) { 1514 for (auto const& og: lc_other_page_shared) {
1507 - m->part8.emplace_back(qpdf.getObject(og)); 1515 + part8_.emplace_back(qpdf.getObject(og));
1508 } 1516 }
1509 1517
1510 // Part 9: other objects 1518 // Part 9: other objects
@@ -1515,14 +1523,13 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data) @@ -1515,14 +1523,13 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data)
1515 // we throw all remaining objects in arbitrary order. 1523 // we throw all remaining objects in arbitrary order.
1516 1524
1517 // Place the pages tree. 1525 // Place the pages tree.
1518 - std::set<QPDFObjGen> pages_ogs =  
1519 - m->obj_user_to_objects[ObjUser(ObjUser::ou_root_key, "/Pages")]; 1526 + auto& pages_ogs = obj_user_to_objects_[{ObjUser::ou_root_key, "/Pages"}];
1520 no_ci_stop_if( 1527 no_ci_stop_if(
1521 pages_ogs.empty(), "found empty pages tree while calculating linearization data" // 1528 pages_ogs.empty(), "found empty pages tree while calculating linearization data" //
1522 ); 1529 );
1523 for (auto const& og: pages_ogs) { 1530 for (auto const& og: pages_ogs) {
1524 if (lc_other.erase(og)) { 1531 if (lc_other.erase(og)) {
1525 - m->part9.emplace_back(qpdf.getObject(og)); 1532 + part9_.emplace_back(qpdf.getObject(og));
1526 } 1533 }
1527 } 1534 }
1528 1535
@@ -1534,17 +1541,16 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data) @@ -1534,17 +1541,16 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data)
1534 QPDFObjGen thumb_og(thumb.getObjGen()); 1541 QPDFObjGen thumb_og(thumb.getObjGen());
1535 // Output the thumbnail itself 1542 // Output the thumbnail itself
1536 if (lc_thumbnail_private.erase(thumb_og) && !thumb.null()) { 1543 if (lc_thumbnail_private.erase(thumb_og) && !thumb.null()) {
1537 - m->part9.emplace_back(thumb); 1544 + part9_.emplace_back(thumb);
1538 } else { 1545 } else {
1539 // No internal error this time...there's nothing to stop this object from having 1546 // No internal error this time...there's nothing to stop this object from having
1540 // been referred to somewhere else outside of a page's /Thumb, and if it had been, 1547 // been referred to somewhere else outside of a page's /Thumb, and if it had been,
1541 // there's nothing to prevent it from having been in some set other than 1548 // there's nothing to prevent it from having been in some set other than
1542 // lc_thumbnail_private. 1549 // lc_thumbnail_private.
1543 } 1550 }
1544 - std::set<QPDFObjGen>& ogs = m->obj_user_to_objects[ObjUser(ObjUser::ou_thumb, i)];  
1545 - for (auto const& og: ogs) { 1551 + for (auto const& og: obj_user_to_objects_[{ObjUser::ou_thumb, i}]) {
1546 if (lc_thumbnail_private.erase(og)) { 1552 if (lc_thumbnail_private.erase(og)) {
1547 - m->part9.emplace_back(qpdf.getObject(og)); 1553 + part9_.emplace_back(qpdf.getObject(og));
1548 } 1554 }
1549 } 1555 }
1550 } 1556 }
@@ -1556,24 +1562,24 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data) @@ -1556,24 +1562,24 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data)
1556 1562
1557 // Place shared thumbnail objects 1563 // Place shared thumbnail objects
1558 for (auto const& og: lc_thumbnail_shared) { 1564 for (auto const& og: lc_thumbnail_shared) {
1559 - m->part9.emplace_back(qpdf.getObject(og)); 1565 + part9_.emplace_back(qpdf.getObject(og));
1560 } 1566 }
1561 1567
1562 // Place outlines unless in first page 1568 // Place outlines unless in first page
1563 if (!outlines_in_first_page) { 1569 if (!outlines_in_first_page) {
1564 - pushOutlinesToPart(m->part9, lc_outlines, object_stream_data); 1570 + pushOutlinesToPart(part9_, lc_outlines, object_stream_data);
1565 } 1571 }
1566 1572
1567 // Place all remaining objects 1573 // Place all remaining objects
1568 for (auto const& og: lc_other) { 1574 for (auto const& og: lc_other) {
1569 - m->part9.emplace_back(qpdf.getObject(og)); 1575 + part9_.emplace_back(qpdf.getObject(og));
1570 } 1576 }
1571 1577
1572 // Make sure we got everything exactly once. 1578 // Make sure we got everything exactly once.
1573 1579
1574 size_t num_placed = 1580 size_t num_placed =
1575 - m->part4.size() + m->part6.size() + m->part7.size() + m->part8.size() + m->part9.size();  
1576 - size_t num_wanted = m->object_to_obj_users.size(); 1581 + part4_.size() + part6_.size() + part7_.size() + part8_.size() + part9_.size();
  1582 + size_t num_wanted = object_to_obj_users_.size();
1577 no_ci_stop_if( 1583 no_ci_stop_if(
1578 // This can happen with damaged files, e.g. if the root is part of the the pages tree. 1584 // This can happen with damaged files, e.g. if the root is part of the the pages tree.
1579 num_placed != num_wanted, 1585 num_placed != num_wanted,
@@ -1593,20 +1599,20 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data) @@ -1593,20 +1599,20 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data)
1593 // only without regards to generation. 1599 // only without regards to generation.
1594 std::map<int, int> obj_to_index; 1600 std::map<int, int> obj_to_index;
1595 1601
1596 - m->c_shared_object_data.nshared_first_page = toI(m->part6.size());  
1597 - m->c_shared_object_data.nshared_total =  
1598 - m->c_shared_object_data.nshared_first_page + toI(m->part8.size()); 1602 + c_shared_object_data_.nshared_first_page = toI(part6_.size());
  1603 + c_shared_object_data_.nshared_total =
  1604 + c_shared_object_data_.nshared_first_page + toI(part8_.size());
1599 1605
1600 - std::vector<CHSharedObjectEntry>& shared = m->c_shared_object_data.entries;  
1601 - for (auto& oh: m->part6) { 1606 + std::vector<CHSharedObjectEntry>& shared = c_shared_object_data_.entries;
  1607 + for (auto& oh: part6_) {
1602 int obj = oh.getObjectID(); 1608 int obj = oh.getObjectID();
1603 obj_to_index[obj] = toI(shared.size()); 1609 obj_to_index[obj] = toI(shared.size());
1604 shared.emplace_back(obj); 1610 shared.emplace_back(obj);
1605 } 1611 }
1606 - QTC::TC("qpdf", "QPDF lin part 8 empty", m->part8.empty() ? 1 : 0);  
1607 - if (!m->part8.empty()) {  
1608 - m->c_shared_object_data.first_shared_obj = m->part8.at(0).getObjectID();  
1609 - for (auto& oh: m->part8) { 1612 + QTC::TC("qpdf", "QPDF lin part 8 empty", part8_.empty() ? 1 : 0);
  1613 + if (!part8_.empty()) {
  1614 + c_shared_object_data_.first_shared_obj = part8_.at(0).getObjectID();
  1615 + for (auto& oh: part8_) {
1610 int obj = oh.getObjectID(); 1616 int obj = oh.getObjectID();
1611 obj_to_index[obj] = toI(shared.size()); 1617 obj_to_index[obj] = toI(shared.size());
1612 shared.emplace_back(obj); 1618 shared.emplace_back(obj);
@@ -1614,22 +1620,22 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data) @@ -1614,22 +1620,22 @@ Lin::calculateLinearizationData(T const&amp; object_stream_data)
1614 } 1620 }
1615 no_ci_stop_if( 1621 no_ci_stop_if(
1616 std::cmp_not_equal( 1622 std::cmp_not_equal(
1617 - m->c_shared_object_data.nshared_total, m->c_shared_object_data.entries.size()), 1623 + c_shared_object_data_.nshared_total, c_shared_object_data_.entries.size()),
1618 "shared object hint table has wrong number of entries" // 1624 "shared object hint table has wrong number of entries" //
1619 ); 1625 );
1620 1626
1621 // Now compute the list of shared objects for each page after the first page. 1627 // Now compute the list of shared objects for each page after the first page.
1622 1628
1623 for (size_t i = 1; i < npages; ++i) { 1629 for (size_t i = 1; i < npages; ++i) {
1624 - CHPageOffsetEntry& pe = m->c_page_offset_data.entries.at(i); 1630 + CHPageOffsetEntry& pe = c_page_offset_data_.entries.at(i);
1625 ObjUser ou(ObjUser::ou_page, i); 1631 ObjUser ou(ObjUser::ou_page, i);
1626 no_ci_stop_if( 1632 no_ci_stop_if(
1627 - !m->obj_user_to_objects.contains(ou), 1633 + !obj_user_to_objects_.contains(ou),
1628 "found unreferenced page while calculating linearization data" // 1634 "found unreferenced page while calculating linearization data" //
1629 ); 1635 );
1630 1636
1631 - for (auto const& og: m->obj_user_to_objects[ou]) {  
1632 - if ((m->object_to_obj_users[og].size() > 1) && (obj_to_index.contains(og.getObj()))) { 1637 + for (auto const& og: obj_user_to_objects_[ou]) {
  1638 + if (object_to_obj_users_[og].size() > 1 && obj_to_index.contains(og.getObj())) {
1633 int idx = obj_to_index[og.getObj()]; 1639 int idx = obj_to_index[og.getObj()];
1634 ++pe.nshared_objects; 1640 ++pe.nshared_objects;
1635 pe.shared_identifiers.push_back(idx); 1641 pe.shared_identifiers.push_back(idx);
@@ -1655,22 +1661,22 @@ Lin::pushOutlinesToPart( @@ -1655,22 +1661,22 @@ Lin::pushOutlinesToPart(
1655 QTC::TC( 1661 QTC::TC(
1656 "qpdf", 1662 "qpdf",
1657 "QPDF lin outlines in part", 1663 "QPDF lin outlines in part",
1658 - &part == &m->part6 ? 0  
1659 - : (&part == &m->part9) ? 1  
1660 - : 9999); // can't happen 1664 + &part == &part6_ ? 0
  1665 + : (&part == &part9_) ? 1
  1666 + : 9999); // can't happen
1661 if (lc_outlines.erase(outlines_og)) { 1667 if (lc_outlines.erase(outlines_og)) {
1662 // Make sure outlines is in lc_outlines in case the file is damaged. in which case it may be 1668 // Make sure outlines is in lc_outlines in case the file is damaged. in which case it may be
1663 // included in an earlier part. 1669 // included in an earlier part.
1664 part.emplace_back(outlines); 1670 part.emplace_back(outlines);
1665 - m->c_outline_data.first_object = outlines_og.getObj();  
1666 - m->c_outline_data.nobjects = 1; 1671 + c_outline_data_.first_object = outlines_og.getObj();
  1672 + c_outline_data_.nobjects = 1;
1667 } 1673 }
1668 for (auto const& og: lc_outlines) { 1674 for (auto const& og: lc_outlines) {
1669 - if (!m->c_outline_data.first_object) {  
1670 - m->c_outline_data.first_object = og.getObj(); 1675 + if (!c_outline_data_.first_object) {
  1676 + c_outline_data_.first_object = og.getObj();
1671 } 1677 }
1672 part.emplace_back(qpdf.getObject(og)); 1678 part.emplace_back(qpdf.getObject(og));
1673 - ++m->c_outline_data.nobjects; 1679 + ++c_outline_data_.nobjects;
1674 } 1680 }
1675 } 1681 }
1676 1682
@@ -1684,11 +1690,11 @@ Lin::getLinearizedParts( @@ -1684,11 +1690,11 @@ Lin::getLinearizedParts(
1684 std::vector<QPDFObjectHandle>& part9) 1690 std::vector<QPDFObjectHandle>& part9)
1685 { 1691 {
1686 calculateLinearizationData(obj); 1692 calculateLinearizationData(obj);
1687 - part4 = m->part4;  
1688 - part6 = m->part6;  
1689 - part7 = m->part7;  
1690 - part8 = m->part8;  
1691 - part9 = m->part9; 1693 + part4 = part4_;
  1694 + part6 = part6_;
  1695 + part7 = part7_;
  1696 + part8 = part8_;
  1697 + part9 = part9_;
1692 } 1698 }
1693 1699
1694 static inline int 1700 static inline int
@@ -1728,7 +1734,7 @@ Lin::calculateHPageOffset(QPDFWriter::NewObjTable const&amp; new_obj, QPDFWriter::Ob @@ -1728,7 +1734,7 @@ Lin::calculateHPageOffset(QPDFWriter::NewObjTable const&amp; new_obj, QPDFWriter::Ob
1728 1734
1729 auto const& all_pages = pages.all(); 1735 auto const& all_pages = pages.all();
1730 size_t npages = all_pages.size(); 1736 size_t npages = all_pages.size();
1731 - CHPageOffset& cph = m->c_page_offset_data; 1737 + CHPageOffset& cph = c_page_offset_data_;
1732 std::vector<CHPageOffsetEntry>& cphe = cph.entries; 1738 std::vector<CHPageOffsetEntry>& cphe = cph.entries;
1733 1739
1734 // Calculate minimum and maximum values for number of objects per page and page length. 1740 // Calculate minimum and maximum values for number of objects per page and page length.
@@ -1739,7 +1745,7 @@ Lin::calculateHPageOffset(QPDFWriter::NewObjTable const&amp; new_obj, QPDFWriter::Ob @@ -1739,7 +1745,7 @@ Lin::calculateHPageOffset(QPDFWriter::NewObjTable const&amp; new_obj, QPDFWriter::Ob
1739 int max_length = 0; 1745 int max_length = 0;
1740 int max_shared = 0; 1746 int max_shared = 0;
1741 1747
1742 - HPageOffset& ph = m->page_offset_hints; 1748 + HPageOffset& ph = page_offset_hints_;
1743 std::vector<HPageOffsetEntry>& phe = ph.entries; 1749 std::vector<HPageOffsetEntry>& phe = ph.entries;
1744 // npages is the size of the existing pages array. 1750 // npages is the size of the existing pages array.
1745 phe = std::vector<HPageOffsetEntry>(npages); 1751 phe = std::vector<HPageOffsetEntry>(npages);
@@ -1774,7 +1780,7 @@ Lin::calculateHPageOffset(QPDFWriter::NewObjTable const&amp; new_obj, QPDFWriter::Ob @@ -1774,7 +1780,7 @@ Lin::calculateHPageOffset(QPDFWriter::NewObjTable const&amp; new_obj, QPDFWriter::Ob
1774 ph.min_page_length = min_length; 1780 ph.min_page_length = min_length;
1775 ph.nbits_delta_page_length = nbits(max_length - min_length); 1781 ph.nbits_delta_page_length = nbits(max_length - min_length);
1776 ph.nbits_nshared_objects = nbits(max_shared); 1782 ph.nbits_nshared_objects = nbits(max_shared);
1777 - ph.nbits_shared_identifier = nbits(m->c_shared_object_data.nshared_total); 1783 + ph.nbits_shared_identifier = nbits(c_shared_object_data_.nshared_total);
1778 ph.shared_denominator = 4; // doesn't matter 1784 ph.shared_denominator = 4; // doesn't matter
1779 1785
1780 // It isn't clear how to compute content offset and content length. Since we are not 1786 // It isn't clear how to compute content offset and content length. Since we are not
@@ -1806,9 +1812,9 @@ Lin::calculateHPageOffset(QPDFWriter::NewObjTable const&amp; new_obj, QPDFWriter::Ob @@ -1806,9 +1812,9 @@ Lin::calculateHPageOffset(QPDFWriter::NewObjTable const&amp; new_obj, QPDFWriter::Ob
1806 void 1812 void
1807 Lin::calculateHSharedObject(QPDFWriter::NewObjTable const& new_obj, QPDFWriter::ObjTable const& obj) 1813 Lin::calculateHSharedObject(QPDFWriter::NewObjTable const& new_obj, QPDFWriter::ObjTable const& obj)
1808 { 1814 {
1809 - CHSharedObject& cso = m->c_shared_object_data; 1815 + CHSharedObject& cso = c_shared_object_data_;
1810 std::vector<CHSharedObjectEntry>& csoe = cso.entries; 1816 std::vector<CHSharedObjectEntry>& csoe = cso.entries;
1811 - HSharedObject& so = m->shared_object_hints; 1817 + HSharedObject& so = shared_object_hints_;
1812 std::vector<HSharedObjectEntry>& soe = so.entries; 1818 std::vector<HSharedObjectEntry>& soe = so.entries;
1813 soe.clear(); 1819 soe.clear();
1814 1820
@@ -1851,13 +1857,13 @@ Lin::calculateHSharedObject(QPDFWriter::NewObjTable const&amp; new_obj, QPDFWriter:: @@ -1851,13 +1857,13 @@ Lin::calculateHSharedObject(QPDFWriter::NewObjTable const&amp; new_obj, QPDFWriter::
1851 void 1857 void
1852 Lin::calculateHOutline(QPDFWriter::NewObjTable const& new_obj, QPDFWriter::ObjTable const& obj) 1858 Lin::calculateHOutline(QPDFWriter::NewObjTable const& new_obj, QPDFWriter::ObjTable const& obj)
1853 { 1859 {
1854 - HGeneric& cho = m->c_outline_data; 1860 + HGeneric& cho = c_outline_data_;
1855 1861
1856 if (cho.nobjects == 0) { 1862 if (cho.nobjects == 0) {
1857 return; 1863 return;
1858 } 1864 }
1859 1865
1860 - HGeneric& ho = m->outline_hints; 1866 + HGeneric& ho = outline_hints_;
1861 1867
1862 ho.first_object = obj[cho.first_object].renumber; 1868 ho.first_object = obj[cho.first_object].renumber;
1863 ho.first_object_offset = new_obj[ho.first_object].xref.getOffset(); 1869 ho.first_object_offset = new_obj[ho.first_object].xref.getOffset();
@@ -1902,7 +1908,7 @@ write_vector_vector( @@ -1902,7 +1908,7 @@ write_vector_vector(
1902 void 1908 void
1903 Lin::writeHPageOffset(BitWriter& w) 1909 Lin::writeHPageOffset(BitWriter& w)
1904 { 1910 {
1905 - HPageOffset& t = m->page_offset_hints; 1911 + HPageOffset& t = page_offset_hints_;
1906 1912
1907 w.writeBitsInt(t.min_nobjects, 32); // 1 1913 w.writeBitsInt(t.min_nobjects, 32); // 1
1908 w.writeBits(toULL(t.first_page_offset), 32); // 2 1914 w.writeBits(toULL(t.first_page_offset), 32); // 2
@@ -1949,7 +1955,7 @@ Lin::writeHPageOffset(BitWriter&amp; w) @@ -1949,7 +1955,7 @@ Lin::writeHPageOffset(BitWriter&amp; w)
1949 void 1955 void
1950 Lin::writeHSharedObject(BitWriter& w) 1956 Lin::writeHSharedObject(BitWriter& w)
1951 { 1957 {
1952 - HSharedObject& t = m->shared_object_hints; 1958 + HSharedObject& t = shared_object_hints_;
1953 1959
1954 w.writeBitsInt(t.first_shared_obj, 32); // 1 1960 w.writeBitsInt(t.first_shared_obj, 32); // 1
1955 w.writeBits(toULL(t.first_shared_offset), 32); // 2 1961 w.writeBits(toULL(t.first_shared_offset), 32); // 2
@@ -2011,9 +2017,9 @@ Lin::generateHintStream( @@ -2011,9 +2017,9 @@ Lin::generateHintStream(
2011 S = toI(c.getCount()); 2017 S = toI(c.getCount());
2012 writeHSharedObject(w); 2018 writeHSharedObject(w);
2013 O = 0; 2019 O = 0;
2014 - if (m->outline_hints.nobjects > 0) { 2020 + if (outline_hints_.nobjects > 0) {
2015 O = toI(c.getCount()); 2021 O = toI(c.getCount());
2016 - writeHGeneric(w, m->outline_hints); 2022 + writeHGeneric(w, outline_hints_);
2017 } 2023 }
2018 if (compressed) { 2024 if (compressed) {
2019 hint_buffer = pl::pipe<Pl_Flate>(hint_buffer, Pl_Flate::a_deflate); 2025 hint_buffer = pl::pipe<Pl_Flate>(hint_buffer, Pl_Flate::a_deflate);
libqpdf/QPDF_objects.cc
@@ -157,7 +157,7 @@ Objects::parse(char const* password) @@ -157,7 +157,7 @@ Objects::parse(char const* password)
157 throw damagedPDF("", -1, std::string("error reading xref: ") + e.what()); 157 throw damagedPDF("", -1, std::string("error reading xref: ") + e.what());
158 } 158 }
159 } catch (QPDFExc& e) { 159 } catch (QPDFExc& e) {
160 - if (m->attempt_recovery) { 160 + if (!cf.surpress_recovery()) {
161 reconstruct_xref(e, xref_offset > 0); 161 reconstruct_xref(e, xref_offset > 0);
162 } else { 162 } else {
163 throw; 163 throw;
@@ -694,7 +694,7 @@ Objects::read_xrefTable(qpdf_offset_t xref_offset) @@ -694,7 +694,7 @@ Objects::read_xrefTable(qpdf_offset_t xref_offset)
694 for (qpdf_offset_t i = obj; i - num < obj; ++i) { 694 for (qpdf_offset_t i = obj; i - num < obj; ++i) {
695 if (i == 0) { 695 if (i == 0) {
696 // This is needed by checkLinearization() 696 // This is needed by checkLinearization()
697 - m->first_xref_item_offset = m->file->tell(); 697 + first_xref_item_offset_ = m->file->tell();
698 } 698 }
699 // For xref_table, these will always be small enough to be ints 699 // For xref_table, these will always be small enough to be ints
700 qpdf_offset_t f1 = 0; 700 qpdf_offset_t f1 = 0;
@@ -736,7 +736,7 @@ Objects::read_xrefTable(qpdf_offset_t xref_offset) @@ -736,7 +736,7 @@ Objects::read_xrefTable(qpdf_offset_t xref_offset)
736 } 736 }
737 737
738 if (cur_trailer.hasKey("/XRefStm")) { 738 if (cur_trailer.hasKey("/XRefStm")) {
739 - if (m->ignore_xref_streams) { 739 + if (cf.ignore_xref_streams()) {
740 QTC::TC("qpdf", "QPDF ignoring XRefStm in trailer"); 740 QTC::TC("qpdf", "QPDF ignoring XRefStm in trailer");
741 } else { 741 } else {
742 if (cur_trailer.getKey("/XRefStm").isInteger()) { 742 if (cur_trailer.getKey("/XRefStm").isInteger()) {
@@ -763,7 +763,7 @@ Objects::read_xrefTable(qpdf_offset_t xref_offset) @@ -763,7 +763,7 @@ Objects::read_xrefTable(qpdf_offset_t xref_offset)
763 qpdf_offset_t 763 qpdf_offset_t
764 Objects::read_xrefStream(qpdf_offset_t xref_offset, bool in_stream_recovery) 764 Objects::read_xrefStream(qpdf_offset_t xref_offset, bool in_stream_recovery)
765 { 765 {
766 - if (!m->ignore_xref_streams) { 766 + if (!cf.ignore_xref_streams()) {
767 QPDFObjectHandle xref_obj; 767 QPDFObjectHandle xref_obj;
768 try { 768 try {
769 m->in_read_xref_stream = true; 769 m->in_read_xref_stream = true;
@@ -956,14 +956,14 @@ Objects::processXRefStream( @@ -956,14 +956,14 @@ Objects::processXRefStream(
956 // object record, in which case the generation number appears as the third field. 956 // object record, in which case the generation number appears as the third field.
957 if (saw_first_compressed_object) { 957 if (saw_first_compressed_object) {
958 if (fields[0] != 2) { 958 if (fields[0] != 2) {
959 - m->uncompressed_after_compressed = true; 959 + uncompressed_after_compressed_ = true;
960 } 960 }
961 } else if (fields[0] == 2) { 961 } else if (fields[0] == 2) {
962 saw_first_compressed_object = true; 962 saw_first_compressed_object = true;
963 } 963 }
964 if (obj == 0) { 964 if (obj == 0) {
965 // This is needed by checkLinearization() 965 // This is needed by checkLinearization()
966 - m->first_xref_item_offset = xref_offset; 966 + first_xref_item_offset_ = xref_offset;
967 } else if (fields[0] == 0) { 967 } else if (fields[0] == 0) {
968 // Ignore fields[2], which we don't care about in this case. This works around the 968 // Ignore fields[2], which we don't care about in this case. This works around the
969 // issue of some PDF files that put invalid values, like -1, here for deleted 969 // issue of some PDF files that put invalid values, like -1, here for deleted
@@ -1073,7 +1073,7 @@ Objects::insertFreeXrefEntry(QPDFObjGen og) @@ -1073,7 +1073,7 @@ Objects::insertFreeXrefEntry(QPDFObjGen og)
1073 void 1073 void
1074 QPDF::showXRefTable() 1074 QPDF::showXRefTable()
1075 { 1075 {
1076 - auto& cout = *m->log->getInfo(); 1076 + auto& cout = *m->cf.log()->getInfo();
1077 for (auto const& iter: m->xref_table) { 1077 for (auto const& iter: m->xref_table) {
1078 QPDFObjGen const& og = iter.first; 1078 QPDFObjGen const& og = iter.first;
1079 QPDFXRefEntry const& entry = iter.second; 1079 QPDFXRefEntry const& entry = iter.second;
@@ -1084,15 +1084,15 @@ QPDF::showXRefTable() @@ -1084,15 +1084,15 @@ QPDF::showXRefTable()
1084 break; 1084 break;
1085 1085
1086 case 2: 1086 case 2:
1087 - *m->log->getInfo() << "compressed; stream = " << entry.getObjStreamNumber()  
1088 - << ", index = " << entry.getObjStreamIndex(); 1087 + *m->cf.log()->getInfo() << "compressed; stream = " << entry.getObjStreamNumber()
  1088 + << ", index = " << entry.getObjStreamIndex();
1089 break; 1089 break;
1090 1090
1091 default: 1091 default:
1092 throw std::logic_error("unknown cross-reference table type while showing xref_table"); 1092 throw std::logic_error("unknown cross-reference table type while showing xref_table");
1093 break; 1093 break;
1094 } 1094 }
1095 - m->log->info("\n"); 1095 + m->cf.log()->info("\n");
1096 } 1096 }
1097 } 1097 }
1098 1098
@@ -1248,7 +1248,7 @@ Objects::readStream(QPDFObjectHandle&amp; object, QPDFObjGen og, qpdf_offset_t offse @@ -1248,7 +1248,7 @@ Objects::readStream(QPDFObjectHandle&amp; object, QPDFObjGen og, qpdf_offset_t offse
1248 throw damagedPDF("expected endstream"); 1248 throw damagedPDF("expected endstream");
1249 } 1249 }
1250 } catch (QPDFExc& e) { 1250 } catch (QPDFExc& e) {
1251 - if (m->attempt_recovery) { 1251 + if (!cf.surpress_recovery()) {
1252 warn(e); 1252 warn(e);
1253 length = recoverStreamLength(m->file, og, stream_offset); 1253 length = recoverStreamLength(m->file, og, stream_offset);
1254 } else { 1254 } else {
@@ -1431,7 +1431,7 @@ Objects::readObjectAtOffset( @@ -1431,7 +1431,7 @@ Objects::readObjectAtOffset(
1431 QPDFObjGen og; 1431 QPDFObjGen og;
1432 setLastObjectDescription(description, exp_og); 1432 setLastObjectDescription(description, exp_og);
1433 1433
1434 - if (!m->attempt_recovery) { 1434 + if (cf.surpress_recovery()) {
1435 try_recovery = false; 1435 try_recovery = false;
1436 } 1436 }
1437 1437
libqpdf/qpdf/QPDFJob_private.hh
@@ -5,6 +5,7 @@ @@ -5,6 +5,7 @@
5 5
6 #include <qpdf/ClosedFileInputSource.hh> 6 #include <qpdf/ClosedFileInputSource.hh>
7 #include <qpdf/QPDFLogger.hh> 7 #include <qpdf/QPDFLogger.hh>
  8 +#include <qpdf/QPDF_private.hh>
8 9
9 // A selection of pages from a single input PDF to be included in the output. This corresponds to a 10 // A selection of pages from a single input PDF to be included in the output. This corresponds to a
10 // single clause in the --pages option. 11 // single clause in the --pages option.
@@ -149,7 +150,7 @@ class QPDFJob::Members @@ -149,7 +150,7 @@ class QPDFJob::Members
149 150
150 public: 151 public:
151 Members(QPDFJob& job) : 152 Members(QPDFJob& job) :
152 - log(QPDFLogger::defaultLogger()), 153 + log(qcf.log()),
153 inputs(job) 154 inputs(job)
154 { 155 {
155 } 156 }
@@ -167,6 +168,7 @@ class QPDFJob::Members @@ -167,6 +168,7 @@ class QPDFJob::Members
167 static int constexpr DEFAULT_OI_MIN_AREA = 16384; 168 static int constexpr DEFAULT_OI_MIN_AREA = 16384;
168 static int constexpr DEFAULT_II_MIN_BYTES = 1024; 169 static int constexpr DEFAULT_II_MIN_BYTES = 1024;
169 170
  171 + qpdf::Doc::Config qcf;
170 std::shared_ptr<QPDFLogger> log; 172 std::shared_ptr<QPDFLogger> log;
171 std::string message_prefix{"qpdf"}; 173 std::string message_prefix{"qpdf"};
172 bool warnings{false}; 174 bool warnings{false};
@@ -179,11 +181,9 @@ class QPDFJob::Members @@ -179,11 +181,9 @@ class QPDFJob::Members
179 int split_pages{0}; 181 int split_pages{0};
180 bool progress{false}; 182 bool progress{false};
181 std::function<void(int)> progress_handler{nullptr}; 183 std::function<void(int)> progress_handler{nullptr};
182 - bool suppress_warnings{false};  
183 bool warnings_exit_zero{false}; 184 bool warnings_exit_zero{false};
184 bool copy_encryption{false}; 185 bool copy_encryption{false};
185 bool encrypt{false}; 186 bool encrypt{false};
186 - bool password_is_hex_key{false};  
187 bool suppress_password_recovery{false}; 187 bool suppress_password_recovery{false};
188 password_mode_e password_mode{pm_auto}; 188 password_mode_e password_mode{pm_auto};
189 bool allow_insecure{false}; 189 bool allow_insecure{false};
@@ -218,10 +218,8 @@ class QPDFJob::Members @@ -218,10 +218,8 @@ class QPDFJob::Members
218 bool decode_level_set{false}; 218 bool decode_level_set{false};
219 bool normalize_set{false}; 219 bool normalize_set{false};
220 bool normalize{false}; 220 bool normalize{false};
221 - bool suppress_recovery{false};  
222 bool object_stream_set{false}; 221 bool object_stream_set{false};
223 qpdf_object_stream_e object_stream_mode{qpdf_o_preserve}; 222 qpdf_object_stream_e object_stream_mode{qpdf_o_preserve};
224 - bool ignore_xref_streams{false};  
225 bool qdf_mode{false}; 223 bool qdf_mode{false};
226 bool preserve_unreferenced_objects{false}; 224 bool preserve_unreferenced_objects{false};
227 remove_unref_e remove_unreferenced_page_resources{re_auto}; 225 remove_unref_e remove_unreferenced_page_resources{re_auto};
libqpdf/qpdf/QPDF_private.hh
@@ -5,6 +5,7 @@ @@ -5,6 +5,7 @@
5 5
6 #include <qpdf/QPDFAcroFormDocumentHelper.hh> 6 #include <qpdf/QPDFAcroFormDocumentHelper.hh>
7 #include <qpdf/QPDFEmbeddedFileDocumentHelper.hh> 7 #include <qpdf/QPDFEmbeddedFileDocumentHelper.hh>
  8 +#include <qpdf/QPDFLogger.hh>
8 #include <qpdf/QPDFObject_private.hh> 9 #include <qpdf/QPDFObject_private.hh>
9 #include <qpdf/QPDFOutlineDocumentHelper.hh> 10 #include <qpdf/QPDFOutlineDocumentHelper.hh>
10 #include <qpdf/QPDFPageDocumentHelper.hh> 11 #include <qpdf/QPDFPageDocumentHelper.hh>
@@ -20,6 +21,135 @@ namespace qpdf @@ -20,6 +21,135 @@ namespace qpdf
20 { 21 {
21 class OffsetBuffer; 22 class OffsetBuffer;
22 } // namespace is 23 } // namespace is
  24 +
  25 + class Doc: public QPDF
  26 + {
  27 + public:
  28 + class Config
  29 + {
  30 + public:
  31 + Config() :
  32 + log_(QPDFLogger::defaultLogger())
  33 + {
  34 + }
  35 +
  36 + bool
  37 + password_is_hex_key() const
  38 + {
  39 + return password_is_hex_key_;
  40 + }
  41 +
  42 + Config&
  43 + password_is_hex_key(bool val)
  44 + {
  45 + password_is_hex_key_ = val;
  46 + return *this;
  47 + }
  48 +
  49 + bool
  50 + ignore_xref_streams() const
  51 + {
  52 + return ignore_xref_streams_;
  53 + }
  54 +
  55 + Config&
  56 + ignore_xref_streams(bool val)
  57 + {
  58 + ignore_xref_streams_ = val;
  59 + return *this;
  60 + }
  61 +
  62 + std::shared_ptr<QPDFLogger>
  63 + log() const
  64 + {
  65 + return log_;
  66 + }
  67 +
  68 + Config&
  69 + log(std::shared_ptr<QPDFLogger> val)
  70 + {
  71 + log_ = val;
  72 + return *this;
  73 + }
  74 +
  75 + bool
  76 + suppress_warnings() const
  77 + {
  78 + return suppress_warnings_;
  79 + }
  80 +
  81 + Config&
  82 + suppress_warnings(bool val)
  83 + {
  84 + suppress_warnings_ = val;
  85 + return *this;
  86 + }
  87 +
  88 + size_t
  89 + max_warnings() const
  90 + {
  91 + return max_warnings_;
  92 + }
  93 +
  94 + Config&
  95 + max_warnings(size_t val)
  96 + {
  97 + max_warnings_ = val;
  98 + return *this;
  99 + }
  100 +
  101 + bool
  102 + surpress_recovery() const
  103 + {
  104 + return surpress_recovery_;
  105 + }
  106 +
  107 + Config&
  108 + surpress_recovery(bool val)
  109 + {
  110 + surpress_recovery_ = val;
  111 + return *this;
  112 + }
  113 +
  114 + bool
  115 + immediate_copy_from() const
  116 + {
  117 + return immediate_copy_from_;
  118 + }
  119 +
  120 + Config&
  121 + immediate_copy_from(bool val)
  122 + {
  123 + immediate_copy_from_ = val;
  124 + return *this;
  125 + }
  126 +
  127 + bool
  128 + check_mode() const
  129 + {
  130 + return check_mode_;
  131 + }
  132 +
  133 + Config&
  134 + check_mode(bool val)
  135 + {
  136 + check_mode_ = val;
  137 + return *this;
  138 + }
  139 +
  140 + private:
  141 + std::shared_ptr<QPDFLogger> log_;
  142 +
  143 + size_t max_warnings_{0};
  144 +
  145 + bool password_is_hex_key_{false};
  146 + bool ignore_xref_streams_{false};
  147 + bool suppress_warnings_{false};
  148 + bool surpress_recovery_{false};
  149 + bool check_mode_{false};
  150 + bool immediate_copy_from_{false};
  151 + }; // Class Config
  152 + }; // class Doc
23 } // namespace qpdf 153 } // namespace qpdf
24 154
25 class BitStream; 155 class BitStream;
@@ -106,168 +236,8 @@ class QPDF::StringDecrypter final: public QPDFObjectHandle::StringDecrypter @@ -106,168 +236,8 @@ class QPDF::StringDecrypter final: public QPDFObjectHandle::StringDecrypter
106 QPDF* qpdf; 236 QPDF* qpdf;
107 QPDFObjGen og; 237 QPDFObjGen og;
108 }; 238 };
109 -  
110 -// PDF 1.4: Table F.4  
111 -struct QPDF::HPageOffsetEntry  
112 -{  
113 - int delta_nobjects{0}; // 1  
114 - qpdf_offset_t delta_page_length{0}; // 2  
115 - // vectors' sizes = nshared_objects  
116 - int nshared_objects{0}; // 3  
117 - std::vector<int> shared_identifiers; // 4  
118 - std::vector<int> shared_numerators; // 5  
119 - qpdf_offset_t delta_content_offset{0}; // 6  
120 - qpdf_offset_t delta_content_length{0}; // 7  
121 -};  
122 -  
123 -// PDF 1.4: Table F.3  
124 -struct QPDF::HPageOffset  
125 -{  
126 - int min_nobjects{0}; // 1  
127 - qpdf_offset_t first_page_offset{0}; // 2  
128 - int nbits_delta_nobjects{0}; // 3  
129 - int min_page_length{0}; // 4  
130 - int nbits_delta_page_length{0}; // 5  
131 - int min_content_offset{0}; // 6  
132 - int nbits_delta_content_offset{0}; // 7  
133 - int min_content_length{0}; // 8  
134 - int nbits_delta_content_length{0}; // 9  
135 - int nbits_nshared_objects{0}; // 10  
136 - int nbits_shared_identifier{0}; // 11  
137 - int nbits_shared_numerator{0}; // 12  
138 - int shared_denominator{0}; // 13  
139 - // vector size is npages  
140 - std::vector<HPageOffsetEntry> entries;  
141 -};  
142 -  
143 -// PDF 1.4: Table F.6  
144 -struct QPDF::HSharedObjectEntry  
145 -{  
146 - // Item 3 is a 128-bit signature (unsupported by Acrobat)  
147 - int delta_group_length{0}; // 1  
148 - int signature_present{0}; // 2 -- always 0  
149 - int nobjects_minus_one{0}; // 4 -- always 0  
150 -};  
151 -  
152 -// PDF 1.4: Table F.5  
153 -struct QPDF::HSharedObject  
154 -{  
155 - int first_shared_obj{0}; // 1  
156 - qpdf_offset_t first_shared_offset{0}; // 2  
157 - int nshared_first_page{0}; // 3  
158 - int nshared_total{0}; // 4  
159 - int nbits_nobjects{0}; // 5  
160 - int min_group_length{0}; // 6  
161 - int nbits_delta_group_length{0}; // 7  
162 - // vector size is nshared_total  
163 - std::vector<HSharedObjectEntry> entries;  
164 -};  
165 -  
166 -// PDF 1.4: Table F.9  
167 -struct QPDF::HGeneric  
168 -{  
169 - int first_object{0}; // 1  
170 - qpdf_offset_t first_object_offset{0}; // 2  
171 - int nobjects{0}; // 3  
172 - int group_length{0}; // 4  
173 -};  
174 -  
175 // Other linearization data structures 239 // Other linearization data structures
176 240
177 -// Initialized from Linearization Parameter dictionary  
178 -struct QPDF::LinParameters  
179 -{  
180 - qpdf_offset_t file_size{0}; // /L  
181 - int first_page_object{0}; // /O  
182 - qpdf_offset_t first_page_end{0}; // /E  
183 - size_t npages{0}; // /N  
184 - qpdf_offset_t xref_zero_offset{0}; // /T  
185 - int first_page{0}; // /P  
186 - qpdf_offset_t H_offset{0}; // offset of primary hint stream  
187 - qpdf_offset_t H_length{0}; // length of primary hint stream  
188 -};  
189 -  
190 -// Computed hint table value data structures. These tables contain the computed values on which  
191 -// the hint table values are based. They exclude things like number of bits and store actual  
192 -// values instead of mins and deltas. File offsets are also absolute rather than being offset  
193 -// by the size of the primary hint table. We populate the hint table structures from these  
194 -// during writing and compare the hint table values with these during validation. We ignore  
195 -// some values for various reasons described in the code. Those values are omitted from these  
196 -// structures. Note also that object numbers are object numbers from the input file, not the  
197 -// output file.  
198 -  
199 -// Naming convention: CHSomething is analogous to HSomething above. "CH" is computed hint.  
200 -  
201 -struct QPDF::CHPageOffsetEntry  
202 -{  
203 - int nobjects{0};  
204 - int nshared_objects{0};  
205 - // vectors' sizes = nshared_objects  
206 - std::vector<int> shared_identifiers;  
207 -};  
208 -  
209 -struct QPDF::CHPageOffset  
210 -{  
211 - // vector size is npages  
212 - std::vector<CHPageOffsetEntry> entries;  
213 -};  
214 -  
215 -struct QPDF::CHSharedObjectEntry  
216 -{  
217 - CHSharedObjectEntry(int object) :  
218 - object(object)  
219 - {  
220 - }  
221 -  
222 - int object;  
223 -};  
224 -  
225 -// PDF 1.4: Table F.5  
226 -struct QPDF::CHSharedObject  
227 -{  
228 - int first_shared_obj{0};  
229 - int nshared_first_page{0};  
230 - int nshared_total{0};  
231 - // vector size is nshared_total  
232 - std::vector<CHSharedObjectEntry> entries;  
233 -};  
234 -  
235 -// No need for CHGeneric -- HGeneric is fine as is.  
236 -  
237 -// Data structures to support optimization -- implemented in QPDF_optimization.cc  
238 -  
239 -class QPDF::ObjUser  
240 -{  
241 - public:  
242 - enum user_e { ou_page = 1, ou_thumb, ou_trailer_key, ou_root_key, ou_root };  
243 -  
244 - ObjUser() = delete;  
245 -  
246 - // type must be ou_root  
247 - ObjUser(user_e type);  
248 -  
249 - // type must be one of ou_page or ou_thumb  
250 - ObjUser(user_e type, size_t pageno);  
251 -  
252 - // type must be one of ou_trailer_key or ou_root_key  
253 - ObjUser(user_e type, std::string const& key);  
254 -  
255 - bool operator<(ObjUser const&) const;  
256 -  
257 - user_e ou_type;  
258 - size_t pageno{0}; // if ou_page;  
259 - std::string key; // if ou_trailer_key or ou_root_key  
260 -};  
261 -  
262 -struct QPDF::UpdateObjectMapsFrame  
263 -{  
264 - UpdateObjectMapsFrame(ObjUser const& ou, QPDFObjectHandle oh, bool top);  
265 -  
266 - ObjUser const& ou;  
267 - QPDFObjectHandle oh;  
268 - bool top;  
269 -};  
270 -  
271 class QPDF::PatternFinder final: public InputSource::Finder 241 class QPDF::PatternFinder final: public InputSource::Finder
272 { 242 {
273 public: 243 public:
@@ -296,7 +266,6 @@ class QPDF::Doc @@ -296,7 +266,6 @@ class QPDF::Doc
296 { 266 {
297 public: 267 public:
298 class Encryption; 268 class Encryption;
299 - class JobSetter;  
300 class Linearization; 269 class Linearization;
301 class Objects; 270 class Objects;
302 class Pages; 271 class Pages;
@@ -352,7 +321,9 @@ class QPDF::Doc @@ -352,7 +321,9 @@ class QPDF::Doc
352 QPDF& qpdf; 321 QPDF& qpdf;
353 QPDF::Members* m; 322 QPDF::Members* m;
354 323
  324 + qpdf::Doc::Config& cf;
355 QPDF::Doc::Pages& pages; 325 QPDF::Doc::Pages& pages;
  326 + QPDF::Doc::Objects& objects;
356 }; 327 };
357 328
358 Doc() = delete; 329 Doc() = delete;
@@ -368,6 +339,18 @@ class QPDF::Doc @@ -368,6 +339,18 @@ class QPDF::Doc
368 { 339 {
369 } 340 }
370 341
  342 + qpdf::Doc::Config&
  343 + config()
  344 + {
  345 + return cf;
  346 + }
  347 +
  348 + void
  349 + config(qpdf::Doc::Config val)
  350 + {
  351 + cf = val;
  352 + }
  353 +
371 inline Linearization& linearization(); 354 inline Linearization& linearization();
372 355
373 inline Objects& objects(); 356 inline Objects& objects();
@@ -421,10 +404,13 @@ class QPDF::Doc @@ -421,10 +404,13 @@ class QPDF::Doc
421 return *page_labels_; 404 return *page_labels_;
422 } 405 }
423 406
424 - private: 407 + protected:
425 QPDF& qpdf; 408 QPDF& qpdf;
426 QPDF::Members* m; 409 QPDF::Members* m;
427 410
  411 + qpdf::Doc::Config cf;
  412 +
  413 + private:
428 // Document Helpers; 414 // Document Helpers;
429 std::unique_ptr<QPDFAcroFormDocumentHelper> acroform_; 415 std::unique_ptr<QPDFAcroFormDocumentHelper> acroform_;
430 std::unique_ptr<QPDFEmbeddedFileDocumentHelper> embedded_files_; 416 std::unique_ptr<QPDFEmbeddedFileDocumentHelper> embedded_files_;
@@ -564,6 +550,10 @@ class QPDF::Doc::Linearization: Common @@ -564,6 +550,10 @@ class QPDF::Doc::Linearization: Common
564 { 550 {
565 } 551 }
566 552
  553 + bool linearized();
  554 + bool check();
  555 + void show_data();
  556 +
567 // For QPDFWriter: 557 // For QPDFWriter:
568 558
569 template <typename T> 559 template <typename T>
@@ -593,6 +583,168 @@ class QPDF::Doc::Linearization: Common @@ -593,6 +583,168 @@ class QPDF::Doc::Linearization: Common
593 int& O, 583 int& O,
594 bool compressed); 584 bool compressed);
595 585
  586 + private:
  587 + // Data structures to support optimization -- implemented in QPDF_optimization.cc
  588 +
  589 + class ObjUser
  590 + {
  591 + public:
  592 + enum user_e { ou_page = 1, ou_thumb, ou_trailer_key, ou_root_key, ou_root };
  593 +
  594 + ObjUser() = delete;
  595 +
  596 + // type must be ou_root
  597 + ObjUser(user_e type);
  598 +
  599 + // type must be one of ou_page or ou_thumb
  600 + ObjUser(user_e type, size_t pageno);
  601 +
  602 + // type must be one of ou_trailer_key or ou_root_key
  603 + ObjUser(user_e type, std::string const& key);
  604 +
  605 + bool operator<(ObjUser const&) const;
  606 +
  607 + user_e ou_type;
  608 + size_t pageno{0}; // if ou_page;
  609 + std::string key; // if ou_trailer_key or ou_root_key
  610 + };
  611 +
  612 + struct UpdateObjectMapsFrame
  613 + {
  614 + UpdateObjectMapsFrame(ObjUser const& ou, QPDFObjectHandle oh, bool top);
  615 +
  616 + ObjUser const& ou;
  617 + QPDFObjectHandle oh;
  618 + bool top;
  619 + };
  620 +
  621 + // PDF 1.4: Table F.4
  622 + struct HPageOffsetEntry
  623 + {
  624 + int delta_nobjects{0}; // 1
  625 + qpdf_offset_t delta_page_length{0}; // 2
  626 + // vectors' sizes = nshared_objects
  627 + int nshared_objects{0}; // 3
  628 + std::vector<int> shared_identifiers; // 4
  629 + std::vector<int> shared_numerators; // 5
  630 + qpdf_offset_t delta_content_offset{0}; // 6
  631 + qpdf_offset_t delta_content_length{0}; // 7
  632 + };
  633 +
  634 + // PDF 1.4: Table F.3
  635 + struct HPageOffset
  636 + {
  637 + int min_nobjects{0}; // 1
  638 + qpdf_offset_t first_page_offset{0}; // 2
  639 + int nbits_delta_nobjects{0}; // 3
  640 + int min_page_length{0}; // 4
  641 + int nbits_delta_page_length{0}; // 5
  642 + int min_content_offset{0}; // 6
  643 + int nbits_delta_content_offset{0}; // 7
  644 + int min_content_length{0}; // 8
  645 + int nbits_delta_content_length{0}; // 9
  646 + int nbits_nshared_objects{0}; // 10
  647 + int nbits_shared_identifier{0}; // 11
  648 + int nbits_shared_numerator{0}; // 12
  649 + int shared_denominator{0}; // 13
  650 + // vector size is npages
  651 + std::vector<HPageOffsetEntry> entries;
  652 + };
  653 +
  654 + // PDF 1.4: Table F.6
  655 + struct HSharedObjectEntry
  656 + {
  657 + // Item 3 is a 128-bit signature (unsupported by Acrobat)
  658 + int delta_group_length{0}; // 1
  659 + int signature_present{0}; // 2 -- always 0
  660 + int nobjects_minus_one{0}; // 4 -- always 0
  661 + };
  662 +
  663 + // PDF 1.4: Table F.5
  664 + struct HSharedObject
  665 + {
  666 + int first_shared_obj{0}; // 1
  667 + qpdf_offset_t first_shared_offset{0}; // 2
  668 + int nshared_first_page{0}; // 3
  669 + int nshared_total{0}; // 4
  670 + int nbits_nobjects{0}; // 5
  671 + int min_group_length{0}; // 6
  672 + int nbits_delta_group_length{0}; // 7
  673 + // vector size is nshared_total
  674 + std::vector<HSharedObjectEntry> entries;
  675 + };
  676 +
  677 + // PDF 1.4: Table F.9
  678 + struct HGeneric
  679 + {
  680 + int first_object{0}; // 1
  681 + qpdf_offset_t first_object_offset{0}; // 2
  682 + int nobjects{0}; // 3
  683 + int group_length{0}; // 4
  684 + };
  685 +
  686 + // Other linearization data structures
  687 +
  688 + // Initialized from Linearization Parameter dictionary
  689 + struct LinParameters
  690 + {
  691 + qpdf_offset_t file_size{0}; // /L
  692 + int first_page_object{0}; // /O
  693 + qpdf_offset_t first_page_end{0}; // /E
  694 + size_t npages{0}; // /N
  695 + qpdf_offset_t xref_zero_offset{0}; // /T
  696 + int first_page{0}; // /P
  697 + qpdf_offset_t H_offset{0}; // offset of primary hint stream
  698 + qpdf_offset_t H_length{0}; // length of primary hint stream
  699 + };
  700 +
  701 + // Computed hint table value data structures. These tables contain the computed values on which
  702 + // the hint table values are based. They exclude things like number of bits and store actual
  703 + // values instead of mins and deltas. File offsets are also absolute rather than being offset
  704 + // by the size of the primary hint table. We populate the hint table structures from these
  705 + // during writing and compare the hint table values with these during validation. We ignore
  706 + // some values for various reasons described in the code. Those values are omitted from these
  707 + // structures. Note also that object numbers are object numbers from the input file, not the
  708 + // output file.
  709 +
  710 + // Naming convention: CHSomething is analogous to HSomething above. "CH" is computed hint.
  711 +
  712 + struct CHPageOffsetEntry
  713 + {
  714 + int nobjects{0};
  715 + int nshared_objects{0};
  716 + // vectors' sizes = nshared_objects
  717 + std::vector<int> shared_identifiers;
  718 + };
  719 +
  720 + struct CHPageOffset
  721 + {
  722 + // vector size is npages
  723 + std::vector<CHPageOffsetEntry> entries;
  724 + };
  725 +
  726 + struct CHSharedObjectEntry
  727 + {
  728 + CHSharedObjectEntry(int object) :
  729 + object(object)
  730 + {
  731 + }
  732 +
  733 + int object;
  734 + };
  735 +
  736 + // PDF 1.4: Table F.5
  737 + struct CHSharedObject
  738 + {
  739 + int first_shared_obj{0};
  740 + int nshared_first_page{0};
  741 + int nshared_total{0};
  742 + // vector size is nshared_total
  743 + std::vector<CHSharedObjectEntry> entries;
  744 + };
  745 +
  746 + // No need for CHGeneric -- HGeneric is fine as is.
  747 +
596 // methods to support linearization checking -- implemented in QPDF_linearization.cc 748 // methods to support linearization checking -- implemented in QPDF_linearization.cc
597 749
598 void readLinearizationData(); 750 void readLinearizationData();
@@ -647,6 +799,36 @@ class QPDF::Doc::Linearization: Common @@ -647,6 +799,36 @@ class QPDF::Doc::Linearization: Common
647 std::function<int(QPDFObjectHandle&)> skip_stream_parameters); 799 std::function<int(QPDFObjectHandle&)> skip_stream_parameters);
648 void filterCompressedObjects(std::map<int, int> const& object_stream_data); 800 void filterCompressedObjects(std::map<int, int> const& object_stream_data);
649 void filterCompressedObjects(QPDFWriter::ObjTable const& object_stream_data); 801 void filterCompressedObjects(QPDFWriter::ObjTable const& object_stream_data);
  802 +
  803 + // Optimization data
  804 + std::map<ObjUser, std::set<QPDFObjGen>> obj_user_to_objects_;
  805 + std::map<QPDFObjGen, std::set<ObjUser>> object_to_obj_users_;
  806 +
  807 + // Linearization data
  808 + bool linearization_warnings_{false}; // set by linearizationWarning, used by checkLinearization
  809 +
  810 + // Linearization parameter dictionary and hint table data: may be read from file or computed
  811 + // prior to writing a linearized file
  812 + QPDFObjectHandle lindict_;
  813 + LinParameters linp_;
  814 + HPageOffset page_offset_hints_;
  815 + HSharedObject shared_object_hints_;
  816 + HGeneric outline_hints_;
  817 +
  818 + // Computed linearization data: used to populate above tables during writing and to compare
  819 + // with them during validation. c_ means computed.
  820 + LinParameters c_linp_;
  821 + CHPageOffset c_page_offset_data_;
  822 + CHSharedObject c_shared_object_data_;
  823 + HGeneric c_outline_data_;
  824 +
  825 + // Object ordering data for linearized files: initialized by calculateLinearizationData().
  826 + // Part numbers refer to the PDF 1.4 specification.
  827 + std::vector<QPDFObjectHandle> part4_;
  828 + std::vector<QPDFObjectHandle> part6_;
  829 + std::vector<QPDFObjectHandle> part7_;
  830 + std::vector<QPDFObjectHandle> part8_;
  831 + std::vector<QPDFObjectHandle> part9_;
650 }; 832 };
651 833
652 class QPDF::Doc::Objects: Common 834 class QPDF::Doc::Objects: Common
@@ -743,7 +925,7 @@ class QPDF::Doc::Objects: Common @@ -743,7 +925,7 @@ class QPDF::Doc::Objects: Common
743 return copier_; 925 return copier_;
744 } 926 }
745 927
746 - bool immediate_copy_from() const; 928 + // bool immediate_copy_from() const;
747 929
748 private: 930 private:
749 std::shared_ptr<Copier> copier_; 931 std::shared_ptr<Copier> copier_;
@@ -776,6 +958,17 @@ class QPDF::Doc::Objects: Common @@ -776,6 +958,17 @@ class QPDF::Doc::Objects: Common
776 return streams_; 958 return streams_;
777 } 959 }
778 960
  961 + // actual value from file
  962 + qpdf_offset_t
  963 + first_xref_item_offset() const
  964 + {
  965 + return first_xref_item_offset_;
  966 + }
  967 + bool
  968 + uncompressed_after_compressed() const
  969 + {
  970 + return uncompressed_after_compressed_;
  971 + }
779 void parse(char const* password); 972 void parse(char const* password);
780 std::shared_ptr<QPDFObject> const& resolve(QPDFObjGen og); 973 std::shared_ptr<QPDFObject> const& resolve(QPDFObjGen og);
781 void inParse(bool); 974 void inParse(bool);
@@ -847,6 +1040,10 @@ class QPDF::Doc::Objects: Common @@ -847,6 +1040,10 @@ class QPDF::Doc::Objects: Common
847 1040
848 Foreign foreign_; 1041 Foreign foreign_;
849 Streams streams_; 1042 Streams streams_;
  1043 +
  1044 + // Linearization data
  1045 + qpdf_offset_t first_xref_item_offset_{0}; // actual value from file
  1046 + bool uncompressed_after_compressed_{false};
850 }; // class QPDF::Doc::Objects 1047 }; // class QPDF::Doc::Objects
851 1048
852 // This class is used to represent a PDF Pages tree. 1049 // This class is used to represent a PDF Pages tree.
@@ -969,18 +1166,11 @@ class QPDF::Members: Doc @@ -969,18 +1166,11 @@ class QPDF::Members: Doc
969 Doc::Linearization lin; 1166 Doc::Linearization lin;
970 Doc::Objects objects; 1167 Doc::Objects objects;
971 Doc::Pages pages; 1168 Doc::Pages pages;
972 - std::shared_ptr<QPDFLogger> log;  
973 unsigned long long unique_id{0}; 1169 unsigned long long unique_id{0};
974 qpdf::Tokenizer tokenizer; 1170 qpdf::Tokenizer tokenizer;
975 std::shared_ptr<InputSource> file; 1171 std::shared_ptr<InputSource> file;
976 std::string last_object_description; 1172 std::string last_object_description;
977 std::shared_ptr<QPDFObject::Description> last_ostream_description; 1173 std::shared_ptr<QPDFObject::Description> last_ostream_description;
978 - bool provided_password_is_hex_key{false};  
979 - bool ignore_xref_streams{false};  
980 - bool suppress_warnings{false};  
981 - size_t max_warnings{0};  
982 - bool attempt_recovery{true};  
983 - bool check_mode{false};  
984 std::shared_ptr<EncryptionParameters> encp; 1174 std::shared_ptr<EncryptionParameters> encp;
985 std::string pdf_version; 1175 std::string pdf_version;
986 std::map<QPDFObjGen, QPDFXRefEntry> xref_table; 1176 std::map<QPDFObjGen, QPDFXRefEntry> xref_table;
@@ -995,42 +1185,9 @@ class QPDF::Members: Doc @@ -995,42 +1185,9 @@ class QPDF::Members: Doc
995 bool reconstructed_xref{false}; 1185 bool reconstructed_xref{false};
996 bool in_read_xref_stream{false}; 1186 bool in_read_xref_stream{false};
997 bool fixed_dangling_refs{false}; 1187 bool fixed_dangling_refs{false};
998 - bool immediate_copy_from{false};  
999 bool in_parse{false}; 1188 bool in_parse{false};
1000 bool parsed{false}; 1189 bool parsed{false};
1001 std::set<int> resolved_object_streams; 1190 std::set<int> resolved_object_streams;
1002 -  
1003 - // Linearization data  
1004 - qpdf_offset_t first_xref_item_offset{0}; // actual value from file  
1005 - bool uncompressed_after_compressed{false};  
1006 - bool linearization_warnings{false}; // set by linearizationWarning, used by checkLinearization  
1007 -  
1008 - // Linearization parameter dictionary and hint table data: may be read from file or computed  
1009 - // prior to writing a linearized file  
1010 - QPDFObjectHandle lindict;  
1011 - LinParameters linp;  
1012 - HPageOffset page_offset_hints;  
1013 - HSharedObject shared_object_hints;  
1014 - HGeneric outline_hints;  
1015 -  
1016 - // Computed linearization data: used to populate above tables during writing and to compare  
1017 - // with them during validation. c_ means computed.  
1018 - LinParameters c_linp;  
1019 - CHPageOffset c_page_offset_data;  
1020 - CHSharedObject c_shared_object_data;  
1021 - HGeneric c_outline_data;  
1022 -  
1023 - // Object ordering data for linearized files: initialized by calculateLinearizationData().  
1024 - // Part numbers refer to the PDF 1.4 specification.  
1025 - std::vector<QPDFObjectHandle> part4;  
1026 - std::vector<QPDFObjectHandle> part6;  
1027 - std::vector<QPDFObjectHandle> part7;  
1028 - std::vector<QPDFObjectHandle> part8;  
1029 - std::vector<QPDFObjectHandle> part9;  
1030 -  
1031 - // Optimization data  
1032 - std::map<ObjUser, std::set<QPDFObjGen>> obj_user_to_objects;  
1033 - std::map<QPDFObjGen, std::set<ObjUser>> object_to_obj_users;  
1034 }; 1191 };
1035 1192
1036 // The Resolver class is restricted to QPDFObject and BaseHandle so that only it can resolve 1193 // The Resolver class is restricted to QPDFObject and BaseHandle so that only it can resolve
@@ -1051,7 +1208,9 @@ class QPDF::Doc::Resolver @@ -1051,7 +1208,9 @@ class QPDF::Doc::Resolver
1051 inline QPDF::Doc::Common::Common(QPDF& qpdf, QPDF::Members* m) : 1208 inline QPDF::Doc::Common::Common(QPDF& qpdf, QPDF::Members* m) :
1052 qpdf(qpdf), 1209 qpdf(qpdf),
1053 m(m), 1210 m(m),
1054 - pages(m->pages) 1211 + cf(m->cf),
  1212 + pages(m->pages),
  1213 + objects(m->objects)
1055 { 1214 {
1056 } 1215 }
1057 1216
qpdf/qpdf.testcov
@@ -248,7 +248,6 @@ QPDFJob password not encodable 0 @@ -248,7 +248,6 @@ QPDFJob password not encodable 0
248 QPDFJob auto-encode password 0 248 QPDFJob auto-encode password 0
249 QPDFJob bytes fallback warning 0 249 QPDFJob bytes fallback warning 0
250 QPDFJob invalid utf-8 in auto 0 250 QPDFJob invalid utf-8 in auto 0
251 -QPDFJob input password hex-bytes 0  
252 QPDFFormFieldObjectHelper replaced BMC at EOF 0 251 QPDFFormFieldObjectHelper replaced BMC at EOF 0
253 QPDFFormFieldObjectHelper fallback Tf 0 252 QPDFFormFieldObjectHelper fallback Tf 0
254 QPDFPageObjectHelper copy shared attribute 1 253 QPDFPageObjectHelper copy shared attribute 1
qpdf/qtest/qpdf/catalgg.out
1 -checking catalgg.pdf  
2 WARNING: catalgg.pdf: catalog /Type entry missing or invalid 1 WARNING: catalgg.pdf: catalog /Type entry missing or invalid
  2 +checking catalgg.pdf
3 PDF Version: 1.3 3 PDF Version: 1.3
4 File is not encrypted 4 File is not encrypted
5 File is not linearized 5 File is not linearized