Commit 05cb757df564e4196c89fbf88c00489b23b65102

Authored by m-holger
Committed by GitHub
2 parents 7e623fd9 a944fe35

Merge pull request #1544 from m-holger/writer

Remove implementation detail from QPDFWriter.hh
include/qpdf/QPDFWriter.hh
... ... @@ -20,11 +20,19 @@
20 20 #ifndef QPDFWRITER_HH
21 21 #define QPDFWRITER_HH
22 22  
  23 +#include <qpdf/Constants.h>
23 24 #include <qpdf/DLL.h>
24 25 #include <qpdf/Types.h>
25 26  
  27 +#include <qpdf/Buffer.hh>
  28 +#include <qpdf/PDFVersion.hh>
  29 +#include <qpdf/Pipeline.hh>
  30 +#include <qpdf/Pl_Buffer.hh>
  31 +#include <qpdf/QPDFObjGen.hh>
  32 +#include <qpdf/QPDFObjectHandle.hh>
  33 +#include <qpdf/QPDFXRefEntry.hh>
  34 +
26 35 #include <bitset>
27   -#include <concepts>
28 36 #include <cstdio>
29 37 #include <functional>
30 38 #include <list>
... ... @@ -35,24 +43,7 @@
35 43 #include <string_view>
36 44 #include <vector>
37 45  
38   -#include <qpdf/Constants.h>
39   -
40   -#include <qpdf/Buffer.hh>
41   -#include <qpdf/PDFVersion.hh>
42   -#include <qpdf/Pipeline.hh>
43   -#include <qpdf/Pl_Buffer.hh>
44   -#include <qpdf/QPDFObjGen.hh>
45   -#include <qpdf/QPDFObjectHandle.hh>
46   -#include <qpdf/QPDFXRefEntry.hh>
47   -
48   -namespace qpdf::pl
49   -{
50   - struct Link;
51   -}
52   -
53 46 class QPDF;
54   -class Pl_Count;
55   -class Pl_MD5;
56 47  
57 48 // This class implements a simple writer for saving QPDF objects to new PDF files. See comments
58 49 // through the header file for additional details.
... ... @@ -449,132 +440,8 @@ class QPDFWriter
449 440 class NewObjTable;
450 441  
451 442 private:
452   - // flags used by unparseObject
453   - static int const f_stream = 1 << 0;
454   - static int const f_filtered = 1 << 1;
455   - static int const f_in_ostream = 1 << 2;
456   - static int const f_hex_string = 1 << 3;
457   - static int const f_no_encryption = 1 << 4;
458   -
459   - enum trailer_e { t_normal, t_lin_first, t_lin_second };
460   -
461   - unsigned int bytesNeeded(long long n);
462   - void writeBinary(unsigned long long val, unsigned int bytes);
463   - QPDFWriter& write(std::string_view str);
464   - QPDFWriter& write(size_t count, char c);
465   - QPDFWriter& write(std::integral auto val);
466   - QPDFWriter& write_name(std::string const& str);
467   - QPDFWriter& write_string(std::string const& str, bool force_binary = false);
468   - QPDFWriter& write_encrypted(std::string_view str);
469   -
470   - template <typename... Args>
471   - QPDFWriter& write_qdf(Args&&... args);
472   - template <typename... Args>
473   - QPDFWriter& write_no_qdf(Args&&... args);
474   - void assignCompressedObjectNumbers(QPDFObjGen og);
475   - void enqueueObject(QPDFObjectHandle object);
476   - void writeObjectStreamOffsets(std::vector<qpdf_offset_t>& offsets, int first_obj);
477   - void writeObjectStream(QPDFObjectHandle object);
478   - void writeObject(QPDFObjectHandle object, int object_stream_index = -1);
479   - void writeTrailer(
480   - trailer_e which, int size, bool xref_stream, qpdf_offset_t prev, int linearization_pass);
481   - bool willFilterStream(
482   - QPDFObjectHandle stream,
483   - bool& compress_stream,
484   - bool& is_metadata,
485   - std::string* stream_data);
486   - void unparseObject(
487   - QPDFObjectHandle object,
488   - size_t level,
489   - int flags,
490   - // for stream dictionaries
491   - size_t stream_length = 0,
492   - bool compress = false);
493   - void unparseChild(QPDFObjectHandle const& child, size_t level, int flags);
494   - void initializeSpecialStreams();
495   - void preserveObjectStreams();
496   - void generateObjectStreams();
497   - std::string getOriginalID1();
498   - void generateID(bool encrypted);
499   - void interpretR3EncryptionParameters(
500   - bool allow_accessibility,
501   - bool allow_extract,
502   - bool allow_assemble,
503   - bool allow_annotate_and_form,
504   - bool allow_form_filling,
505   - bool allow_modify_other,
506   - qpdf_r3_print_e print,
507   - qpdf_r3_modify_e modify);
508   - void disableIncompatibleEncryption(int major, int minor, int extension_level);
509   - void parseVersion(std::string const& version, int& major, int& minor) const;
510   - int compareVersions(int major1, int minor1, int major2, int minor2) const;
511   - void setEncryptionParameters(char const* user_password, char const* owner_password);
512   - void setEncryptionMinimumVersion();
513   - void setDataKey(int objid);
514   - int openObject(int objid = 0);
515   - void closeObject(int objid);
516   - QPDFObjectHandle getTrimmedTrailer();
517   - void prepareFileForWrite();
518   - void enqueueObjectsStandard();
519   - void enqueueObjectsPCLm();
520   - void indicateProgress(bool decrement, bool finished);
521   - void writeStandard();
522   - void writeLinearized();
523   - void enqueuePart(std::vector<QPDFObjectHandle>& part);
524   - void writeEncryptionDictionary();
525   - void initializeTables(size_t extra = 0);
526   - void doWriteSetup();
527   - void writeHeader();
528   - void writeHintStream(int hint_id);
529   - qpdf_offset_t writeXRefTable(trailer_e which, int first, int last, int size);
530   - qpdf_offset_t writeXRefTable(
531   - trailer_e which,
532   - int first,
533   - int last,
534   - int size,
535   - // for linearization
536   - qpdf_offset_t prev,
537   - bool suppress_offsets,
538   - int hint_id,
539   - qpdf_offset_t hint_offset,
540   - qpdf_offset_t hint_length,
541   - int linearization_pass);
542   - qpdf_offset_t writeXRefStream(
543   - int objid,
544   - int max_id,
545   - qpdf_offset_t max_offset,
546   - trailer_e which,
547   - int first,
548   - int last,
549   - int size);
550   - qpdf_offset_t writeXRefStream(
551   - int objid,
552   - int max_id,
553   - qpdf_offset_t max_offset,
554   - trailer_e which,
555   - int first,
556   - int last,
557   - int size,
558   - // for linearization
559   - qpdf_offset_t prev,
560   - int hint_id,
561   - qpdf_offset_t hint_offset,
562   - qpdf_offset_t hint_length,
563   - bool skip_compression,
564   - int linearization_pass);
565   - size_t calculateXrefStreamPadding(qpdf_offset_t xref_bytes);
566   -
567   - // When filtering subsections, push additional pipelines to the stack. When ready to switch,
568   - // activate the pipeline stack. When the passed in PipelinePopper goes out of scope, the stack
569   - // is popped.
570   -
571   - void adjustAESStreamLength(size_t& length);
572   - void computeDeterministicIDData();
573   -
574 443 class Members;
575 444  
576   - // Keep all member variables inside the Members object, which we dynamically allocate. This
577   - // makes it possible to add new private members without breaking binary compatibility.
578 445 std::shared_ptr<Members> m;
579 446 };
580 447  
... ...
libqpdf/NNTree.cc
... ... @@ -666,7 +666,7 @@ NNTreeImpl::repair()
666 666 NNTreeImpl repl(qpdf, new_node, key_type, value_valid, false);
667 667 std::map<QPDFObjectHandle, QPDFObjectHandle, Cmp> items;
668 668 for (auto const& [key, value]: *this) {
669   - if (key && value && repl.keyValid(key) && repl.value_valid(value) ) {
  669 + if (key && value && repl.keyValid(key) && repl.value_valid(value)) {
670 670 items.insert_or_assign(key, value);
671 671 }
672 672 }
... ...
libqpdf/QPDFWriter.cc
... ... @@ -11,7 +11,6 @@
11 11 #include <qpdf/Pl_PNGFilter.hh>
12 12 #include <qpdf/Pl_RC4.hh>
13 13 #include <qpdf/Pl_StdioFile.hh>
14   -#include <qpdf/Pl_String.hh>
15 14 #include <qpdf/QIntC.hh>
16 15 #include <qpdf/QPDFObjectHandle_private.hh>
17 16 #include <qpdf/QPDFObject_private.hh>
... ... @@ -22,6 +21,7 @@
22 21 #include <qpdf/Util.hh>
23 22  
24 23 #include <algorithm>
  24 +#include <concepts>
25 25 #include <cstdlib>
26 26 #include <stdexcept>
27 27  
... ... @@ -265,12 +265,152 @@ class QPDFWriter::Members
265 265 friend class QPDFWriter;
266 266  
267 267 public:
268   - ~Members();
  268 + // flags used by unparseObject
  269 + static int const f_stream = 1 << 0;
  270 + static int const f_filtered = 1 << 1;
  271 + static int const f_in_ostream = 1 << 2;
  272 + static int const f_hex_string = 1 << 3;
  273 + static int const f_no_encryption = 1 << 4;
  274 +
  275 + enum trailer_e { t_normal, t_lin_first, t_lin_second };
  276 +
  277 + Members(QPDFWriter& w, QPDF& pdf) :
  278 + w(w),
  279 + pdf(pdf),
  280 + root_og(
  281 + pdf.getRoot().getObjGen().isIndirect() ? pdf.getRoot().getObjGen() : QPDFObjGen(-1, 0)),
  282 + pipeline_stack(pipeline)
  283 + {
  284 + }
269 285  
270   - private:
271   - Members(QPDF& pdf);
272 286 Members(Members const&) = delete;
273 287  
  288 + ~Members()
  289 + {
  290 + if (file && close_file) {
  291 + fclose(file);
  292 + }
  293 + delete output_buffer;
  294 + }
  295 +
  296 + void write();
  297 + std::map<QPDFObjGen, QPDFXRefEntry> getWrittenXRefTable();
  298 + void setMinimumPDFVersion(std::string const& version, int extension_level);
  299 + void copyEncryptionParameters(QPDF&);
  300 + void doWriteSetup();
  301 + void prepareFileForWrite();
  302 +
  303 + void disableIncompatibleEncryption(int major, int minor, int extension_level);
  304 + void interpretR3EncryptionParameters(
  305 + bool allow_accessibility,
  306 + bool allow_extract,
  307 + bool allow_assemble,
  308 + bool allow_annotate_and_form,
  309 + bool allow_form_filling,
  310 + bool allow_modify_other,
  311 + qpdf_r3_print_e print,
  312 + qpdf_r3_modify_e modify);
  313 + void setEncryptionParameters(char const* user_password, char const* owner_password);
  314 + void setEncryptionMinimumVersion();
  315 + void parseVersion(std::string const& version, int& major, int& minor) const;
  316 + int compareVersions(int major1, int minor1, int major2, int minor2) const;
  317 + void generateID(bool encrypted);
  318 + std::string getOriginalID1();
  319 + void initializeTables(size_t extra = 0);
  320 + void preserveObjectStreams();
  321 + void generateObjectStreams();
  322 + void initializeSpecialStreams();
  323 + void enqueueObject(QPDFObjectHandle object);
  324 + void enqueueObjectsStandard();
  325 + void enqueueObjectsPCLm();
  326 + void enqueuePart(std::vector<QPDFObjectHandle>& part);
  327 + void assignCompressedObjectNumbers(QPDFObjGen og);
  328 + QPDFObjectHandle getTrimmedTrailer();
  329 +
  330 + bool willFilterStream(
  331 + QPDFObjectHandle stream,
  332 + bool& compress_stream,
  333 + bool& is_metadata,
  334 + std::string* stream_data);
  335 + unsigned int bytesNeeded(long long n);
  336 + void writeBinary(unsigned long long val, unsigned int bytes);
  337 + Members& write(std::string_view str);
  338 + Members& write(size_t count, char c);
  339 + Members& write(std::integral auto val);
  340 + Members& write_name(std::string const& str);
  341 + Members& write_string(std::string const& str, bool force_binary = false);
  342 + Members& write_encrypted(std::string_view str);
  343 +
  344 + template <typename... Args>
  345 + Members& write_qdf(Args&&... args);
  346 + template <typename... Args>
  347 + Members& write_no_qdf(Args&&... args);
  348 + void writeObjectStreamOffsets(std::vector<qpdf_offset_t>& offsets, int first_obj);
  349 + void writeObjectStream(QPDFObjectHandle object);
  350 + void writeObject(QPDFObjectHandle object, int object_stream_index = -1);
  351 + void writeTrailer(
  352 + trailer_e which, int size, bool xref_stream, qpdf_offset_t prev, int linearization_pass);
  353 + void unparseObject(
  354 + QPDFObjectHandle object,
  355 + size_t level,
  356 + int flags,
  357 + // for stream dictionaries
  358 + size_t stream_length = 0,
  359 + bool compress = false);
  360 + void unparseChild(QPDFObjectHandle const& child, size_t level, int flags);
  361 + int openObject(int objid = 0);
  362 + void closeObject(int objid);
  363 + void writeStandard();
  364 + void writeLinearized();
  365 + void writeEncryptionDictionary();
  366 + void writeHeader();
  367 + void writeHintStream(int hint_id);
  368 + qpdf_offset_t writeXRefTable(trailer_e which, int first, int last, int size);
  369 + qpdf_offset_t writeXRefTable(
  370 + trailer_e which,
  371 + int first,
  372 + int last,
  373 + int size,
  374 + // for linearization
  375 + qpdf_offset_t prev,
  376 + bool suppress_offsets,
  377 + int hint_id,
  378 + qpdf_offset_t hint_offset,
  379 + qpdf_offset_t hint_length,
  380 + int linearization_pass);
  381 + qpdf_offset_t writeXRefStream(
  382 + int objid,
  383 + int max_id,
  384 + qpdf_offset_t max_offset,
  385 + trailer_e which,
  386 + int first,
  387 + int last,
  388 + int size);
  389 + qpdf_offset_t writeXRefStream(
  390 + int objid,
  391 + int max_id,
  392 + qpdf_offset_t max_offset,
  393 + trailer_e which,
  394 + int first,
  395 + int last,
  396 + int size,
  397 + // for linearization
  398 + qpdf_offset_t prev,
  399 + int hint_id,
  400 + qpdf_offset_t hint_offset,
  401 + qpdf_offset_t hint_length,
  402 + bool skip_compression,
  403 + int linearization_pass);
  404 +
  405 + void setDataKey(int objid);
  406 + void indicateProgress(bool decrement, bool finished);
  407 + size_t calculateXrefStreamPadding(qpdf_offset_t xref_bytes);
  408 +
  409 + void adjustAESStreamLength(size_t& length);
  410 + void computeDeterministicIDData();
  411 +
  412 + private:
  413 + QPDFWriter& w;
274 414 QPDF& pdf;
275 415 QPDFObjGen root_og{-1, 0};
276 416 char const* filename{"unspecified"};
... ... @@ -341,34 +481,19 @@ class QPDFWriter::Members
341 481 int next_progress_report{0};
342 482 };
343 483  
344   -QPDFWriter::Members::Members(QPDF& pdf) :
345   - pdf(pdf),
346   - root_og(pdf.getRoot().getObjGen().isIndirect() ? pdf.getRoot().getObjGen() : QPDFObjGen(-1, 0)),
347   - pipeline_stack(pipeline)
348   -{
349   -}
350   -
351   -QPDFWriter::Members::~Members()
352   -{
353   - if (file && close_file) {
354   - fclose(file);
355   - }
356   - delete output_buffer;
357   -}
358   -
359 484 QPDFWriter::QPDFWriter(QPDF& pdf) :
360   - m(new Members(pdf))
  485 + m(std::make_shared<Members>(*this, pdf))
361 486 {
362 487 }
363 488  
364 489 QPDFWriter::QPDFWriter(QPDF& pdf, char const* filename) :
365   - m(new Members(pdf))
  490 + m(std::make_shared<Members>(*this, pdf))
366 491 {
367 492 setOutputFilename(filename);
368 493 }
369 494  
370 495 QPDFWriter::QPDFWriter(QPDF& pdf, char const* description, FILE* file, bool close_file) :
371   - m(new Members(pdf))
  496 + m(std::make_shared<Members>(*this, pdf))
372 497 {
373 498 setOutputFile(description, file, close_file);
374 499 }
... ... @@ -381,11 +506,9 @@ QPDFWriter::setOutputFilename(char const* filename)
381 506 bool close_file = false;
382 507 if (filename == nullptr) {
383 508 description = "standard output";
384   - QTC::TC("qpdf", "QPDFWriter write to stdout");
385 509 f = stdout;
386 510 QUtil::binary_stdout();
387 511 } else {
388   - QTC::TC("qpdf", "QPDFWriter write to file");
389 512 f = QUtil::safe_fopen(filename, "wb+");
390 513 close_file = true;
391 514 }
... ... @@ -508,9 +631,15 @@ QPDFWriter::setNewlineBeforeEndstream(bool val)
508 631 void
509 632 QPDFWriter::setMinimumPDFVersion(std::string const& version, int extension_level)
510 633 {
  634 + m->setMinimumPDFVersion(version, extension_level);
  635 +}
  636 +
  637 +void
  638 +QPDFWriter::Members::setMinimumPDFVersion(std::string const& version, int extension_level)
  639 +{
511 640 bool set_version = false;
512 641 bool set_extension_level = false;
513   - if (m->min_pdf_version.empty()) {
  642 + if (min_pdf_version.empty()) {
514 643 set_version = true;
515 644 set_extension_level = true;
516 645 } else {
... ... @@ -519,25 +648,24 @@ QPDFWriter::setMinimumPDFVersion(std::string const&amp; version, int extension_level
519 648 int min_major = 0;
520 649 int min_minor = 0;
521 650 parseVersion(version, old_major, old_minor);
522   - parseVersion(m->min_pdf_version, min_major, min_minor);
  651 + parseVersion(min_pdf_version, min_major, min_minor);
523 652 int compare = compareVersions(old_major, old_minor, min_major, min_minor);
524 653 if (compare > 0) {
525 654 QTC::TC("qpdf", "QPDFWriter increasing minimum version", extension_level == 0 ? 0 : 1);
526 655 set_version = true;
527 656 set_extension_level = true;
528 657 } else if (compare == 0) {
529   - if (extension_level > m->min_extension_level) {
530   - QTC::TC("qpdf", "QPDFWriter increasing extension level");
  658 + if (extension_level > min_extension_level) {
531 659 set_extension_level = true;
532 660 }
533 661 }
534 662 }
535 663  
536 664 if (set_version) {
537   - m->min_pdf_version = version;
  665 + min_pdf_version = version;
538 666 }
539 667 if (set_extension_level) {
540   - m->min_extension_level = extension_level;
  668 + min_extension_level = extension_level;
541 669 }
542 670 }
543 671  
... ... @@ -562,7 +690,6 @@ QPDFWriter::setExtraHeaderText(std::string const&amp; text)
562 690 {
563 691 m->extra_header_text = text;
564 692 if (!m->extra_header_text.empty() && *m->extra_header_text.rbegin() != '\n') {
565   - QTC::TC("qpdf", "QPDFWriter extra header text add newline");
566 693 m->extra_header_text += "\n";
567 694 } else {
568 695 QTC::TC("qpdf", "QPDFWriter extra header text no newline");
... ... @@ -647,7 +774,7 @@ QPDFWriter::setR2EncryptionParametersInsecure(
647 774 if (!allow_annotate) {
648 775 m->encryption->setP(6, false);
649 776 }
650   - setEncryptionParameters(user_password, owner_password);
  777 + m->setEncryptionParameters(user_password, owner_password);
651 778 }
652 779  
653 780 void
... ... @@ -663,7 +790,7 @@ QPDFWriter::setR3EncryptionParametersInsecure(
663 790 qpdf_r3_print_e print)
664 791 {
665 792 m->encryption = std::make_unique<QPDF::EncryptionData>(2, 3, 16, true);
666   - interpretR3EncryptionParameters(
  793 + m->interpretR3EncryptionParameters(
667 794 allow_accessibility,
668 795 allow_extract,
669 796 allow_assemble,
... ... @@ -672,7 +799,7 @@ QPDFWriter::setR3EncryptionParametersInsecure(
672 799 allow_modify_other,
673 800 print,
674 801 qpdf_r3m_all);
675   - setEncryptionParameters(user_password, owner_password);
  802 + m->setEncryptionParameters(user_password, owner_password);
676 803 }
677 804  
678 805 void
... ... @@ -691,7 +818,7 @@ QPDFWriter::setR4EncryptionParametersInsecure(
691 818 {
692 819 m->encryption = std::make_unique<QPDF::EncryptionData>(4, 4, 16, encrypt_metadata);
693 820 m->encrypt_use_aes = use_aes;
694   - interpretR3EncryptionParameters(
  821 + m->interpretR3EncryptionParameters(
695 822 allow_accessibility,
696 823 allow_extract,
697 824 allow_assemble,
... ... @@ -700,7 +827,7 @@ QPDFWriter::setR4EncryptionParametersInsecure(
700 827 allow_modify_other,
701 828 print,
702 829 qpdf_r3m_all);
703   - setEncryptionParameters(user_password, owner_password);
  830 + m->setEncryptionParameters(user_password, owner_password);
704 831 }
705 832  
706 833 void
... ... @@ -718,7 +845,7 @@ QPDFWriter::setR5EncryptionParameters(
718 845 {
719 846 m->encryption = std::make_unique<QPDF::EncryptionData>(5, 5, 32, encrypt_metadata);
720 847 m->encrypt_use_aes = true;
721   - interpretR3EncryptionParameters(
  848 + m->interpretR3EncryptionParameters(
722 849 allow_accessibility,
723 850 allow_extract,
724 851 allow_assemble,
... ... @@ -727,7 +854,7 @@ QPDFWriter::setR5EncryptionParameters(
727 854 allow_modify_other,
728 855 print,
729 856 qpdf_r3m_all);
730   - setEncryptionParameters(user_password, owner_password);
  857 + m->setEncryptionParameters(user_password, owner_password);
731 858 }
732 859  
733 860 void
... ... @@ -744,7 +871,7 @@ QPDFWriter::setR6EncryptionParameters(
744 871 bool encrypt_metadata)
745 872 {
746 873 m->encryption = std::make_unique<QPDF::EncryptionData>(5, 6, 32, encrypt_metadata);
747   - interpretR3EncryptionParameters(
  874 + m->interpretR3EncryptionParameters(
748 875 allow_accessibility,
749 876 allow_extract,
750 877 allow_assemble,
... ... @@ -754,11 +881,11 @@ QPDFWriter::setR6EncryptionParameters(
754 881 print,
755 882 qpdf_r3m_all);
756 883 m->encrypt_use_aes = true;
757   - setEncryptionParameters(user_password, owner_password);
  884 + m->setEncryptionParameters(user_password, owner_password);
758 885 }
759 886  
760 887 void
761   -QPDFWriter::interpretR3EncryptionParameters(
  888 +QPDFWriter::Members::interpretR3EncryptionParameters(
762 889 bool allow_accessibility,
763 890 bool allow_extract,
764 891 bool allow_assemble,
... ... @@ -797,21 +924,21 @@ QPDFWriter::interpretR3EncryptionParameters(
797 924 // 10: accessibility; ignored by readers, should always be set
798 925 // 11: document assembly even if 4 is clear
799 926 // 12: high-resolution printing
800   - if (!allow_accessibility && m->encryption->getR() <= 3) {
  927 + if (!allow_accessibility && encryption->getR() <= 3) {
801 928 // Bit 10 is deprecated and should always be set. This used to mean accessibility. There
802 929 // is no way to disable accessibility with R > 3.
803   - m->encryption->setP(10, false);
  930 + encryption->setP(10, false);
804 931 }
805 932 if (!allow_extract) {
806   - m->encryption->setP(5, false);
  933 + encryption->setP(5, false);
807 934 }
808 935  
809 936 switch (print) {
810 937 case qpdf_r3p_none:
811   - m->encryption->setP(3, false); // any printing
  938 + encryption->setP(3, false); // any printing
812 939 [[fallthrough]];
813 940 case qpdf_r3p_low:
814   - m->encryption->setP(12, false); // high resolution printing
  941 + encryption->setP(12, false); // high resolution printing
815 942 [[fallthrough]];
816 943 case qpdf_r3p_full:
817 944 break;
... ... @@ -825,16 +952,16 @@ QPDFWriter::interpretR3EncryptionParameters(
825 952 // NOT EXERCISED IN TEST SUITE
826 953 switch (modify) {
827 954 case qpdf_r3m_none:
828   - m->encryption->setP(11, false); // document assembly
  955 + encryption->setP(11, false); // document assembly
829 956 [[fallthrough]];
830 957 case qpdf_r3m_assembly:
831   - m->encryption->setP(9, false); // filling in form fields
  958 + encryption->setP(9, false); // filling in form fields
832 959 [[fallthrough]];
833 960 case qpdf_r3m_form:
834   - m->encryption->setP(6, false); // modify annotations, fill in form fields
  961 + encryption->setP(6, false); // modify annotations, fill in form fields
835 962 [[fallthrough]];
836 963 case qpdf_r3m_annotate:
837   - m->encryption->setP(4, false); // other modifications
  964 + encryption->setP(4, false); // other modifications
838 965 [[fallthrough]];
839 966 case qpdf_r3m_all:
840 967 break;
... ... @@ -843,36 +970,42 @@ QPDFWriter::interpretR3EncryptionParameters(
843 970 // END NOT EXERCISED IN TEST SUITE
844 971  
845 972 if (!allow_assemble) {
846   - m->encryption->setP(11, false);
  973 + encryption->setP(11, false);
847 974 }
848 975 if (!allow_annotate_and_form) {
849   - m->encryption->setP(6, false);
  976 + encryption->setP(6, false);
850 977 }
851 978 if (!allow_form_filling) {
852   - m->encryption->setP(9, false);
  979 + encryption->setP(9, false);
853 980 }
854 981 if (!allow_modify_other) {
855   - m->encryption->setP(4, false);
  982 + encryption->setP(4, false);
856 983 }
857 984 }
858 985  
859 986 void
860   -QPDFWriter::setEncryptionParameters(char const* user_password, char const* owner_password)
  987 +QPDFWriter::Members::setEncryptionParameters(char const* user_password, char const* owner_password)
861 988 {
862 989 generateID(true);
863   - m->encryption->setId1(m->id1);
864   - m->encryption_key = m->encryption->compute_parameters(user_password, owner_password);
  990 + encryption->setId1(id1);
  991 + encryption_key = encryption->compute_parameters(user_password, owner_password);
865 992 setEncryptionMinimumVersion();
866 993 }
867 994  
868 995 void
869 996 QPDFWriter::copyEncryptionParameters(QPDF& qpdf)
870 997 {
871   - m->preserve_encryption = false;
  998 + m->copyEncryptionParameters(qpdf);
  999 +}
  1000 +
  1001 +void
  1002 +QPDFWriter::Members::copyEncryptionParameters(QPDF& qpdf)
  1003 +{
  1004 + preserve_encryption = false;
872 1005 QPDFObjectHandle trailer = qpdf.getTrailer();
873 1006 if (trailer.hasKey("/Encrypt")) {
874 1007 generateID(true);
875   - m->id1 = trailer.getKey("/ID").getArrayItem(0).getStringValue();
  1008 + id1 = trailer.getKey("/ID").getArrayItem(0).getStringValue();
876 1009 QPDFObjectHandle encrypt = trailer.getKey("/Encrypt");
877 1010 int V = encrypt.getKey("/V").getIntValueAsInt();
878 1011 int key_len = 5;
... ... @@ -888,12 +1021,12 @@ QPDFWriter::copyEncryptionParameters(QPDF&amp; qpdf)
888 1021 // Acrobat doesn't create files with V >= 4 that don't use AES, and the logic of
889 1022 // figuring out whether AES is used or not is complicated with /StmF, /StrF, and /EFF
890 1023 // all potentially having different values.
891   - m->encrypt_use_aes = true;
  1024 + encrypt_use_aes = true;
892 1025 }
893 1026 QTC::TC("qpdf", "QPDFWriter copy encrypt metadata", encrypt_metadata ? 0 : 1);
894   - QTC::TC("qpdf", "QPDFWriter copy use_aes", m->encrypt_use_aes ? 0 : 1);
  1027 + QTC::TC("qpdf", "QPDFWriter copy use_aes", encrypt_use_aes ? 0 : 1);
895 1028  
896   - m->encryption = std::make_unique<QPDF::EncryptionData>(
  1029 + encryption = std::make_unique<QPDF::EncryptionData>(
897 1030 V,
898 1031 encrypt.getKey("/R").getIntValueAsInt(),
899 1032 key_len,
... ... @@ -903,54 +1036,53 @@ QPDFWriter::copyEncryptionParameters(QPDF&amp; qpdf)
903 1036 V < 5 ? "" : encrypt.getKey("/OE").getStringValue(),
904 1037 V < 5 ? "" : encrypt.getKey("/UE").getStringValue(),
905 1038 V < 5 ? "" : encrypt.getKey("/Perms").getStringValue(),
906   - m->id1, // m->id1 == the other file's id1
  1039 + id1, // id1 == the other file's id1
907 1040 encrypt_metadata);
908   - m->encryption_key = V >= 5
909   - ? qpdf.getEncryptionKey()
910   - : m->encryption->compute_encryption_key(qpdf.getPaddedUserPassword());
  1041 + encryption_key = V >= 5 ? qpdf.getEncryptionKey()
  1042 + : encryption->compute_encryption_key(qpdf.getPaddedUserPassword());
911 1043 setEncryptionMinimumVersion();
912 1044 }
913 1045 }
914 1046  
915 1047 void
916   -QPDFWriter::disableIncompatibleEncryption(int major, int minor, int extension_level)
  1048 +QPDFWriter::Members::disableIncompatibleEncryption(int major, int minor, int extension_level)
917 1049 {
918   - if (!m->encryption) {
  1050 + if (!encryption) {
919 1051 return;
920 1052 }
921 1053 if (compareVersions(major, minor, 1, 3) < 0) {
922   - m->encryption = nullptr;
  1054 + encryption = nullptr;
923 1055 return;
924 1056 }
925   - int V = m->encryption->getV();
926   - int R = m->encryption->getR();
  1057 + int V = encryption->getV();
  1058 + int R = encryption->getR();
927 1059 if (compareVersions(major, minor, 1, 4) < 0) {
928 1060 if (V > 1 || R > 2) {
929   - m->encryption = nullptr;
  1061 + encryption = nullptr;
930 1062 }
931 1063 } else if (compareVersions(major, minor, 1, 5) < 0) {
932 1064 if (V > 2 || R > 3) {
933   - m->encryption = nullptr;
  1065 + encryption = nullptr;
934 1066 }
935 1067 } else if (compareVersions(major, minor, 1, 6) < 0) {
936   - if (m->encrypt_use_aes) {
937   - m->encryption = nullptr;
  1068 + if (encrypt_use_aes) {
  1069 + encryption = nullptr;
938 1070 }
939 1071 } else if (
940 1072 (compareVersions(major, minor, 1, 7) < 0) ||
941 1073 ((compareVersions(major, minor, 1, 7) == 0) && extension_level < 3)) {
942 1074 if (V >= 5 || R >= 5) {
943   - m->encryption = nullptr;
  1075 + encryption = nullptr;
944 1076 }
945 1077 }
946 1078  
947   - if (!m->encryption) {
  1079 + if (!encryption) {
948 1080 QTC::TC("qpdf", "QPDFWriter forced version disabled encryption");
949 1081 }
950 1082 }
951 1083  
952 1084 void
953   -QPDFWriter::parseVersion(std::string const& version, int& major, int& minor) const
  1085 +QPDFWriter::Members::parseVersion(std::string const& version, int& major, int& minor) const
954 1086 {
955 1087 major = QUtil::string_to_int(version.c_str());
956 1088 minor = 0;
... ... @@ -967,54 +1099,48 @@ QPDFWriter::parseVersion(std::string const&amp; version, int&amp; major, int&amp; minor) con
967 1099 }
968 1100  
969 1101 int
970   -QPDFWriter::compareVersions(int major1, int minor1, int major2, int minor2) const
  1102 +QPDFWriter::Members::compareVersions(int major1, int minor1, int major2, int minor2) const
971 1103 {
972 1104 if (major1 < major2) {
973 1105 return -1;
974   - } else if (major1 > major2) {
  1106 + }
  1107 + if (major1 > major2) {
975 1108 return 1;
976   - } else if (minor1 < minor2) {
  1109 + }
  1110 + if (minor1 < minor2) {
977 1111 return -1;
978   - } else if (minor1 > minor2) {
979   - return 1;
980   - } else {
981   - return 0;
982 1112 }
  1113 + return minor1 > minor2 ? 1 : 0;
983 1114 }
984 1115  
985 1116 void
986   -QPDFWriter::setEncryptionMinimumVersion()
  1117 +QPDFWriter::Members::setEncryptionMinimumVersion()
987 1118 {
988   - auto const R = m->encryption->getR();
  1119 + auto const R = encryption->getR();
989 1120 if (R >= 6) {
990   - setMinimumPDFVersion("1.7", 8);
  1121 + w.setMinimumPDFVersion("1.7", 8);
991 1122 } else if (R == 5) {
992   - setMinimumPDFVersion("1.7", 3);
  1123 + w.setMinimumPDFVersion("1.7", 3);
993 1124 } else if (R == 4) {
994   - setMinimumPDFVersion(m->encrypt_use_aes ? "1.6" : "1.5");
  1125 + w.setMinimumPDFVersion(encrypt_use_aes ? "1.6" : "1.5");
995 1126 } else if (R == 3) {
996   - setMinimumPDFVersion("1.4");
  1127 + w.setMinimumPDFVersion("1.4");
997 1128 } else {
998   - setMinimumPDFVersion("1.3");
  1129 + w.setMinimumPDFVersion("1.3");
999 1130 }
1000 1131 }
1001 1132  
1002 1133 void
1003   -QPDFWriter::setDataKey(int objid)
  1134 +QPDFWriter::Members::setDataKey(int objid)
1004 1135 {
1005   - if (m->encryption) {
1006   - m->cur_data_key = QPDF::compute_data_key(
1007   - m->encryption_key,
1008   - objid,
1009   - 0,
1010   - m->encrypt_use_aes,
1011   - m->encryption->getV(),
1012   - m->encryption->getR());
  1136 + if (encryption) {
  1137 + cur_data_key = QPDF::compute_data_key(
  1138 + encryption_key, objid, 0, encrypt_use_aes, encryption->getV(), encryption->getR());
1013 1139 }
1014 1140 }
1015 1141  
1016 1142 unsigned int
1017   -QPDFWriter::bytesNeeded(long long n)
  1143 +QPDFWriter::Members::bytesNeeded(long long n)
1018 1144 {
1019 1145 unsigned int bytes = 0;
1020 1146 while (n) {
... ... @@ -1025,7 +1151,7 @@ QPDFWriter::bytesNeeded(long long n)
1025 1151 }
1026 1152  
1027 1153 void
1028   -QPDFWriter::writeBinary(unsigned long long val, unsigned int bytes)
  1154 +QPDFWriter::Members::writeBinary(unsigned long long val, unsigned int bytes)
1029 1155 {
1030 1156 if (bytes > sizeof(unsigned long long)) {
1031 1157 throw std::logic_error("QPDFWriter::writeBinary called with too many bytes");
... ... @@ -1035,191 +1161,189 @@ QPDFWriter::writeBinary(unsigned long long val, unsigned int bytes)
1035 1161 data[bytes - i - 1] = static_cast<unsigned char>(val & 0xff);
1036 1162 val >>= 8;
1037 1163 }
1038   - m->pipeline->write(data, bytes);
  1164 + pipeline->write(data, bytes);
1039 1165 }
1040 1166  
1041   -QPDFWriter&
1042   -QPDFWriter::write(std::string_view str)
  1167 +QPDFWriter::Members&
  1168 +QPDFWriter::Members::write(std::string_view str)
1043 1169 {
1044   - m->pipeline->write(str);
  1170 + pipeline->write(str);
1045 1171 return *this;
1046 1172 }
1047 1173  
1048   -QPDFWriter&
1049   -QPDFWriter::write(std::integral auto val)
  1174 +QPDFWriter::Members&
  1175 +QPDFWriter::Members::write(std::integral auto val)
1050 1176 {
1051   - m->pipeline->write(std::to_string(val));
  1177 + pipeline->write(std::to_string(val));
1052 1178 return *this;
1053 1179 }
1054 1180  
1055   -QPDFWriter&
1056   -QPDFWriter::write(size_t count, char c)
  1181 +QPDFWriter::Members&
  1182 +QPDFWriter::Members::write(size_t count, char c)
1057 1183 {
1058   - m->pipeline->write(count, c);
  1184 + pipeline->write(count, c);
1059 1185 return *this;
1060 1186 }
1061 1187  
1062   -QPDFWriter&
1063   -QPDFWriter::write_name(std::string const& str)
  1188 +QPDFWriter::Members&
  1189 +QPDFWriter::Members::write_name(std::string const& str)
1064 1190 {
1065   - m->pipeline->write(Name::normalize(str));
  1191 + pipeline->write(Name::normalize(str));
1066 1192 return *this;
1067 1193 }
1068 1194  
1069   -QPDFWriter&
1070   -QPDFWriter::write_string(std::string const& str, bool force_binary)
  1195 +QPDFWriter::Members&
  1196 +QPDFWriter::Members::write_string(std::string const& str, bool force_binary)
1071 1197 {
1072   - m->pipeline->write(QPDF_String(str).unparse(force_binary));
  1198 + pipeline->write(QPDF_String(str).unparse(force_binary));
1073 1199 return *this;
1074 1200 }
1075 1201  
1076 1202 template <typename... Args>
1077   -QPDFWriter&
1078   -QPDFWriter::write_qdf(Args&&... args)
  1203 +QPDFWriter::Members&
  1204 +QPDFWriter::Members::write_qdf(Args&&... args)
1079 1205 {
1080   - if (m->qdf_mode) {
1081   - m->pipeline->write(std::forward<Args>(args)...);
  1206 + if (qdf_mode) {
  1207 + pipeline->write(std::forward<Args>(args)...);
1082 1208 }
1083 1209 return *this;
1084 1210 }
1085 1211  
1086 1212 template <typename... Args>
1087   -QPDFWriter&
1088   -QPDFWriter::write_no_qdf(Args&&... args)
  1213 +QPDFWriter::Members&
  1214 +QPDFWriter::Members::write_no_qdf(Args&&... args)
1089 1215 {
1090   - if (!m->qdf_mode) {
1091   - m->pipeline->write(std::forward<Args>(args)...);
  1216 + if (!qdf_mode) {
  1217 + pipeline->write(std::forward<Args>(args)...);
1092 1218 }
1093 1219 return *this;
1094 1220 }
1095 1221  
1096 1222 void
1097   -QPDFWriter::adjustAESStreamLength(size_t& length)
  1223 +QPDFWriter::Members::adjustAESStreamLength(size_t& length)
1098 1224 {
1099   - if (m->encryption && !m->cur_data_key.empty() && m->encrypt_use_aes) {
  1225 + if (encryption && !cur_data_key.empty() && encrypt_use_aes) {
1100 1226 // Stream length will be padded with 1 to 16 bytes to end up as a multiple of 16. It will
1101 1227 // also be prepended by 16 bits of random data.
1102 1228 length += 32 - (length & 0xf);
1103 1229 }
1104 1230 }
1105 1231  
1106   -QPDFWriter&
1107   -QPDFWriter::write_encrypted(std::string_view str)
  1232 +QPDFWriter::Members&
  1233 +QPDFWriter::Members::write_encrypted(std::string_view str)
1108 1234 {
1109   - if (!(m->encryption && !m->cur_data_key.empty())) {
  1235 + if (!(encryption && !cur_data_key.empty())) {
1110 1236 write(str);
1111   - } else if (m->encrypt_use_aes) {
1112   - write(pl::pipe<Pl_AES_PDF>(str, true, m->cur_data_key));
  1237 + } else if (encrypt_use_aes) {
  1238 + write(pl::pipe<Pl_AES_PDF>(str, true, cur_data_key));
1113 1239 } else {
1114   - write(pl::pipe<Pl_RC4>(str, m->cur_data_key));
  1240 + write(pl::pipe<Pl_RC4>(str, cur_data_key));
1115 1241 }
1116 1242  
1117 1243 return *this;
1118 1244 }
1119 1245  
1120 1246 void
1121   -QPDFWriter::computeDeterministicIDData()
  1247 +QPDFWriter::Members::computeDeterministicIDData()
1122 1248 {
1123   - if (!m->id2.empty()) {
  1249 + if (!id2.empty()) {
1124 1250 // Can't happen in the code
1125 1251 throw std::logic_error(
1126 1252 "Deterministic ID computation enabled after ID generation has already occurred.");
1127 1253 }
1128   - qpdf_assert_debug(m->deterministic_id_data.empty());
1129   - m->deterministic_id_data = m->pipeline_stack.hex_digest();
  1254 + qpdf_assert_debug(deterministic_id_data.empty());
  1255 + deterministic_id_data = pipeline_stack.hex_digest();
1130 1256 }
1131 1257  
1132 1258 int
1133   -QPDFWriter::openObject(int objid)
  1259 +QPDFWriter::Members::openObject(int objid)
1134 1260 {
1135 1261 if (objid == 0) {
1136   - objid = m->next_objid++;
  1262 + objid = next_objid++;
1137 1263 }
1138   - m->new_obj[objid].xref = QPDFXRefEntry(m->pipeline->getCount());
  1264 + new_obj[objid].xref = QPDFXRefEntry(pipeline->getCount());
1139 1265 write(objid).write(" 0 obj\n");
1140 1266 return objid;
1141 1267 }
1142 1268  
1143 1269 void
1144   -QPDFWriter::closeObject(int objid)
  1270 +QPDFWriter::Members::closeObject(int objid)
1145 1271 {
1146 1272 // Write a newline before endobj as it makes the file easier to repair.
1147 1273 write("\nendobj\n").write_qdf("\n");
1148   - auto& new_obj = m->new_obj[objid];
1149   - new_obj.length = m->pipeline->getCount() - new_obj.xref.getOffset();
  1274 + auto& no = new_obj[objid];
  1275 + no.length = pipeline->getCount() - no.xref.getOffset();
1150 1276 }
1151 1277  
1152 1278 void
1153   -QPDFWriter::assignCompressedObjectNumbers(QPDFObjGen og)
  1279 +QPDFWriter::Members::assignCompressedObjectNumbers(QPDFObjGen og)
1154 1280 {
1155 1281 int objid = og.getObj();
1156   - if ((og.getGen() != 0) || (!m->object_stream_to_objects.contains(objid))) {
  1282 + if (og.getGen() != 0 || !object_stream_to_objects.contains(objid)) {
1157 1283 // This is not an object stream.
1158 1284 return;
1159 1285 }
1160 1286  
1161 1287 // Reserve numbers for the objects that belong to this object stream.
1162   - for (auto const& iter: m->object_stream_to_objects[objid]) {
1163   - m->obj[iter].renumber = m->next_objid++;
  1288 + for (auto const& iter: object_stream_to_objects[objid]) {
  1289 + obj[iter].renumber = next_objid++;
1164 1290 }
1165 1291 }
1166 1292  
1167 1293 void
1168   -QPDFWriter::enqueueObject(QPDFObjectHandle object)
  1294 +QPDFWriter::Members::enqueueObject(QPDFObjectHandle object)
1169 1295 {
1170 1296 if (object.isIndirect()) {
1171 1297 // This owner check can only be done for indirect objects. It is possible for a direct
1172 1298 // object to have an owning QPDF that is from another file if a direct QPDFObjectHandle from
1173 1299 // one file was insert into another file without copying. Doing that is safe even if the
1174 1300 // original QPDF gets destroyed, which just disconnects the QPDFObjectHandle from its owner.
1175   - if (object.getOwningQPDF() != &(m->pdf)) {
1176   - QTC::TC("qpdf", "QPDFWriter foreign object");
  1301 + if (object.getOwningQPDF() != &pdf) {
1177 1302 throw std::logic_error(
1178 1303 "QPDFObjectHandle from different QPDF found while writing. Use "
1179 1304 "QPDF::copyForeignObject to add objects from another file.");
1180 1305 }
1181 1306  
1182   - if (m->qdf_mode && object.isStreamOfType("/XRef")) {
  1307 + if (qdf_mode && object.isStreamOfType("/XRef")) {
1183 1308 // As a special case, do not output any extraneous XRef streams in QDF mode. Doing so
1184 1309 // will confuse fix-qdf, which expects to see only one XRef stream at the end of the
1185 1310 // file. This case can occur when creating a QDF from a file with object streams when
1186 1311 // preserving unreferenced objects since the old cross reference streams are not
1187 1312 // actually referenced by object number.
1188   - QTC::TC("qpdf", "QPDFWriter ignore XRef in qdf mode");
1189 1313 return;
1190 1314 }
1191 1315  
1192 1316 QPDFObjGen og = object.getObjGen();
1193   - auto& obj = m->obj[og];
  1317 + auto& o = obj[og];
1194 1318  
1195   - if (obj.renumber == 0) {
1196   - if (obj.object_stream > 0) {
  1319 + if (o.renumber == 0) {
  1320 + if (o.object_stream > 0) {
1197 1321 // This is in an object stream. Don't process it here. Instead, enqueue the object
1198 1322 // stream. Object streams always have generation 0.
1199 1323 // Detect loops by storing invalid object ID -1, which will get overwritten later.
1200   - obj.renumber = -1;
1201   - enqueueObject(m->pdf.getObject(obj.object_stream, 0));
  1324 + o.renumber = -1;
  1325 + enqueueObject(pdf.getObject(o.object_stream, 0));
1202 1326 } else {
1203   - m->object_queue.push_back(object);
1204   - obj.renumber = m->next_objid++;
  1327 + object_queue.emplace_back(object);
  1328 + o.renumber = next_objid++;
1205 1329  
1206   - if ((og.getGen() == 0) && m->object_stream_to_objects.contains(og.getObj())) {
  1330 + if (og.getGen() == 0 && object_stream_to_objects.contains(og.getObj())) {
1207 1331 // For linearized files, uncompressed objects go at end, and we take care of
1208 1332 // assigning numbers to them elsewhere.
1209   - if (!m->linearized) {
  1333 + if (!linearized) {
1210 1334 assignCompressedObjectNumbers(og);
1211 1335 }
1212   - } else if ((!m->direct_stream_lengths) && object.isStream()) {
  1336 + } else if (!direct_stream_lengths && object.isStream()) {
1213 1337 // reserve next object ID for length
1214   - ++m->next_objid;
  1338 + ++next_objid;
1215 1339 }
1216 1340 }
1217   - } else if (obj.renumber == -1) {
  1341 + } else if (o.renumber == -1) {
1218 1342 // This can happen if a specially constructed file indicates that an object stream is
1219 1343 // inside itself.
1220 1344 }
1221 1345 return;
1222   - } else if (!m->linearized) {
  1346 + } else if (!linearized) {
1223 1347 if (object.isArray()) {
1224 1348 for (auto& item: object.as_array()) {
1225 1349 enqueueObject(item);
... ... @@ -1237,25 +1361,25 @@ QPDFWriter::enqueueObject(QPDFObjectHandle object)
1237 1361 }
1238 1362  
1239 1363 void
1240   -QPDFWriter::unparseChild(QPDFObjectHandle const& child, size_t level, int flags)
  1364 +QPDFWriter::Members::unparseChild(QPDFObjectHandle const& child, size_t level, int flags)
1241 1365 {
1242   - if (!m->linearized) {
  1366 + if (!linearized) {
1243 1367 enqueueObject(child);
1244 1368 }
1245 1369 if (child.isIndirect()) {
1246   - write(m->obj[child].renumber).write(" 0 R");
  1370 + write(obj[child].renumber).write(" 0 R");
1247 1371 } else {
1248 1372 unparseObject(child, level, flags);
1249 1373 }
1250 1374 }
1251 1375  
1252 1376 void
1253   -QPDFWriter::writeTrailer(
  1377 +QPDFWriter::Members::writeTrailer(
1254 1378 trailer_e which, int size, bool xref_stream, qpdf_offset_t prev, int linearization_pass)
1255 1379 {
1256 1380 QPDFObjectHandle trailer = getTrimmedTrailer();
1257 1381 if (xref_stream) {
1258   - m->cur_data_key.clear();
  1382 + cur_data_key.clear();
1259 1383 } else {
1260 1384 write("trailer <<");
1261 1385 }
... ... @@ -1272,8 +1396,8 @@ QPDFWriter::writeTrailer(
1272 1396 write(size);
1273 1397 if (which == t_lin_first) {
1274 1398 write(" /Prev ");
1275   - qpdf_offset_t pos = m->pipeline->getCount();
1276   - write(prev).write(QIntC::to_size(pos - m->pipeline->getCount() + 21), ' ');
  1399 + qpdf_offset_t pos = pipeline->getCount();
  1400 + write(prev).write(QIntC::to_size(pos - pipeline->getCount() + 21), ' ');
1277 1401 }
1278 1402 } else {
1279 1403 unparseChild(value, 1, 0);
... ... @@ -1298,18 +1422,18 @@ QPDFWriter::writeTrailer(
1298 1422 }
1299 1423 write("<00000000000000000000000000000000>");
1300 1424 } else {
1301   - if (linearization_pass == 0 && m->deterministic_id) {
  1425 + if (linearization_pass == 0 && deterministic_id) {
1302 1426 computeDeterministicIDData();
1303 1427 }
1304   - generateID(m->encryption.get());
1305   - write_string(m->id1, true).write_string(m->id2, true);
  1428 + generateID(encryption.get());
  1429 + write_string(id1, true).write_string(id2, true);
1306 1430 }
1307 1431 write("]");
1308 1432  
1309 1433 if (which != t_lin_second) {
1310 1434 // Write reference to encryption dictionary
1311   - if (m->encryption) {
1312   - write(" /Encrypt ").write(m->encryption_dict_objid).write(" 0 R");
  1435 + if (encryption) {
  1436 + write(" /Encrypt ").write(encryption_dict_objid).write(" 0 R");
1313 1437 }
1314 1438 }
1315 1439  
... ... @@ -1317,7 +1441,7 @@ QPDFWriter::writeTrailer(
1317 1441 }
1318 1442  
1319 1443 bool
1320   -QPDFWriter::willFilterStream(
  1444 +QPDFWriter::Members::willFilterStream(
1321 1445 QPDFObjectHandle stream,
1322 1446 bool& compress_stream, // out only
1323 1447 bool& is_root_metadata, // out only
... ... @@ -1332,38 +1456,33 @@ QPDFWriter::willFilterStream(
1332 1456 if (stream.isRootMetadata()) {
1333 1457 is_root_metadata = true;
1334 1458 }
1335   - bool filter = stream.isDataModified() || m->compress_streams || m->stream_decode_level;
  1459 + bool filter = stream.isDataModified() || compress_streams || stream_decode_level;
1336 1460 bool filter_on_write = stream.getFilterOnWrite();
1337 1461 if (!filter_on_write) {
1338   - QTC::TC("qpdf", "QPDFWriter getFilterOnWrite false");
1339 1462 filter = false;
1340 1463 }
1341   - if (filter_on_write && m->compress_streams) {
  1464 + if (filter_on_write && compress_streams) {
1342 1465 // Don't filter if the stream is already compressed with FlateDecode. This way we don't make
1343 1466 // it worse if the original file used a better Flate algorithm, and we don't spend time and
1344 1467 // CPU cycles uncompressing and recompressing stuff. This can be overridden with
1345 1468 // setRecompressFlate(true).
1346 1469 QPDFObjectHandle filter_obj = stream_dict.getKey("/Filter");
1347   - if (!m->recompress_flate && !stream.isDataModified() && filter_obj.isName() &&
  1470 + if (!recompress_flate && !stream.isDataModified() && filter_obj.isName() &&
1348 1471 (filter_obj.getName() == "/FlateDecode" || filter_obj.getName() == "/Fl")) {
1349   - QTC::TC("qpdf", "QPDFWriter not recompressing /FlateDecode");
1350 1472 filter = false;
1351 1473 }
1352 1474 }
1353 1475 bool normalize = false;
1354 1476 bool uncompress = false;
1355   - if (filter_on_write && is_root_metadata &&
1356   - (!m->encryption || !m->encryption->getEncryptMetadata())) {
1357   - QTC::TC("qpdf", "QPDFWriter not compressing metadata");
  1477 + if (filter_on_write && is_root_metadata && (!encryption || !encryption->getEncryptMetadata())) {
1358 1478 filter = true;
1359 1479 compress_stream = false;
1360 1480 uncompress = true;
1361   - } else if (filter_on_write && m->normalize_content && m->normalized_streams.contains(old_og)) {
  1481 + } else if (filter_on_write && normalize_content && normalized_streams.contains(old_og)) {
1362 1482 normalize = true;
1363 1483 filter = true;
1364   - } else if (filter_on_write && filter && m->compress_streams) {
  1484 + } else if (filter_on_write && filter && compress_streams) {
1365 1485 compress_stream = true;
1366   - QTC::TC("qpdf", "QPDFWriter compressing uncompressed stream");
1367 1486 }
1368 1487  
1369 1488 // Disable compression for empty streams to improve compatibility
... ... @@ -1375,16 +1494,16 @@ QPDFWriter::willFilterStream(
1375 1494  
1376 1495 bool filtered = false;
1377 1496 for (bool first_attempt: {true, false}) {
1378   - auto pp_stream_data = stream_data ? m->pipeline_stack.activate(*stream_data)
1379   - : m->pipeline_stack.activate(true);
  1497 + auto pp_stream_data =
  1498 + stream_data ? pipeline_stack.activate(*stream_data) : pipeline_stack.activate(true);
1380 1499  
1381 1500 try {
1382 1501 filtered = stream.pipeStreamData(
1383   - m->pipeline,
  1502 + pipeline,
1384 1503 !filter ? 0
1385 1504 : ((normalize ? qpdf_ef_normalize : 0) |
1386 1505 (compress_stream ? qpdf_ef_compress : 0)),
1387   - !filter ? qpdf_dl_none : (uncompress ? qpdf_dl_all : m->stream_decode_level),
  1506 + !filter ? qpdf_dl_none : (uncompress ? qpdf_dl_all : stream_decode_level),
1388 1507 false,
1389 1508 first_attempt);
1390 1509 if (filter && !filtered) {
... ... @@ -1416,7 +1535,7 @@ QPDFWriter::willFilterStream(
1416 1535 }
1417 1536  
1418 1537 void
1419   -QPDFWriter::unparseObject(
  1538 +QPDFWriter::Members::unparseObject(
1420 1539 QPDFObjectHandle object, size_t level, int flags, size_t stream_length, bool compress)
1421 1540 {
1422 1541 QPDFObjGen old_og = object.getObjGen();
... ... @@ -1424,11 +1543,11 @@ QPDFWriter::unparseObject(
1424 1543 // For non-qdf, "indent" and "indent_large" are a single space between tokens. For qdf, they
1425 1544 // include the preceding newline.
1426 1545 std::string indent_large = " ";
1427   - if (m->qdf_mode) {
  1546 + if (qdf_mode) {
1428 1547 indent_large.append(2 * (level + 1), ' ');
1429 1548 indent_large[0] = '\n';
1430 1549 }
1431   - std::string_view indent{indent_large.data(), m->qdf_mode ? indent_large.size() - 2 : 1};
  1550 + std::string_view indent{indent_large.data(), qdf_mode ? indent_large.size() - 2 : 1};
1432 1551  
1433 1552 if (auto const tc = object.getTypeCode(); tc == ::ot_array) {
1434 1553 // Note: PDF spec 1.4 implementation note 121 states that Acrobat requires a space after the
... ... @@ -1443,7 +1562,7 @@ QPDFWriter::unparseObject(
1443 1562 } else if (tc == ::ot_dictionary) {
1444 1563 // Handle special cases for specific dictionaries.
1445 1564  
1446   - if (old_og == m->root_og) {
  1565 + if (old_og == root_og) {
1447 1566 // Extensions dictionaries.
1448 1567  
1449 1568 // We have one of several cases:
... ... @@ -1464,7 +1583,7 @@ QPDFWriter::unparseObject(
1464 1583  
1465 1584 auto extensions = object.getKey("/Extensions");
1466 1585 const bool has_extensions = extensions.isDictionary();
1467   - const bool need_extensions_adbe = m->final_extension_level > 0;
  1586 + const bool need_extensions_adbe = final_extension_level > 0;
1468 1587  
1469 1588 if (has_extensions || need_extensions_adbe) {
1470 1589 // Make a shallow copy of this object so we can modify it safely without affecting
... ... @@ -1484,7 +1603,7 @@ QPDFWriter::unparseObject(
1484 1603 if (need_extensions_adbe) {
1485 1604 if (!(have_extensions_other || have_extensions_adbe)) {
1486 1605 // We need Extensions and don't have it. Create it here.
1487   - QTC::TC("qpdf", "QPDFWriter create Extensions", m->qdf_mode ? 0 : 1);
  1606 + QTC::TC("qpdf", "QPDFWriter create Extensions", qdf_mode ? 0 : 1);
1488 1607 extensions = object.replaceKeyAndGetNew(
1489 1608 "/Extensions", QPDFObjectHandle::newDictionary());
1490 1609 }
... ... @@ -1501,21 +1620,17 @@ QPDFWriter::unparseObject(
1501 1620 QTC::TC("qpdf", "QPDFWriter preserve Extensions");
1502 1621 QPDFObjectHandle adbe = extensions.getKey("/ADBE");
1503 1622 if (adbe.isDictionary() &&
1504   - adbe.getKey("/BaseVersion").isNameAndEquals("/" + m->final_pdf_version) &&
  1623 + adbe.getKey("/BaseVersion").isNameAndEquals("/" + final_pdf_version) &&
1505 1624 adbe.getKey("/ExtensionLevel").isInteger() &&
1506   - (adbe.getKey("/ExtensionLevel").getIntValue() ==
1507   - m->final_extension_level)) {
1508   - QTC::TC("qpdf", "QPDFWriter preserve ADBE");
  1625 + (adbe.getKey("/ExtensionLevel").getIntValue() == final_extension_level)) {
1509 1626 } else {
1510 1627 if (need_extensions_adbe) {
1511 1628 extensions.replaceKey(
1512 1629 "/ADBE",
1513 1630 QPDFObjectHandle::parse(
1514   - "<< /BaseVersion /" + m->final_pdf_version +
1515   - " /ExtensionLevel " + std::to_string(m->final_extension_level) +
1516   - " >>"));
  1631 + "<< /BaseVersion /" + final_pdf_version + " /ExtensionLevel " +
  1632 + std::to_string(final_extension_level) + " >>"));
1517 1633 } else {
1518   - QTC::TC("qpdf", "QPDFWriter remove ADBE");
1519 1634 extensions.removeKey("/ADBE");
1520 1635 }
1521 1636 }
... ... @@ -1593,10 +1708,10 @@ QPDFWriter::unparseObject(
1593 1708 if (flags & f_stream) {
1594 1709 write(indent_large).write("/Length ");
1595 1710  
1596   - if (m->direct_stream_lengths) {
  1711 + if (direct_stream_lengths) {
1597 1712 write(stream_length);
1598 1713 } else {
1599   - write(m->cur_stream_length_id).write(" 0 R");
  1714 + write(cur_stream_length_id).write(" 0 R");
1600 1715 }
1601 1716 if (compress && (flags & f_filtered)) {
1602 1717 write(indent_large).write("/Filter /FlateDecode");
... ... @@ -1606,8 +1721,8 @@ QPDFWriter::unparseObject(
1606 1721 write(indent).write(">>");
1607 1722 } else if (tc == ::ot_stream) {
1608 1723 // Write stream data to a buffer.
1609   - if (!m->direct_stream_lengths) {
1610   - m->cur_stream_length_id = m->obj[old_og].renumber + 1;
  1724 + if (!direct_stream_lengths) {
  1725 + cur_stream_length_id = obj[old_og].renumber + 1;
1611 1726 }
1612 1727  
1613 1728 flags |= f_stream;
... ... @@ -1619,25 +1734,25 @@ QPDFWriter::unparseObject(
1619 1734 }
1620 1735 QPDFObjectHandle stream_dict = object.getDict();
1621 1736  
1622   - m->cur_stream_length = stream_data.size();
1623   - if (is_metadata && m->encryption && !m->encryption->getEncryptMetadata()) {
  1737 + cur_stream_length = stream_data.size();
  1738 + if (is_metadata && encryption && !encryption->getEncryptMetadata()) {
1624 1739 // Don't encrypt stream data for the metadata stream
1625   - m->cur_data_key.clear();
  1740 + cur_data_key.clear();
1626 1741 }
1627   - adjustAESStreamLength(m->cur_stream_length);
1628   - unparseObject(stream_dict, 0, flags, m->cur_stream_length, compress_stream);
  1742 + adjustAESStreamLength(cur_stream_length);
  1743 + unparseObject(stream_dict, 0, flags, cur_stream_length, compress_stream);
1629 1744 char last_char = stream_data.empty() ? '\0' : stream_data.back();
1630 1745 write("\nstream\n").write_encrypted(stream_data);
1631   - m->added_newline = m->newline_before_endstream || (m->qdf_mode && last_char != '\n');
1632   - write(m->added_newline ? "\nendstream" : "endstream");
  1746 + added_newline = newline_before_endstream || (qdf_mode && last_char != '\n');
  1747 + write(added_newline ? "\nendstream" : "endstream");
1633 1748 } else if (tc == ::ot_string) {
1634 1749 std::string val;
1635   - if (m->encryption && !(flags & f_in_ostream) && !(flags & f_no_encryption) &&
1636   - !m->cur_data_key.empty()) {
  1750 + if (encryption && !(flags & f_in_ostream) && !(flags & f_no_encryption) &&
  1751 + !cur_data_key.empty()) {
1637 1752 val = object.getStringValue();
1638   - if (m->encrypt_use_aes) {
  1753 + if (encrypt_use_aes) {
1639 1754 Pl_Buffer bufpl("encrypted string");
1640   - Pl_AES_PDF pl("aes encrypt string", &bufpl, true, m->cur_data_key);
  1755 + Pl_AES_PDF pl("aes encrypt string", &bufpl, true, cur_data_key);
1641 1756 pl.writeString(val);
1642 1757 pl.finish();
1643 1758 val = QPDF_String(bufpl.getString()).unparse(true);
... ... @@ -1646,8 +1761,8 @@ QPDFWriter::unparseObject(
1646 1761 char* tmp = tmp_ph.get();
1647 1762 size_t vlen = val.length();
1648 1763 RC4 rc4(
1649   - QUtil::unsigned_char_pointer(m->cur_data_key),
1650   - QIntC::to_int(m->cur_data_key.length()));
  1764 + QUtil::unsigned_char_pointer(cur_data_key),
  1765 + QIntC::to_int(cur_data_key.length()));
1651 1766 auto data = QUtil::unsigned_char_pointer(tmp);
1652 1767 rc4.process(data, vlen, data);
1653 1768 val = QPDF_String(std::string(tmp, vlen)).unparse();
... ... @@ -1664,7 +1779,7 @@ QPDFWriter::unparseObject(
1664 1779 }
1665 1780  
1666 1781 void
1667   -QPDFWriter::writeObjectStreamOffsets(std::vector<qpdf_offset_t>& offsets, int first_obj)
  1782 +QPDFWriter::Members::writeObjectStreamOffsets(std::vector<qpdf_offset_t>& offsets, int first_obj)
1668 1783 {
1669 1784 qpdf_assert_debug(first_obj > 0);
1670 1785 bool is_first = true;
... ... @@ -1683,7 +1798,7 @@ QPDFWriter::writeObjectStreamOffsets(std::vector&lt;qpdf_offset_t&gt;&amp; offsets, int fi
1683 1798 }
1684 1799  
1685 1800 void
1686   -QPDFWriter::writeObjectStream(QPDFObjectHandle object)
  1801 +QPDFWriter::Members::writeObjectStream(QPDFObjectHandle object)
1687 1802 {
1688 1803 // Note: object might be null if this is a place-holder for an object stream that we are
1689 1804 // generating from scratch.
... ... @@ -1691,7 +1806,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1691 1806 QPDFObjGen old_og = object.getObjGen();
1692 1807 qpdf_assert_debug(old_og.getGen() == 0);
1693 1808 int old_id = old_og.getObj();
1694   - int new_stream_id = m->obj[old_og].renumber;
  1809 + int new_stream_id = obj[old_og].renumber;
1695 1810  
1696 1811 std::vector<qpdf_offset_t> offsets;
1697 1812 qpdf_offset_t first = 0;
... ... @@ -1701,39 +1816,38 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1701 1816 std::string stream_buffer_pass1;
1702 1817 std::string stream_buffer_pass2;
1703 1818 int first_obj = -1;
1704   - const bool compressed = m->compress_streams && !m->qdf_mode;
  1819 + const bool compressed = compress_streams && !qdf_mode;
1705 1820 {
1706 1821 // Pass 1
1707   - auto pp_ostream_pass1 = m->pipeline_stack.activate(stream_buffer_pass1);
  1822 + auto pp_ostream_pass1 = pipeline_stack.activate(stream_buffer_pass1);
1708 1823  
1709 1824 int count = -1;
1710   - for (auto const& obj: m->object_stream_to_objects[old_id]) {
  1825 + for (auto const& og: object_stream_to_objects[old_id]) {
1711 1826 ++count;
1712   - int new_obj = m->obj[obj].renumber;
  1827 + int new_o = obj[og].renumber;
1713 1828 if (first_obj == -1) {
1714   - first_obj = new_obj;
  1829 + first_obj = new_o;
1715 1830 }
1716   - if (m->qdf_mode) {
1717   - write("%% Object stream: object ").write(new_obj).write(", index ").write(count);
1718   - if (!m->suppress_original_object_ids) {
1719   - write("; original object ID: ").write(obj.getObj());
  1831 + if (qdf_mode) {
  1832 + write("%% Object stream: object ").write(new_o).write(", index ").write(count);
  1833 + if (!suppress_original_object_ids) {
  1834 + write("; original object ID: ").write(og.getObj());
1720 1835 // For compatibility, only write the generation if non-zero. While object
1721 1836 // streams only allow objects with generation 0, if we are generating object
1722 1837 // streams, the old object could have a non-zero generation.
1723   - if (obj.getGen() != 0) {
1724   - QTC::TC("qpdf", "QPDFWriter original obj non-zero gen");
1725   - write(" ").write(obj.getGen());
  1838 + if (og.getGen() != 0) {
  1839 + write(" ").write(og.getGen());
1726 1840 }
1727 1841 }
1728 1842 write("\n");
1729 1843 }
1730 1844  
1731   - offsets.push_back(m->pipeline->getCount());
  1845 + offsets.push_back(pipeline->getCount());
1732 1846 // To avoid double-counting objects being written in object streams for progress
1733 1847 // reporting, decrement in pass 1.
1734 1848 indicateProgress(true, false);
1735 1849  
1736   - QPDFObjectHandle obj_to_write = m->pdf.getObject(obj);
  1850 + QPDFObjectHandle obj_to_write = pdf.getObject(og);
1737 1851 if (obj_to_write.isStream()) {
1738 1852 // This condition occurred in a fuzz input. Ideally we should block it at parse
1739 1853 // time, but it's not clear to me how to construct a case for this.
... ... @@ -1742,7 +1856,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1742 1856 }
1743 1857 writeObject(obj_to_write, count);
1744 1858  
1745   - m->new_obj[new_obj].xref = QPDFXRefEntry(new_stream_id, count);
  1859 + new_obj[new_o].xref = QPDFXRefEntry(new_stream_id, count);
1746 1860 }
1747 1861 }
1748 1862 {
... ... @@ -1754,13 +1868,13 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1754 1868  
1755 1869 // Take one pass at writing pairs of numbers so we can get their size information
1756 1870 {
1757   - auto pp_discard = m->pipeline_stack.activate(true);
  1871 + auto pp_discard = pipeline_stack.activate(true);
1758 1872 writeObjectStreamOffsets(offsets, first_obj);
1759   - first += m->pipeline->getCount();
  1873 + first += pipeline->getCount();
1760 1874 }
1761 1875  
1762 1876 // Set up a stream to write the stream data into a buffer.
1763   - auto pp_ostream = m->pipeline_stack.activate(stream_buffer_pass2);
  1877 + auto pp_ostream = pipeline_stack.activate(stream_buffer_pass2);
1764 1878  
1765 1879 writeObjectStreamOffsets(offsets, first_obj);
1766 1880 write(stream_buffer_pass1);
... ... @@ -1792,65 +1906,65 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1792 1906 }
1793 1907 }
1794 1908 write_qdf("\n").write_no_qdf(" ").write(">>\nstream\n").write_encrypted(stream_buffer_pass2);
1795   - if (m->encryption) {
  1909 + if (encryption) {
1796 1910 QTC::TC("qpdf", "QPDFWriter encrypt object stream");
1797 1911 }
1798   - write(m->newline_before_endstream ? "\nendstream" : "endstream");
1799   - m->cur_data_key.clear();
  1912 + write(newline_before_endstream ? "\nendstream" : "endstream");
  1913 + cur_data_key.clear();
1800 1914 closeObject(new_stream_id);
1801 1915 }
1802 1916  
1803 1917 void
1804   -QPDFWriter::writeObject(QPDFObjectHandle object, int object_stream_index)
  1918 +QPDFWriter::Members::writeObject(QPDFObjectHandle object, int object_stream_index)
1805 1919 {
1806 1920 QPDFObjGen old_og = object.getObjGen();
1807 1921  
1808 1922 if (object_stream_index == -1 && old_og.getGen() == 0 &&
1809   - m->object_stream_to_objects.contains(old_og.getObj())) {
  1923 + object_stream_to_objects.contains(old_og.getObj())) {
1810 1924 writeObjectStream(object);
1811 1925 return;
1812 1926 }
1813 1927  
1814 1928 indicateProgress(false, false);
1815   - auto new_id = m->obj[old_og].renumber;
1816   - if (m->qdf_mode) {
1817   - if (m->page_object_to_seq.contains(old_og)) {
1818   - write("%% Page ").write(m->page_object_to_seq[old_og]).write("\n");
  1929 + auto new_id = obj[old_og].renumber;
  1930 + if (qdf_mode) {
  1931 + if (page_object_to_seq.contains(old_og)) {
  1932 + write("%% Page ").write(page_object_to_seq[old_og]).write("\n");
1819 1933 }
1820   - if (m->contents_to_page_seq.contains(old_og)) {
1821   - write("%% Contents for page ").write(m->contents_to_page_seq[old_og]).write("\n");
  1934 + if (contents_to_page_seq.contains(old_og)) {
  1935 + write("%% Contents for page ").write(contents_to_page_seq[old_og]).write("\n");
1822 1936 }
1823 1937 }
1824 1938 if (object_stream_index == -1) {
1825   - if (m->qdf_mode && (!m->suppress_original_object_ids)) {
  1939 + if (qdf_mode && !suppress_original_object_ids) {
1826 1940 write("%% Original object ID: ").write(object.getObjGen().unparse(' ')).write("\n");
1827 1941 }
1828 1942 openObject(new_id);
1829 1943 setDataKey(new_id);
1830 1944 unparseObject(object, 0, 0);
1831   - m->cur_data_key.clear();
  1945 + cur_data_key.clear();
1832 1946 closeObject(new_id);
1833 1947 } else {
1834 1948 unparseObject(object, 0, f_in_ostream);
1835 1949 write("\n");
1836 1950 }
1837 1951  
1838   - if (!m->direct_stream_lengths && object.isStream()) {
1839   - if (m->qdf_mode) {
1840   - if (m->added_newline) {
  1952 + if (!direct_stream_lengths && object.isStream()) {
  1953 + if (qdf_mode) {
  1954 + if (added_newline) {
1841 1955 write("%QDF: ignore_newline\n");
1842 1956 }
1843 1957 }
1844 1958 openObject(new_id + 1);
1845   - write(m->cur_stream_length);
  1959 + write(cur_stream_length);
1846 1960 closeObject(new_id + 1);
1847 1961 }
1848 1962 }
1849 1963  
1850 1964 std::string
1851   -QPDFWriter::getOriginalID1()
  1965 +QPDFWriter::Members::getOriginalID1()
1852 1966 {
1853   - QPDFObjectHandle trailer = m->pdf.getTrailer();
  1967 + QPDFObjectHandle trailer = pdf.getTrailer();
1854 1968 if (trailer.hasKey("/ID")) {
1855 1969 return trailer.getKey("/ID").getArrayItem(0).getStringValue();
1856 1970 } else {
... ... @@ -1859,20 +1973,20 @@ QPDFWriter::getOriginalID1()
1859 1973 }
1860 1974  
1861 1975 void
1862   -QPDFWriter::generateID(bool encrypted)
  1976 +QPDFWriter::Members::generateID(bool encrypted)
1863 1977 {
1864 1978 // Generate the ID lazily so that we can handle the user's preference to use static or
1865 1979 // deterministic ID generation.
1866 1980  
1867   - if (!m->id2.empty()) {
  1981 + if (!id2.empty()) {
1868 1982 return;
1869 1983 }
1870 1984  
1871   - QPDFObjectHandle trailer = m->pdf.getTrailer();
  1985 + QPDFObjectHandle trailer = pdf.getTrailer();
1872 1986  
1873 1987 std::string result;
1874 1988  
1875   - if (m->static_id) {
  1989 + if (static_id) {
1876 1990 // For test suite use only...
1877 1991 static unsigned char tmp[] = {
1878 1992 0x31,
... ... @@ -1904,20 +2018,20 @@ QPDFWriter::generateID(bool encrypted)
1904 2018 // that case, would have the same ID regardless of the output file's name.
1905 2019  
1906 2020 std::string seed;
1907   - if (m->deterministic_id) {
  2021 + if (deterministic_id) {
1908 2022 if (encrypted) {
1909 2023 throw std::runtime_error(
1910 2024 "QPDFWriter: unable to generated a deterministic ID because the file to be "
1911 2025 "written is encrypted (even though the file may not require a password)");
1912 2026 }
1913   - if (m->deterministic_id_data.empty()) {
  2027 + if (deterministic_id_data.empty()) {
1914 2028 throw std::logic_error(
1915 2029 "INTERNAL ERROR: QPDFWriter::generateID has no data for deterministic ID");
1916 2030 }
1917   - seed += m->deterministic_id_data;
  2031 + seed += deterministic_id_data;
1918 2032 } else {
1919 2033 seed += std::to_string(QUtil::get_current_time());
1920   - seed += m->filename;
  2034 + seed += filename;
1921 2035 seed += " ";
1922 2036 }
1923 2037 seed += " QPDF ";
... ... @@ -1930,32 +2044,32 @@ QPDFWriter::generateID(bool encrypted)
1930 2044 }
1931 2045 }
1932 2046  
1933   - MD5 m;
1934   - m.encodeString(seed.c_str());
  2047 + MD5 md5;
  2048 + md5.encodeString(seed.c_str());
1935 2049 MD5::Digest digest;
1936   - m.digest(digest);
  2050 + md5.digest(digest);
1937 2051 result = std::string(reinterpret_cast<char*>(digest), sizeof(MD5::Digest));
1938 2052 }
1939 2053  
1940 2054 // If /ID already exists, follow the spec: use the original first word and generate a new second
1941 2055 // word. Otherwise, we'll use the generated ID for both.
1942 2056  
1943   - m->id2 = result;
  2057 + id2 = result;
1944 2058 // Note: keep /ID from old file even if --static-id was given.
1945   - m->id1 = getOriginalID1();
1946   - if (m->id1.empty()) {
1947   - m->id1 = m->id2;
  2059 + id1 = getOriginalID1();
  2060 + if (id1.empty()) {
  2061 + id1 = id2;
1948 2062 }
1949 2063 }
1950 2064  
1951 2065 void
1952   -QPDFWriter::initializeSpecialStreams()
  2066 +QPDFWriter::Members::initializeSpecialStreams()
1953 2067 {
1954 2068 // Mark all page content streams in case we are filtering or normalizing.
1955   - std::vector<QPDFObjectHandle> pages = m->pdf.getAllPages();
  2069 + std::vector<QPDFObjectHandle> pages = pdf.getAllPages();
1956 2070 int num = 0;
1957 2071 for (auto& page: pages) {
1958   - m->page_object_to_seq[page.getObjGen()] = ++num;
  2072 + page_object_to_seq[page.getObjGen()] = ++num;
1959 2073 QPDFObjectHandle contents = page.getKey("/Contents");
1960 2074 std::vector<QPDFObjGen> contents_objects;
1961 2075 if (contents.isArray()) {
... ... @@ -1968,16 +2082,16 @@ QPDFWriter::initializeSpecialStreams()
1968 2082 }
1969 2083  
1970 2084 for (auto const& c: contents_objects) {
1971   - m->contents_to_page_seq[c] = num;
1972   - m->normalized_streams.insert(c);
  2085 + contents_to_page_seq[c] = num;
  2086 + normalized_streams.insert(c);
1973 2087 }
1974 2088 }
1975 2089 }
1976 2090  
1977 2091 void
1978   -QPDFWriter::preserveObjectStreams()
  2092 +QPDFWriter::Members::preserveObjectStreams()
1979 2093 {
1980   - auto const& xref = QPDF::Writer::getXRefTable(m->pdf);
  2094 + auto const& xref = QPDF::Writer::getXRefTable(pdf);
1981 2095 // Our object_to_object_stream map has to map ObjGen -> ObjGen since we may be generating object
1982 2096 // streams out of old objects that have generation numbers greater than zero. However in an
1983 2097 // existing PDF, all object stream objects and all objects in them must have generation 0
... ... @@ -1986,14 +2100,13 @@ QPDFWriter::preserveObjectStreams()
1986 2100 // erroneously included in object streams in the source PDF, it also prevents unreferenced
1987 2101 // objects from being included.
1988 2102 auto end = xref.cend();
1989   - m->obj.streams_empty = true;
1990   - if (m->preserve_unreferenced_objects) {
  2103 + obj.streams_empty = true;
  2104 + if (preserve_unreferenced_objects) {
1991 2105 for (auto iter = xref.cbegin(); iter != end; ++iter) {
1992 2106 if (iter->second.getType() == 2) {
1993 2107 // Pdf contains object streams.
1994   - QTC::TC("qpdf", "QPDFWriter preserve object streams preserve unreferenced");
1995   - m->obj.streams_empty = false;
1996   - m->obj[iter->first].object_stream = iter->second.getObjStreamNumber();
  2108 + obj.streams_empty = false;
  2109 + obj[iter->first].object_stream = iter->second.getObjStreamNumber();
1997 2110 }
1998 2111 }
1999 2112 } else {
... ... @@ -2002,9 +2115,8 @@ QPDFWriter::preserveObjectStreams()
2002 2115 for (auto iter = xref.cbegin(); iter != end; ++iter) {
2003 2116 if (iter->second.getType() == 2) {
2004 2117 // Pdf contains object streams.
2005   - QTC::TC("qpdf", "QPDFWriter preserve object streams");
2006   - m->obj.streams_empty = false;
2007   - auto eligible = QPDF::Writer::getCompressibleObjSet(m->pdf);
  2118 + obj.streams_empty = false;
  2119 + auto eligible = QPDF::Writer::getCompressibleObjSet(pdf);
2008 2120 // The object pointed to by iter may be a previous generation, in which case it is
2009 2121 // removed by getCompressibleObjSet. We need to restart the loop (while the object
2010 2122 // table may contain multiple generations of an object).
... ... @@ -2012,7 +2124,7 @@ QPDFWriter::preserveObjectStreams()
2012 2124 if (iter->second.getType() == 2) {
2013 2125 auto id = static_cast<size_t>(iter->first.getObj());
2014 2126 if (id < eligible.size() && eligible[id]) {
2015   - m->obj[iter->first].object_stream = iter->second.getObjStreamNumber();
  2127 + obj[iter->first].object_stream = iter->second.getObjStreamNumber();
2016 2128 } else {
2017 2129 QTC::TC("qpdf", "QPDFWriter exclude from object stream");
2018 2130 }
... ... @@ -2025,7 +2137,7 @@ QPDFWriter::preserveObjectStreams()
2025 2137 }
2026 2138  
2027 2139 void
2028   -QPDFWriter::generateObjectStreams()
  2140 +QPDFWriter::Members::generateObjectStreams()
2029 2141 {
2030 2142 // Basic strategy: make a list of objects that can go into an object stream. Then figure out
2031 2143 // how many object streams are needed so that we can distribute objects approximately evenly
... ... @@ -2035,12 +2147,12 @@ QPDFWriter::generateObjectStreams()
2035 2147  
2036 2148 // This code doesn't do anything with /Extends.
2037 2149  
2038   - std::vector<QPDFObjGen> eligible = QPDF::Writer::getCompressibleObjGens(m->pdf);
  2150 + std::vector<QPDFObjGen> eligible = QPDF::Writer::getCompressibleObjGens(pdf);
2039 2151 size_t n_object_streams = (eligible.size() + 99U) / 100U;
2040 2152  
2041 2153 initializeTables(2U * n_object_streams);
2042 2154 if (n_object_streams == 0) {
2043   - m->obj.streams_empty = true;
  2155 + obj.streams_empty = true;
2044 2156 return;
2045 2157 }
2046 2158 size_t n_per = eligible.size() / n_object_streams;
... ... @@ -2048,28 +2160,27 @@ QPDFWriter::generateObjectStreams()
2048 2160 ++n_per;
2049 2161 }
2050 2162 unsigned int n = 0;
2051   - int cur_ostream = m->pdf.newIndirectNull().getObjectID();
  2163 + int cur_ostream = pdf.newIndirectNull().getObjectID();
2052 2164 for (auto const& item: eligible) {
2053 2165 if (n == n_per) {
2054   - QTC::TC("qpdf", "QPDFWriter generate >1 ostream");
2055 2166 n = 0;
2056 2167 // Construct a new null object as the "original" object stream. The rest of the code
2057 2168 // knows that this means we're creating the object stream from scratch.
2058   - cur_ostream = m->pdf.newIndirectNull().getObjectID();
  2169 + cur_ostream = pdf.newIndirectNull().getObjectID();
2059 2170 }
2060   - auto& obj = m->obj[item];
2061   - obj.object_stream = cur_ostream;
2062   - obj.gen = item.getGen();
  2171 + auto& o = obj[item];
  2172 + o.object_stream = cur_ostream;
  2173 + o.gen = item.getGen();
2063 2174 ++n;
2064 2175 }
2065 2176 }
2066 2177  
2067 2178 QPDFObjectHandle
2068   -QPDFWriter::getTrimmedTrailer()
  2179 +QPDFWriter::Members::getTrimmedTrailer()
2069 2180 {
2070 2181 // Remove keys from the trailer that necessarily have to be replaced when writing the file.
2071 2182  
2072   - QPDFObjectHandle trailer = m->pdf.getTrailer().unsafeShallowCopy();
  2183 + QPDFObjectHandle trailer = pdf.getTrailer().unsafeShallowCopy();
2073 2184  
2074 2185 // Remove encryption keys
2075 2186 trailer.removeKey("/ID");
... ... @@ -2092,10 +2203,10 @@ QPDFWriter::getTrimmedTrailer()
2092 2203  
2093 2204 // Make document extension level information direct as required by the spec.
2094 2205 void
2095   -QPDFWriter::prepareFileForWrite()
  2206 +QPDFWriter::Members::prepareFileForWrite()
2096 2207 {
2097   - m->pdf.fixDanglingReferences();
2098   - auto root = m->pdf.getRoot();
  2208 + pdf.fixDanglingReferences();
  2209 + auto root = pdf.getRoot();
2099 2210 auto oh = root.getKey("/Extensions");
2100 2211 if (oh.isDictionary()) {
2101 2212 const bool extensions_indirect = oh.isIndirect();
... ... @@ -2115,84 +2226,83 @@ QPDFWriter::prepareFileForWrite()
2115 2226 }
2116 2227  
2117 2228 void
2118   -QPDFWriter::initializeTables(size_t extra)
  2229 +QPDFWriter::Members::initializeTables(size_t extra)
2119 2230 {
2120   - auto size = QIntC::to_size(QPDF::Writer::tableSize(m->pdf) + 100) + extra;
2121   - m->obj.resize(size);
2122   - m->new_obj.resize(size);
  2231 + auto size = QIntC::to_size(QPDF::Writer::tableSize(pdf) + 100) + extra;
  2232 + obj.resize(size);
  2233 + new_obj.resize(size);
2123 2234 }
2124 2235  
2125 2236 void
2126   -QPDFWriter::doWriteSetup()
  2237 +QPDFWriter::Members::doWriteSetup()
2127 2238 {
2128   - if (m->did_write_setup) {
  2239 + if (did_write_setup) {
2129 2240 return;
2130 2241 }
2131   - m->did_write_setup = true;
  2242 + did_write_setup = true;
2132 2243  
2133 2244 // Do preliminary setup
2134 2245  
2135   - if (m->linearized) {
2136   - m->qdf_mode = false;
  2246 + if (linearized) {
  2247 + qdf_mode = false;
2137 2248 }
2138 2249  
2139   - if (m->pclm) {
2140   - m->stream_decode_level = qpdf_dl_none;
2141   - m->compress_streams = false;
2142   - m->encryption = nullptr;
  2250 + if (pclm) {
  2251 + stream_decode_level = qpdf_dl_none;
  2252 + compress_streams = false;
  2253 + encryption = nullptr;
2143 2254 }
2144 2255  
2145   - if (m->qdf_mode) {
2146   - if (!m->normalize_content_set) {
2147   - m->normalize_content = true;
  2256 + if (qdf_mode) {
  2257 + if (!normalize_content_set) {
  2258 + normalize_content = true;
2148 2259 }
2149   - if (!m->compress_streams_set) {
2150   - m->compress_streams = false;
  2260 + if (!compress_streams_set) {
  2261 + compress_streams = false;
2151 2262 }
2152   - if (!m->stream_decode_level_set) {
2153   - m->stream_decode_level = qpdf_dl_generalized;
  2263 + if (!stream_decode_level_set) {
  2264 + stream_decode_level = qpdf_dl_generalized;
2154 2265 }
2155 2266 }
2156 2267  
2157   - if (m->encryption) {
  2268 + if (encryption) {
2158 2269 // Encryption has been explicitly set
2159   - m->preserve_encryption = false;
2160   - } else if (m->normalize_content || !m->compress_streams || m->pclm || m->qdf_mode) {
  2270 + preserve_encryption = false;
  2271 + } else if (normalize_content || !compress_streams || pclm || qdf_mode) {
2161 2272 // Encryption makes looking at contents pretty useless. If the user explicitly encrypted
2162 2273 // though, we still obey that.
2163   - m->preserve_encryption = false;
  2274 + preserve_encryption = false;
2164 2275 }
2165 2276  
2166   - if (m->preserve_encryption) {
2167   - copyEncryptionParameters(m->pdf);
  2277 + if (preserve_encryption) {
  2278 + copyEncryptionParameters(pdf);
2168 2279 }
2169 2280  
2170   - if (!m->forced_pdf_version.empty()) {
  2281 + if (!forced_pdf_version.empty()) {
2171 2282 int major = 0;
2172 2283 int minor = 0;
2173   - parseVersion(m->forced_pdf_version, major, minor);
2174   - disableIncompatibleEncryption(major, minor, m->forced_extension_level);
  2284 + parseVersion(forced_pdf_version, major, minor);
  2285 + disableIncompatibleEncryption(major, minor, forced_extension_level);
2175 2286 if (compareVersions(major, minor, 1, 5) < 0) {
2176   - QTC::TC("qpdf", "QPDFWriter forcing object stream disable");
2177   - m->object_stream_mode = qpdf_o_disable;
  2287 + object_stream_mode = qpdf_o_disable;
2178 2288 }
2179 2289 }
2180 2290  
2181   - if (m->qdf_mode || m->normalize_content) {
  2291 + if (qdf_mode || normalize_content) {
2182 2292 initializeSpecialStreams();
2183 2293 }
2184 2294  
2185   - if (m->qdf_mode) {
  2295 + if (qdf_mode) {
2186 2296 // Generate indirect stream lengths for qdf mode since fix-qdf uses them for storing
2187 2297 // recomputed stream length data. Certain streams such as object streams, xref streams, and
2188 2298 // hint streams always get direct stream lengths.
2189   - m->direct_stream_lengths = false;
  2299 + direct_stream_lengths = false;
2190 2300 }
2191 2301  
2192   - switch (m->object_stream_mode) {
  2302 + switch (object_stream_mode) {
2193 2303 case qpdf_o_disable:
2194 2304 initializeTables();
2195   - m->obj.streams_empty = true;
  2305 + obj.streams_empty = true;
2196 2306 break;
2197 2307  
2198 2308 case qpdf_o_preserve:
... ... @@ -2207,82 +2317,85 @@ QPDFWriter::doWriteSetup()
2207 2317 // no default so gcc will warn for missing case tag
2208 2318 }
2209 2319  
2210   - if (!m->obj.streams_empty) {
2211   - if (m->linearized) {
  2320 + if (!obj.streams_empty) {
  2321 + if (linearized) {
2212 2322 // Page dictionaries are not allowed to be compressed objects.
2213   - for (auto& page: m->pdf.getAllPages()) {
2214   - if (m->obj[page].object_stream > 0) {
2215   - QTC::TC("qpdf", "QPDFWriter uncompressing page dictionary");
2216   - m->obj[page].object_stream = 0;
  2323 + for (auto& page: pdf.getAllPages()) {
  2324 + if (obj[page].object_stream > 0) {
  2325 + obj[page].object_stream = 0;
2217 2326 }
2218 2327 }
2219 2328 }
2220 2329  
2221   - if (m->linearized || m->encryption) {
  2330 + if (linearized || encryption) {
2222 2331 // The document catalog is not allowed to be compressed in linearized files either. It
2223 2332 // also appears that Adobe Reader 8.0.0 has a bug that prevents it from being able to
2224 2333 // handle encrypted files with compressed document catalogs, so we disable them in that
2225 2334 // case as well.
2226   - if (m->obj[m->root_og].object_stream > 0) {
2227   - QTC::TC("qpdf", "QPDFWriter uncompressing root");
2228   - m->obj[m->root_og].object_stream = 0;
  2335 + if (obj[root_og].object_stream > 0) {
  2336 + obj[root_og].object_stream = 0;
2229 2337 }
2230 2338 }
2231 2339  
2232 2340 // Generate reverse mapping from object stream to objects
2233   - m->obj.forEach([this](auto id, auto const& item) -> void {
  2341 + obj.forEach([this](auto id, auto const& item) -> void {
2234 2342 if (item.object_stream > 0) {
2235   - auto& vec = m->object_stream_to_objects[item.object_stream];
  2343 + auto& vec = object_stream_to_objects[item.object_stream];
2236 2344 vec.emplace_back(id, item.gen);
2237   - if (m->max_ostream_index < vec.size()) {
2238   - ++m->max_ostream_index;
  2345 + if (max_ostream_index < vec.size()) {
  2346 + ++max_ostream_index;
2239 2347 }
2240 2348 }
2241 2349 });
2242   - --m->max_ostream_index;
  2350 + --max_ostream_index;
2243 2351  
2244   - if (m->object_stream_to_objects.empty()) {
2245   - m->obj.streams_empty = true;
  2352 + if (object_stream_to_objects.empty()) {
  2353 + obj.streams_empty = true;
2246 2354 } else {
2247   - setMinimumPDFVersion("1.5");
  2355 + w.setMinimumPDFVersion("1.5");
2248 2356 }
2249 2357 }
2250 2358  
2251   - setMinimumPDFVersion(m->pdf.getPDFVersion(), m->pdf.getExtensionLevel());
2252   - m->final_pdf_version = m->min_pdf_version;
2253   - m->final_extension_level = m->min_extension_level;
2254   - if (!m->forced_pdf_version.empty()) {
2255   - QTC::TC("qpdf", "QPDFWriter using forced PDF version");
2256   - m->final_pdf_version = m->forced_pdf_version;
2257   - m->final_extension_level = m->forced_extension_level;
  2359 + setMinimumPDFVersion(pdf.getPDFVersion(), pdf.getExtensionLevel());
  2360 + final_pdf_version = min_pdf_version;
  2361 + final_extension_level = min_extension_level;
  2362 + if (!forced_pdf_version.empty()) {
  2363 + final_pdf_version = forced_pdf_version;
  2364 + final_extension_level = forced_extension_level;
2258 2365 }
2259 2366 }
2260 2367  
2261 2368 void
2262 2369 QPDFWriter::write()
2263 2370 {
  2371 + m->write();
  2372 +}
  2373 +
  2374 +void
  2375 +QPDFWriter::Members::write()
  2376 +{
2264 2377 doWriteSetup();
2265 2378  
2266 2379 // Set up progress reporting. For linearized files, we write two passes. events_expected is an
2267 2380 // approximation, but it's good enough for progress reporting, which is mostly a guess anyway.
2268   - m->events_expected = QIntC::to_int(m->pdf.getObjectCount() * (m->linearized ? 2 : 1));
  2381 + events_expected = QIntC::to_int(pdf.getObjectCount() * (linearized ? 2 : 1));
2269 2382  
2270 2383 prepareFileForWrite();
2271 2384  
2272   - if (m->linearized) {
  2385 + if (linearized) {
2273 2386 writeLinearized();
2274 2387 } else {
2275 2388 writeStandard();
2276 2389 }
2277 2390  
2278   - m->pipeline->finish();
2279   - if (m->close_file) {
2280   - fclose(m->file);
  2391 + pipeline->finish();
  2392 + if (close_file) {
  2393 + fclose(file);
2281 2394 }
2282   - m->file = nullptr;
2283   - if (m->buffer_pipeline) {
2284   - m->output_buffer = m->buffer_pipeline->getBuffer();
2285   - m->buffer_pipeline = nullptr;
  2395 + file = nullptr;
  2396 + if (buffer_pipeline) {
  2397 + output_buffer = buffer_pipeline->getBuffer();
  2398 + buffer_pipeline = nullptr;
2286 2399 }
2287 2400 indicateProgress(false, true);
2288 2401 }
... ... @@ -2296,10 +2409,16 @@ QPDFWriter::getRenumberedObjGen(QPDFObjGen og)
2296 2409 std::map<QPDFObjGen, QPDFXRefEntry>
2297 2410 QPDFWriter::getWrittenXRefTable()
2298 2411 {
  2412 + return m->getWrittenXRefTable();
  2413 +}
  2414 +
  2415 +std::map<QPDFObjGen, QPDFXRefEntry>
  2416 +QPDFWriter::Members::getWrittenXRefTable()
  2417 +{
2299 2418 std::map<QPDFObjGen, QPDFXRefEntry> result;
2300 2419  
2301 2420 auto it = result.begin();
2302   - m->new_obj.forEach([&it, &result](auto id, auto const& item) -> void {
  2421 + new_obj.forEach([&it, &result](auto id, auto const& item) -> void {
2303 2422 if (item.xref.getType() != 0) {
2304 2423 it = result.emplace_hint(it, QPDFObjGen(id, 0), item.xref);
2305 2424 }
... ... @@ -2308,7 +2427,7 @@ QPDFWriter::getWrittenXRefTable()
2308 2427 }
2309 2428  
2310 2429 void
2311   -QPDFWriter::enqueuePart(std::vector<QPDFObjectHandle>& part)
  2430 +QPDFWriter::Members::enqueuePart(std::vector<QPDFObjectHandle>& part)
2312 2431 {
2313 2432 for (auto const& oh: part) {
2314 2433 enqueueObject(oh);
... ... @@ -2316,20 +2435,20 @@ QPDFWriter::enqueuePart(std::vector&lt;QPDFObjectHandle&gt;&amp; part)
2316 2435 }
2317 2436  
2318 2437 void
2319   -QPDFWriter::writeEncryptionDictionary()
  2438 +QPDFWriter::Members::writeEncryptionDictionary()
2320 2439 {
2321   - m->encryption_dict_objid = openObject(m->encryption_dict_objid);
2322   - auto& enc = *m->encryption;
  2440 + encryption_dict_objid = openObject(encryption_dict_objid);
  2441 + auto& enc = *encryption;
2323 2442 auto const V = enc.getV();
2324 2443  
2325 2444 write("<<");
2326 2445 if (V >= 4) {
2327 2446 write(" /CF << /StdCF << /AuthEvent /DocOpen /CFM ");
2328   - write(m->encrypt_use_aes ? ((V < 5) ? "/AESV2" : "/AESV3") : "/V2");
  2447 + write(encrypt_use_aes ? ((V < 5) ? "/AESV2" : "/AESV3") : "/V2");
2329 2448 // The PDF spec says the /Length key is optional, but the PDF previewer on some versions of
2330 2449 // MacOS won't open encrypted files without it.
2331 2450 write((V < 5) ? " /Length 16 >> >>" : " /Length 32 >> >>");
2332   - if (!m->encryption->getEncryptMetadata()) {
  2451 + if (!encryption->getEncryptMetadata()) {
2333 2452 write(" /EncryptMetadata false");
2334 2453 }
2335 2454 }
... ... @@ -2352,21 +2471,21 @@ QPDFWriter::writeEncryptionDictionary()
2352 2471 write(" /UE ").write_string(enc.getUE(), true);
2353 2472 }
2354 2473 write(" /V ").write(enc.getV()).write(" >>");
2355   - closeObject(m->encryption_dict_objid);
  2474 + closeObject(encryption_dict_objid);
2356 2475 }
2357 2476  
2358 2477 std::string
2359 2478 QPDFWriter::getFinalVersion()
2360 2479 {
2361   - doWriteSetup();
  2480 + m->doWriteSetup();
2362 2481 return m->final_pdf_version;
2363 2482 }
2364 2483  
2365 2484 void
2366   -QPDFWriter::writeHeader()
  2485 +QPDFWriter::Members::writeHeader()
2367 2486 {
2368   - write("%PDF-").write(m->final_pdf_version);
2369   - if (m->pclm) {
  2487 + write("%PDF-").write(final_pdf_version);
  2488 + if (pclm) {
2370 2489 // PCLm version
2371 2490 write("\n%PCLm 1.0\n");
2372 2491 } else {
... ... @@ -2383,13 +2502,13 @@ QPDFWriter::writeHeader()
2383 2502 }
2384 2503  
2385 2504 void
2386   -QPDFWriter::writeHintStream(int hint_id)
  2505 +QPDFWriter::Members::writeHintStream(int hint_id)
2387 2506 {
2388 2507 std::string hint_buffer;
2389 2508 int S = 0;
2390 2509 int O = 0;
2391   - bool compressed = m->compress_streams && !m->qdf_mode;
2392   - QPDF::Writer::generateHintStream(m->pdf, m->new_obj, m->obj, hint_buffer, S, O, compressed);
  2510 + bool compressed = compress_streams && !qdf_mode;
  2511 + QPDF::Writer::generateHintStream(pdf, new_obj, obj, hint_buffer, S, O, compressed);
2393 2512  
2394 2513 openObject(hint_id);
2395 2514 setDataKey(hint_id);
... ... @@ -2408,7 +2527,7 @@ QPDFWriter::writeHintStream(int hint_id)
2408 2527 write(" /Length ").write(hlen);
2409 2528 write(" >>\nstream\n").write_encrypted(hint_buffer);
2410 2529  
2411   - if (m->encryption) {
  2530 + if (encryption) {
2412 2531 QTC::TC("qpdf", "QPDFWriter encrypted hint stream");
2413 2532 }
2414 2533  
... ... @@ -2417,7 +2536,7 @@ QPDFWriter::writeHintStream(int hint_id)
2417 2536 }
2418 2537  
2419 2538 qpdf_offset_t
2420   -QPDFWriter::writeXRefTable(trailer_e which, int first, int last, int size)
  2539 +QPDFWriter::Members::writeXRefTable(trailer_e which, int first, int last, int size)
2421 2540 {
2422 2541 // There are too many extra arguments to replace overloaded function with defaults in the header
2423 2542 // file...too much risk of leaving something off.
... ... @@ -2425,7 +2544,7 @@ QPDFWriter::writeXRefTable(trailer_e which, int first, int last, int size)
2425 2544 }
2426 2545  
2427 2546 qpdf_offset_t
2428   -QPDFWriter::writeXRefTable(
  2547 +QPDFWriter::Members::writeXRefTable(
2429 2548 trailer_e which,
2430 2549 int first,
2431 2550 int last,
... ... @@ -2438,7 +2557,7 @@ QPDFWriter::writeXRefTable(
2438 2557 int linearization_pass)
2439 2558 {
2440 2559 write("xref\n").write(first).write(" ").write(last - first + 1);
2441   - qpdf_offset_t space_before_zero = m->pipeline->getCount();
  2560 + qpdf_offset_t space_before_zero = pipeline->getCount();
2442 2561 write("\n");
2443 2562 if (first == 0) {
2444 2563 write("0000000000 65535 f \n");
... ... @@ -2447,7 +2566,7 @@ QPDFWriter::writeXRefTable(
2447 2566 for (int i = first; i <= last; ++i) {
2448 2567 qpdf_offset_t offset = 0;
2449 2568 if (!suppress_offsets) {
2450   - offset = m->new_obj[i].xref.getOffset();
  2569 + offset = new_obj[i].xref.getOffset();
2451 2570 if ((hint_id != 0) && (i != hint_id) && (offset >= hint_offset)) {
2452 2571 offset += hint_length;
2453 2572 }
... ... @@ -2460,7 +2579,7 @@ QPDFWriter::writeXRefTable(
2460 2579 }
2461 2580  
2462 2581 qpdf_offset_t
2463   -QPDFWriter::writeXRefStream(
  2582 +QPDFWriter::Members::writeXRefStream(
2464 2583 int objid, int max_id, qpdf_offset_t max_offset, trailer_e which, int first, int last, int size)
2465 2584 {
2466 2585 // There are too many extra arguments to replace overloaded function with defaults in the header
... ... @@ -2470,7 +2589,7 @@ QPDFWriter::writeXRefStream(
2470 2589 }
2471 2590  
2472 2591 qpdf_offset_t
2473   -QPDFWriter::writeXRefStream(
  2592 +QPDFWriter::Members::writeXRefStream(
2474 2593 int xref_id,
2475 2594 int max_id,
2476 2595 qpdf_offset_t max_offset,
... ... @@ -2485,28 +2604,28 @@ QPDFWriter::writeXRefStream(
2485 2604 bool skip_compression,
2486 2605 int linearization_pass)
2487 2606 {
2488   - qpdf_offset_t xref_offset = m->pipeline->getCount();
  2607 + qpdf_offset_t xref_offset = pipeline->getCount();
2489 2608 qpdf_offset_t space_before_zero = xref_offset - 1;
2490 2609  
2491 2610 // field 1 contains offsets and object stream identifiers
2492 2611 unsigned int f1_size = std::max(bytesNeeded(max_offset + hint_length), bytesNeeded(max_id));
2493 2612  
2494 2613 // field 2 contains object stream indices
2495   - unsigned int f2_size = bytesNeeded(QIntC::to_longlong(m->max_ostream_index));
  2614 + unsigned int f2_size = bytesNeeded(QIntC::to_longlong(max_ostream_index));
2496 2615  
2497 2616 unsigned int esize = 1 + f1_size + f2_size;
2498 2617  
2499 2618 // Must store in xref table in advance of writing the actual data rather than waiting for
2500 2619 // openObject to do it.
2501   - m->new_obj[xref_id].xref = QPDFXRefEntry(m->pipeline->getCount());
  2620 + new_obj[xref_id].xref = QPDFXRefEntry(pipeline->getCount());
2502 2621  
2503 2622 std::string xref_data;
2504   - const bool compressed = m->compress_streams && !m->qdf_mode;
  2623 + const bool compressed = compress_streams && !qdf_mode;
2505 2624 {
2506   - auto pp_xref = m->pipeline_stack.activate(xref_data);
  2625 + auto pp_xref = pipeline_stack.activate(xref_data);
2507 2626  
2508 2627 for (int i = first; i <= last; ++i) {
2509   - QPDFXRefEntry& e = m->new_obj[i].xref;
  2628 + QPDFXRefEntry& e = new_obj[i].xref;
2510 2629 switch (e.getType()) {
2511 2630 case 0:
2512 2631 writeBinary(0, 1);
... ... @@ -2566,7 +2685,7 @@ QPDFWriter::writeXRefStream(
2566 2685 }
2567 2686  
2568 2687 size_t
2569   -QPDFWriter::calculateXrefStreamPadding(qpdf_offset_t xref_bytes)
  2688 +QPDFWriter::Members::calculateXrefStreamPadding(qpdf_offset_t xref_bytes)
2570 2689 {
2571 2690 // This routine is called right after a linearization first pass xref stream has been written
2572 2691 // without compression. Calculate the amount of padding that would be required in the worst
... ... @@ -2578,7 +2697,7 @@ QPDFWriter::calculateXrefStreamPadding(qpdf_offset_t xref_bytes)
2578 2697 }
2579 2698  
2580 2699 void
2581   -QPDFWriter::writeLinearized()
  2700 +QPDFWriter::Members::writeLinearized()
2582 2701 {
2583 2702 // Optimize file and enqueue objects in order
2584 2703  
... ... @@ -2598,14 +2717,14 @@ QPDFWriter::writeLinearized()
2598 2717 return result;
2599 2718 };
2600 2719  
2601   - QPDF::Writer::optimize(m->pdf, m->obj, skip_stream_parameters);
  2720 + QPDF::Writer::optimize(pdf, obj, skip_stream_parameters);
2602 2721  
2603 2722 std::vector<QPDFObjectHandle> part4;
2604 2723 std::vector<QPDFObjectHandle> part6;
2605 2724 std::vector<QPDFObjectHandle> part7;
2606 2725 std::vector<QPDFObjectHandle> part8;
2607 2726 std::vector<QPDFObjectHandle> part9;
2608   - QPDF::Writer::getLinearizedParts(m->pdf, m->obj, part4, part6, part7, part8, part9);
  2727 + QPDF::Writer::getLinearizedParts(pdf, obj, part4, part6, part7, part8, part9);
2609 2728  
2610 2729 // Object number sequence:
2611 2730 //
... ... @@ -2627,11 +2746,11 @@ QPDFWriter::writeLinearized()
2627 2746 int second_half_uncompressed = QIntC::to_int(part7.size() + part8.size() + part9.size());
2628 2747 int second_half_first_obj = 1;
2629 2748 int after_second_half = 1 + second_half_uncompressed;
2630   - m->next_objid = after_second_half;
  2749 + next_objid = after_second_half;
2631 2750 int second_half_xref = 0;
2632   - bool need_xref_stream = !m->obj.streams_empty;
  2751 + bool need_xref_stream = !obj.streams_empty;
2633 2752 if (need_xref_stream) {
2634   - second_half_xref = m->next_objid++;
  2753 + second_half_xref = next_objid++;
2635 2754 }
2636 2755 // Assign numbers to all compressed objects in the second half.
2637 2756 std::vector<QPDFObjectHandle>* vecs2[] = {&part7, &part8, &part9};
... ... @@ -2640,26 +2759,26 @@ QPDFWriter::writeLinearized()
2640 2759 assignCompressedObjectNumbers(oh.getObjGen());
2641 2760 }
2642 2761 }
2643   - int second_half_end = m->next_objid - 1;
2644   - int second_trailer_size = m->next_objid;
  2762 + int second_half_end = next_objid - 1;
  2763 + int second_trailer_size = next_objid;
2645 2764  
2646 2765 // First half objects
2647   - int first_half_start = m->next_objid;
2648   - int lindict_id = m->next_objid++;
  2766 + int first_half_start = next_objid;
  2767 + int lindict_id = next_objid++;
2649 2768 int first_half_xref = 0;
2650 2769 if (need_xref_stream) {
2651   - first_half_xref = m->next_objid++;
2652   - }
2653   - int part4_first_obj = m->next_objid;
2654   - m->next_objid += QIntC::to_int(part4.size());
2655   - int after_part4 = m->next_objid;
2656   - if (m->encryption) {
2657   - m->encryption_dict_objid = m->next_objid++;
2658   - }
2659   - int hint_id = m->next_objid++;
2660   - int part6_first_obj = m->next_objid;
2661   - m->next_objid += QIntC::to_int(part6.size());
2662   - int after_part6 = m->next_objid;
  2770 + first_half_xref = next_objid++;
  2771 + }
  2772 + int part4_first_obj = next_objid;
  2773 + next_objid += QIntC::to_int(part4.size());
  2774 + int after_part4 = next_objid;
  2775 + if (encryption) {
  2776 + encryption_dict_objid = next_objid++;
  2777 + }
  2778 + int hint_id = next_objid++;
  2779 + int part6_first_obj = next_objid;
  2780 + next_objid += QIntC::to_int(part6.size());
  2781 + int after_part6 = next_objid;
2663 2782 // Assign numbers to all compressed objects in the first half
2664 2783 std::vector<QPDFObjectHandle>* vecs1[] = {&part4, &part6};
2665 2784 for (int i = 0; i < 2; ++i) {
... ... @@ -2667,8 +2786,8 @@ QPDFWriter::writeLinearized()
2667 2786 assignCompressedObjectNumbers(oh.getObjGen());
2668 2787 }
2669 2788 }
2670   - int first_half_end = m->next_objid - 1;
2671   - int first_trailer_size = m->next_objid;
  2789 + int first_half_end = next_objid - 1;
  2790 + int first_trailer_size = next_objid;
2672 2791  
2673 2792 int part4_end_marker = part4.back().getObjectID();
2674 2793 int part6_end_marker = part6.back().getObjectID();
... ... @@ -2680,23 +2799,23 @@ QPDFWriter::writeLinearized()
2680 2799 qpdf_offset_t first_xref_end = 0;
2681 2800 qpdf_offset_t second_xref_end = 0;
2682 2801  
2683   - m->next_objid = part4_first_obj;
  2802 + next_objid = part4_first_obj;
2684 2803 enqueuePart(part4);
2685   - if (m->next_objid != after_part4) {
  2804 + if (next_objid != after_part4) {
2686 2805 // This can happen with very botched files as in the fuzzer test. There are likely some
2687 2806 // faulty assumptions in calculateLinearizationData
2688 2807 throw std::runtime_error("error encountered after writing part 4 of linearized data");
2689 2808 }
2690   - m->next_objid = part6_first_obj;
  2809 + next_objid = part6_first_obj;
2691 2810 enqueuePart(part6);
2692   - if (m->next_objid != after_part6) {
  2811 + if (next_objid != after_part6) {
2693 2812 throw std::runtime_error("error encountered after writing part 6 of linearized data");
2694 2813 }
2695   - m->next_objid = second_half_first_obj;
  2814 + next_objid = second_half_first_obj;
2696 2815 enqueuePart(part7);
2697 2816 enqueuePart(part8);
2698 2817 enqueuePart(part9);
2699   - if (m->next_objid != after_second_half) {
  2818 + if (next_objid != after_second_half) {
2700 2819 throw std::runtime_error("error encountered after writing part 9 of linearized data");
2701 2820 }
2702 2821  
... ... @@ -2706,20 +2825,20 @@ QPDFWriter::writeLinearized()
2706 2825 // Write file in two passes. Part numbers refer to PDF spec 1.4.
2707 2826  
2708 2827 FILE* lin_pass1_file = nullptr;
2709   - auto pp_pass1 = m->pipeline_stack.popper();
2710   - auto pp_md5 = m->pipeline_stack.popper();
  2828 + auto pp_pass1 = pipeline_stack.popper();
  2829 + auto pp_md5 = pipeline_stack.popper();
2711 2830 for (int pass: {1, 2}) {
2712 2831 if (pass == 1) {
2713   - if (!m->lin_pass1_filename.empty()) {
2714   - lin_pass1_file = QUtil::safe_fopen(m->lin_pass1_filename.c_str(), "wb");
2715   - m->pipeline_stack.activate(
  2832 + if (!lin_pass1_filename.empty()) {
  2833 + lin_pass1_file = QUtil::safe_fopen(lin_pass1_filename.c_str(), "wb");
  2834 + pipeline_stack.activate(
2716 2835 pp_pass1,
2717 2836 std::make_unique<Pl_StdioFile>("linearization pass1", lin_pass1_file));
2718 2837 } else {
2719   - m->pipeline_stack.activate(pp_pass1, true);
  2838 + pipeline_stack.activate(pp_pass1, true);
2720 2839 }
2721   - if (m->deterministic_id) {
2722   - m->pipeline_stack.activate_md5(pp_md5);
  2840 + if (deterministic_id) {
  2841 + pipeline_stack.activate_md5(pp_md5);
2723 2842 }
2724 2843 }
2725 2844  
... ... @@ -2733,16 +2852,16 @@ QPDFWriter::writeLinearized()
2733 2852 // linearization parameter dictionary must appear within the first 1024 characters of the
2734 2853 // file.
2735 2854  
2736   - qpdf_offset_t pos = m->pipeline->getCount();
  2855 + qpdf_offset_t pos = pipeline->getCount();
2737 2856 openObject(lindict_id);
2738 2857 write("<<");
2739 2858 if (pass == 2) {
2740   - std::vector<QPDFObjectHandle> const& pages = m->pdf.getAllPages();
2741   - int first_page_object = m->obj[pages.at(0)].renumber;
  2859 + std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages();
  2860 + int first_page_object = obj[pages.at(0)].renumber;
2742 2861  
2743 2862 write(" /Linearized 1 /L ").write(file_size + hint_length);
2744 2863 // Implementation note 121 states that a space is mandatory after this open bracket.
2745   - write(" /H [ ").write(m->new_obj[hint_id].xref.getOffset()).write(" ");
  2864 + write(" /H [ ").write(new_obj[hint_id].xref.getOffset()).write(" ");
2746 2865 write(hint_length);
2747 2866 write(" ] /O ").write(first_page_object);
2748 2867 write(" /E ").write(part6_end_offset + hint_length);
... ... @@ -2752,18 +2871,18 @@ QPDFWriter::writeLinearized()
2752 2871 write(" >>");
2753 2872 closeObject(lindict_id);
2754 2873 static int const pad = 200;
2755   - write(QIntC::to_size(pos - m->pipeline->getCount() + pad), ' ').write("\n");
  2874 + write(QIntC::to_size(pos - pipeline->getCount() + pad), ' ').write("\n");
2756 2875  
2757 2876 // If the user supplied any additional header text, write it here after the linearization
2758 2877 // parameter dictionary.
2759   - write(m->extra_header_text);
  2878 + write(extra_header_text);
2760 2879  
2761 2880 // Part 3: first page cross reference table and trailer.
2762 2881  
2763   - qpdf_offset_t first_xref_offset = m->pipeline->getCount();
  2882 + qpdf_offset_t first_xref_offset = pipeline->getCount();
2764 2883 qpdf_offset_t hint_offset = 0;
2765 2884 if (pass == 2) {
2766   - hint_offset = m->new_obj[hint_id].xref.getOffset();
  2885 + hint_offset = new_obj[hint_id].xref.getOffset();
2767 2886 }
2768 2887 if (need_xref_stream) {
2769 2888 // Must pad here too.
... ... @@ -2775,7 +2894,7 @@ QPDFWriter::writeLinearized()
2775 2894 // value for this, but it's okay if it's smaller.
2776 2895 first_half_max_obj_offset = 1 << 25;
2777 2896 }
2778   - pos = m->pipeline->getCount();
  2897 + pos = pipeline->getCount();
2779 2898 writeXRefStream(
2780 2899 first_half_xref,
2781 2900 first_half_end,
... ... @@ -2790,16 +2909,16 @@ QPDFWriter::writeLinearized()
2790 2909 hint_length,
2791 2910 (pass == 1),
2792 2911 pass);
2793   - qpdf_offset_t endpos = m->pipeline->getCount();
  2912 + qpdf_offset_t endpos = pipeline->getCount();
2794 2913 if (pass == 1) {
2795 2914 // Pad so we have enough room for the real xref stream.
2796 2915 write(calculateXrefStreamPadding(endpos - pos), ' ');
2797   - first_xref_end = m->pipeline->getCount();
  2916 + first_xref_end = pipeline->getCount();
2798 2917 } else {
2799 2918 // Pad so that the next object starts at the same place as in pass 1.
2800 2919 write(QIntC::to_size(first_xref_end - endpos), ' ');
2801 2920  
2802   - if (m->pipeline->getCount() != first_xref_end) {
  2921 + if (pipeline->getCount() != first_xref_end) {
2803 2922 throw std::logic_error(
2804 2923 "insufficient padding for first pass xref stream; first_xref_end=" +
2805 2924 std::to_string(first_xref_end) + "; endpos=" + std::to_string(endpos));
... ... @@ -2823,24 +2942,24 @@ QPDFWriter::writeLinearized()
2823 2942  
2824 2943 // Parts 4 through 9
2825 2944  
2826   - for (auto const& cur_object: m->object_queue) {
  2945 + for (auto const& cur_object: object_queue) {
2827 2946 if (cur_object.getObjectID() == part6_end_marker) {
2828   - first_half_max_obj_offset = m->pipeline->getCount();
  2947 + first_half_max_obj_offset = pipeline->getCount();
2829 2948 }
2830 2949 writeObject(cur_object);
2831 2950 if (cur_object.getObjectID() == part4_end_marker) {
2832   - if (m->encryption) {
  2951 + if (encryption) {
2833 2952 writeEncryptionDictionary();
2834 2953 }
2835 2954 if (pass == 1) {
2836   - m->new_obj[hint_id].xref = QPDFXRefEntry(m->pipeline->getCount());
  2955 + new_obj[hint_id].xref = QPDFXRefEntry(pipeline->getCount());
2837 2956 } else {
2838 2957 // Part 5: hint stream
2839 2958 write(hint_buffer);
2840 2959 }
2841 2960 }
2842 2961 if (cur_object.getObjectID() == part6_end_marker) {
2843   - part6_end_offset = m->pipeline->getCount();
  2962 + part6_end_offset = pipeline->getCount();
2844 2963 }
2845 2964 }
2846 2965  
... ... @@ -2848,9 +2967,9 @@ QPDFWriter::writeLinearized()
2848 2967  
2849 2968 // Part 11: main cross reference table and trailer
2850 2969  
2851   - second_xref_offset = m->pipeline->getCount();
  2970 + second_xref_offset = pipeline->getCount();
2852 2971 if (need_xref_stream) {
2853   - pos = m->pipeline->getCount();
  2972 + pos = pipeline->getCount();
2854 2973 space_before_zero = writeXRefStream(
2855 2974 second_half_xref,
2856 2975 second_half_end,
... ... @@ -2865,21 +2984,21 @@ QPDFWriter::writeLinearized()
2865 2984 0,
2866 2985 (pass == 1),
2867 2986 pass);
2868   - qpdf_offset_t endpos = m->pipeline->getCount();
  2987 + qpdf_offset_t endpos = pipeline->getCount();
2869 2988  
2870 2989 if (pass == 1) {
2871 2990 // Pad so we have enough room for the real xref stream. See comments for previous
2872 2991 // xref stream on how we calculate the padding.
2873 2992 write(calculateXrefStreamPadding(endpos - pos), ' ').write("\n");
2874   - second_xref_end = m->pipeline->getCount();
  2993 + second_xref_end = pipeline->getCount();
2875 2994 } else {
2876 2995 // Make the file size the same.
2877 2996 auto padding =
2878   - QIntC::to_size(second_xref_end + hint_length - 1 - m->pipeline->getCount());
  2997 + QIntC::to_size(second_xref_end + hint_length - 1 - pipeline->getCount());
2879 2998 write(padding, ' ').write("\n");
2880 2999  
2881 3000 // If this assertion fails, maybe we didn't have enough padding above.
2882   - if (m->pipeline->getCount() != second_xref_end + hint_length) {
  3001 + if (pipeline->getCount() != second_xref_end + hint_length) {
2883 3002 throw std::logic_error(
2884 3003 "count mismatch after xref stream; possible insufficient padding?");
2885 3004 }
... ... @@ -2891,28 +3010,28 @@ QPDFWriter::writeLinearized()
2891 3010 write("startxref\n").write(first_xref_offset).write("\n%%EOF\n");
2892 3011  
2893 3012 if (pass == 1) {
2894   - if (m->deterministic_id) {
  3013 + if (deterministic_id) {
2895 3014 QTC::TC("qpdf", "QPDFWriter linearized deterministic ID", need_xref_stream ? 0 : 1);
2896 3015 computeDeterministicIDData();
2897 3016 pp_md5.pop();
2898 3017 }
2899 3018  
2900 3019 // Close first pass pipeline
2901   - file_size = m->pipeline->getCount();
  3020 + file_size = pipeline->getCount();
2902 3021 pp_pass1.pop();
2903 3022  
2904 3023 // Save hint offset since it will be set to zero by calling openObject.
2905   - qpdf_offset_t hint_offset1 = m->new_obj[hint_id].xref.getOffset();
  3024 + qpdf_offset_t hint_offset1 = new_obj[hint_id].xref.getOffset();
2906 3025  
2907 3026 // Write hint stream to a buffer
2908 3027 {
2909   - auto pp_hint = m->pipeline_stack.activate(hint_buffer);
  3028 + auto pp_hint = pipeline_stack.activate(hint_buffer);
2910 3029 writeHintStream(hint_id);
2911 3030 }
2912 3031 hint_length = QIntC::to_offset(hint_buffer.size());
2913 3032  
2914 3033 // Restore hint offset
2915   - m->new_obj[hint_id].xref = QPDFXRefEntry(hint_offset1);
  3034 + new_obj[hint_id].xref = QPDFXRefEntry(hint_offset1);
2916 3035 if (lin_pass1_file) {
2917 3036 // Write some debugging information
2918 3037 fprintf(
... ... @@ -2934,11 +3053,10 @@ QPDFWriter::writeLinearized()
2934 3053 }
2935 3054  
2936 3055 void
2937   -QPDFWriter::enqueueObjectsStandard()
  3056 +QPDFWriter::Members::enqueueObjectsStandard()
2938 3057 {
2939   - if (m->preserve_unreferenced_objects) {
2940   - QTC::TC("qpdf", "QPDFWriter preserve unreferenced standard");
2941   - for (auto const& oh: m->pdf.getAllObjects()) {
  3058 + if (preserve_unreferenced_objects) {
  3059 + for (auto const& oh: pdf.getAllObjects()) {
2942 3060 enqueueObject(oh);
2943 3061 }
2944 3062 }
... ... @@ -2957,14 +3075,14 @@ QPDFWriter::enqueueObjectsStandard()
2957 3075 }
2958 3076  
2959 3077 void
2960   -QPDFWriter::enqueueObjectsPCLm()
  3078 +QPDFWriter::Members::enqueueObjectsPCLm()
2961 3079 {
2962 3080 // Image transform stream content for page strip images. Each of this new stream has to come
2963 3081 // after every page image strip written in the pclm file.
2964 3082 std::string image_transform_content = "q /image Do Q\n";
2965 3083  
2966 3084 // enqueue all pages first
2967   - std::vector<QPDFObjectHandle> all = m->pdf.getAllPages();
  3085 + std::vector<QPDFObjectHandle> all = pdf.getAllPages();
2968 3086 for (auto& page: all) {
2969 3087 // enqueue page
2970 3088 enqueueObject(page);
... ... @@ -2977,7 +3095,7 @@ QPDFWriter::enqueueObjectsPCLm()
2977 3095 for (auto& image: strips.as_dictionary()) {
2978 3096 if (!image.second.null()) {
2979 3097 enqueueObject(image.second);
2980   - enqueueObject(QPDFObjectHandle::newStream(&m->pdf, image_transform_content));
  3098 + enqueueObject(QPDFObjectHandle::newStream(&pdf, image_transform_content));
2981 3099 }
2982 3100 }
2983 3101 }
... ... @@ -2988,30 +3106,30 @@ QPDFWriter::enqueueObjectsPCLm()
2988 3106 }
2989 3107  
2990 3108 void
2991   -QPDFWriter::indicateProgress(bool decrement, bool finished)
  3109 +QPDFWriter::Members::indicateProgress(bool decrement, bool finished)
2992 3110 {
2993 3111 if (decrement) {
2994   - --m->events_seen;
  3112 + --events_seen;
2995 3113 return;
2996 3114 }
2997 3115  
2998   - ++m->events_seen;
  3116 + ++events_seen;
2999 3117  
3000   - if (!m->progress_reporter.get()) {
  3118 + if (!progress_reporter.get()) {
3001 3119 return;
3002 3120 }
3003 3121  
3004   - if (finished || (m->events_seen >= m->next_progress_report)) {
  3122 + if (finished || events_seen >= next_progress_report) {
3005 3123 int percentage =
3006 3124 (finished ? 100
3007   - : m->next_progress_report == 0
  3125 + : next_progress_report == 0
3008 3126 ? 0
3009   - : std::min(99, 1 + ((100 * m->events_seen) / m->events_expected)));
3010   - m->progress_reporter->reportProgress(percentage);
  3127 + : std::min(99, 1 + ((100 * events_seen) / events_expected)));
  3128 + progress_reporter->reportProgress(percentage);
3011 3129 }
3012   - int increment = std::max(1, (m->events_expected / 100));
3013   - while (m->events_seen >= m->next_progress_report) {
3014   - m->next_progress_report += increment;
  3130 + int increment = std::max(1, (events_expected / 100));
  3131 + while (events_seen >= next_progress_report) {
  3132 + next_progress_report += increment;
3015 3133 }
3016 3134 }
3017 3135  
... ... @@ -3022,53 +3140,52 @@ QPDFWriter::registerProgressReporter(std::shared_ptr&lt;ProgressReporter&gt; pr)
3022 3140 }
3023 3141  
3024 3142 void
3025   -QPDFWriter::writeStandard()
  3143 +QPDFWriter::Members::writeStandard()
3026 3144 {
3027   - auto pp_md5 = m->pipeline_stack.popper();
3028   - if (m->deterministic_id) {
3029   - m->pipeline_stack.activate_md5(pp_md5);
  3145 + auto pp_md5 = pipeline_stack.popper();
  3146 + if (deterministic_id) {
  3147 + pipeline_stack.activate_md5(pp_md5);
3030 3148 }
3031 3149  
3032 3150 // Start writing
3033 3151  
3034 3152 writeHeader();
3035   - write(m->extra_header_text);
  3153 + write(extra_header_text);
3036 3154  
3037   - if (m->pclm) {
  3155 + if (pclm) {
3038 3156 enqueueObjectsPCLm();
3039 3157 } else {
3040 3158 enqueueObjectsStandard();
3041 3159 }
3042 3160  
3043 3161 // Now start walking queue, outputting each object.
3044   - while (m->object_queue_front < m->object_queue.size()) {
3045   - QPDFObjectHandle cur_object = m->object_queue.at(m->object_queue_front);
3046   - ++m->object_queue_front;
  3162 + while (object_queue_front < object_queue.size()) {
  3163 + QPDFObjectHandle cur_object = object_queue.at(object_queue_front);
  3164 + ++object_queue_front;
3047 3165 writeObject(cur_object);
3048 3166 }
3049 3167  
3050 3168 // Write out the encryption dictionary, if any
3051   - if (m->encryption) {
  3169 + if (encryption) {
3052 3170 writeEncryptionDictionary();
3053 3171 }
3054 3172  
3055 3173 // Now write out xref. next_objid is now the number of objects.
3056   - qpdf_offset_t xref_offset = m->pipeline->getCount();
3057   - if (m->object_stream_to_objects.empty()) {
  3174 + qpdf_offset_t xref_offset = pipeline->getCount();
  3175 + if (object_stream_to_objects.empty()) {
3058 3176 // Write regular cross-reference table
3059   - writeXRefTable(t_normal, 0, m->next_objid - 1, m->next_objid);
  3177 + writeXRefTable(t_normal, 0, next_objid - 1, next_objid);
3060 3178 } else {
3061 3179 // Write cross-reference stream.
3062   - int xref_id = m->next_objid++;
3063   - writeXRefStream(
3064   - xref_id, xref_id, xref_offset, t_normal, 0, m->next_objid - 1, m->next_objid);
  3180 + int xref_id = next_objid++;
  3181 + writeXRefStream(xref_id, xref_id, xref_offset, t_normal, 0, next_objid - 1, next_objid);
3065 3182 }
3066 3183 write("startxref\n").write(xref_offset).write("\n%%EOF\n");
3067 3184  
3068   - if (m->deterministic_id) {
  3185 + if (deterministic_id) {
3069 3186 QTC::TC(
3070 3187 "qpdf",
3071 3188 "QPDFWriter standard deterministic ID",
3072   - m->object_stream_to_objects.empty() ? 0 : 1);
  3189 + object_stream_to_objects.empty() ? 0 : 1);
3073 3190 }
3074 3191 }
... ...
qpdf/qpdf.testcov
... ... @@ -40,8 +40,6 @@ main QTest array indirect 1
40 40 main QTest dictionary 0
41 41 main QTest dictionary indirect 1
42 42 main QTest stream 0
43   -QPDFWriter write to stdout 0
44   -QPDFWriter write to file 0
45 43 QPDF lin write nshared_total > nshared_first_page 1
46 44 QPDFWriter encrypted hint stream 0
47 45 QPDF opt inherited scalar 0
... ... @@ -83,11 +81,7 @@ QPDF xref deleted object 0
83 81 SF_FlateLzwDecode PNG filter 0
84 82 QPDF xref /Index is array 1
85 83 QPDFWriter encrypt object stream 0
86   -QPDFWriter uncompressing page dictionary 0
87   -QPDFWriter uncompressing root 0
88   -QPDFWriter compressing uncompressed stream 0
89 84 QPDF exclude indirect length 0
90   -QPDFWriter generate >1 ostream 0
91 85 QPDF exclude encryption dictionary 0
92 86 QPDF loop detected traversing objects 0
93 87 QPDF reconstructed xref table 0
... ... @@ -95,7 +89,6 @@ QPDF recovered in readObjectAtOffset 0
95 89 QPDF recovered stream length 0
96 90 QPDF found wrong endstream in recovery 0
97 91 QPDF_Stream pipeStreamData with null pipeline 0
98   -QPDFWriter not recompressing /FlateDecode 0
99 92 QPDFJob unable to filter 0
100 93 QUtil non-trivial UTF-16 0
101 94 QPDF xref overwrite invalid objgen 0
... ... @@ -135,14 +128,11 @@ qpdf-c called qpdf_allow_modify_annotation 0
135 128 qpdf-c called qpdf_allow_modify_other 0
136 129 qpdf-c called qpdf_allow_modify_all 0
137 130 QPDFWriter increasing minimum version 1
138   -QPDFWriter using forced PDF version 0
139 131 qpdf-c called qpdf_set_minimum_pdf_version 0
140 132 qpdf-c called qpdf_force_pdf_version 0
141 133 qpdf-c called qpdf_init_write multiple times 0
142 134 QPDF_encryption rc4 decode string 0
143   -QPDFWriter not compressing metadata 0
144 135 QPDF_encryption aes decode string 0
145   -QPDFWriter forcing object stream disable 0
146 136 QPDFWriter forced version disabled encryption 0
147 137 qpdf-c called qpdf_set_r4_encryption_parameters_insecure 0
148 138 qpdf-c called qpdf_set_static_aes_IV 0
... ... @@ -195,7 +185,6 @@ QPDF replace dictionary 0
195 185 QPDF replace stream 0
196 186 QPDF replace foreign indirect with null 0
197 187 QPDF insert foreign page 0
198   -QPDFWriter foreign object 0
199 188 QPDFWriter copy use_aes 1
200 189 QPDFParser indirect without context 0
201 190 QPDFObjectHandle trailing data in parse 0
... ... @@ -203,17 +192,13 @@ QPDFJob pages encryption password 0
203 192 QPDFTokenizer EOF reading token 0
204 193 QPDFTokenizer EOF reading appendable token 0
205 194 QPDFWriter extra header text no newline 0
206   -QPDFWriter extra header text add newline 0
207 195 QPDF bogus 0 offset 0
208 196 QPDF global offset 0
209   -QPDFWriter increasing extension level 0
210 197 QPDFWriter make Extensions direct 0
211 198 QPDFWriter make ADBE direct 1
212 199 QPDFWriter preserve Extensions 0
213 200 QPDFWriter create Extensions 1
214   -QPDFWriter remove ADBE 0
215 201 QPDFWriter remove existing Extensions 0
216   -QPDFWriter preserve ADBE 0
217 202 QPDF_encryption skip 0x28 0
218 203 qpdf-c called qpdf_get_pdf_extension_level 0
219 204 qpdf-c called qpdf_set_r5_encryption_parameters 0
... ... @@ -221,7 +206,6 @@ qpdf-c called qpdf_set_r6_encryption_parameters 0
221 206 QPDFObjectHandle EOF in inline image 0
222 207 QPDFObjectHandle inline image token 0
223 208 QPDF not caching overridden objstm object 0
224   -QPDFWriter original obj non-zero gen 0
225 209 QPDF_optimization indirect outlines 0
226 210 QPDF xref space 2
227 211 QPDFJob pages range omitted in middle 0
... ... @@ -237,7 +221,6 @@ QPDFParser treat word as string in parseRemainder 0
237 221 QPDFParser found fake 1
238 222 QPDFParser no val for last key 0
239 223 QPDF resolve failure to null 0
240   -QPDFWriter preserve unreferenced standard 0
241 224 QPDFObjectHandle errors in parsecontent 0
242 225 QPDFJob same file error 0
243 226 QPDFJob split-pages %d 0
... ... @@ -374,7 +357,6 @@ QPDF_encryption same password 1
374 357 QPDFParser duplicate dict key 0
375 358 QPDFWriter no encryption sig contents 0
376 359 QPDFPageObjectHelper colorspace lookup 0
377   -QPDFWriter ignore XRef in qdf mode 0
378 360 QPDFPageObjectHelper filter form xobject 0
379 361 QPDFJob found resources in non-leaf 0
380 362 QPDFJob found shared resources in leaf 0
... ... @@ -463,7 +445,6 @@ qpdf-c called qpdf_oh_get_generation 0
463 445 qpdf-c called qpdf_oh_unparse 0
464 446 qpdf-c called qpdf_oh_unparse_resolved 0
465 447 qpdf-c called qpdf_oh_unparse_binary 0
466   -QPDFWriter getFilterOnWrite false 0
467 448 QPDFPageObjectHelper::forEachXObject 3
468 449 NNTree erased last kid/item in tree 1
469 450 QPDFPageObjectHelper unresolved names 0
... ... @@ -481,8 +462,6 @@ QPDFAcroFormDocumentHelper AP parse error 1
481 462 QPDFJob copy fields not this file 0
482 463 QPDFJob copy fields non-first from orig 0
483 464 QPDF resolve duplicated page in insert 0
484   -QPDFWriter preserve object streams 0
485   -QPDFWriter preserve object streams preserve unreferenced 0
486 465 QPDFWriter exclude from object stream 0
487 466 QPDF_pages findPage not found 0
488 467 QPDFJob weak crypto error 0
... ...
qpdf/test_driver.cc
... ... @@ -2624,7 +2624,7 @@ test_76(QPDF&amp; pdf, char const* arg2)
2624 2624 {
2625 2625 // Embedded files. arg2 is a file to attach. Hard-code the
2626 2626 // mime type and file name for test purposes.
2627   - auto &efdh = QPDFEmbeddedFileDocumentHelper::get(pdf);
  2627 + auto& efdh = QPDFEmbeddedFileDocumentHelper::get(pdf);
2628 2628 auto fs1 = QPDFFileSpecObjectHelper::createFileSpec(pdf, "att1.txt", arg2);
2629 2629 fs1.setDescription("some text");
2630 2630 auto efs1 = QPDFEFStreamObjectHelper(fs1.getEmbeddedFileStream());
... ...