Coverage Report

Created: 2026-04-15 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qpdf/libqpdf/QPDFWriter.cc
Line
Count
Source
1
#include <qpdf/qpdf-config.h> // include early for large file support
2
3
#include <qpdf/QPDFWriter_private.hh>
4
5
#include <qpdf/MD5.hh>
6
#include <qpdf/Pl_AES_PDF.hh>
7
#include <qpdf/Pl_Flate.hh>
8
#include <qpdf/Pl_MD5.hh>
9
#include <qpdf/Pl_PNGFilter.hh>
10
#include <qpdf/Pl_RC4.hh>
11
#include <qpdf/Pl_StdioFile.hh>
12
#include <qpdf/QIntC.hh>
13
#include <qpdf/QPDFObjectHandle_private.hh>
14
#include <qpdf/QPDFObject_private.hh>
15
#include <qpdf/QPDF_private.hh>
16
#include <qpdf/QTC.hh>
17
#include <qpdf/QUtil.hh>
18
#include <qpdf/RC4.hh>
19
#include <qpdf/Util.hh>
20
21
#include <algorithm>
22
#include <concepts>
23
#include <cstdlib>
24
#include <stdexcept>
25
#include <tuple>
26
27
using namespace std::literals;
28
using namespace qpdf;
29
30
using Encryption = impl::Doc::Encryption;
31
using Config = Writer::Config;
32
33
QPDFWriter::ProgressReporter::~ProgressReporter() // NOLINT (modernize-use-equals-default)
34
0
{
35
    // Must be explicit and not inline -- see QPDF_DLL_CLASS in README-maintainer
36
0
}
37
38
QPDFWriter::FunctionProgressReporter::FunctionProgressReporter(std::function<void(int)> handler) :
39
0
    handler(handler)
40
0
{
41
0
}
42
43
QPDFWriter::FunctionProgressReporter::~FunctionProgressReporter() // NOLINT
44
                                                                  // (modernize-use-equals-default)
45
0
{
46
    // Must be explicit and not inline -- see QPDF_DLL_CLASS in README-maintainer
47
0
}
48
49
void
50
QPDFWriter::FunctionProgressReporter::reportProgress(int progress)
51
0
{
52
0
    handler(progress);
53
0
}
54
55
namespace
56
{
57
    class Pl_stack
58
    {
59
        // A pipeline Popper is normally returned by Pl_stack::activate, or, if necessary, a
60
        // reference to a Popper instance can be passed into activate. When the Popper goes out of
61
        // scope, the pipeline stack is popped. This causes finish to be called on the current
62
        // pipeline and the pipeline stack to be popped until the top of stack is a previous active
63
        // top of stack and restores the pipeline to that point. It deletes any pipelines that it
64
        // pops.
65
        class Popper
66
        {
67
            friend class Pl_stack;
68
69
          public:
70
            Popper() = default;
71
            Popper(Popper const&) = delete;
72
            Popper(Popper&& other) noexcept
73
0
            {
74
0
                // For MSVC, default pops the stack
75
0
                if (this != &other) {
76
0
                    stack = other.stack;
77
0
                    stack_id = other.stack_id;
78
0
                    other.stack = nullptr;
79
0
                    other.stack_id = 0;
80
0
                };
81
0
            }
82
            Popper& operator=(Popper const&) = delete;
83
            Popper&
84
            operator=(Popper&& other) noexcept
85
0
            {
86
0
                // For MSVC, default pops the stack
87
0
                if (this != &other) {
88
0
                    stack = other.stack;
89
0
                    stack_id = other.stack_id;
90
0
                    other.stack = nullptr;
91
0
                    other.stack_id = 0;
92
0
                };
93
0
                return *this;
94
0
            }
95
96
            ~Popper();
97
98
            // Manually pop pipeline from the pipeline stack.
99
            void pop();
100
101
          private:
102
            Popper(Pl_stack& stack) :
103
651k
                stack(&stack)
104
651k
            {
105
651k
            }
106
107
            Pl_stack* stack{nullptr};
108
            unsigned long stack_id{0};
109
        };
110
111
      public:
112
        Pl_stack(pl::Count*& top) :
113
62.3k
            top(top)
114
62.3k
        {
115
62.3k
        }
116
117
        Popper
118
        popper()
119
82.0k
        {
120
82.0k
            return {*this};
121
82.0k
        }
122
123
        void
124
        initialize(Pipeline* p)
125
62.3k
        {
126
62.3k
            auto c = std::make_unique<pl::Count>(++last_id, p);
127
62.3k
            top = c.get();
128
62.3k
            stack.emplace_back(std::move(c));
129
62.3k
        }
130
131
        Popper
132
        activate(std::string& str)
133
533k
        {
134
533k
            Popper pp{*this};
135
533k
            activate(pp, str);
136
533k
            return pp;
137
533k
        }
138
139
        void
140
        activate(Popper& pp, std::string& str)
141
533k
        {
142
533k
            activate(pp, false, &str, nullptr);
143
533k
        }
144
145
        void
146
        activate(Popper& pp, std::unique_ptr<Pipeline> next)
147
0
        {
148
0
            count_buffer.clear();
149
0
            activate(pp, false, &count_buffer, std::move(next));
150
0
        }
151
152
        Popper
153
        activate(
154
            bool discard = false,
155
            std::string* str = nullptr,
156
            std::unique_ptr<Pipeline> next = nullptr)
157
36.4k
        {
158
36.4k
            Popper pp{*this};
159
36.4k
            activate(pp, discard, str, std::move(next));
160
36.4k
            return pp;
161
36.4k
        }
162
163
        void
164
        activate(
165
            Popper& pp,
166
            bool discard = false,
167
            std::string* str = nullptr,
168
            std::unique_ptr<Pipeline> next = nullptr)
169
595k
        {
170
595k
            std::unique_ptr<pl::Count> c;
171
595k
            if (next) {
172
0
                c = std::make_unique<pl::Count>(++last_id, count_buffer, std::move(next));
173
595k
            } else if (discard) {
174
62.3k
                c = std::make_unique<pl::Count>(++last_id, nullptr);
175
533k
            } else if (!str) {
176
0
                c = std::make_unique<pl::Count>(++last_id, top);
177
533k
            } else {
178
533k
                c = std::make_unique<pl::Count>(++last_id, *str);
179
533k
            }
180
595k
            pp.stack_id = last_id;
181
595k
            top = c.get();
182
595k
            stack.emplace_back(std::move(c));
183
595k
        }
184
        void
185
        activate_md5(Popper& pp)
186
29.4k
        {
187
29.4k
            qpdf_assert_debug(!md5_pipeline);
188
29.4k
            qpdf_assert_debug(md5_id == 0);
189
29.4k
            qpdf_assert_debug(top->getCount() == 0);
190
29.4k
            md5_pipeline = std::make_unique<Pl_MD5>("qpdf md5", top);
191
29.4k
            md5_pipeline->persistAcrossFinish(true);
192
            // Special case code in pop clears m->md5_pipeline upon deletion.
193
29.4k
            auto c = std::make_unique<pl::Count>(++last_id, md5_pipeline.get());
194
29.4k
            pp.stack_id = last_id;
195
29.4k
            md5_id = last_id;
196
29.4k
            top = c.get();
197
29.4k
            stack.emplace_back(std::move(c));
198
29.4k
        }
199
200
        // Return the hex digest and disable the MD5 pipeline.
201
        std::string
202
        hex_digest()
203
28.2k
        {
204
28.2k
            qpdf_assert_debug(md5_pipeline);
205
28.2k
            auto digest = md5_pipeline->getHexDigest();
206
28.2k
            md5_pipeline->enable(false);
207
28.2k
            return digest;
208
28.2k
        }
209
210
        void
211
        clear_buffer()
212
0
        {
213
0
            count_buffer.clear();
214
0
        }
215
216
      private:
217
        void
218
        pop(unsigned long stack_id)
219
651k
        {
220
651k
            if (!stack_id) {
221
26.6k
                return;
222
26.6k
            }
223
625k
            qpdf_assert_debug(stack.size() >= 2);
224
625k
            top->finish();
225
625k
            qpdf_assert_debug(stack.back().get() == top);
226
            // It used to be possible for this assertion to fail if writeLinearized exits by
227
            // exception when deterministic ID. There are no longer any cases in which two
228
            // dynamically allocated pipeline Popper objects ever exist at the same time, so the
229
            // assertion will fail if they get popped out of order from automatic destruction.
230
625k
            qpdf_assert_debug(top->id() == stack_id);
231
625k
            if (stack_id == md5_id) {
232
29.4k
                md5_pipeline = nullptr;
233
29.4k
                md5_id = 0;
234
29.4k
            }
235
625k
            stack.pop_back();
236
625k
            top = stack.back().get();
237
625k
        }
238
239
        std::vector<std::unique_ptr<pl::Count>> stack;
240
        pl::Count*& top;
241
        std::unique_ptr<Pl_MD5> md5_pipeline{nullptr};
242
        unsigned long last_id{0};
243
        unsigned long md5_id{0};
244
        std::string count_buffer;
245
    };
246
} // namespace
247
248
Pl_stack::Popper::~Popper()
249
651k
{
250
651k
    if (stack) {
251
614k
        stack->pop(stack_id);
252
614k
    }
253
651k
}
254
255
void
256
Pl_stack::Popper::pop()
257
36.8k
{
258
36.8k
    if (stack) {
259
36.8k
        stack->pop(stack_id);
260
36.8k
    }
261
36.8k
    stack_id = 0;
262
36.8k
    stack = nullptr;
263
36.8k
}
264
265
namespace qpdf::impl
266
{
267
    // Writer class is restricted to QPDFWriter so that only it can call certain methods.
268
    class Writer: protected Doc::Common
269
    {
270
      public:
271
        // flags used by unparseObject
272
        static int const f_stream = 1 << 0;
273
        static int const f_filtered = 1 << 1;
274
        static int const f_in_ostream = 1 << 2;
275
        static int const f_hex_string = 1 << 3;
276
        static int const f_no_encryption = 1 << 4;
277
278
        enum trailer_e { t_normal, t_lin_first, t_lin_second };
279
280
        Writer() = delete;
281
        Writer(Writer const&) = delete;
282
        Writer(Writer&&) = delete;
283
        Writer& operator=(Writer const&) = delete;
284
        Writer& operator=(Writer&&) = delete;
285
        ~Writer()
286
62.3k
        {
287
62.3k
            if (file && close_file) {
288
0
                fclose(file);
289
0
            }
290
62.3k
            delete output_buffer;
291
62.3k
        }
292
        Writer(QPDF& qpdf, QPDFWriter& w) :
293
63.6k
            Common(qpdf.doc()),
294
63.6k
            lin(qpdf.doc().linearization()),
295
63.6k
            cfg(true),
296
63.6k
            root_og(qpdf.getRoot().indirect() ? qpdf.getRoot().id_gen() : QPDFObjGen(-1, 0)),
297
63.6k
            pipeline_stack(pipeline)
298
63.6k
        {
299
63.6k
        }
300
301
        void write();
302
        std::map<QPDFObjGen, QPDFXRefEntry> getWrittenXRefTable();
303
        void setMinimumPDFVersion(std::string const& version, int extension_level = 0);
304
        void copyEncryptionParameters(QPDF&);
305
        void doWriteSetup();
306
        void prepareFileForWrite();
307
308
        void disableIncompatibleEncryption(int major, int minor, int extension_level);
309
        void interpretR3EncryptionParameters(
310
            bool allow_accessibility,
311
            bool allow_extract,
312
            bool allow_assemble,
313
            bool allow_annotate_and_form,
314
            bool allow_form_filling,
315
            bool allow_modify_other,
316
            qpdf_r3_print_e print,
317
            qpdf_r3_modify_e modify);
318
        void setEncryptionParameters(char const* user_password, char const* owner_password);
319
        void setEncryptionMinimumVersion();
320
        void parseVersion(std::string const& version, int& major, int& minor) const;
321
        int compareVersions(int major1, int minor1, int major2, int minor2) const;
322
        void generateID(bool encrypted);
323
        std::string getOriginalID1();
324
        void initializeTables(size_t extra = 0);
325
        void preserveObjectStreams();
326
        void generateObjectStreams();
327
        void initializeSpecialStreams();
328
        void enqueue(QPDFObjectHandle const& object);
329
        void enqueueObjectsStandard();
330
        void enqueueObjectsPCLm();
331
        void enqueuePart(std::vector<QPDFObjectHandle>& part);
332
        void assignCompressedObjectNumbers(QPDFObjGen og);
333
        Dictionary trimmed_trailer();
334
335
        // Returns tuple<filter, compress_stream, is_root_metadata>
336
        std::tuple<const bool, const bool, const bool>
337
        will_filter_stream(QPDFObjectHandle stream, std::string* stream_data);
338
339
        // Test whether stream would be filtered if it were written.
340
        bool will_filter_stream(QPDFObjectHandle stream);
341
        unsigned int bytesNeeded(long long n);
342
        void writeBinary(unsigned long long val, unsigned int bytes);
343
        Writer& write(std::string_view str);
344
        Writer& write(size_t count, char c);
345
        Writer& write(std::integral auto val);
346
        Writer& write_name(std::string const& str);
347
        Writer& write_string(std::string const& str, bool force_binary = false);
348
        Writer& write_encrypted(std::string_view str);
349
350
        template <typename... Args>
351
        Writer& write_qdf(Args&&... args);
352
        template <typename... Args>
353
        Writer& write_no_qdf(Args&&... args);
354
        void writeObjectStreamOffsets(std::vector<qpdf_offset_t>& offsets, int first_obj);
355
        void writeObjectStream(QPDFObjectHandle object);
356
        void writeObject(QPDFObjectHandle object, int object_stream_index = -1);
357
        void writeTrailer(
358
            trailer_e which,
359
            int size,
360
            bool xref_stream,
361
            qpdf_offset_t prev,
362
            int linearization_pass);
363
        void unparseObject(
364
            QPDFObjectHandle object,
365
            size_t level,
366
            int flags,
367
            // for stream dictionaries
368
            size_t stream_length = 0,
369
            bool compress = false);
370
        void unparseChild(QPDFObjectHandle const& child, size_t level, int flags);
371
        int openObject(int objid = 0);
372
        void closeObject(int objid);
373
        void writeStandard();
374
        void writeLinearized();
375
        void writeEncryptionDictionary();
376
        void writeHeader();
377
        void writeHintStream(int hint_id);
378
        qpdf_offset_t writeXRefTable(trailer_e which, int first, int last, int size);
379
        qpdf_offset_t writeXRefTable(
380
            trailer_e which,
381
            int first,
382
            int last,
383
            int size,
384
            // for linearization
385
            qpdf_offset_t prev,
386
            bool suppress_offsets,
387
            int hint_id,
388
            qpdf_offset_t hint_offset,
389
            qpdf_offset_t hint_length,
390
            int linearization_pass);
391
        qpdf_offset_t writeXRefStream(
392
            int objid,
393
            int max_id,
394
            qpdf_offset_t max_offset,
395
            trailer_e which,
396
            int first,
397
            int last,
398
            int size);
399
        qpdf_offset_t writeXRefStream(
400
            int objid,
401
            int max_id,
402
            qpdf_offset_t max_offset,
403
            trailer_e which,
404
            int first,
405
            int last,
406
            int size,
407
            // for linearization
408
            qpdf_offset_t prev,
409
            int hint_id,
410
            qpdf_offset_t hint_offset,
411
            qpdf_offset_t hint_length,
412
            bool skip_compression,
413
            int linearization_pass);
414
415
        void setDataKey(int objid);
416
        void indicateProgress(bool decrement, bool finished);
417
        size_t calculateXrefStreamPadding(qpdf_offset_t xref_bytes);
418
419
        void adjustAESStreamLength(size_t& length);
420
        void computeDeterministicIDData();
421
422
      protected:
423
        Doc::Linearization& lin;
424
425
        qpdf::Writer::Config cfg;
426
427
        QPDFObjGen root_og{-1, 0};
428
        char const* filename{"unspecified"};
429
        FILE* file{nullptr};
430
        bool close_file{false};
431
        std::unique_ptr<Pl_Buffer> buffer_pipeline{nullptr};
432
        Buffer* output_buffer{nullptr};
433
434
        std::unique_ptr<QPDF::Doc::Encryption> encryption;
435
        std::string encryption_key;
436
437
        std::string id1; // for /ID key of
438
        std::string id2; // trailer dictionary
439
        std::string final_pdf_version;
440
        int final_extension_level{0};
441
        std::string min_pdf_version;
442
        int min_extension_level{0};
443
        int encryption_dict_objid{0};
444
        std::string cur_data_key;
445
        std::unique_ptr<Pipeline> file_pl;
446
        qpdf::pl::Count* pipeline{nullptr};
447
        std::vector<QPDFObjectHandle> object_queue;
448
        size_t object_queue_front{0};
449
        QPDFWriter::ObjTable obj;
450
        QPDFWriter::NewObjTable new_obj;
451
        int next_objid{1};
452
        int cur_stream_length_id{0};
453
        size_t cur_stream_length{0};
454
        bool added_newline{false};
455
        size_t max_ostream_index{0};
456
        std::set<QPDFObjGen> normalized_streams;
457
        std::map<QPDFObjGen, int> page_object_to_seq;
458
        std::map<QPDFObjGen, int> contents_to_page_seq;
459
        std::map<int, std::vector<QPDFObjGen>> object_stream_to_objects;
460
        Pl_stack pipeline_stack;
461
        std::string deterministic_id_data;
462
        bool did_write_setup{false};
463
464
        // For progress reporting
465
        std::shared_ptr<QPDFWriter::ProgressReporter> progress_reporter;
466
        int events_expected{0};
467
        int events_seen{0};
468
        int next_progress_report{0};
469
    }; // class qpdf::impl::Writer
470
471
} // namespace qpdf::impl
472
473
class QPDFWriter::Members: impl::Writer
474
{
475
    friend class QPDFWriter;
476
    friend class qpdf::Writer;
477
478
  public:
479
    Members(QPDFWriter& w, QPDF& qpdf) :
480
63.6k
        impl::Writer(qpdf, w)
481
63.6k
    {
482
63.6k
    }
483
};
484
485
qpdf::Writer::Writer(QPDF& qpdf, Config cfg) :
486
0
    QPDFWriter(qpdf)
487
0
{
488
0
    m->cfg = cfg;
489
0
}
490
QPDFWriter::QPDFWriter(QPDF& pdf) :
491
63.6k
    m(std::make_shared<Members>(*this, pdf))
492
63.6k
{
493
63.6k
}
494
495
QPDFWriter::QPDFWriter(QPDF& pdf, char const* filename) :
496
0
    m(std::make_shared<Members>(*this, pdf))
497
0
{
498
0
    setOutputFilename(filename);
499
0
}
500
501
QPDFWriter::QPDFWriter(QPDF& pdf, char const* description, FILE* file, bool close_file) :
502
0
    m(std::make_shared<Members>(*this, pdf))
503
0
{
504
0
    setOutputFile(description, file, close_file);
505
0
}
506
507
void
508
QPDFWriter::setOutputFilename(char const* filename)
509
0
{
510
0
    char const* description = filename;
511
0
    FILE* f = nullptr;
512
0
    bool close_file = false;
513
0
    if (filename == nullptr) {
514
0
        description = "standard output";
515
0
        f = stdout;
516
0
        QUtil::binary_stdout();
517
0
    } else {
518
0
        f = QUtil::safe_fopen(filename, "wb+");
519
0
        close_file = true;
520
0
    }
521
0
    setOutputFile(description, f, close_file);
522
0
}
523
524
void
525
QPDFWriter::setOutputFile(char const* description, FILE* file, bool close_file)
526
0
{
527
0
    m->filename = description;
528
0
    m->file = file;
529
0
    m->close_file = close_file;
530
0
    m->file_pl = std::make_unique<Pl_StdioFile>("qpdf output", file);
531
0
    m->pipeline_stack.initialize(m->file_pl.get());
532
0
}
533
534
void
535
QPDFWriter::setOutputMemory()
536
0
{
537
0
    m->filename = "memory buffer";
538
0
    m->buffer_pipeline = std::make_unique<Pl_Buffer>("qpdf output");
539
0
    m->pipeline_stack.initialize(m->buffer_pipeline.get());
540
0
}
541
542
Buffer*
543
QPDFWriter::getBuffer()
544
0
{
545
0
    Buffer* result = m->output_buffer;
546
0
    m->output_buffer = nullptr;
547
0
    return result;
548
0
}
549
550
std::shared_ptr<Buffer>
551
QPDFWriter::getBufferSharedPointer()
552
0
{
553
0
    return std::shared_ptr<Buffer>(getBuffer());
554
0
}
555
556
void
557
QPDFWriter::setOutputPipeline(Pipeline* p)
558
62.3k
{
559
62.3k
    m->filename = "custom pipeline";
560
62.3k
    m->pipeline_stack.initialize(p);
561
62.3k
}
562
563
void
564
QPDFWriter::setObjectStreamMode(qpdf_object_stream_e mode)
565
31.1k
{
566
31.1k
    m->cfg.object_streams(mode);
567
31.1k
}
568
569
void
570
QPDFWriter::setStreamDataMode(qpdf_stream_data_e mode)
571
0
{
572
0
    m->cfg.stream_data(mode);
573
0
}
574
575
Config&
576
Config::stream_data(qpdf_stream_data_e mode)
577
0
{
578
0
    switch (mode) {
579
0
    case qpdf_s_uncompress:
580
0
        decode_level(std::max(qpdf_dl_generalized, decode_level_));
581
0
        compress_streams(false);
582
0
        return *this;
583
584
0
    case qpdf_s_preserve:
585
0
        decode_level(qpdf_dl_none);
586
0
        compress_streams(false);
587
0
        return *this;
588
589
0
    case qpdf_s_compress:
590
0
        decode_level(std::max(qpdf_dl_generalized, decode_level_));
591
0
        compress_streams(true);
592
0
    }
593
0
    return *this;
594
0
}
595
596
void
597
QPDFWriter::setCompressStreams(bool val)
598
0
{
599
0
    m->cfg.compress_streams(val);
600
0
}
601
602
Config&
603
Config::compress_streams(bool val)
604
16.4k
{
605
16.4k
    if (pclm_) {
606
0
        usage("compress_streams cannot be set when pclm is set");
607
0
        return *this;
608
0
    }
609
16.4k
    compress_streams_set_ = true;
610
16.4k
    compress_streams_ = val;
611
16.4k
    return *this;
612
16.4k
}
613
614
void
615
QPDFWriter::setDecodeLevel(qpdf_stream_decode_level_e val)
616
62.3k
{
617
62.3k
    m->cfg.decode_level(val);
618
62.3k
}
619
620
Config&
621
Config::decode_level(qpdf_stream_decode_level_e val)
622
62.3k
{
623
62.3k
    if (pclm_) {
624
0
        usage("stream_decode_level cannot be set when pclm is set");
625
0
        return *this;
626
0
    }
627
62.3k
    decode_level_set_ = true;
628
62.3k
    decode_level_ = val;
629
62.3k
    return *this;
630
62.3k
}
631
632
void
633
QPDFWriter::setRecompressFlate(bool val)
634
0
{
635
0
    m->cfg.recompress_flate(val);
636
0
}
637
638
void
639
QPDFWriter::setContentNormalization(bool val)
640
0
{
641
0
    m->cfg.normalize_content(val);
642
0
}
643
644
void
645
QPDFWriter::setQDFMode(bool val)
646
16.4k
{
647
16.4k
    m->cfg.qdf(val);
648
16.4k
}
649
650
Config&
651
Config::qdf(bool val)
652
47.9k
{
653
47.9k
    if (pclm_ || linearize_) {
654
31.5k
        usage("qdf cannot be set when linearize or pclm are set");
655
31.5k
    }
656
47.9k
    if (preserve_encryption_) {
657
47.9k
        usage("preserve_encryption cannot be set when qdf is set");
658
47.9k
    }
659
47.9k
    qdf_ = val;
660
47.9k
    if (val) {
661
16.4k
        if (!normalize_content_set_) {
662
16.4k
            normalize_content(true);
663
16.4k
        }
664
16.4k
        if (!compress_streams_set_) {
665
16.4k
            compress_streams(false);
666
16.4k
        }
667
16.4k
        if (!decode_level_set_) {
668
0
            decode_level(qpdf_dl_generalized);
669
0
        }
670
16.4k
        preserve_encryption_ = false;
671
        // Generate indirect stream lengths for qdf mode since fix-qdf uses them for storing
672
        // recomputed stream length data. Certain streams such as object streams, xref streams, and
673
        // hint streams always get direct stream lengths.
674
16.4k
        direct_stream_lengths_ = false;
675
16.4k
    }
676
47.9k
    return *this;
677
47.9k
}
678
679
void
680
QPDFWriter::setPreserveUnreferencedObjects(bool val)
681
0
{
682
0
    m->cfg.preserve_unreferenced(val);
683
0
}
684
685
void
686
QPDFWriter::setNewlineBeforeEndstream(bool val)
687
0
{
688
0
    m->cfg.newline_before_endstream(val);
689
0
}
690
691
void
692
QPDFWriter::setMinimumPDFVersion(std::string const& version, int extension_level)
693
0
{
694
0
    m->setMinimumPDFVersion(version, extension_level);
695
0
}
696
697
void
698
impl::Writer::setMinimumPDFVersion(std::string const& version, int extension_level)
699
107k
{
700
107k
    bool set_version = false;
701
107k
    bool set_extension_level = false;
702
107k
    if (min_pdf_version.empty()) {
703
61.7k
        set_version = true;
704
61.7k
        set_extension_level = true;
705
61.7k
    } else {
706
45.8k
        int old_major = 0;
707
45.8k
        int old_minor = 0;
708
45.8k
        int min_major = 0;
709
45.8k
        int min_minor = 0;
710
45.8k
        parseVersion(version, old_major, old_minor);
711
45.8k
        parseVersion(min_pdf_version, min_major, min_minor);
712
45.8k
        int compare = compareVersions(old_major, old_minor, min_major, min_minor);
713
45.8k
        if (compare > 0) {
714
2.73k
            QTC::TC("qpdf", "QPDFWriter increasing minimum version", extension_level == 0 ? 0 : 1);
715
2.73k
            set_version = true;
716
2.73k
            set_extension_level = true;
717
43.0k
        } else if (compare == 0) {
718
1.83k
            if (extension_level > min_extension_level) {
719
25
                set_extension_level = true;
720
25
            }
721
1.83k
        }
722
45.8k
    }
723
724
107k
    if (set_version) {
725
64.4k
        min_pdf_version = version;
726
64.4k
    }
727
107k
    if (set_extension_level) {
728
64.4k
        min_extension_level = extension_level;
729
64.4k
    }
730
107k
}
731
732
void
733
QPDFWriter::setMinimumPDFVersion(PDFVersion const& v)
734
0
{
735
0
    std::string version;
736
0
    int extension_level;
737
0
    v.getVersion(version, extension_level);
738
0
    setMinimumPDFVersion(version, extension_level);
739
0
}
740
741
void
742
QPDFWriter::forcePDFVersion(std::string const& version, int extension_level)
743
0
{
744
0
    m->cfg.forced_pdf_version(version, extension_level);
745
0
}
746
747
void
748
QPDFWriter::setExtraHeaderText(std::string const& text)
749
0
{
750
0
    m->cfg.extra_header_text(text);
751
0
}
752
753
Config&
754
Config::extra_header_text(std::string const& val)
755
0
{
756
0
    extra_header_text_ = val;
757
0
    if (!extra_header_text_.empty() && extra_header_text_.back() != '\n') {
758
0
        extra_header_text_ += "\n";
759
0
    } else {
760
0
        QTC::TC("qpdf", "QPDFWriter extra header text no newline");
761
0
    }
762
0
    return *this;
763
0
}
764
765
void
766
QPDFWriter::setStaticID(bool val)
767
29.0k
{
768
29.0k
    m->cfg.static_id(val);
769
29.0k
}
770
771
void
772
QPDFWriter::setDeterministicID(bool val)
773
33.2k
{
774
33.2k
    m->cfg.deterministic_id(val);
775
33.2k
}
776
777
void
778
QPDFWriter::setStaticAesIV(bool val)
779
0
{
780
0
    if (val) {
781
0
        Pl_AES_PDF::useStaticIV();
782
0
    }
783
0
}
784
785
void
786
QPDFWriter::setSuppressOriginalObjectIDs(bool val)
787
0
{
788
0
    m->cfg.no_original_object_ids(val);
789
0
}
790
791
void
792
QPDFWriter::setPreserveEncryption(bool val)
793
0
{
794
0
    m->cfg.preserve_encryption(val);
795
0
}
796
797
void
798
QPDFWriter::setLinearization(bool val)
799
31.5k
{
800
31.5k
    m->cfg.linearize(val);
801
31.5k
}
802
803
Config&
804
Config::linearize(bool val)
805
31.5k
{
806
31.5k
    if (pclm_ || qdf_) {
807
0
        usage("linearize cannot be set when qdf or pclm are set");
808
0
        return *this;
809
0
    }
810
31.5k
    linearize_ = val;
811
31.5k
    return *this;
812
31.5k
}
813
814
void
815
QPDFWriter::setLinearizationPass1Filename(std::string const& filename)
816
0
{
817
0
    m->cfg.linearize_pass1(filename);
818
0
}
819
820
void
821
QPDFWriter::setPCLm(bool val)
822
0
{
823
0
    m->cfg.pclm(val);
824
0
}
825
826
Config&
827
Config::pclm(bool val)
828
0
{
829
0
    if (decode_level_set_ || compress_streams_set_ || linearize_) {
830
0
        usage(
831
0
            "pclm cannot be set when stream_decode_level, compress_streams, linearize or qdf are "
832
0
            "set");
833
0
        return *this;
834
0
    }
835
0
    pclm_ = val;
836
0
    if (val) {
837
0
        decode_level_ = qpdf_dl_none;
838
0
        compress_streams_ = false;
839
0
        linearize_ = false;
840
0
    }
841
842
0
    return *this;
843
0
}
844
845
void
846
QPDFWriter::setR2EncryptionParametersInsecure(
847
    char const* user_password,
848
    char const* owner_password,
849
    bool allow_print,
850
    bool allow_modify,
851
    bool allow_extract,
852
    bool allow_annotate)
853
0
{
854
0
    m->encryption = std::make_unique<Encryption>(1, 2, 5, true);
855
0
    if (!allow_print) {
856
0
        m->encryption->setP(3, false);
857
0
    }
858
0
    if (!allow_modify) {
859
0
        m->encryption->setP(4, false);
860
0
    }
861
0
    if (!allow_extract) {
862
0
        m->encryption->setP(5, false);
863
0
    }
864
0
    if (!allow_annotate) {
865
0
        m->encryption->setP(6, false);
866
0
    }
867
0
    m->setEncryptionParameters(user_password, owner_password);
868
0
}
869
870
void
871
QPDFWriter::setR3EncryptionParametersInsecure(
872
    char const* user_password,
873
    char const* owner_password,
874
    bool allow_accessibility,
875
    bool allow_extract,
876
    bool allow_assemble,
877
    bool allow_annotate_and_form,
878
    bool allow_form_filling,
879
    bool allow_modify_other,
880
    qpdf_r3_print_e print)
881
14.3k
{
882
14.3k
    m->encryption = std::make_unique<Encryption>(2, 3, 16, true);
883
14.3k
    m->interpretR3EncryptionParameters(
884
14.3k
        allow_accessibility,
885
14.3k
        allow_extract,
886
14.3k
        allow_assemble,
887
14.3k
        allow_annotate_and_form,
888
14.3k
        allow_form_filling,
889
14.3k
        allow_modify_other,
890
14.3k
        print,
891
14.3k
        qpdf_r3m_all);
892
14.3k
    m->setEncryptionParameters(user_password, owner_password);
893
14.3k
}
894
895
void
896
QPDFWriter::setR4EncryptionParametersInsecure(
897
    char const* user_password,
898
    char const* owner_password,
899
    bool allow_accessibility,
900
    bool allow_extract,
901
    bool allow_assemble,
902
    bool allow_annotate_and_form,
903
    bool allow_form_filling,
904
    bool allow_modify_other,
905
    qpdf_r3_print_e print,
906
    bool encrypt_metadata,
907
    bool use_aes)
908
0
{
909
0
    m->encryption = std::make_unique<Encryption>(4, 4, 16, encrypt_metadata);
910
0
    m->cfg.encrypt_use_aes(use_aes);
911
0
    m->interpretR3EncryptionParameters(
912
0
        allow_accessibility,
913
0
        allow_extract,
914
0
        allow_assemble,
915
0
        allow_annotate_and_form,
916
0
        allow_form_filling,
917
0
        allow_modify_other,
918
0
        print,
919
0
        qpdf_r3m_all);
920
0
    m->setEncryptionParameters(user_password, owner_password);
921
0
}
922
923
void
924
QPDFWriter::setR5EncryptionParameters(
925
    char const* user_password,
926
    char const* owner_password,
927
    bool allow_accessibility,
928
    bool allow_extract,
929
    bool allow_assemble,
930
    bool allow_annotate_and_form,
931
    bool allow_form_filling,
932
    bool allow_modify_other,
933
    qpdf_r3_print_e print,
934
    bool encrypt_metadata)
935
0
{
936
0
    m->encryption = std::make_unique<Encryption>(5, 5, 32, encrypt_metadata);
937
0
    m->cfg.encrypt_use_aes(true);
938
0
    m->interpretR3EncryptionParameters(
939
0
        allow_accessibility,
940
0
        allow_extract,
941
0
        allow_assemble,
942
0
        allow_annotate_and_form,
943
0
        allow_form_filling,
944
0
        allow_modify_other,
945
0
        print,
946
0
        qpdf_r3m_all);
947
0
    m->setEncryptionParameters(user_password, owner_password);
948
0
}
949
950
void
951
QPDFWriter::setR6EncryptionParameters(
952
    char const* user_password,
953
    char const* owner_password,
954
    bool allow_accessibility,
955
    bool allow_extract,
956
    bool allow_assemble,
957
    bool allow_annotate_and_form,
958
    bool allow_form_filling,
959
    bool allow_modify_other,
960
    qpdf_r3_print_e print,
961
    bool encrypt_metadata)
962
14.7k
{
963
14.7k
    m->encryption = std::make_unique<Encryption>(5, 6, 32, encrypt_metadata);
964
14.7k
    m->interpretR3EncryptionParameters(
965
14.7k
        allow_accessibility,
966
14.7k
        allow_extract,
967
14.7k
        allow_assemble,
968
14.7k
        allow_annotate_and_form,
969
14.7k
        allow_form_filling,
970
14.7k
        allow_modify_other,
971
14.7k
        print,
972
14.7k
        qpdf_r3m_all);
973
14.7k
    m->cfg.encrypt_use_aes(true);
974
14.7k
    m->setEncryptionParameters(user_password, owner_password);
975
14.7k
}
976
977
void
978
impl::Writer::interpretR3EncryptionParameters(
979
    bool allow_accessibility,
980
    bool allow_extract,
981
    bool allow_assemble,
982
    bool allow_annotate_and_form,
983
    bool allow_form_filling,
984
    bool allow_modify_other,
985
    qpdf_r3_print_e print,
986
    qpdf_r3_modify_e modify)
987
29.0k
{
988
    // Acrobat 5 security options:
989
990
    // Checkboxes:
991
    //   Enable Content Access for the Visually Impaired
992
    //   Allow Content Copying and Extraction
993
994
    // Allowed changes menu:
995
    //   None
996
    //   Only Document Assembly
997
    //   Only Form Field Fill-in or Signing
998
    //   Comment Authoring, Form Field Fill-in or Signing
999
    //   General Editing, Comment and Form Field Authoring
1000
1001
    // Allowed printing menu:
1002
    //   None
1003
    //   Low Resolution
1004
    //   Full printing
1005
1006
    // Meanings of bits in P when R >= 3
1007
    //
1008
    //  3: low-resolution printing
1009
    //  4: document modification except as controlled by 6, 9, and 11
1010
    //  5: extraction
1011
    //  6: add/modify annotations (comment), fill in forms
1012
    //     if 4+6 are set, also allows modification of form fields
1013
    //  9: fill in forms even if 6 is clear
1014
    // 10: accessibility; ignored by readers, should always be set
1015
    // 11: document assembly even if 4 is clear
1016
    // 12: high-resolution printing
1017
29.0k
    if (!allow_accessibility && encryption->getR() <= 3) {
1018
        // Bit 10 is deprecated and should always be set.  This used to mean accessibility.  There
1019
        // is no way to disable accessibility with R > 3.
1020
0
        encryption->setP(10, false);
1021
0
    }
1022
29.0k
    if (!allow_extract) {
1023
0
        encryption->setP(5, false);
1024
0
    }
1025
1026
29.0k
    switch (print) {
1027
0
    case qpdf_r3p_none:
1028
0
        encryption->setP(3, false); // any printing
1029
0
        [[fallthrough]];
1030
0
    case qpdf_r3p_low:
1031
0
        encryption->setP(12, false); // high resolution printing
1032
0
        [[fallthrough]];
1033
29.0k
    case qpdf_r3p_full:
1034
29.0k
        break;
1035
        // no default so gcc warns for missing cases
1036
29.0k
    }
1037
1038
    // Modify options. The qpdf_r3_modify_e options control groups of bits and lack the full
1039
    // flexibility of the spec. This is unfortunate, but it's been in the API for ages, and we're
1040
    // stuck with it. See also allow checks below to control the bits individually.
1041
1042
    // NOT EXERCISED IN TEST SUITE
1043
29.0k
    switch (modify) {
1044
0
    case qpdf_r3m_none:
1045
0
        encryption->setP(11, false); // document assembly
1046
0
        [[fallthrough]];
1047
0
    case qpdf_r3m_assembly:
1048
0
        encryption->setP(9, false); // filling in form fields
1049
0
        [[fallthrough]];
1050
0
    case qpdf_r3m_form:
1051
0
        encryption->setP(6, false); // modify annotations, fill in form fields
1052
0
        [[fallthrough]];
1053
0
    case qpdf_r3m_annotate:
1054
0
        encryption->setP(4, false); // other modifications
1055
0
        [[fallthrough]];
1056
29.0k
    case qpdf_r3m_all:
1057
29.0k
        break;
1058
        // no default so gcc warns for missing cases
1059
29.0k
    }
1060
    // END NOT EXERCISED IN TEST SUITE
1061
1062
29.0k
    if (!allow_assemble) {
1063
0
        encryption->setP(11, false);
1064
0
    }
1065
29.0k
    if (!allow_annotate_and_form) {
1066
0
        encryption->setP(6, false);
1067
0
    }
1068
29.0k
    if (!allow_form_filling) {
1069
0
        encryption->setP(9, false);
1070
0
    }
1071
29.0k
    if (!allow_modify_other) {
1072
0
        encryption->setP(4, false);
1073
0
    }
1074
29.0k
}
1075
1076
void
1077
impl::Writer::setEncryptionParameters(char const* user_password, char const* owner_password)
1078
29.0k
{
1079
29.0k
    generateID(true);
1080
29.0k
    encryption->setId1(id1);
1081
29.0k
    encryption_key = encryption->compute_parameters(user_password, owner_password);
1082
29.0k
    setEncryptionMinimumVersion();
1083
29.0k
}
1084
1085
void
1086
QPDFWriter::copyEncryptionParameters(QPDF& qpdf)
1087
0
{
1088
0
    m->copyEncryptionParameters(qpdf);
1089
0
}
1090
1091
void
1092
impl::Writer::copyEncryptionParameters(QPDF& qpdf)
1093
16.8k
{
1094
16.8k
    cfg.preserve_encryption(false);
1095
16.8k
    QPDFObjectHandle trailer = qpdf.getTrailer();
1096
16.8k
    if (trailer.hasKey("/Encrypt")) {
1097
128
        generateID(true);
1098
128
        id1 = trailer.getKey("/ID").getArrayItem(0).getStringValue();
1099
128
        QPDFObjectHandle encrypt = trailer.getKey("/Encrypt");
1100
128
        int V = encrypt.getKey("/V").getIntValueAsInt();
1101
128
        int key_len = 5;
1102
128
        if (V > 1) {
1103
0
            key_len = encrypt.getKey("/Length").getIntValueAsInt() / 8;
1104
0
        }
1105
128
        const bool encrypt_metadata =
1106
128
            encrypt.hasKey("/EncryptMetadata") && encrypt.getKey("/EncryptMetadata").isBool()
1107
128
            ? encrypt.getKey("/EncryptMetadata").getBoolValue()
1108
128
            : true;
1109
128
        if (V >= 4) {
1110
            // When copying encryption parameters, use AES even if the original file did not.
1111
            // Acrobat doesn't create files with V >= 4 that don't use AES, and the logic of
1112
            // figuring out whether AES is used or not is complicated with /StmF, /StrF, and /EFF
1113
            // all potentially having different values.
1114
0
            cfg.encrypt_use_aes(true);
1115
0
        }
1116
128
        QTC::TC("qpdf", "QPDFWriter copy encrypt metadata", encrypt_metadata ? 0 : 1);
1117
128
        QTC::TC("qpdf", "QPDFWriter copy use_aes", cfg.encrypt_use_aes() ? 0 : 1);
1118
1119
128
        encryption = std::make_unique<Encryption>(
1120
128
            V,
1121
128
            encrypt.getKey("/R").getIntValueAsInt(),
1122
128
            key_len,
1123
128
            static_cast<int>(encrypt.getKey("/P").getIntValue()),
1124
128
            encrypt.getKey("/O").getStringValue(),
1125
128
            encrypt.getKey("/U").getStringValue(),
1126
128
            V < 5 ? "" : encrypt.getKey("/OE").getStringValue(),
1127
128
            V < 5 ? "" : encrypt.getKey("/UE").getStringValue(),
1128
128
            V < 5 ? "" : encrypt.getKey("/Perms").getStringValue(),
1129
128
            id1, // id1 == the other file's id1
1130
128
            encrypt_metadata);
1131
128
        encryption_key = V >= 5 ? qpdf.getEncryptionKey()
1132
128
                                : encryption->compute_encryption_key(qpdf.getPaddedUserPassword());
1133
128
        setEncryptionMinimumVersion();
1134
128
    }
1135
16.8k
}
1136
1137
void
1138
impl::Writer::disableIncompatibleEncryption(int major, int minor, int extension_level)
1139
0
{
1140
0
    if (!encryption) {
1141
0
        return;
1142
0
    }
1143
0
    if (compareVersions(major, minor, 1, 3) < 0) {
1144
0
        encryption = nullptr;
1145
0
        return;
1146
0
    }
1147
0
    int V = encryption->getV();
1148
0
    int R = encryption->getR();
1149
0
    if (compareVersions(major, minor, 1, 4) < 0) {
1150
0
        if (V > 1 || R > 2) {
1151
0
            encryption = nullptr;
1152
0
        }
1153
0
    } else if (compareVersions(major, minor, 1, 5) < 0) {
1154
0
        if (V > 2 || R > 3) {
1155
0
            encryption = nullptr;
1156
0
        }
1157
0
    } else if (compareVersions(major, minor, 1, 6) < 0) {
1158
0
        if (cfg.encrypt_use_aes()) {
1159
0
            encryption = nullptr;
1160
0
        }
1161
0
    } else if (
1162
0
        (compareVersions(major, minor, 1, 7) < 0) ||
1163
0
        ((compareVersions(major, minor, 1, 7) == 0) && extension_level < 3)) {
1164
0
        if (V >= 5 || R >= 5) {
1165
0
            encryption = nullptr;
1166
0
        }
1167
0
    }
1168
1169
0
    if (!encryption) {
1170
0
        QTC::TC("qpdf", "QPDFWriter forced version disabled encryption");
1171
0
    }
1172
0
}
1173
1174
void
1175
impl::Writer::parseVersion(std::string const& version, int& major, int& minor) const
1176
91.5k
{
1177
91.5k
    major = QUtil::string_to_int(version.c_str());
1178
91.5k
    minor = 0;
1179
91.5k
    size_t p = version.find('.');
1180
91.5k
    if ((p != std::string::npos) && (version.length() > p)) {
1181
91.5k
        minor = QUtil::string_to_int(version.substr(p + 1).c_str());
1182
91.5k
    }
1183
91.5k
    std::string tmp = std::to_string(major) + "." + std::to_string(minor);
1184
91.5k
    if (tmp != version) {
1185
        // The version number in the input is probably invalid. This happens with some files that
1186
        // are designed to exercise bugs, such as files in the fuzzer corpus. Unfortunately
1187
        // QPDFWriter doesn't have a way to give a warning, so we just ignore this case.
1188
541
    }
1189
91.5k
}
1190
1191
int
1192
impl::Writer::compareVersions(int major1, int minor1, int major2, int minor2) const
1193
45.7k
{
1194
45.7k
    if (major1 < major2) {
1195
478
        return -1;
1196
478
    }
1197
45.2k
    if (major1 > major2) {
1198
615
        return 1;
1199
615
    }
1200
44.6k
    if (minor1 < minor2) {
1201
40.6k
        return -1;
1202
40.6k
    }
1203
3.95k
    return minor1 > minor2 ? 1 : 0;
1204
44.6k
}
1205
1206
void
1207
impl::Writer::setEncryptionMinimumVersion()
1208
28.9k
{
1209
28.9k
    auto const R = encryption->getR();
1210
28.9k
    if (R >= 6) {
1211
14.7k
        setMinimumPDFVersion("1.7", 8);
1212
14.7k
    } else if (R == 5) {
1213
0
        setMinimumPDFVersion("1.7", 3);
1214
14.2k
    } else if (R == 4) {
1215
0
        setMinimumPDFVersion(cfg.encrypt_use_aes() ? "1.6" : "1.5");
1216
14.2k
    } else if (R == 3) {
1217
14.2k
        setMinimumPDFVersion("1.4");
1218
14.2k
    } else {
1219
0
        setMinimumPDFVersion("1.3");
1220
0
    }
1221
28.9k
}
1222
1223
void
1224
impl::Writer::setDataKey(int objid)
1225
856k
{
1226
856k
    if (encryption) {
1227
484k
        cur_data_key = QPDF::compute_data_key(
1228
484k
            encryption_key,
1229
484k
            objid,
1230
484k
            0,
1231
484k
            cfg.encrypt_use_aes(),
1232
484k
            encryption->getV(),
1233
484k
            encryption->getR());
1234
484k
    }
1235
856k
}
1236
1237
unsigned int
1238
impl::Writer::bytesNeeded(long long n)
1239
153k
{
1240
153k
    unsigned int bytes = 0;
1241
352k
    while (n) {
1242
198k
        ++bytes;
1243
198k
        n >>= 8;
1244
198k
    }
1245
153k
    return bytes;
1246
153k
}
1247
1248
void
1249
impl::Writer::writeBinary(unsigned long long val, unsigned int bytes)
1250
2.61M
{
1251
2.61M
    if (bytes > sizeof(unsigned long long)) {
1252
0
        throw std::logic_error("QPDFWriter::writeBinary called with too many bytes");
1253
0
    }
1254
2.61M
    unsigned char data[sizeof(unsigned long long)];
1255
6.41M
    for (unsigned int i = 0; i < bytes; ++i) {
1256
3.80M
        data[bytes - i - 1] = static_cast<unsigned char>(val & 0xff);
1257
3.80M
        val >>= 8;
1258
3.80M
    }
1259
2.61M
    pipeline->write(data, bytes);
1260
2.61M
}
1261
1262
impl::Writer&
1263
impl::Writer::write(std::string_view str)
1264
51.9M
{
1265
51.9M
    pipeline->write(str);
1266
51.9M
    return *this;
1267
51.9M
}
1268
1269
impl::Writer&
1270
impl::Writer::write(std::integral auto val)
1271
5.20M
{
1272
5.20M
    pipeline->write(std::to_string(val));
1273
5.20M
    return *this;
1274
5.20M
}
_ZN4qpdf4impl6Writer5writeITkNSt3__18integralEiEERS1_T_
Line
Count
Source
1271
3.51M
{
1272
3.51M
    pipeline->write(std::to_string(val));
1273
3.51M
    return *this;
1274
3.51M
}
_ZN4qpdf4impl6Writer5writeITkNSt3__18integralExEERS1_T_
Line
Count
Source
1271
1.13M
{
1272
1.13M
    pipeline->write(std::to_string(val));
1273
1.13M
    return *this;
1274
1.13M
}
_ZN4qpdf4impl6Writer5writeITkNSt3__18integralEmEERS1_T_
Line
Count
Source
1271
396k
{
1272
396k
    pipeline->write(std::to_string(val));
1273
396k
    return *this;
1274
396k
}
_ZN4qpdf4impl6Writer5writeITkNSt3__18integralEjEERS1_T_
Line
Count
Source
1271
152k
{
1272
152k
    pipeline->write(std::to_string(val));
1273
152k
    return *this;
1274
152k
}
1275
1276
impl::Writer&
1277
impl::Writer::write(size_t count, char c)
1278
113k
{
1279
113k
    pipeline->write(count, c);
1280
113k
    return *this;
1281
113k
}
1282
1283
impl::Writer&
1284
impl::Writer::write_name(std::string const& str)
1285
3.82M
{
1286
3.82M
    pipeline->write(Name::normalize(str));
1287
3.82M
    return *this;
1288
3.82M
}
1289
1290
impl::Writer&
1291
impl::Writer::write_string(std::string const& str, bool force_binary)
1292
304k
{
1293
304k
    pipeline->write(QPDF_String(str).unparse(force_binary));
1294
304k
    return *this;
1295
304k
}
1296
1297
template <typename... Args>
1298
impl::Writer&
1299
impl::Writer::write_qdf(Args&&... args)
1300
3.03M
{
1301
3.03M
    if (cfg.qdf()) {
1302
389k
        pipeline->write(std::forward<Args>(args)...);
1303
389k
    }
1304
3.03M
    return *this;
1305
3.03M
}
qpdf::impl::Writer& qpdf::impl::Writer::write_qdf<char const (&) [2]>(char const (&) [2])
Line
Count
Source
1300
2.28M
{
1301
2.28M
    if (cfg.qdf()) {
1302
314k
        pipeline->write(std::forward<Args>(args)...);
1303
314k
    }
1304
2.28M
    return *this;
1305
2.28M
}
qpdf::impl::Writer& qpdf::impl::Writer::write_qdf<char const (&) [3]>(char const (&) [3])
Line
Count
Source
1300
542k
{
1301
542k
    if (cfg.qdf()) {
1302
43.4k
        pipeline->write(std::forward<Args>(args)...);
1303
43.4k
    }
1304
542k
    return *this;
1305
542k
}
qpdf::impl::Writer& qpdf::impl::Writer::write_qdf<char const (&) [4]>(char const (&) [4])
Line
Count
Source
1300
127k
{
1301
127k
    if (cfg.qdf()) {
1302
15.4k
        pipeline->write(std::forward<Args>(args)...);
1303
15.4k
    }
1304
127k
    return *this;
1305
127k
}
qpdf::impl::Writer& qpdf::impl::Writer::write_qdf<char const (&) [11]>(char const (&) [11])
Line
Count
Source
1300
80.4k
{
1301
80.4k
    if (cfg.qdf()) {
1302
16.1k
        pipeline->write(std::forward<Args>(args)...);
1303
16.1k
    }
1304
80.4k
    return *this;
1305
80.4k
}
1306
1307
template <typename... Args>
1308
impl::Writer&
1309
impl::Writer::write_no_qdf(Args&&... args)
1310
1.12M
{
1311
1.12M
    if (!cfg.qdf()) {
1312
1.01M
        pipeline->write(std::forward<Args>(args)...);
1313
1.01M
    }
1314
1.12M
    return *this;
1315
1.12M
}
qpdf::impl::Writer& qpdf::impl::Writer::write_no_qdf<char const (&) [2]>(char const (&) [2])
Line
Count
Source
1310
995k
{
1311
995k
    if (!cfg.qdf()) {
1312
899k
        pipeline->write(std::forward<Args>(args)...);
1313
899k
    }
1314
995k
    return *this;
1315
995k
}
qpdf::impl::Writer& qpdf::impl::Writer::write_no_qdf<char const (&) [4]>(char const (&) [4])
Line
Count
Source
1310
127k
{
1311
127k
    if (!cfg.qdf()) {
1312
112k
        pipeline->write(std::forward<Args>(args)...);
1313
112k
    }
1314
127k
    return *this;
1315
127k
}
1316
1317
void
1318
impl::Writer::adjustAESStreamLength(size_t& length)
1319
285k
{
1320
285k
    if (encryption && !cur_data_key.empty() && cfg.encrypt_use_aes()) {
1321
        // Stream length will be padded with 1 to 16 bytes to end up as a multiple of 16.  It will
1322
        // also be prepended by 16 bits of random data.
1323
92.5k
        length += 32 - (length & 0xf);
1324
92.5k
    }
1325
285k
}
1326
1327
impl::Writer&
1328
impl::Writer::write_encrypted(std::string_view str)
1329
283k
{
1330
283k
    if (!(encryption && !cur_data_key.empty())) {
1331
155k
        write(str);
1332
155k
    } else if (cfg.encrypt_use_aes()) {
1333
91.9k
        write(pl::pipe<Pl_AES_PDF>(str, true, cur_data_key));
1334
91.9k
    } else {
1335
36.8k
        write(pl::pipe<Pl_RC4>(str, cur_data_key));
1336
36.8k
    }
1337
1338
283k
    return *this;
1339
283k
}
1340
1341
void
1342
impl::Writer::computeDeterministicIDData()
1343
28.2k
{
1344
28.2k
    if (!id2.empty()) {
1345
        // Can't happen in the code
1346
0
        throw std::logic_error(
1347
0
            "Deterministic ID computation enabled after ID generation has already occurred.");
1348
0
    }
1349
28.2k
    qpdf_assert_debug(deterministic_id_data.empty());
1350
28.2k
    deterministic_id_data = pipeline_stack.hex_digest();
1351
28.2k
}
1352
1353
int
1354
impl::Writer::openObject(int objid)
1355
1.03M
{
1356
1.03M
    if (objid == 0) {
1357
13.7k
        objid = next_objid++;
1358
13.7k
    }
1359
1.03M
    new_obj[objid].xref = QPDFXRefEntry(pipeline->getCount());
1360
1.03M
    write(objid).write(" 0 obj\n");
1361
1.03M
    return objid;
1362
1.03M
}
1363
1364
void
1365
impl::Writer::closeObject(int objid)
1366
1.03M
{
1367
    // Write a newline before endobj as it makes the file easier to repair.
1368
1.03M
    write("\nendobj\n").write_qdf("\n");
1369
1.03M
    auto& no = new_obj[objid];
1370
1.03M
    no.length = pipeline->getCount() - no.xref.getOffset();
1371
1.03M
}
1372
1373
void
1374
impl::Writer::assignCompressedObjectNumbers(QPDFObjGen og)
1375
346k
{
1376
346k
    int objid = og.getObj();
1377
346k
    if (og.getGen() != 0 || !object_stream_to_objects.contains(objid)) {
1378
        // This is not an object stream.
1379
324k
        return;
1380
324k
    }
1381
1382
    // Reserve numbers for the objects that belong to this object stream.
1383
269k
    for (auto const& iter: object_stream_to_objects[objid]) {
1384
269k
        obj[iter].renumber = next_objid++;
1385
269k
    }
1386
22.6k
}
1387
1388
void
1389
impl::Writer::enqueue(QPDFObjectHandle const& object)
1390
66.3M
{
1391
66.3M
    if (object.indirect()) {
1392
2.04M
        util::assertion(
1393
            // This owner check can only be done for indirect objects. It is possible for a direct
1394
            // object to have an owning QPDF that is from another file if a direct QPDFObjectHandle
1395
            // from one file was insert into another file without copying. Doing that is safe even
1396
            // if the original QPDF gets destroyed, which just disconnects the QPDFObjectHandle from
1397
            // its owner.
1398
2.04M
            object.qpdf() == &qpdf,
1399
2.04M
            "QPDFObjectHandle from different QPDF found while writing.  "
1400
2.04M
            "Use QPDF::copyForeignObject to add objects from another file." //
1401
2.04M
        );
1402
1403
2.04M
        if (cfg.qdf() && object.isStreamOfType("/XRef")) {
1404
            // As a special case, do not output any extraneous XRef streams in QDF mode. Doing so
1405
            // will confuse fix-qdf, which expects to see only one XRef stream at the end of the
1406
            // file. This case can occur when creating a QDF from a file with object streams when
1407
            // preserving unreferenced objects since the old cross reference streams are not
1408
            // actually referenced by object number.
1409
1.24k
            return;
1410
1.24k
        }
1411
1412
2.04M
        QPDFObjGen og = object.getObjGen();
1413
2.04M
        auto& o = obj[og];
1414
1415
2.04M
        if (o.renumber == 0) {
1416
609k
            if (o.object_stream > 0) {
1417
                // This is in an object stream.  Don't process it here.  Instead, enqueue the object
1418
                // stream.  Object streams always have generation 0.
1419
                // Detect loops by storing invalid object ID -1, which will get overwritten later.
1420
5.80k
                o.renumber = -1;
1421
5.80k
                enqueue(qpdf.getObject(o.object_stream, 0));
1422
603k
            } else {
1423
603k
                object_queue.emplace_back(object);
1424
603k
                o.renumber = next_objid++;
1425
1426
603k
                if (og.getGen() == 0 && object_stream_to_objects.contains(og.getObj())) {
1427
                    // For linearized files, uncompressed objects go at end, and we take care of
1428
                    // assigning numbers to them elsewhere.
1429
22.3k
                    if (!cfg.linearize()) {
1430
3.85k
                        assignCompressedObjectNumbers(og);
1431
3.85k
                    }
1432
581k
                } else if (!cfg.direct_stream_lengths() && object.isStream()) {
1433
                    // reserve next object ID for length
1434
40.7k
                    ++next_objid;
1435
40.7k
                }
1436
603k
            }
1437
609k
        }
1438
2.04M
        return;
1439
2.04M
    }
1440
1441
64.2M
    if (cfg.linearize()) {
1442
583
        return;
1443
583
    }
1444
1445
64.2M
    if (Array array = object) {
1446
52.6M
        for (auto& item: array) {
1447
52.6M
            enqueue(item);
1448
52.6M
        }
1449
1.70M
        return;
1450
1.70M
    }
1451
1452
62.5M
    for (auto const& item: Dictionary(object)) {
1453
4.08M
        if (!item.second.null()) {
1454
3.71M
            enqueue(item.second);
1455
3.71M
        }
1456
4.08M
    }
1457
62.5M
}
1458
1459
void
1460
impl::Writer::unparseChild(QPDFObjectHandle const& child, size_t level, int flags)
1461
16.5M
{
1462
16.5M
    if (!cfg.linearize()) {
1463
9.54M
        enqueue(child);
1464
9.54M
    }
1465
16.5M
    if (child.indirect()) {
1466
1.78M
        write(obj[child].renumber).write(" 0 R");
1467
14.7M
    } else {
1468
14.7M
        unparseObject(child, level, flags);
1469
14.7M
    }
1470
16.5M
}
1471
1472
void
1473
impl::Writer::writeTrailer(
1474
    trailer_e which, int size, bool xref_stream, qpdf_offset_t prev, int linearization_pass)
1475
128k
{
1476
128k
    auto trailer = trimmed_trailer();
1477
128k
    if (xref_stream) {
1478
51.1k
        cur_data_key.clear();
1479
77.3k
    } else {
1480
77.3k
        write("trailer <<");
1481
77.3k
    }
1482
128k
    write_qdf("\n");
1483
128k
    if (which == t_lin_second) {
1484
48.6k
        write(" /Size ").write(size);
1485
79.8k
    } else {
1486
175k
        for (auto const& [key, value]: trailer) {
1487
175k
            if (value.null()) {
1488
34.4k
                continue;
1489
34.4k
            }
1490
141k
            write_qdf("  ").write_no_qdf(" ").write_name(key).write(" ");
1491
141k
            if (key == "/Size") {
1492
15.8k
                write(size);
1493
15.8k
                if (which == t_lin_first) {
1494
9.22k
                    write(" /Prev ");
1495
9.22k
                    qpdf_offset_t pos = pipeline->getCount();
1496
9.22k
                    write(prev).write(QIntC::to_size(pos - pipeline->getCount() + 21), ' ');
1497
9.22k
                }
1498
125k
            } else {
1499
125k
                unparseChild(value, 1, 0);
1500
125k
            }
1501
141k
            write_qdf("\n");
1502
141k
        }
1503
79.8k
    }
1504
1505
    // Write ID
1506
128k
    write_qdf(" ").write(" /ID [");
1507
128k
    if (linearization_pass == 1) {
1508
50.3k
        std::string original_id1 = getOriginalID1();
1509
50.3k
        if (original_id1.empty()) {
1510
46.1k
            write("<00000000000000000000000000000000>");
1511
46.1k
        } else {
1512
            // Write a string of zeroes equal in length to the representation of the original ID.
1513
            // While writing the original ID would have the same number of bytes, it would cause a
1514
            // change to the deterministic ID generated by older versions of the software that
1515
            // hard-coded the length of the ID to 16 bytes.
1516
4.15k
            size_t len = QPDF_String(original_id1).unparse(true).length() - 2;
1517
4.15k
            write("<").write(len, '0').write(">");
1518
4.15k
        }
1519
50.3k
        write("<00000000000000000000000000000000>");
1520
78.1k
    } else {
1521
78.1k
        if (linearization_pass == 0 && cfg.deterministic_id()) {
1522
15.8k
            computeDeterministicIDData();
1523
15.8k
        }
1524
78.1k
        generateID(encryption.get());
1525
78.1k
        write_string(id1, true).write_string(id2, true);
1526
78.1k
    }
1527
128k
    write("]");
1528
1529
128k
    if (which != t_lin_second) {
1530
        // Write reference to encryption dictionary
1531
79.2k
        if (encryption) {
1532
38.1k
            write(" /Encrypt ").write(encryption_dict_objid).write(" 0 R");
1533
38.1k
        }
1534
79.2k
    }
1535
1536
128k
    write_qdf("\n>>").write_no_qdf(" >>");
1537
128k
}
1538
1539
bool
1540
impl::Writer::will_filter_stream(QPDFObjectHandle stream)
1541
82.4k
{
1542
82.4k
    std::string s;
1543
82.4k
    [[maybe_unused]] auto [filter, ignore1, ignore2] = will_filter_stream(stream, &s);
1544
82.4k
    return filter;
1545
82.4k
}
1546
1547
std::tuple<const bool, const bool, const bool>
1548
impl::Writer::will_filter_stream(QPDFObjectHandle stream, std::string* stream_data)
1549
307k
{
1550
307k
    const bool is_root_metadata = stream.isRootMetadata();
1551
307k
    bool filter = false;
1552
307k
    auto decode_level = cfg.decode_level();
1553
307k
    int encode_flags = 0;
1554
307k
    Dictionary stream_dict = stream.getDict();
1555
1556
307k
    if (stream.getFilterOnWrite()) {
1557
256k
        filter = stream.isDataModified() || cfg.compress_streams() || decode_level != qpdf_dl_none;
1558
256k
        if (cfg.compress_streams()) {
1559
            // Don't filter if the stream is already compressed with FlateDecode. This way we don't
1560
            // make it worse if the original file used a better Flate algorithm, and we don't spend
1561
            // time and CPU cycles uncompressing and recompressing stuff. This can be overridden
1562
            // with setRecompressFlate(true).
1563
216k
            Name Filter = stream_dict["/Filter"];
1564
216k
            if (Filter && !cfg.recompress_flate() && !stream.isDataModified() &&
1565
80.2k
                (Filter == "/FlateDecode" || Filter == "/Fl")) {
1566
41.6k
                filter = false;
1567
41.6k
            }
1568
216k
        }
1569
256k
        if (is_root_metadata && (!encryption || !encryption->getEncryptMetadata())) {
1570
572
            filter = true;
1571
572
            decode_level = qpdf_dl_all;
1572
255k
        } else if (cfg.normalize_content() && normalized_streams.contains(stream)) {
1573
5.50k
            encode_flags = qpdf_ef_normalize;
1574
5.50k
            filter = true;
1575
250k
        } else if (filter && cfg.compress_streams()) {
1576
173k
            encode_flags = qpdf_ef_compress;
1577
173k
        }
1578
256k
    }
1579
1580
    // Disable compression for empty streams to improve compatibility
1581
307k
    if (Integer(stream_dict["/Length"]) == 0) {
1582
14.8k
        filter = true;
1583
14.8k
        encode_flags = 0;
1584
14.8k
    }
1585
1586
384k
    for (bool first_attempt: {true, false}) {
1587
384k
        auto pp_stream_data =
1588
384k
            stream_data ? pipeline_stack.activate(*stream_data) : pipeline_stack.activate(true);
1589
1590
384k
        try {
1591
384k
            if (stream.pipeStreamData(
1592
384k
                    pipeline,
1593
384k
                    filter ? encode_flags : 0,
1594
384k
                    filter ? decode_level : qpdf_dl_none,
1595
384k
                    false,
1596
384k
                    first_attempt)) {
1597
147k
                return {true, encode_flags & qpdf_ef_compress, is_root_metadata};
1598
147k
            }
1599
237k
            if (!filter) {
1600
159k
                break;
1601
159k
            }
1602
237k
        } catch (std::runtime_error& e) {
1603
644
            if (!(filter && first_attempt)) {
1604
118
                throw std::runtime_error(
1605
118
                    "error while getting stream data for " + stream.unparse() + ": " + e.what());
1606
118
            }
1607
526
            stream.warn("error while getting stream data: "s + e.what());
1608
526
            stream.warn("qpdf will attempt to write the damaged stream unchanged");
1609
526
        }
1610
        // Try again
1611
77.4k
        filter = false;
1612
77.4k
        stream.setFilterOnWrite(false);
1613
77.4k
        if (stream_data) {
1614
77.4k
            stream_data->clear();
1615
77.4k
        }
1616
77.4k
    }
1617
159k
    return {false, false, is_root_metadata};
1618
307k
}
1619
1620
void
1621
impl::Writer::unparseObject(
1622
    QPDFObjectHandle object, size_t level, int flags, size_t stream_length, bool compress)
1623
16.2M
{
1624
16.2M
    QPDFObjGen old_og = object.getObjGen();
1625
16.2M
    int child_flags = flags & ~f_stream;
1626
    // For non-qdf, "indent" and "indent_large" are a single space between tokens. For qdf, they
1627
    // include the preceding newline.
1628
16.2M
    std::string indent_large = " ";
1629
16.2M
    if (cfg.qdf()) {
1630
8.63M
        indent_large.append(2 * (level + 1), ' ');
1631
8.63M
        indent_large[0] = '\n';
1632
8.63M
    }
1633
16.2M
    std::string_view indent{indent_large.data(), cfg.qdf() ? indent_large.size() - 2 : 1};
1634
1635
16.2M
    if (auto const tc = object.getTypeCode(); tc == ::ot_array) {
1636
        // Note: PDF spec 1.4 implementation note 121 states that Acrobat requires a space after the
1637
        // [ in the /H key of the linearization parameter dictionary.  We'll do this unconditionally
1638
        // for all arrays because it looks nicer and doesn't make the files that much bigger.
1639
590k
        write("[");
1640
12.7M
        for (auto const& item: object.as_array()) {
1641
12.7M
            write(indent_large);
1642
12.7M
            unparseChild(item, level + 1, child_flags);
1643
12.7M
        }
1644
590k
        write(indent).write("]");
1645
15.6M
    } else if (tc == ::ot_dictionary) {
1646
        // Handle special cases for specific dictionaries.
1647
1648
1.21M
        if (old_og == root_og) {
1649
            // Extensions dictionaries.
1650
1651
            // We have one of several cases:
1652
            //
1653
            // * We need ADBE
1654
            //    - We already have Extensions
1655
            //       - If it has the right ADBE, preserve it
1656
            //       - Otherwise, replace ADBE
1657
            //    - We don't have Extensions: create one from scratch
1658
            // * We don't want ADBE
1659
            //    - We already have Extensions
1660
            //       - If it only has ADBE, remove it
1661
            //       - If it has other things, keep those and remove ADBE
1662
            //    - We have no extensions: no action required
1663
            //
1664
            // Before writing, we guarantee that /Extensions, if present, is direct through the ADBE
1665
            // dictionary, so we can modify in place.
1666
1667
77.7k
            auto extensions = object.getKey("/Extensions");
1668
77.7k
            const bool has_extensions = extensions.isDictionary();
1669
77.7k
            const bool need_extensions_adbe = final_extension_level > 0;
1670
1671
77.7k
            if (has_extensions || need_extensions_adbe) {
1672
                // Make a shallow copy of this object so we can modify it safely without affecting
1673
                // the original. This code has logic to skip certain keys in agreement with
1674
                // prepareFileForWrite and with skip_stream_parameters so that replacing them
1675
                // doesn't leave unreferenced objects in the output. We can use unsafeShallowCopy
1676
                // here because all we are doing is removing or replacing top-level keys.
1677
26.2k
                object = object.unsafeShallowCopy();
1678
26.2k
                if (!has_extensions) {
1679
22.7k
                    extensions = QPDFObjectHandle();
1680
22.7k
                }
1681
1682
26.2k
                const bool have_extensions_adbe = extensions && extensions.hasKey("/ADBE");
1683
26.2k
                const bool have_extensions_other =
1684
26.2k
                    extensions && extensions.getKeys().size() > (have_extensions_adbe ? 1u : 0u);
1685
1686
26.2k
                if (need_extensions_adbe) {
1687
23.9k
                    if (!(have_extensions_other || have_extensions_adbe)) {
1688
                        // We need Extensions and don't have it.  Create it here.
1689
22.7k
                        QTC::TC("qpdf", "QPDFWriter create Extensions", cfg.qdf() ? 0 : 1);
1690
22.7k
                        extensions = object.replaceKeyAndGetNew(
1691
22.7k
                            "/Extensions", QPDFObjectHandle::newDictionary());
1692
22.7k
                    }
1693
23.9k
                } else if (!have_extensions_other) {
1694
                    // We have Extensions dictionary and don't want one.
1695
1.14k
                    if (have_extensions_adbe) {
1696
981
                        QTC::TC("qpdf", "QPDFWriter remove existing Extensions");
1697
981
                        object.removeKey("/Extensions");
1698
981
                        extensions = QPDFObjectHandle(); // uninitialized
1699
981
                    }
1700
1.14k
                }
1701
1702
26.2k
                if (extensions) {
1703
25.2k
                    QTC::TC("qpdf", "QPDFWriter preserve Extensions");
1704
25.2k
                    QPDFObjectHandle adbe = extensions.getKey("/ADBE");
1705
25.2k
                    if (adbe.isDictionary() &&
1706
1.13k
                        adbe.getKey("/BaseVersion").isNameAndEquals("/" + final_pdf_version) &&
1707
690
                        adbe.getKey("/ExtensionLevel").isInteger() &&
1708
681
                        (adbe.getKey("/ExtensionLevel").getIntValue() == final_extension_level)) {
1709
24.7k
                    } else {
1710
24.7k
                        if (need_extensions_adbe) {
1711
23.3k
                            extensions.replaceKey(
1712
23.3k
                                "/ADBE",
1713
23.3k
                                QPDFObjectHandle::parse(
1714
23.3k
                                    "<< /BaseVersion /" + final_pdf_version + " /ExtensionLevel " +
1715
23.3k
                                    std::to_string(final_extension_level) + " >>"));
1716
23.3k
                        } else {
1717
1.38k
                            extensions.removeKey("/ADBE");
1718
1.38k
                        }
1719
24.7k
                    }
1720
25.2k
                }
1721
26.2k
            }
1722
77.7k
        }
1723
1724
        // Stream dictionaries.
1725
1726
1.21M
        if (flags & f_stream) {
1727
            // Suppress /Length since we will write it manually
1728
1729
            // Make a shallow copy of this object so we can modify it safely without affecting the
1730
            // original. This code has logic to skip certain keys in agreement with
1731
            // prepareFileForWrite and with skip_stream_parameters so that replacing them doesn't
1732
            // leave unreferenced objects in the output. We can use unsafeShallowCopy here because
1733
            // all we are doing is removing or replacing top-level keys.
1734
224k
            object = object.unsafeShallowCopy();
1735
1736
224k
            object.removeKey("/Length");
1737
1738
            // If /DecodeParms is an empty list, remove it.
1739
224k
            if (object.getKey("/DecodeParms").empty()) {
1740
213k
                object.removeKey("/DecodeParms");
1741
213k
            }
1742
1743
224k
            if (flags & f_filtered) {
1744
                // We will supply our own filter and decode parameters.
1745
106k
                object.removeKey("/Filter");
1746
106k
                object.removeKey("/DecodeParms");
1747
118k
            } else {
1748
                // Make sure, no matter what else we have, that we don't have /Crypt in the output
1749
                // filters.
1750
118k
                QPDFObjectHandle filter = object.getKey("/Filter");
1751
118k
                QPDFObjectHandle decode_parms = object.getKey("/DecodeParms");
1752
118k
                if (filter.isOrHasName("/Crypt")) {
1753
2.49k
                    if (filter.isName()) {
1754
267
                        object.removeKey("/Filter");
1755
267
                        object.removeKey("/DecodeParms");
1756
2.22k
                    } else {
1757
2.22k
                        int idx = 0;
1758
87.0k
                        for (auto const& item: filter.as_array()) {
1759
87.0k
                            if (item.isNameAndEquals("/Crypt")) {
1760
                                // If filter is an array, then the code in QPDF_Stream has already
1761
                                // verified that DecodeParms and Filters are arrays of the same
1762
                                // length, but if they weren't for some reason, eraseItem does type
1763
                                // and bounds checking. Fuzzing tells us that this can actually
1764
                                // happen.
1765
2.22k
                                filter.eraseItem(idx);
1766
2.22k
                                decode_parms.eraseItem(idx);
1767
2.22k
                                break;
1768
2.22k
                            }
1769
84.7k
                            ++idx;
1770
84.7k
                        }
1771
2.22k
                    }
1772
2.49k
                }
1773
118k
            }
1774
224k
        }
1775
1776
1.21M
        write("<<");
1777
1778
4.34M
        for (auto const& [key, value]: object.as_dictionary()) {
1779
4.34M
            if (!value.null()) {
1780
3.68M
                write(indent_large).write_name(key).write(" ");
1781
3.68M
                if (key == "/Contents" && object.isDictionaryOfType("/Sig") &&
1782
351
                    object.hasKey("/ByteRange")) {
1783
315
                    QTC::TC("qpdf", "QPDFWriter no encryption sig contents");
1784
315
                    unparseChild(value, level + 1, child_flags | f_hex_string | f_no_encryption);
1785
3.68M
                } else {
1786
3.68M
                    unparseChild(value, level + 1, child_flags);
1787
3.68M
                }
1788
3.68M
            }
1789
4.34M
        }
1790
1791
1.21M
        if (flags & f_stream) {
1792
223k
            write(indent_large).write("/Length ");
1793
1794
223k
            if (cfg.direct_stream_lengths()) {
1795
183k
                write(stream_length);
1796
183k
            } else {
1797
40.0k
                write(cur_stream_length_id).write(" 0 R");
1798
40.0k
            }
1799
223k
            if (compress && (flags & f_filtered)) {
1800
86.2k
                write(indent_large).write("/Filter /FlateDecode");
1801
86.2k
            }
1802
223k
        }
1803
1804
1.21M
        write(indent).write(">>");
1805
14.4M
    } else if (tc == ::ot_stream) {
1806
        // Write stream data to a buffer.
1807
224k
        if (!cfg.direct_stream_lengths()) {
1808
40.2k
            cur_stream_length_id = obj[old_og].renumber + 1;
1809
40.2k
        }
1810
1811
224k
        flags |= f_stream;
1812
224k
        std::string stream_data;
1813
224k
        auto [filter, compress_stream, is_root_metadata] = will_filter_stream(object, &stream_data);
1814
224k
        if (filter) {
1815
106k
            flags |= f_filtered;
1816
106k
        }
1817
224k
        QPDFObjectHandle stream_dict = object.getDict();
1818
1819
224k
        cur_stream_length = stream_data.size();
1820
224k
        if (is_root_metadata && encryption && !encryption->getEncryptMetadata()) {
1821
            // Don't encrypt stream data for the metadata stream
1822
0
            cur_data_key.clear();
1823
0
        }
1824
224k
        adjustAESStreamLength(cur_stream_length);
1825
224k
        unparseObject(stream_dict, 0, flags, cur_stream_length, compress_stream);
1826
224k
        char last_char = stream_data.empty() ? '\0' : stream_data.back();
1827
224k
        write("\nstream\n").write_encrypted(stream_data);
1828
224k
        added_newline = cfg.newline_before_endstream() || (cfg.qdf() && last_char != '\n');
1829
224k
        write(added_newline ? "\nendstream" : "endstream");
1830
14.2M
    } else if (tc == ::ot_string) {
1831
278k
        std::string val;
1832
278k
        if (encryption && !(flags & f_in_ostream) && !(flags & f_no_encryption) &&
1833
115k
            !cur_data_key.empty()) {
1834
86.2k
            val = object.getStringValue();
1835
86.2k
            if (cfg.encrypt_use_aes()) {
1836
64.7k
                Pl_Buffer bufpl("encrypted string");
1837
64.7k
                Pl_AES_PDF pl("aes encrypt string", &bufpl, true, cur_data_key);
1838
64.7k
                pl.writeString(val);
1839
64.7k
                pl.finish();
1840
64.7k
                val = QPDF_String(bufpl.getString()).unparse(true);
1841
64.7k
            } else {
1842
21.5k
                auto tmp_ph = QUtil::make_unique_cstr(val);
1843
21.5k
                char* tmp = tmp_ph.get();
1844
21.5k
                size_t vlen = val.length();
1845
21.5k
                RC4 rc4(
1846
21.5k
                    QUtil::unsigned_char_pointer(cur_data_key),
1847
21.5k
                    QIntC::to_int(cur_data_key.length()));
1848
21.5k
                auto data = QUtil::unsigned_char_pointer(tmp);
1849
21.5k
                rc4.process(data, vlen, data);
1850
21.5k
                val = QPDF_String(std::string(tmp, vlen)).unparse();
1851
21.5k
            }
1852
191k
        } else if (flags & f_hex_string) {
1853
369
            val = QPDF_String(object.getStringValue()).unparse(true);
1854
191k
        } else {
1855
191k
            val = object.unparseResolved();
1856
191k
        }
1857
278k
        write(val);
1858
13.9M
    } else {
1859
13.9M
        write(object.unparseResolved());
1860
13.9M
    }
1861
16.2M
}
1862
1863
void
1864
impl::Writer::writeObjectStreamOffsets(std::vector<qpdf_offset_t>& offsets, int first_obj)
1865
72.9k
{
1866
72.9k
    qpdf_assert_debug(first_obj > 0);
1867
72.9k
    bool is_first = true;
1868
72.9k
    auto id = std::to_string(first_obj) + ' ';
1869
890k
    for (auto& offset: offsets) {
1870
890k
        if (is_first) {
1871
72.9k
            is_first = false;
1872
817k
        } else {
1873
817k
            write_qdf("\n").write_no_qdf(" ");
1874
817k
        }
1875
890k
        write(id);
1876
890k
        util::increment(id, 1);
1877
890k
        write(offset);
1878
890k
    }
1879
72.9k
    write("\n");
1880
72.9k
}
1881
1882
void
1883
impl::Writer::writeObjectStream(QPDFObjectHandle object)
1884
36.4k
{
1885
    // Note: object might be null if this is a place-holder for an object stream that we are
1886
    // generating from scratch.
1887
1888
36.4k
    QPDFObjGen old_og = object.getObjGen();
1889
36.4k
    qpdf_assert_debug(old_og.getGen() == 0);
1890
36.4k
    int old_id = old_og.getObj();
1891
36.4k
    int new_stream_id = obj[old_og].renumber;
1892
1893
36.4k
    std::vector<qpdf_offset_t> offsets;
1894
36.4k
    qpdf_offset_t first = 0;
1895
1896
    // Generate stream itself.  We have to do this in two passes so we can calculate offsets in the
1897
    // first pass.
1898
36.4k
    std::string stream_buffer_pass1;
1899
36.4k
    std::string stream_buffer_pass2;
1900
36.4k
    int first_obj = -1;
1901
36.4k
    const bool compressed = cfg.compress_streams() && !cfg.qdf();
1902
36.4k
    {
1903
        // Pass 1
1904
36.4k
        auto pp_ostream_pass1 = pipeline_stack.activate(stream_buffer_pass1);
1905
1906
36.4k
        int count = -1;
1907
445k
        for (auto const& og: object_stream_to_objects[old_id]) {
1908
445k
            ++count;
1909
445k
            int new_o = obj[og].renumber;
1910
445k
            if (first_obj == -1) {
1911
36.4k
                first_obj = new_o;
1912
36.4k
            }
1913
445k
            if (cfg.qdf()) {
1914
36.9k
                write("%% Object stream: object ").write(new_o).write(", index ").write(count);
1915
36.9k
                if (!cfg.no_original_object_ids()) {
1916
36.9k
                    write("; original object ID: ").write(og.getObj());
1917
                    // For compatibility, only write the generation if non-zero.  While object
1918
                    // streams only allow objects with generation 0, if we are generating object
1919
                    // streams, the old object could have a non-zero generation.
1920
36.9k
                    if (og.getGen() != 0) {
1921
0
                        write(" ").write(og.getGen());
1922
0
                    }
1923
36.9k
                }
1924
36.9k
                write("\n");
1925
36.9k
            }
1926
1927
445k
            offsets.push_back(pipeline->getCount());
1928
            // To avoid double-counting objects being written in object streams for progress
1929
            // reporting, decrement in pass 1.
1930
445k
            indicateProgress(true, false);
1931
1932
445k
            QPDFObjectHandle obj_to_write = qpdf.getObject(og);
1933
445k
            if (obj_to_write.isStream()) {
1934
                // This condition occurred in a fuzz input. Ideally we should block it at parse
1935
                // time, but it's not clear to me how to construct a case for this.
1936
1
                obj_to_write.warn("stream found inside object stream; treating as null");
1937
1
                obj_to_write = QPDFObjectHandle::newNull();
1938
1
            }
1939
445k
            writeObject(obj_to_write, count);
1940
1941
445k
            new_obj[new_o].xref = QPDFXRefEntry(new_stream_id, count);
1942
445k
        }
1943
36.4k
    }
1944
36.4k
    {
1945
        // Adjust offsets to skip over comment before first object
1946
36.4k
        first = offsets.at(0);
1947
445k
        for (auto& iter: offsets) {
1948
445k
            iter -= first;
1949
445k
        }
1950
1951
        // Take one pass at writing pairs of numbers so we can get their size information
1952
36.4k
        {
1953
36.4k
            auto pp_discard = pipeline_stack.activate(true);
1954
36.4k
            writeObjectStreamOffsets(offsets, first_obj);
1955
36.4k
            first += pipeline->getCount();
1956
36.4k
        }
1957
1958
        // Set up a stream to write the stream data into a buffer.
1959
36.4k
        auto pp_ostream = pipeline_stack.activate(stream_buffer_pass2);
1960
1961
36.4k
        writeObjectStreamOffsets(offsets, first_obj);
1962
36.4k
        write(stream_buffer_pass1);
1963
36.4k
        stream_buffer_pass1.clear();
1964
36.4k
        stream_buffer_pass1.shrink_to_fit();
1965
36.4k
        if (compressed) {
1966
32.6k
            stream_buffer_pass2 = pl::pipe<Pl_Flate>(stream_buffer_pass2, Pl_Flate::a_deflate);
1967
32.6k
        }
1968
36.4k
    }
1969
1970
    // Write the object
1971
36.4k
    openObject(new_stream_id);
1972
36.4k
    setDataKey(new_stream_id);
1973
36.4k
    write("<<").write_qdf("\n ").write(" /Type /ObjStm").write_qdf("\n ");
1974
36.4k
    size_t length = stream_buffer_pass2.size();
1975
36.4k
    adjustAESStreamLength(length);
1976
36.4k
    write(" /Length ").write(length).write_qdf("\n ");
1977
36.4k
    if (compressed) {
1978
32.6k
        write(" /Filter /FlateDecode");
1979
32.6k
    }
1980
36.4k
    write(" /N ").write(offsets.size()).write_qdf("\n ").write(" /First ").write(first);
1981
36.4k
    if (!object.null()) {
1982
        // If the original object has an /Extends key, preserve it.
1983
3.54k
        QPDFObjectHandle dict = object.getDict();
1984
3.54k
        QPDFObjectHandle extends = dict.getKey("/Extends");
1985
3.54k
        if (extends.isIndirect()) {
1986
1.10k
            write_qdf("\n ").write(" /Extends ");
1987
1.10k
            unparseChild(extends, 1, f_in_ostream);
1988
1.10k
        }
1989
3.54k
    }
1990
36.4k
    write_qdf("\n").write_no_qdf(" ").write(">>\nstream\n").write_encrypted(stream_buffer_pass2);
1991
36.4k
    write(cfg.newline_before_endstream() ? "\nendstream" : "endstream");
1992
36.4k
    if (encryption) {
1993
6.91k
        cur_data_key.clear();
1994
6.91k
    }
1995
36.4k
    closeObject(new_stream_id);
1996
36.4k
}
1997
1998
void
1999
impl::Writer::writeObject(QPDFObjectHandle object, int object_stream_index)
2000
1.27M
{
2001
1.27M
    QPDFObjGen old_og = object.getObjGen();
2002
2003
1.27M
    if (object_stream_index == -1 && old_og.getGen() == 0 &&
2004
823k
        object_stream_to_objects.contains(old_og.getObj())) {
2005
36.4k
        writeObjectStream(object);
2006
36.4k
        return;
2007
36.4k
    }
2008
2009
1.24M
    indicateProgress(false, false);
2010
1.24M
    auto new_id = obj[old_og].renumber;
2011
1.24M
    if (cfg.qdf()) {
2012
179k
        if (page_object_to_seq.contains(old_og)) {
2013
18.9k
            write("%% Page ").write(page_object_to_seq[old_og]).write("\n");
2014
18.9k
        }
2015
179k
        if (contents_to_page_seq.contains(old_og)) {
2016
15.0k
            write("%% Contents for page ").write(contents_to_page_seq[old_og]).write("\n");
2017
15.0k
        }
2018
179k
    }
2019
1.24M
    if (object_stream_index == -1) {
2020
795k
        if (cfg.qdf() && !cfg.no_original_object_ids()) {
2021
142k
            write("%% Original object ID: ").write(object.getObjGen().unparse(' ')).write("\n");
2022
142k
        }
2023
795k
        openObject(new_id);
2024
795k
        setDataKey(new_id);
2025
795k
        unparseObject(object, 0, 0);
2026
795k
        cur_data_key.clear();
2027
795k
        closeObject(new_id);
2028
795k
    } else {
2029
445k
        unparseObject(object, 0, f_in_ostream);
2030
445k
        write("\n");
2031
445k
    }
2032
2033
1.24M
    if (!cfg.direct_stream_lengths() && object.isStream()) {
2034
40.0k
        if (cfg.qdf()) {
2035
40.0k
            if (added_newline) {
2036
23.4k
                write("%QDF: ignore_newline\n");
2037
23.4k
            }
2038
40.0k
        }
2039
40.0k
        openObject(new_id + 1);
2040
40.0k
        write(cur_stream_length);
2041
40.0k
        closeObject(new_id + 1);
2042
40.0k
    }
2043
1.24M
}
2044
2045
std::string
2046
impl::Writer::getOriginalID1()
2047
107k
{
2048
107k
    QPDFObjectHandle trailer = qpdf.getTrailer();
2049
107k
    if (trailer.hasKey("/ID")) {
2050
9.72k
        return trailer.getKey("/ID").getArrayItem(0).getStringValue();
2051
97.9k
    } else {
2052
97.9k
        return "";
2053
97.9k
    }
2054
107k
}
2055
2056
void
2057
impl::Writer::generateID(bool encrypted)
2058
107k
{
2059
    // Generate the ID lazily so that we can handle the user's preference to use static or
2060
    // deterministic ID generation.
2061
2062
107k
    if (!id2.empty()) {
2063
49.8k
        return;
2064
49.8k
    }
2065
2066
57.4k
    QPDFObjectHandle trailer = qpdf.getTrailer();
2067
2068
57.4k
    std::string result;
2069
2070
57.4k
    if (cfg.static_id()) {
2071
        // For test suite use only...
2072
29.0k
        static unsigned char tmp[] = {
2073
29.0k
            0x31,
2074
29.0k
            0x41,
2075
29.0k
            0x59,
2076
29.0k
            0x26,
2077
29.0k
            0x53,
2078
29.0k
            0x58,
2079
29.0k
            0x97,
2080
29.0k
            0x93,
2081
29.0k
            0x23,
2082
29.0k
            0x84,
2083
29.0k
            0x62,
2084
29.0k
            0x64,
2085
29.0k
            0x33,
2086
29.0k
            0x83,
2087
29.0k
            0x27,
2088
29.0k
            0x95,
2089
29.0k
            0x00};
2090
29.0k
        result = reinterpret_cast<char*>(tmp);
2091
29.0k
    } else {
2092
        // The PDF specification has guidelines for creating IDs, but it states clearly that the
2093
        // only thing that's really important is that it is very likely to be unique.  We can't
2094
        // really follow the guidelines in the spec exactly because we haven't written the file yet.
2095
        // This scheme should be fine though.  The deterministic ID case uses a digest of a
2096
        // sufficient portion of the file's contents such no two non-matching files would match in
2097
        // the subsets used for this computation.  Note that we explicitly omit the filename from
2098
        // the digest calculation for deterministic ID so that the same file converted with qpdf, in
2099
        // that case, would have the same ID regardless of the output file's name.
2100
2101
28.4k
        std::string seed;
2102
28.4k
        if (cfg.deterministic_id()) {
2103
28.4k
            if (encrypted) {
2104
128
                throw std::runtime_error(
2105
128
                    "QPDFWriter: unable to generated a deterministic ID because the file to be "
2106
128
                    "written is encrypted (even though the file may not require a password)");
2107
128
            }
2108
28.2k
            if (deterministic_id_data.empty()) {
2109
0
                throw std::logic_error(
2110
0
                    "INTERNAL ERROR: QPDFWriter::generateID has no data for deterministic ID");
2111
0
            }
2112
28.2k
            seed += deterministic_id_data;
2113
28.2k
        } else {
2114
0
            seed += std::to_string(QUtil::get_current_time());
2115
0
            seed += filename;
2116
0
            seed += " ";
2117
0
        }
2118
28.2k
        seed += " QPDF ";
2119
28.2k
        if (trailer.hasKey("/Info")) {
2120
18.0k
            for (auto const& item: trailer.getKey("/Info").as_dictionary()) {
2121
18.0k
                if (item.second.isString()) {
2122
5.60k
                    seed += " ";
2123
5.60k
                    seed += item.second.getStringValue();
2124
5.60k
                }
2125
18.0k
            }
2126
962
        }
2127
2128
28.2k
        MD5 md5;
2129
28.2k
        md5.encodeString(seed.c_str());
2130
28.2k
        MD5::Digest digest;
2131
28.2k
        md5.digest(digest);
2132
28.2k
        result = std::string(reinterpret_cast<char*>(digest), sizeof(MD5::Digest));
2133
28.2k
    }
2134
2135
    // If /ID already exists, follow the spec: use the original first word and generate a new second
2136
    // word.  Otherwise, we'll use the generated ID for both.
2137
2138
57.3k
    id2 = result;
2139
    // Note: keep /ID from old file even if --static-id was given.
2140
57.3k
    id1 = getOriginalID1();
2141
57.3k
    if (id1.empty()) {
2142
52.3k
        id1 = id2;
2143
52.3k
    }
2144
57.3k
}
2145
2146
void
2147
impl::Writer::initializeSpecialStreams()
2148
16.4k
{
2149
    // Mark all page content streams in case we are filtering or normalizing.
2150
16.4k
    int num = 0;
2151
19.5k
    for (auto& page: pages) {
2152
19.5k
        page_object_to_seq[page.getObjGen()] = ++num;
2153
19.5k
        QPDFObjectHandle contents = page.getKey("/Contents");
2154
19.5k
        std::vector<QPDFObjGen> contents_objects;
2155
19.5k
        if (contents.isArray()) {
2156
1.05k
            int n = static_cast<int>(contents.size());
2157
60.9k
            for (int i = 0; i < n; ++i) {
2158
59.9k
                contents_objects.push_back(contents.getArrayItem(i).getObjGen());
2159
59.9k
            }
2160
18.5k
        } else if (contents.isStream()) {
2161
3.19k
            contents_objects.push_back(contents.getObjGen());
2162
3.19k
        }
2163
2164
63.0k
        for (auto const& c: contents_objects) {
2165
63.0k
            contents_to_page_seq[c] = num;
2166
63.0k
            normalized_streams.insert(c);
2167
63.0k
        }
2168
19.5k
    }
2169
16.4k
}
2170
2171
void
2172
impl::Writer::preserveObjectStreams()
2173
31.0k
{
2174
31.0k
    auto const& xref = objects.xref_table();
2175
    // Our object_to_object_stream map has to map ObjGen -> ObjGen since we may be generating object
2176
    // streams out of old objects that have generation numbers greater than zero. However in an
2177
    // existing PDF, all object stream objects and all objects in them must have generation 0
2178
    // because the PDF spec does not provide any way to do otherwise. This code filters out objects
2179
    // that are not allowed to be in object streams. In addition to removing objects that were
2180
    // erroneously included in object streams in the source PDF, it also prevents unreferenced
2181
    // objects from being included.
2182
31.0k
    auto end = xref.cend();
2183
31.0k
    obj.streams_empty = true;
2184
31.0k
    if (cfg.preserve_unreferenced()) {
2185
0
        for (auto iter = xref.cbegin(); iter != end; ++iter) {
2186
0
            if (iter->second.getType() == 2) {
2187
                // Pdf contains object streams.
2188
0
                obj.streams_empty = false;
2189
0
                obj[iter->first].object_stream = iter->second.getObjStreamNumber();
2190
0
            }
2191
0
        }
2192
31.0k
    } else {
2193
        // Start by scanning for first compressed object in case we don't have any object streams to
2194
        // process.
2195
313k
        for (auto iter = xref.cbegin(); iter != end; ++iter) {
2196
286k
            if (iter->second.getType() == 2) {
2197
                // Pdf contains object streams.
2198
3.65k
                obj.streams_empty = false;
2199
3.65k
                auto eligible = objects.compressible_set();
2200
                // The object pointed to by iter may be a previous generation, in which case it is
2201
                // removed by compressible_set. We need to restart the loop (while the object
2202
                // table may contain multiple generations of an object).
2203
868k
                for (iter = xref.cbegin(); iter != end; ++iter) {
2204
864k
                    if (iter->second.getType() == 2) {
2205
793k
                        auto id = static_cast<size_t>(iter->first.getObj());
2206
793k
                        if (id < eligible.size() && eligible[id]) {
2207
137k
                            obj[iter->first].object_stream = iter->second.getObjStreamNumber();
2208
656k
                        } else {
2209
656k
                            QTC::TC("qpdf", "QPDFWriter exclude from object stream");
2210
656k
                        }
2211
793k
                    }
2212
864k
                }
2213
3.65k
                return;
2214
3.65k
            }
2215
286k
        }
2216
31.0k
    }
2217
31.0k
}
2218
2219
void
2220
impl::Writer::generateObjectStreams()
2221
16.7k
{
2222
    // Basic strategy: make a list of objects that can go into an object stream.  Then figure out
2223
    // how many object streams are needed so that we can distribute objects approximately evenly
2224
    // without having any object stream exceed 100 members.  We don't have to worry about linearized
2225
    // files here -- if the file is linearized, we take care of excluding things that aren't allowed
2226
    // here later.
2227
2228
    // This code doesn't do anything with /Extends.
2229
2230
16.7k
    auto eligible = objects.compressible_vector();
2231
16.7k
    size_t n_object_streams = (eligible.size() + 99U) / 100U;
2232
2233
16.7k
    initializeTables(2U * n_object_streams);
2234
16.7k
    if (n_object_streams == 0) {
2235
90
        obj.streams_empty = true;
2236
90
        return;
2237
90
    }
2238
16.6k
    size_t n_per = eligible.size() / n_object_streams;
2239
16.6k
    if (n_per * n_object_streams < eligible.size()) {
2240
221
        ++n_per;
2241
221
    }
2242
16.6k
    unsigned int n = 0;
2243
16.6k
    int cur_ostream = qpdf.newIndirectNull().getObjectID();
2244
192k
    for (auto const& item: eligible) {
2245
192k
        if (n == n_per) {
2246
1.02k
            n = 0;
2247
            // Construct a new null object as the "original" object stream.  The rest of the code
2248
            // knows that this means we're creating the object stream from scratch.
2249
1.02k
            cur_ostream = qpdf.newIndirectNull().getObjectID();
2250
1.02k
        }
2251
192k
        auto& o = obj[item];
2252
192k
        o.object_stream = cur_ostream;
2253
192k
        o.gen = item.getGen();
2254
192k
        ++n;
2255
192k
    }
2256
16.6k
}
2257
2258
Dictionary
2259
impl::Writer::trimmed_trailer()
2260
158k
{
2261
    // Remove keys from the trailer that necessarily have to be replaced when writing the file.
2262
2263
158k
    Dictionary trailer = qpdf.getTrailer().unsafeShallowCopy();
2264
2265
    // Remove encryption keys
2266
158k
    trailer.erase("/ID");
2267
158k
    trailer.erase("/Encrypt");
2268
2269
    // Remove modification information
2270
158k
    trailer.erase("/Prev");
2271
2272
    // Remove all trailer keys that potentially come from a cross-reference stream
2273
158k
    trailer.erase("/Index");
2274
158k
    trailer.erase("/W");
2275
158k
    trailer.erase("/Length");
2276
158k
    trailer.erase("/Filter");
2277
158k
    trailer.erase("/DecodeParms");
2278
158k
    trailer.erase("/Type");
2279
158k
    trailer.erase("/XRefStm");
2280
2281
158k
    return trailer;
2282
158k
}
2283
2284
// Make document extension level information direct as required by the spec.
2285
void
2286
impl::Writer::prepareFileForWrite()
2287
61.3k
{
2288
61.3k
    qpdf.fixDanglingReferences();
2289
61.3k
    auto root = qpdf.getRoot();
2290
61.3k
    auto oh = root.getKey("/Extensions");
2291
61.3k
    if (oh.isDictionary()) {
2292
2.92k
        const bool extensions_indirect = oh.isIndirect();
2293
2.92k
        if (extensions_indirect) {
2294
1.14k
            QTC::TC("qpdf", "QPDFWriter make Extensions direct");
2295
1.14k
            oh = root.replaceKeyAndGetNew("/Extensions", oh.shallowCopy());
2296
1.14k
        }
2297
2.92k
        if (oh.hasKey("/ADBE")) {
2298
1.85k
            auto adbe = oh.getKey("/ADBE");
2299
1.85k
            if (adbe.isIndirect()) {
2300
1.29k
                QTC::TC("qpdf", "QPDFWriter make ADBE direct", extensions_indirect ? 0 : 1);
2301
1.29k
                adbe.makeDirect();
2302
1.29k
                oh.replaceKey("/ADBE", adbe);
2303
1.29k
            }
2304
1.85k
        }
2305
2.92k
    }
2306
61.3k
}
2307
2308
void
2309
impl::Writer::initializeTables(size_t extra)
2310
61.9k
{
2311
61.9k
    auto size = objects.table_size() + 100u + extra;
2312
61.9k
    obj.resize(size);
2313
61.9k
    new_obj.resize(size);
2314
61.9k
}
2315
2316
void
2317
impl::Writer::doWriteSetup()
2318
62.2k
{
2319
62.2k
    if (did_write_setup) {
2320
0
        return;
2321
0
    }
2322
62.2k
    did_write_setup = true;
2323
2324
    // Do preliminary setup
2325
2326
62.2k
    if (cfg.linearize()) {
2327
31.5k
        cfg.qdf(false);
2328
31.5k
    }
2329
2330
62.2k
    if (cfg.pclm()) {
2331
0
        encryption = nullptr;
2332
0
    }
2333
2334
62.2k
    if (encryption) {
2335
        // Encryption has been explicitly set
2336
28.9k
        cfg.preserve_encryption(false);
2337
33.2k
    } else if (cfg.normalize_content() || cfg.pclm()) {
2338
        // Encryption makes looking at contents pretty useless.  If the user explicitly encrypted
2339
        // though, we still obey that.
2340
16.4k
        cfg.preserve_encryption(false);
2341
16.4k
    }
2342
2343
62.2k
    if (cfg.preserve_encryption()) {
2344
16.8k
        copyEncryptionParameters(qpdf);
2345
16.8k
    }
2346
2347
62.2k
    if (!cfg.forced_pdf_version().empty()) {
2348
0
        int major = 0;
2349
0
        int minor = 0;
2350
0
        parseVersion(cfg.forced_pdf_version(), major, minor);
2351
0
        disableIncompatibleEncryption(major, minor, cfg.forced_extension_level());
2352
0
        if (compareVersions(major, minor, 1, 5) < 0) {
2353
0
            cfg.object_streams(qpdf_o_disable);
2354
0
        }
2355
0
    }
2356
2357
62.2k
    if (cfg.qdf() || cfg.normalize_content()) {
2358
16.4k
        initializeSpecialStreams();
2359
16.4k
    }
2360
2361
62.2k
    switch (cfg.object_streams()) {
2362
14.2k
    case qpdf_o_disable:
2363
14.2k
        initializeTables();
2364
14.2k
        obj.streams_empty = true;
2365
14.2k
        break;
2366
2367
31.0k
    case qpdf_o_preserve:
2368
31.0k
        initializeTables();
2369
31.0k
        preserveObjectStreams();
2370
31.0k
        break;
2371
2372
16.7k
    case qpdf_o_generate:
2373
16.7k
        generateObjectStreams();
2374
16.7k
        break;
2375
62.2k
    }
2376
2377
61.9k
    if (!obj.streams_empty) {
2378
20.1k
        if (cfg.linearize()) {
2379
            // Page dictionaries are not allowed to be compressed objects.
2380
28.8k
            for (auto& page: pages) {
2381
28.8k
                if (obj[page].object_stream > 0) {
2382
23.8k
                    obj[page].object_stream = 0;
2383
23.8k
                }
2384
28.8k
            }
2385
18.5k
        }
2386
2387
20.1k
        if (cfg.linearize() || encryption) {
2388
            // The document catalog is not allowed to be compressed in linearized files either.
2389
            // It also appears that Adobe Reader 8.0.0 has a bug that prevents it from being able to
2390
            // handle encrypted files with compressed document catalogs, so we disable them in that
2391
            // case as well.
2392
18.4k
            if (obj[root_og].object_stream > 0) {
2393
14.6k
                obj[root_og].object_stream = 0;
2394
14.6k
            }
2395
18.4k
        }
2396
2397
        // Generate reverse mapping from object stream to objects
2398
12.6M
        obj.forEach([this](auto id, auto const& item) -> void {
2399
12.6M
            if (item.object_stream > 0) {
2400
289k
                auto& vec = object_stream_to_objects[item.object_stream];
2401
289k
                vec.emplace_back(id, item.gen);
2402
289k
                if (max_ostream_index < vec.size()) {
2403
111k
                    ++max_ostream_index;
2404
111k
                }
2405
289k
            }
2406
12.6M
        });
2407
20.1k
        --max_ostream_index;
2408
2409
20.1k
        if (object_stream_to_objects.empty()) {
2410
3.07k
            obj.streams_empty = true;
2411
17.1k
        } else {
2412
17.1k
            setMinimumPDFVersion("1.5");
2413
17.1k
        }
2414
20.1k
    }
2415
2416
61.9k
    setMinimumPDFVersion(qpdf.getPDFVersion(), qpdf.getExtensionLevel());
2417
61.9k
    final_pdf_version = min_pdf_version;
2418
61.9k
    final_extension_level = min_extension_level;
2419
61.9k
    if (!cfg.forced_pdf_version().empty()) {
2420
0
        final_pdf_version = cfg.forced_pdf_version();
2421
0
        final_extension_level = cfg.forced_extension_level();
2422
0
    }
2423
61.9k
}
2424
2425
void
2426
QPDFWriter::write()
2427
62.2k
{
2428
62.2k
    m->write();
2429
62.2k
}
2430
2431
void
2432
impl::Writer::write()
2433
62.2k
{
2434
62.2k
    doWriteSetup();
2435
2436
    // Set up progress reporting. For linearized files, we write two passes. events_expected is an
2437
    // approximation, but it's good enough for progress reporting, which is mostly a guess anyway.
2438
62.2k
    events_expected = QIntC::to_int(qpdf.getObjectCount() * (cfg.linearize() ? 2 : 1));
2439
2440
62.2k
    prepareFileForWrite();
2441
2442
62.2k
    if (cfg.linearize()) {
2443
30.8k
        writeLinearized();
2444
31.4k
    } else {
2445
31.4k
        writeStandard();
2446
31.4k
    }
2447
2448
62.2k
    pipeline->finish();
2449
62.2k
    if (close_file) {
2450
0
        fclose(file);
2451
0
    }
2452
62.2k
    file = nullptr;
2453
62.2k
    if (buffer_pipeline) {
2454
0
        output_buffer = buffer_pipeline->getBuffer();
2455
0
        buffer_pipeline = nullptr;
2456
0
    }
2457
62.2k
    indicateProgress(false, true);
2458
62.2k
}
2459
2460
QPDFObjGen
2461
QPDFWriter::getRenumberedObjGen(QPDFObjGen og)
2462
0
{
2463
0
    return {m->obj[og].renumber, 0};
2464
0
}
2465
2466
std::map<QPDFObjGen, QPDFXRefEntry>
2467
QPDFWriter::getWrittenXRefTable()
2468
0
{
2469
0
    return m->getWrittenXRefTable();
2470
0
}
2471
2472
std::map<QPDFObjGen, QPDFXRefEntry>
2473
impl::Writer::getWrittenXRefTable()
2474
0
{
2475
0
    std::map<QPDFObjGen, QPDFXRefEntry> result;
2476
2477
0
    auto it = result.begin();
2478
0
    new_obj.forEach([&it, &result](auto id, auto const& item) -> void {
2479
0
        if (item.xref.getType() != 0) {
2480
0
            it = result.emplace_hint(it, QPDFObjGen(id, 0), item.xref);
2481
0
        }
2482
0
    });
2483
0
    return result;
2484
0
}
2485
2486
void
2487
impl::Writer::enqueuePart(std::vector<QPDFObjectHandle>& part)
2488
136k
{
2489
341k
    for (auto const& oh: part) {
2490
341k
        enqueue(oh);
2491
341k
    }
2492
136k
}
2493
2494
void
2495
impl::Writer::writeEncryptionDictionary()
2496
38.0k
{
2497
38.0k
    encryption_dict_objid = openObject(encryption_dict_objid);
2498
38.0k
    auto& enc = *encryption;
2499
38.0k
    auto const V = enc.getV();
2500
2501
38.0k
    write("<<");
2502
38.0k
    if (V >= 4) {
2503
24.3k
        write(" /CF << /StdCF << /AuthEvent /DocOpen /CFM ");
2504
24.3k
        write(cfg.encrypt_use_aes() ? (V < 5 ? "/AESV2" : "/AESV3") : "/V2");
2505
        // The PDF spec says the /Length key is optional, but the PDF previewer on some versions of
2506
        // MacOS won't open encrypted files without it.
2507
24.3k
        write(V < 5 ? " /Length 16 >> >>" : " /Length 32 >> >>");
2508
24.3k
        if (!encryption->getEncryptMetadata()) {
2509
0
            write(" /EncryptMetadata false");
2510
0
        }
2511
24.3k
    }
2512
38.0k
    write(" /Filter /Standard /Length ").write(enc.getLengthBytes() * 8);
2513
38.0k
    write(" /O ").write_string(enc.getO(), true);
2514
38.0k
    if (V >= 4) {
2515
24.3k
        write(" /OE ").write_string(enc.getOE(), true);
2516
24.3k
    }
2517
38.0k
    write(" /P ").write(enc.getP());
2518
38.0k
    if (V >= 5) {
2519
24.3k
        write(" /Perms ").write_string(enc.getPerms(), true);
2520
24.3k
    }
2521
38.0k
    write(" /R ").write(enc.getR());
2522
2523
38.0k
    if (V >= 4) {
2524
24.3k
        write(" /StmF /StdCF /StrF /StdCF");
2525
24.3k
    }
2526
38.0k
    write(" /U ").write_string(enc.getU(), true);
2527
38.0k
    if (V >= 4) {
2528
24.3k
        write(" /UE ").write_string(enc.getUE(), true);
2529
24.3k
    }
2530
38.0k
    write(" /V ").write(enc.getV()).write(" >>");
2531
38.0k
    closeObject(encryption_dict_objid);
2532
38.0k
}
2533
2534
std::string
2535
QPDFWriter::getFinalVersion()
2536
0
{
2537
0
    m->doWriteSetup();
2538
0
    return m->final_pdf_version;
2539
0
}
2540
2541
void
2542
impl::Writer::writeHeader()
2543
80.4k
{
2544
80.4k
    write("%PDF-").write(final_pdf_version);
2545
80.4k
    if (cfg.pclm()) {
2546
        // PCLm version
2547
0
        write("\n%PCLm 1.0\n");
2548
80.4k
    } else {
2549
        // This string of binary characters would not be valid UTF-8, so it really should be treated
2550
        // as binary.
2551
80.4k
        write("\n%\xbf\xf7\xa2\xfe\n");
2552
80.4k
    }
2553
80.4k
    write_qdf("%QDF-1.0\n\n");
2554
2555
    // Note: do not write extra header text here.  Linearized PDFs must include the entire
2556
    // linearization parameter dictionary within the first 1024 characters of the PDF file, so for
2557
    // linearized files, we have to write extra header text after the linearization parameter
2558
    // dictionary.
2559
80.4k
}
2560
2561
void
2562
impl::Writer::writeHintStream(int hint_id)
2563
24.3k
{
2564
24.3k
    std::string hint_buffer;
2565
24.3k
    int S = 0;
2566
24.3k
    int O = 0;
2567
24.3k
    bool compressed = cfg.compress_streams();
2568
24.3k
    lin.generateHintStream(new_obj, obj, hint_buffer, S, O, compressed);
2569
2570
24.3k
    openObject(hint_id);
2571
24.3k
    setDataKey(hint_id);
2572
2573
24.3k
    size_t hlen = hint_buffer.size();
2574
2575
24.3k
    write("<< ");
2576
24.3k
    if (compressed) {
2577
24.3k
        write("/Filter /FlateDecode ");
2578
24.3k
    }
2579
24.3k
    write("/S ").write(S);
2580
24.3k
    if (O) {
2581
612
        write(" /O ").write(O);
2582
612
    }
2583
24.3k
    adjustAESStreamLength(hlen);
2584
24.3k
    write(" /Length ").write(hlen);
2585
24.3k
    write(" >>\nstream\n").write_encrypted(hint_buffer);
2586
2587
24.3k
    if (encryption) {
2588
11.8k
        QTC::TC("qpdf", "QPDFWriter encrypted hint stream");
2589
11.8k
    }
2590
2591
24.3k
    write(hint_buffer.empty() || hint_buffer.back() != '\n' ? "\nendstream" : "endstream");
2592
24.3k
    closeObject(hint_id);
2593
24.3k
}
2594
2595
qpdf_offset_t
2596
impl::Writer::writeXRefTable(trailer_e which, int first, int last, int size)
2597
28.8k
{
2598
    // There are too many extra arguments to replace overloaded function with defaults in the header
2599
    // file...too much risk of leaving something off.
2600
28.8k
    return writeXRefTable(which, first, last, size, 0, false, 0, 0, 0, 0);
2601
28.8k
}
2602
2603
qpdf_offset_t
2604
impl::Writer::writeXRefTable(
2605
    trailer_e which,
2606
    int first,
2607
    int last,
2608
    int size,
2609
    qpdf_offset_t prev,
2610
    bool suppress_offsets,
2611
    int hint_id,
2612
    qpdf_offset_t hint_offset,
2613
    qpdf_offset_t hint_length,
2614
    int linearization_pass)
2615
77.3k
{
2616
77.3k
    write("xref\n").write(first).write(" ").write(last - first + 1);
2617
77.3k
    qpdf_offset_t space_before_zero = pipeline->getCount();
2618
77.3k
    write("\n");
2619
77.3k
    if (first == 0) {
2620
52.8k
        write("0000000000 65535 f \n");
2621
52.8k
        ++first;
2622
52.8k
    }
2623
727k
    for (int i = first; i <= last; ++i) {
2624
650k
        qpdf_offset_t offset = 0;
2625
650k
        if (!suppress_offsets) {
2626
521k
            offset = new_obj[i].xref.getOffset();
2627
521k
            if ((hint_id != 0) && (i != hint_id) && (offset >= hint_offset)) {
2628
65.6k
                offset += hint_length;
2629
65.6k
            }
2630
521k
        }
2631
650k
        write(QUtil::int_to_string(offset, 10)).write(" 00000 n \n");
2632
650k
    }
2633
77.3k
    writeTrailer(which, size, false, prev, linearization_pass);
2634
77.3k
    write("\n");
2635
77.3k
    return space_before_zero;
2636
77.3k
}
2637
2638
qpdf_offset_t
2639
impl::Writer::writeXRefStream(
2640
    int objid, int max_id, qpdf_offset_t max_offset, trailer_e which, int first, int last, int size)
2641
676
{
2642
    // There are too many extra arguments to replace overloaded function with defaults in the header
2643
    // file...too much risk of leaving something off.
2644
676
    return writeXRefStream(
2645
676
        objid, max_id, max_offset, which, first, last, size, 0, 0, 0, 0, false, 0);
2646
676
}
2647
2648
qpdf_offset_t
2649
impl::Writer::writeXRefStream(
2650
    int xref_id,
2651
    int max_id,
2652
    qpdf_offset_t max_offset,
2653
    trailer_e which,
2654
    int first,
2655
    int last,
2656
    int size,
2657
    qpdf_offset_t prev,
2658
    int hint_id,
2659
    qpdf_offset_t hint_offset,
2660
    qpdf_offset_t hint_length,
2661
    bool skip_compression,
2662
    int linearization_pass)
2663
51.1k
{
2664
51.1k
    qpdf_offset_t xref_offset = pipeline->getCount();
2665
51.1k
    qpdf_offset_t space_before_zero = xref_offset - 1;
2666
2667
    // field 1 contains offsets and object stream identifiers
2668
51.1k
    unsigned int f1_size = std::max(bytesNeeded(max_offset + hint_length), bytesNeeded(max_id));
2669
2670
    // field 2 contains object stream indices
2671
51.1k
    unsigned int f2_size = bytesNeeded(QIntC::to_longlong(max_ostream_index));
2672
2673
51.1k
    unsigned int esize = 1 + f1_size + f2_size;
2674
2675
    // Must store in xref table in advance of writing the actual data rather than waiting for
2676
    // openObject to do it.
2677
51.1k
    new_obj[xref_id].xref = QPDFXRefEntry(pipeline->getCount());
2678
2679
51.1k
    std::string xref_data;
2680
51.1k
    const bool compressed = cfg.compress_streams() && !cfg.qdf();
2681
51.1k
    {
2682
51.1k
        auto pp_xref = pipeline_stack.activate(xref_data);
2683
2684
922k
        for (int i = first; i <= last; ++i) {
2685
870k
            QPDFXRefEntry& e = new_obj[i].xref;
2686
870k
            switch (e.getType()) {
2687
205k
            case 0:
2688
205k
                writeBinary(0, 1);
2689
205k
                writeBinary(0, f1_size);
2690
205k
                writeBinary(0, f2_size);
2691
205k
                break;
2692
2693
296k
            case 1:
2694
296k
                {
2695
296k
                    qpdf_offset_t offset = e.getOffset();
2696
296k
                    if ((hint_id != 0) && (i != hint_id) && (offset >= hint_offset)) {
2697
83.9k
                        offset += hint_length;
2698
83.9k
                    }
2699
296k
                    writeBinary(1, 1);
2700
296k
                    writeBinary(QIntC::to_ulonglong(offset), f1_size);
2701
296k
                    writeBinary(0, f2_size);
2702
296k
                }
2703
296k
                break;
2704
2705
368k
            case 2:
2706
368k
                writeBinary(2, 1);
2707
368k
                writeBinary(QIntC::to_ulonglong(e.getObjStreamNumber()), f1_size);
2708
368k
                writeBinary(QIntC::to_ulonglong(e.getObjStreamIndex()), f2_size);
2709
368k
                break;
2710
2711
0
            default:
2712
0
                throw std::logic_error("invalid type writing xref stream");
2713
0
                break;
2714
870k
            }
2715
870k
        }
2716
51.1k
    }
2717
2718
51.1k
    if (compressed) {
2719
50.4k
        xref_data = pl::pipe<Pl_PNGFilter>(xref_data, Pl_PNGFilter::a_encode, esize);
2720
50.4k
        if (!skip_compression) {
2721
            // Write the stream dictionary for compression but don't actually compress.  This
2722
            // helps us with computation of padding for pass 1 of linearization.
2723
24.6k
            xref_data = pl::pipe<Pl_Flate>(xref_data, Pl_Flate::a_deflate);
2724
24.6k
        }
2725
50.4k
    }
2726
2727
51.1k
    openObject(xref_id);
2728
51.1k
    write("<<").write_qdf("\n ").write(" /Type /XRef").write_qdf("\n ");
2729
51.1k
    write(" /Length ").write(xref_data.size());
2730
51.1k
    if (compressed) {
2731
50.4k
        write_qdf("\n ").write(" /Filter /FlateDecode").write_qdf("\n ");
2732
50.4k
        write(" /DecodeParms << /Columns ").write(esize).write(" /Predictor 12 >>");
2733
50.4k
    }
2734
51.1k
    write_qdf("\n ").write(" /W [ 1 ").write(f1_size).write(" ").write(f2_size).write(" ]");
2735
51.1k
    if (!(first == 0 && last == (size - 1))) {
2736
25.8k
        write(" /Index [ ").write(first).write(" ").write(last - first + 1).write(" ]");
2737
25.8k
    }
2738
51.1k
    writeTrailer(which, size, true, prev, linearization_pass);
2739
51.1k
    write("\nstream\n").write(xref_data).write("\nendstream");
2740
51.1k
    closeObject(xref_id);
2741
51.1k
    return space_before_zero;
2742
51.1k
}
2743
2744
size_t
2745
impl::Writer::calculateXrefStreamPadding(qpdf_offset_t xref_bytes)
2746
25.5k
{
2747
    // This routine is called right after a linearization first pass xref stream has been written
2748
    // without compression.  Calculate the amount of padding that would be required in the worst
2749
    // case, assuming the number of uncompressed bytes remains the same. The worst case for zlib is
2750
    // that the output is larger than the input by 6 bytes plus 5 bytes per 16K, and then we'll add
2751
    // 10 extra bytes for number length increases.
2752
2753
25.5k
    return QIntC::to_size(16 + (5 * ((xref_bytes + 16383) / 16384)));
2754
25.5k
}
2755
2756
void
2757
impl::Writer::writeLinearized()
2758
30.8k
{
2759
    // Optimize file and enqueue objects in order
2760
2761
30.8k
    std::map<int, int> stream_cache;
2762
2763
163k
    auto skip_stream_parameters = [this, &stream_cache](QPDFObjectHandle& stream) {
2764
163k
        if (auto& result = stream_cache[stream.getObjectID()]) {
2765
80.9k
            return result;
2766
82.4k
        } else {
2767
82.4k
            return result = will_filter_stream(stream) ? 2 : 1;
2768
82.4k
        }
2769
163k
    };
2770
2771
30.8k
    lin.optimize(obj, skip_stream_parameters);
2772
2773
30.8k
    std::vector<QPDFObjectHandle> part4;
2774
30.8k
    std::vector<QPDFObjectHandle> part6;
2775
30.8k
    std::vector<QPDFObjectHandle> part7;
2776
30.8k
    std::vector<QPDFObjectHandle> part8;
2777
30.8k
    std::vector<QPDFObjectHandle> part9;
2778
30.8k
    lin.parts(obj, part4, part6, part7, part8, part9);
2779
2780
    // Object number sequence:
2781
    //
2782
    //  second half
2783
    //    second half uncompressed objects
2784
    //    second half xref stream, if any
2785
    //    second half compressed objects
2786
    //  first half
2787
    //    linearization dictionary
2788
    //    first half xref stream, if any
2789
    //    part 4 uncompresesd objects
2790
    //    encryption dictionary, if any
2791
    //    hint stream
2792
    //    part 6 uncompressed objects
2793
    //    first half compressed objects
2794
    //
2795
2796
    // Second half objects
2797
30.8k
    int second_half_uncompressed = QIntC::to_int(part7.size() + part8.size() + part9.size());
2798
30.8k
    int second_half_first_obj = 1;
2799
30.8k
    int after_second_half = 1 + second_half_uncompressed;
2800
30.8k
    next_objid = after_second_half;
2801
30.8k
    int second_half_xref = 0;
2802
30.8k
    bool need_xref_stream = !obj.streams_empty;
2803
30.8k
    if (need_xref_stream) {
2804
13.8k
        second_half_xref = next_objid++;
2805
13.8k
    }
2806
    // Assign numbers to all compressed objects in the second half.
2807
30.8k
    std::vector<QPDFObjectHandle>* vecs2[] = {&part7, &part8, &part9};
2808
113k
    for (int i = 0; i < 3; ++i) {
2809
138k
        for (auto const& oh: *vecs2[i]) {
2810
138k
            assignCompressedObjectNumbers(oh.getObjGen());
2811
138k
        }
2812
83.1k
    }
2813
30.8k
    int second_half_end = next_objid - 1;
2814
30.8k
    int second_trailer_size = next_objid;
2815
2816
    // First half objects
2817
30.8k
    int first_half_start = next_objid;
2818
30.8k
    int lindict_id = next_objid++;
2819
30.8k
    int first_half_xref = 0;
2820
30.8k
    if (need_xref_stream) {
2821
13.8k
        first_half_xref = next_objid++;
2822
13.8k
    }
2823
30.8k
    int part4_first_obj = next_objid;
2824
30.8k
    next_objid += QIntC::to_int(part4.size());
2825
30.8k
    int after_part4 = next_objid;
2826
30.8k
    if (encryption) {
2827
13.6k
        encryption_dict_objid = next_objid++;
2828
13.6k
    }
2829
30.8k
    int hint_id = next_objid++;
2830
30.8k
    int part6_first_obj = next_objid;
2831
30.8k
    next_objid += QIntC::to_int(part6.size());
2832
30.8k
    int after_part6 = next_objid;
2833
    // Assign numbers to all compressed objects in the first half
2834
30.8k
    std::vector<QPDFObjectHandle>* vecs1[] = {&part4, &part6};
2835
86.2k
    for (int i = 0; i < 2; ++i) {
2836
204k
        for (auto const& oh: *vecs1[i]) {
2837
204k
            assignCompressedObjectNumbers(oh.getObjGen());
2838
204k
        }
2839
55.4k
    }
2840
30.8k
    int first_half_end = next_objid - 1;
2841
30.8k
    int first_trailer_size = next_objid;
2842
2843
30.8k
    int part4_end_marker = part4.back().getObjectID();
2844
30.8k
    int part6_end_marker = part6.back().getObjectID();
2845
30.8k
    qpdf_offset_t space_before_zero = 0;
2846
30.8k
    qpdf_offset_t file_size = 0;
2847
30.8k
    qpdf_offset_t part6_end_offset = 0;
2848
30.8k
    qpdf_offset_t first_half_max_obj_offset = 0;
2849
30.8k
    qpdf_offset_t second_xref_offset = 0;
2850
30.8k
    qpdf_offset_t first_xref_end = 0;
2851
30.8k
    qpdf_offset_t second_xref_end = 0;
2852
2853
30.8k
    next_objid = part4_first_obj;
2854
30.8k
    enqueuePart(part4);
2855
30.8k
    if (next_objid != after_part4) {
2856
        // This can happen with very botched files as in the fuzzer test. There are likely some
2857
        // faulty assumptions in calculateLinearizationData
2858
29
        throw std::runtime_error("error encountered after writing part 4 of linearized data");
2859
29
    }
2860
30.8k
    next_objid = part6_first_obj;
2861
30.8k
    enqueuePart(part6);
2862
30.8k
    util::no_ci_rt_error_if(
2863
30.8k
        next_objid != after_part6, "error encountered after writing part 6 of linearized data" //
2864
30.8k
    );
2865
30.8k
    next_objid = second_half_first_obj;
2866
30.8k
    enqueuePart(part7);
2867
30.8k
    enqueuePart(part8);
2868
30.8k
    enqueuePart(part9);
2869
30.8k
    util::no_ci_rt_error_if(
2870
30.8k
        next_objid != after_second_half,
2871
30.8k
        "error encountered after writing part 9 of linearized data" //
2872
30.8k
    );
2873
2874
30.8k
    qpdf_offset_t hint_length = 0;
2875
30.8k
    std::string hint_buffer;
2876
2877
    // Write file in two passes.  Part numbers refer to PDF spec 1.4.
2878
2879
30.8k
    FILE* lin_pass1_file = nullptr;
2880
30.8k
    auto pp_pass1 = pipeline_stack.popper();
2881
30.8k
    auto pp_md5 = pipeline_stack.popper();
2882
50.3k
    for (int pass: {1, 2}) {
2883
50.3k
        if (pass == 1) {
2884
25.9k
            if (!cfg.linearize_pass1().empty()) {
2885
0
                lin_pass1_file = QUtil::safe_fopen(cfg.linearize_pass1().data(), "wb");
2886
0
                pipeline_stack.activate(
2887
0
                    pp_pass1,
2888
0
                    std::make_unique<Pl_StdioFile>("linearization pass1", lin_pass1_file));
2889
25.9k
            } else {
2890
25.9k
                pipeline_stack.activate(pp_pass1, true);
2891
25.9k
            }
2892
25.9k
            if (cfg.deterministic_id()) {
2893
13.3k
                pipeline_stack.activate_md5(pp_md5);
2894
13.3k
            }
2895
25.9k
        }
2896
2897
        // Part 1: header
2898
2899
50.3k
        writeHeader();
2900
2901
        // Part 2: linearization parameter dictionary.  Save enough space to write real dictionary.
2902
        // 200 characters is enough space if all numerical values in the parameter dictionary that
2903
        // contain offsets are 20 digits long plus a few extra characters for safety.  The entire
2904
        // linearization parameter dictionary must appear within the first 1024 characters of the
2905
        // file.
2906
2907
50.3k
        qpdf_offset_t pos = pipeline->getCount();
2908
50.3k
        openObject(lindict_id);
2909
50.3k
        write("<<");
2910
50.3k
        if (pass == 2) {
2911
24.3k
            write(" /Linearized 1 /L ").write(file_size + hint_length);
2912
            // Implementation note 121 states that a space is mandatory after this open bracket.
2913
24.3k
            write(" /H [ ").write(new_obj[hint_id].xref.getOffset()).write(" ");
2914
24.3k
            write(hint_length);
2915
24.3k
            write(" ] /O ").write(obj[pages.all().at(0)].renumber);
2916
24.3k
            write(" /E ").write(part6_end_offset + hint_length);
2917
24.3k
            write(" /N ").write(pages.size());
2918
24.3k
            write(" /T ").write(space_before_zero + hint_length);
2919
24.3k
        }
2920
50.3k
        write(" >>");
2921
50.3k
        closeObject(lindict_id);
2922
50.3k
        static int const pad = 200;
2923
50.3k
        write(QIntC::to_size(pos - pipeline->getCount() + pad), ' ').write("\n");
2924
2925
        // If the user supplied any additional header text, write it here after the linearization
2926
        // parameter dictionary.
2927
50.3k
        write(cfg.extra_header_text());
2928
2929
        // Part 3: first page cross reference table and trailer.
2930
2931
50.3k
        qpdf_offset_t first_xref_offset = pipeline->getCount();
2932
50.3k
        qpdf_offset_t hint_offset = 0;
2933
50.3k
        if (pass == 2) {
2934
24.3k
            hint_offset = new_obj[hint_id].xref.getOffset();
2935
24.3k
        }
2936
50.3k
        if (need_xref_stream) {
2937
            // Must pad here too.
2938
25.8k
            if (pass == 1) {
2939
                // Set first_half_max_obj_offset to a value large enough to force four bytes to be
2940
                // reserved for each file offset.  This would provide adequate space for the xref
2941
                // stream as long as the last object in page 1 starts with in the first 4 GB of the
2942
                // file, which is extremely likely.  In the second pass, we will know the actual
2943
                // value for this, but it's okay if it's smaller.
2944
13.4k
                first_half_max_obj_offset = 1 << 25;
2945
13.4k
            }
2946
25.8k
            pos = pipeline->getCount();
2947
25.8k
            writeXRefStream(
2948
25.8k
                first_half_xref,
2949
25.8k
                first_half_end,
2950
25.8k
                first_half_max_obj_offset,
2951
25.8k
                t_lin_first,
2952
25.8k
                first_half_start,
2953
25.8k
                first_half_end,
2954
25.8k
                first_trailer_size,
2955
25.8k
                hint_length + second_xref_offset,
2956
25.8k
                hint_id,
2957
25.8k
                hint_offset,
2958
25.8k
                hint_length,
2959
25.8k
                (pass == 1),
2960
25.8k
                pass);
2961
25.8k
            qpdf_offset_t endpos = pipeline->getCount();
2962
25.8k
            if (pass == 1) {
2963
                // Pad so we have enough room for the real xref stream.
2964
13.1k
                write(calculateXrefStreamPadding(endpos - pos), ' ');
2965
13.1k
                first_xref_end = pipeline->getCount();
2966
13.1k
            } else {
2967
                // Pad so that the next object starts at the same place as in pass 1.
2968
12.6k
                write(QIntC::to_size(first_xref_end - endpos), ' ');
2969
2970
12.6k
                if (pipeline->getCount() != first_xref_end) {
2971
0
                    throw std::logic_error(
2972
0
                        "insufficient padding for first pass xref stream; first_xref_end=" +
2973
0
                        std::to_string(first_xref_end) + "; endpos=" + std::to_string(endpos));
2974
0
                }
2975
12.6k
            }
2976
25.8k
            write("\n");
2977
25.8k
        } else {
2978
24.4k
            writeXRefTable(
2979
24.4k
                t_lin_first,
2980
24.4k
                first_half_start,
2981
24.4k
                first_half_end,
2982
24.4k
                first_trailer_size,
2983
24.4k
                hint_length + second_xref_offset,
2984
24.4k
                (pass == 1),
2985
24.4k
                hint_id,
2986
24.4k
                hint_offset,
2987
24.4k
                hint_length,
2988
24.4k
                pass);
2989
24.4k
            write("startxref\n0\n%%EOF\n");
2990
24.4k
        }
2991
2992
        // Parts 4 through 9
2993
2994
567k
        for (auto const& cur_object: object_queue) {
2995
567k
            if (cur_object.getObjectID() == part6_end_marker) {
2996
49.7k
                first_half_max_obj_offset = pipeline->getCount();
2997
49.7k
            }
2998
567k
            writeObject(cur_object);
2999
567k
            if (cur_object.getObjectID() == part4_end_marker) {
3000
49.9k
                if (encryption) {
3001
24.3k
                    writeEncryptionDictionary();
3002
24.3k
                }
3003
49.9k
                if (pass == 1) {
3004
25.5k
                    new_obj[hint_id].xref = QPDFXRefEntry(pipeline->getCount());
3005
25.5k
                } else {
3006
                    // Part 5: hint stream
3007
24.3k
                    write(hint_buffer);
3008
24.3k
                }
3009
49.9k
            }
3010
567k
            if (cur_object.getObjectID() == part6_end_marker) {
3011
49.3k
                part6_end_offset = pipeline->getCount();
3012
49.3k
            }
3013
567k
        }
3014
3015
        // Part 10: overflow hint stream -- not used
3016
3017
        // Part 11: main cross reference table and trailer
3018
3019
50.3k
        second_xref_offset = pipeline->getCount();
3020
50.3k
        if (need_xref_stream) {
3021
24.6k
            pos = pipeline->getCount();
3022
24.6k
            space_before_zero = writeXRefStream(
3023
24.6k
                second_half_xref,
3024
24.6k
                second_half_end,
3025
24.6k
                second_xref_offset,
3026
24.6k
                t_lin_second,
3027
24.6k
                0,
3028
24.6k
                second_half_end,
3029
24.6k
                second_trailer_size,
3030
24.6k
                0,
3031
24.6k
                0,
3032
24.6k
                0,
3033
24.6k
                0,
3034
24.6k
                (pass == 1),
3035
24.6k
                pass);
3036
24.6k
            qpdf_offset_t endpos = pipeline->getCount();
3037
3038
24.6k
            if (pass == 1) {
3039
                // Pad so we have enough room for the real xref stream.  See comments for previous
3040
                // xref stream on how we calculate the padding.
3041
12.3k
                write(calculateXrefStreamPadding(endpos - pos), ' ').write("\n");
3042
12.3k
                second_xref_end = pipeline->getCount();
3043
12.3k
            } else {
3044
                // Make the file size the same.
3045
12.3k
                auto padding =
3046
12.3k
                    QIntC::to_size(second_xref_end + hint_length - 1 - pipeline->getCount());
3047
12.3k
                write(padding, ' ').write("\n");
3048
3049
                // If this assertion fails, maybe we didn't have enough padding above.
3050
12.3k
                if (pipeline->getCount() != second_xref_end + hint_length) {
3051
0
                    throw std::logic_error(
3052
0
                        "count mismatch after xref stream; possible insufficient padding?");
3053
0
                }
3054
12.3k
            }
3055
25.6k
        } else {
3056
25.6k
            space_before_zero = writeXRefTable(
3057
25.6k
                t_lin_second, 0, second_half_end, second_trailer_size, 0, false, 0, 0, 0, pass);
3058
25.6k
        }
3059
50.3k
        write("startxref\n").write(first_xref_offset).write("\n%%EOF\n");
3060
3061
50.3k
        if (pass == 1) {
3062
24.3k
            if (cfg.deterministic_id()) {
3063
12.4k
                QTC::TC("qpdf", "QPDFWriter linearized deterministic ID", need_xref_stream ? 0 : 1);
3064
12.4k
                computeDeterministicIDData();
3065
12.4k
                pp_md5.pop();
3066
12.4k
            }
3067
3068
            // Close first pass pipeline
3069
24.3k
            file_size = pipeline->getCount();
3070
24.3k
            pp_pass1.pop();
3071
3072
            // Save hint offset since it will be set to zero by calling openObject.
3073
24.3k
            qpdf_offset_t hint_offset1 = new_obj[hint_id].xref.getOffset();
3074
3075
            // Write hint stream to a buffer
3076
24.3k
            {
3077
24.3k
                auto pp_hint = pipeline_stack.activate(hint_buffer);
3078
24.3k
                writeHintStream(hint_id);
3079
24.3k
            }
3080
24.3k
            hint_length = QIntC::to_offset(hint_buffer.size());
3081
3082
            // Restore hint offset
3083
24.3k
            new_obj[hint_id].xref = QPDFXRefEntry(hint_offset1);
3084
24.3k
            if (lin_pass1_file) {
3085
                // Write some debugging information
3086
0
                fprintf(
3087
0
                    lin_pass1_file, "%% hint_offset=%s\n", std::to_string(hint_offset1).c_str());
3088
0
                fprintf(lin_pass1_file, "%% hint_length=%s\n", std::to_string(hint_length).c_str());
3089
0
                fprintf(
3090
0
                    lin_pass1_file,
3091
0
                    "%% second_xref_offset=%s\n",
3092
0
                    std::to_string(second_xref_offset).c_str());
3093
0
                fprintf(
3094
0
                    lin_pass1_file,
3095
0
                    "%% second_xref_end=%s\n",
3096
0
                    std::to_string(second_xref_end).c_str());
3097
0
                fclose(lin_pass1_file);
3098
0
                lin_pass1_file = nullptr;
3099
0
            }
3100
24.3k
        }
3101
50.3k
    }
3102
30.8k
}
3103
3104
void
3105
impl::Writer::enqueueObjectsStandard()
3106
30.1k
{
3107
30.1k
    if (cfg.preserve_unreferenced()) {
3108
0
        for (auto const& oh: qpdf.getAllObjects()) {
3109
0
            enqueue(oh);
3110
0
        }
3111
0
    }
3112
3113
    // Put root first on queue.
3114
30.1k
    auto trailer = trimmed_trailer();
3115
30.1k
    enqueue(trailer["/Root"]);
3116
3117
    // Next place any other objects referenced from the trailer dictionary into the queue, handling
3118
    // direct objects recursively. Root is already there, so enqueuing it a second time is a no-op.
3119
64.1k
    for (auto& item: trailer) {
3120
64.1k
        if (!item.second.null()) {
3121
54.0k
            enqueue(item.second);
3122
54.0k
        }
3123
64.1k
    }
3124
30.1k
}
3125
3126
void
3127
impl::Writer::enqueueObjectsPCLm()
3128
0
{
3129
    // Image transform stream content for page strip images. Each of this new stream has to come
3130
    // after every page image strip written in the pclm file.
3131
0
    std::string image_transform_content = "q /image Do Q\n";
3132
3133
    // enqueue all pages first
3134
0
    for (auto& page: pages) {
3135
0
        enqueue(page);
3136
0
        enqueue(page["/Contents"]);
3137
3138
        // enqueue all the strips for each page
3139
0
        for (auto& image: Dictionary(page["/Resources"]["/XObject"])) {
3140
0
            if (!image.second.null()) {
3141
0
                enqueue(image.second);
3142
0
                enqueue(qpdf.newStream(image_transform_content));
3143
0
            }
3144
0
        }
3145
0
    }
3146
3147
0
    enqueue(trimmed_trailer()["/Root"]);
3148
0
}
3149
3150
void
3151
impl::Writer::indicateProgress(bool decrement, bool finished)
3152
1.73M
{
3153
1.73M
    if (decrement) {
3154
445k
        --events_seen;
3155
445k
        return;
3156
445k
    }
3157
3158
1.29M
    ++events_seen;
3159
3160
1.29M
    if (!progress_reporter.get()) {
3161
1.29M
        return;
3162
1.29M
    }
3163
3164
0
    if (finished || events_seen >= next_progress_report) {
3165
0
        int percentage =
3166
0
            (finished ? 100
3167
0
                 : next_progress_report == 0
3168
0
                 ? 0
3169
0
                 : std::min(99, 1 + ((100 * events_seen) / events_expected)));
3170
0
        progress_reporter->reportProgress(percentage);
3171
0
    }
3172
0
    int increment = std::max(1, (events_expected / 100));
3173
0
    while (events_seen >= next_progress_report) {
3174
0
        next_progress_report += increment;
3175
0
    }
3176
0
}
3177
3178
void
3179
QPDFWriter::registerProgressReporter(std::shared_ptr<ProgressReporter> pr)
3180
0
{
3181
0
    m->progress_reporter = pr;
3182
0
}
3183
3184
void
3185
impl::Writer::writeStandard()
3186
30.1k
{
3187
30.1k
    auto pp_md5 = pipeline_stack.popper();
3188
30.1k
    if (cfg.deterministic_id()) {
3189
16.1k
        pipeline_stack.activate_md5(pp_md5);
3190
16.1k
    }
3191
3192
    // Start writing
3193
3194
30.1k
    writeHeader();
3195
30.1k
    write(cfg.extra_header_text());
3196
3197
30.1k
    if (cfg.pclm()) {
3198
0
        enqueueObjectsPCLm();
3199
30.1k
    } else {
3200
30.1k
        enqueueObjectsStandard();
3201
30.1k
    }
3202
3203
    // Now start walking queue, outputting each object.
3204
294k
    while (object_queue_front < object_queue.size()) {
3205
263k
        QPDFObjectHandle cur_object = object_queue.at(object_queue_front);
3206
263k
        ++object_queue_front;
3207
263k
        writeObject(cur_object);
3208
263k
    }
3209
3210
    // Write out the encryption dictionary, if any
3211
30.1k
    if (encryption) {
3212
13.7k
        writeEncryptionDictionary();
3213
13.7k
    }
3214
3215
    // Now write out xref.  next_objid is now the number of objects.
3216
30.1k
    qpdf_offset_t xref_offset = pipeline->getCount();
3217
30.1k
    if (object_stream_to_objects.empty()) {
3218
        // Write regular cross-reference table
3219
28.8k
        writeXRefTable(t_normal, 0, next_objid - 1, next_objid);
3220
28.8k
    } else {
3221
        // Write cross-reference stream.
3222
1.32k
        int xref_id = next_objid++;
3223
1.32k
        writeXRefStream(xref_id, xref_id, xref_offset, t_normal, 0, next_objid - 1, next_objid);
3224
1.32k
    }
3225
30.1k
    write("startxref\n").write(xref_offset).write("\n%%EOF\n");
3226
3227
30.1k
    if (cfg.deterministic_id()) {
3228
15.4k
        QTC::TC(
3229
15.4k
            "qpdf",
3230
15.4k
            "QPDFWriter standard deterministic ID",
3231
15.4k
            object_stream_to_objects.empty() ? 0 : 1);
3232
15.4k
    }
3233
30.1k
}