Coverage Report

Created: 2024-09-08 06:05

/src/qpdf/libqpdf/QPDFObjectHandle.cc
Line
Count
Source (jump to first uncovered line)
1
#include <qpdf/QPDFObjectHandle.hh>
2
3
#include <qpdf/BufferInputSource.hh>
4
#include <qpdf/JSON_writer.hh>
5
#include <qpdf/Pl_Buffer.hh>
6
#include <qpdf/Pl_QPDFTokenizer.hh>
7
#include <qpdf/QPDF.hh>
8
#include <qpdf/QPDFExc.hh>
9
#include <qpdf/QPDFLogger.hh>
10
#include <qpdf/QPDFMatrix.hh>
11
#include <qpdf/QPDFObject_private.hh>
12
#include <qpdf/QPDFPageObjectHelper.hh>
13
#include <qpdf/QPDFParser.hh>
14
#include <qpdf/QPDF_Array.hh>
15
#include <qpdf/QPDF_Bool.hh>
16
#include <qpdf/QPDF_Dictionary.hh>
17
#include <qpdf/QPDF_InlineImage.hh>
18
#include <qpdf/QPDF_Integer.hh>
19
#include <qpdf/QPDF_Name.hh>
20
#include <qpdf/QPDF_Null.hh>
21
#include <qpdf/QPDF_Operator.hh>
22
#include <qpdf/QPDF_Real.hh>
23
#include <qpdf/QPDF_Reserved.hh>
24
#include <qpdf/QPDF_Stream.hh>
25
#include <qpdf/QPDF_String.hh>
26
#include <qpdf/QPDF_Unresolved.hh>
27
28
#include <qpdf/QIntC.hh>
29
#include <qpdf/QTC.hh>
30
#include <qpdf/QUtil.hh>
31
32
#include <algorithm>
33
#include <array>
34
#include <cctype>
35
#include <climits>
36
#include <cstdlib>
37
#include <cstring>
38
#include <stdexcept>
39
40
using namespace std::literals;
41
42
namespace
43
{
44
    class TerminateParsing
45
    {
46
    };
47
} // namespace
48
49
QPDFObjectHandle::StreamDataProvider::StreamDataProvider(bool supports_retry) :
50
    supports_retry(supports_retry)
51
0
{
52
0
}
53
54
QPDFObjectHandle::StreamDataProvider::~StreamDataProvider() // NOLINT (modernize-use-equals-default)
55
0
{
56
    // Must be explicit and not inline -- see QPDF_DLL_CLASS in README-maintainer
57
0
}
58
59
void
60
QPDFObjectHandle::StreamDataProvider::provideStreamData(QPDFObjGen const& og, Pipeline* pipeline)
61
0
{
62
0
    return provideStreamData(og.getObj(), og.getGen(), pipeline);
63
0
}
64
65
bool
66
QPDFObjectHandle::StreamDataProvider::provideStreamData(
67
    QPDFObjGen const& og, Pipeline* pipeline, bool suppress_warnings, bool will_retry)
68
0
{
69
0
    return provideStreamData(og.getObj(), og.getGen(), pipeline, suppress_warnings, will_retry);
70
0
}
71
72
void
73
QPDFObjectHandle::StreamDataProvider::provideStreamData(
74
    int objid, int generation, Pipeline* pipeline)
75
0
{
76
0
    throw std::logic_error("you must override provideStreamData -- see QPDFObjectHandle.hh");
77
0
}
78
79
bool
80
QPDFObjectHandle::StreamDataProvider::provideStreamData(
81
    int objid, int generation, Pipeline* pipeline, bool suppress_warnings, bool will_retry)
82
0
{
83
0
    throw std::logic_error("you must override provideStreamData -- see QPDFObjectHandle.hh");
84
0
    return false;
85
0
}
86
87
bool
88
QPDFObjectHandle::StreamDataProvider::supportsRetry()
89
0
{
90
0
    return this->supports_retry;
91
0
}
92
93
namespace
94
{
95
    class CoalesceProvider: public QPDFObjectHandle::StreamDataProvider
96
    {
97
      public:
98
        CoalesceProvider(QPDFObjectHandle containing_page, QPDFObjectHandle old_contents) :
99
            containing_page(containing_page),
100
            old_contents(old_contents)
101
0
        {
102
0
        }
103
0
        ~CoalesceProvider() override = default;
104
        void provideStreamData(QPDFObjGen const&, Pipeline* pipeline) override;
105
106
      private:
107
        QPDFObjectHandle containing_page;
108
        QPDFObjectHandle old_contents;
109
    };
110
} // namespace
111
112
void
113
CoalesceProvider::provideStreamData(QPDFObjGen const&, Pipeline* p)
114
0
{
115
0
    QTC::TC("qpdf", "QPDFObjectHandle coalesce provide stream data");
116
0
    std::string description = "page object " + containing_page.getObjGen().unparse(' ');
117
0
    std::string all_description;
118
0
    old_contents.pipeContentStreams(p, description, all_description);
119
0
}
120
121
void
122
QPDFObjectHandle::TokenFilter::handleEOF()
123
0
{
124
0
}
125
126
void
127
QPDFObjectHandle::TokenFilter::setPipeline(Pipeline* p)
128
0
{
129
0
    this->pipeline = p;
130
0
}
131
132
void
133
QPDFObjectHandle::TokenFilter::write(char const* data, size_t len)
134
0
{
135
0
    if (!this->pipeline) {
136
0
        return;
137
0
    }
138
0
    if (len) {
139
0
        this->pipeline->write(data, len);
140
0
    }
141
0
}
142
143
void
144
QPDFObjectHandle::TokenFilter::write(std::string const& str)
145
0
{
146
0
    write(str.c_str(), str.length());
147
0
}
148
149
void
150
QPDFObjectHandle::TokenFilter::writeToken(QPDFTokenizer::Token const& token)
151
0
{
152
0
    std::string const& value = token.getRawValue();
153
0
    write(value.c_str(), value.length());
154
0
}
155
156
void
157
QPDFObjectHandle::ParserCallbacks::handleObject(QPDFObjectHandle)
158
0
{
159
0
    throw std::logic_error("You must override one of the handleObject methods in ParserCallbacks");
160
0
}
161
162
void
163
QPDFObjectHandle::ParserCallbacks::handleObject(QPDFObjectHandle oh, size_t, size_t)
164
0
{
165
    // This version of handleObject was added in qpdf 9. If the developer did not override it, fall
166
    // back to the older interface.
167
0
    handleObject(oh);
168
0
}
169
170
void
171
QPDFObjectHandle::ParserCallbacks::contentSize(size_t)
172
0
{
173
    // Ignore by default; overriding this is optional.
174
0
}
175
176
void
177
QPDFObjectHandle::ParserCallbacks::terminateParsing()
178
0
{
179
0
    throw TerminateParsing();
180
0
}
181
182
namespace
183
{
184
    class LastChar: public Pipeline
185
    {
186
      public:
187
        LastChar(Pipeline* next);
188
        ~LastChar() override = default;
189
        void write(unsigned char const* data, size_t len) override;
190
        void finish() override;
191
        unsigned char getLastChar();
192
193
      private:
194
        unsigned char last_char{0};
195
    };
196
} // namespace
197
198
LastChar::LastChar(Pipeline* next) :
199
    Pipeline("lastchar", next)
200
0
{
201
0
}
202
203
void
204
LastChar::write(unsigned char const* data, size_t len)
205
0
{
206
0
    if (len > 0) {
207
0
        this->last_char = data[len - 1];
208
0
    }
209
0
    getNext()->write(data, len);
210
0
}
211
212
void
213
LastChar::finish()
214
0
{
215
0
    getNext()->finish();
216
0
}
217
218
unsigned char
219
LastChar::getLastChar()
220
0
{
221
0
    return this->last_char;
222
0
}
223
224
#ifndef QPDF_FUTURE
225
bool
226
QPDFObjectHandle::isSameObjectAs(QPDFObjectHandle const& rhs) const
227
{
228
    return this->obj == rhs.obj;
229
}
230
#else
231
bool
232
QPDFObjectHandle::isSameObjectAs(QPDFObjectHandle const& rhs) const noexcept
233
0
{
234
0
    return this->obj == rhs.obj;
235
0
}
236
#endif
237
void
238
QPDFObjectHandle::disconnect()
239
895k
{
240
    // Recursively remove association with any QPDF object. This method may only be called during
241
    // final destruction. QPDF::~QPDF() calls it for indirect objects using the object pointer
242
    // itself, so we don't do that here. Other objects call it through this method.
243
895k
    if (obj && !isIndirect()) {
244
723k
        this->obj->disconnect();
245
723k
    }
246
895k
}
247
248
#ifndef QPDF_FUTURE
249
qpdf_object_type_e
250
QPDFObjectHandle::getTypeCode()
251
#else
252
qpdf_object_type_e
253
QPDFObjectHandle::getTypeCode() const
254
#endif
255
2.87M
{
256
2.87M
    return obj ? obj->getResolvedTypeCode() : ::ot_uninitialized;
257
2.87M
}
258
259
#ifndef QPDF_FUTURE
260
char const*
261
QPDFObjectHandle::getTypeName()
262
#else
263
char const*
264
QPDFObjectHandle::getTypeName() const
265
#endif
266
2.54k
{
267
2.54k
    static constexpr std::array<char const*, 15> tn{
268
2.54k
        "uninitialized",
269
2.54k
        "reserved",
270
2.54k
        "null",
271
2.54k
        "boolean",
272
2.54k
        "integer",
273
2.54k
        "real",
274
2.54k
        "string",
275
2.54k
        "name",
276
2.54k
        "array",
277
2.54k
        "dictionary",
278
2.54k
        "stream",
279
2.54k
        "operator",
280
2.54k
        "inline-image",
281
2.54k
        "unresolved",
282
2.54k
        "destroyed"};
283
2.54k
    return obj ? tn[getTypeCode()] : "uninitialized";
284
2.54k
}
285
286
QPDF_Array*
287
QPDFObjectHandle::asArray() const
288
2.60M
{
289
2.60M
    return obj ? obj->as<QPDF_Array>() : nullptr;
290
2.60M
}
291
292
QPDF_Bool*
293
QPDFObjectHandle::asBool() const
294
0
{
295
0
    return obj ? obj->as<QPDF_Bool>() : nullptr;
296
0
}
297
298
QPDF_Dictionary*
299
QPDFObjectHandle::asDictionary() const
300
3.96M
{
301
3.96M
    return obj ? obj->as<QPDF_Dictionary>() : nullptr;
302
3.96M
}
303
304
QPDF_InlineImage*
305
QPDFObjectHandle::asInlineImage() const
306
0
{
307
0
    return obj ? obj->as<QPDF_InlineImage>() : nullptr;
308
0
}
309
310
QPDF_Integer*
311
QPDFObjectHandle::asInteger() const
312
65.9k
{
313
65.9k
    return obj ? obj->as<QPDF_Integer>() : nullptr;
314
65.9k
}
315
316
QPDF_Name*
317
QPDFObjectHandle::asName() const
318
0
{
319
0
    return obj ? obj->as<QPDF_Name>() : nullptr;
320
0
}
321
322
QPDF_Null*
323
QPDFObjectHandle::asNull() const
324
0
{
325
0
    return obj ? obj->as<QPDF_Null>() : nullptr;
326
0
}
327
328
QPDF_Operator*
329
QPDFObjectHandle::asOperator() const
330
0
{
331
0
    return obj ? obj->as<QPDF_Operator>() : nullptr;
332
0
}
333
334
QPDF_Real*
335
QPDFObjectHandle::asReal() const
336
0
{
337
0
    return obj ? obj->as<QPDF_Real>() : nullptr;
338
0
}
339
340
QPDF_Reserved*
341
QPDFObjectHandle::asReserved() const
342
0
{
343
0
    return obj ? obj->as<QPDF_Reserved>() : nullptr;
344
0
}
345
346
QPDF_Stream*
347
QPDFObjectHandle::asStream() const
348
601k
{
349
601k
    return obj ? obj->as<QPDF_Stream>() : nullptr;
350
601k
}
351
352
QPDF_Stream*
353
QPDFObjectHandle::asStreamWithAssert() const
354
601k
{
355
601k
    auto stream = asStream();
356
601k
    assertType("stream", stream);
357
601k
    return stream;
358
601k
}
359
360
QPDF_String*
361
QPDFObjectHandle::asString() const
362
0
{
363
0
    return obj ? obj->as<QPDF_String>() : nullptr;
364
0
}
365
366
#ifndef QPDF_FUTURE
367
bool
368
QPDFObjectHandle::isDestroyed()
369
#else
370
bool
371
QPDFObjectHandle::isDestroyed() const
372
#endif
373
0
{
374
0
    return obj && obj->getResolvedTypeCode() == ::ot_destroyed;
375
0
}
376
377
#ifndef QPDF_FUTURE
378
bool
379
QPDFObjectHandle::isBool()
380
#else
381
bool
382
QPDFObjectHandle::isBool() const
383
#endif
384
601
{
385
601
    return obj && obj->getResolvedTypeCode() == ::ot_boolean;
386
601
}
387
388
bool
389
QPDFObjectHandle::isDirectNull() const
390
0
{
391
    // Don't call dereference() -- this is a const method, and we know
392
    // objid == 0, so there's nothing to resolve.
393
0
    return (obj && getObjectID() == 0 && obj->getTypeCode() == ::ot_null);
394
0
}
395
396
#ifndef QPDF_FUTURE
397
bool
398
QPDFObjectHandle::isNull()
399
#else
400
bool
401
QPDFObjectHandle::isNull() const
402
#endif
403
3.26M
{
404
3.26M
    return obj && obj->getResolvedTypeCode() == ::ot_null;
405
3.26M
}
406
407
#ifndef QPDF_FUTURE
408
bool
409
QPDFObjectHandle::isInteger()
410
#else
411
bool
412
QPDFObjectHandle::isInteger() const
413
#endif
414
153k
{
415
153k
    return obj && obj->getResolvedTypeCode() == ::ot_integer;
416
153k
}
417
418
#ifndef QPDF_FUTURE
419
bool
420
QPDFObjectHandle::isReal()
421
#else
422
bool
423
QPDFObjectHandle::isReal() const
424
#endif
425
4.69k
{
426
4.69k
    return obj && obj->getResolvedTypeCode() == ::ot_real;
427
4.69k
}
428
429
#ifndef QPDF_FUTURE
430
bool
431
QPDFObjectHandle::isNumber()
432
#else
433
bool
434
QPDFObjectHandle::isNumber() const
435
#endif
436
89.1k
{
437
89.1k
    return (isInteger() || isReal());
438
89.1k
}
439
440
#ifndef QPDF_FUTURE
441
double
442
QPDFObjectHandle::getNumericValue()
443
#else
444
double
445
QPDFObjectHandle::getNumericValue() const
446
#endif
447
0
{
448
0
    if (isInteger()) {
449
0
        return static_cast<double>(getIntValue());
450
0
    } else if (isReal()) {
451
0
        return atof(getRealValue().c_str());
452
0
    } else {
453
0
        typeWarning("number", "returning 0");
454
0
        QTC::TC("qpdf", "QPDFObjectHandle numeric non-numeric");
455
0
        return 0;
456
0
    }
457
0
}
458
459
#ifndef QPDF_FUTURE
460
bool
461
QPDFObjectHandle::getValueAsNumber(double& value)
462
#else
463
bool
464
QPDFObjectHandle::getValueAsNumber(double& value) const
465
#endif
466
0
{
467
0
    if (!isNumber()) {
468
0
        return false;
469
0
    }
470
0
    value = getNumericValue();
471
0
    return true;
472
0
}
473
474
#ifndef QPDF_FUTURE
475
bool
476
QPDFObjectHandle::isName()
477
#else
478
bool
479
QPDFObjectHandle::isName() const
480
#endif
481
1.41M
{
482
1.41M
    return obj && obj->getResolvedTypeCode() == ::ot_name;
483
1.41M
}
484
485
#ifndef QPDF_FUTURE
486
bool
487
QPDFObjectHandle::isString()
488
#else
489
bool
490
QPDFObjectHandle::isString() const
491
#endif
492
10.4k
{
493
10.4k
    return obj && obj->getResolvedTypeCode() == ::ot_string;
494
10.4k
}
495
496
#ifndef QPDF_FUTURE
497
bool
498
QPDFObjectHandle::isOperator()
499
#else
500
bool
501
QPDFObjectHandle::isOperator() const
502
#endif
503
0
{
504
0
    return obj && obj->getResolvedTypeCode() == ::ot_operator;
505
0
}
506
507
#ifndef QPDF_FUTURE
508
bool
509
QPDFObjectHandle::isInlineImage()
510
#else
511
bool
512
QPDFObjectHandle::isInlineImage() const
513
#endif
514
0
{
515
0
    return obj && obj->getResolvedTypeCode() == ::ot_inlineimage;
516
0
}
517
518
#ifndef QPDF_FUTURE
519
bool
520
QPDFObjectHandle::isArray()
521
#else
522
bool
523
QPDFObjectHandle::isArray() const
524
#endif
525
3.03M
{
526
3.03M
    return obj && obj->getResolvedTypeCode() == ::ot_array;
527
3.03M
}
528
529
#ifndef QPDF_FUTURE
530
bool
531
QPDFObjectHandle::isDictionary()
532
#else
533
bool
534
QPDFObjectHandle::isDictionary() const
535
#endif
536
5.54M
{
537
5.54M
    return obj && obj->getResolvedTypeCode() == ::ot_dictionary;
538
5.54M
}
539
540
#ifndef QPDF_FUTURE
541
bool
542
QPDFObjectHandle::isStream()
543
#else
544
bool
545
QPDFObjectHandle::isStream() const
546
#endif
547
3.21M
{
548
3.21M
    return obj && obj->getResolvedTypeCode() == ::ot_stream;
549
3.21M
}
550
551
#ifndef QPDF_FUTURE
552
bool
553
QPDFObjectHandle::isReserved()
554
#else
555
bool
556
QPDFObjectHandle::isReserved() const
557
#endif
558
0
{
559
0
    return obj && obj->getResolvedTypeCode() == ::ot_reserved;
560
0
}
561
562
#ifndef QPDF_FUTURE
563
bool
564
QPDFObjectHandle::isScalar()
565
#else
566
bool
567
QPDFObjectHandle::isScalar() const
568
#endif
569
601
{
570
601
    return isBool() || isInteger() || isName() || isNull() || isReal() || isString();
571
601
}
572
573
#ifndef QPDF_FUTURE
574
bool
575
QPDFObjectHandle::isNameAndEquals(std::string const& name)
576
#else
577
bool
578
QPDFObjectHandle::isNameAndEquals(std::string const& name) const
579
#endif
580
728k
{
581
728k
    return isName() && (getName() == name);
582
728k
}
583
584
#ifndef QPDF_FUTURE
585
bool
586
QPDFObjectHandle::isDictionaryOfType(std::string const& type, std::string const& subtype)
587
#else
588
bool
589
QPDFObjectHandle::isDictionaryOfType(std::string const& type, std::string const& subtype) const
590
#endif
591
2.36M
{
592
2.36M
    return isDictionary() && (type.empty() || getKey("/Type").isNameAndEquals(type)) &&
593
2.36M
        (subtype.empty() || getKey("/Subtype").isNameAndEquals(subtype));
594
2.36M
}
595
596
#ifndef QPDF_FUTURE
597
bool
598
QPDFObjectHandle::isStreamOfType(std::string const& type, std::string const& subtype)
599
#else
600
bool
601
QPDFObjectHandle::isStreamOfType(std::string const& type, std::string const& subtype) const
602
#endif
603
23.3k
{
604
23.3k
    return isStream() && getDict().isDictionaryOfType(type, subtype);
605
23.3k
}
606
607
// Bool accessors
608
609
#ifndef QPDF_FUTURE
610
bool
611
QPDFObjectHandle::getBoolValue()
612
#else
613
bool
614
QPDFObjectHandle::getBoolValue() const
615
#endif
616
0
{
617
0
    auto boolean = asBool();
618
0
    if (boolean) {
619
0
        return boolean->getVal();
620
0
    } else {
621
0
        typeWarning("boolean", "returning false");
622
0
        QTC::TC("qpdf", "QPDFObjectHandle boolean returning false");
623
0
        return false;
624
0
    }
625
0
}
626
627
#ifndef QPDF_FUTURE
628
bool
629
QPDFObjectHandle::getValueAsBool(bool& value)
630
#else
631
bool
632
QPDFObjectHandle::getValueAsBool(bool& value) const
633
#endif
634
0
{
635
0
    auto boolean = asBool();
636
0
    if (boolean == nullptr) {
637
0
        return false;
638
0
    }
639
0
    value = boolean->getVal();
640
0
    return true;
641
0
}
642
643
// Integer accessors
644
645
#ifndef QPDF_FUTURE
646
long long
647
QPDFObjectHandle::getIntValue()
648
#else
649
long long
650
QPDFObjectHandle::getIntValue() const
651
#endif
652
64.6k
{
653
64.6k
    auto integer = asInteger();
654
64.6k
    if (integer) {
655
64.5k
        return integer->getVal();
656
64.5k
    } else {
657
48
        typeWarning("integer", "returning 0");
658
48
        QTC::TC("qpdf", "QPDFObjectHandle integer returning 0");
659
48
        return 0;
660
48
    }
661
64.6k
}
662
663
#ifndef QPDF_FUTURE
664
bool
665
QPDFObjectHandle::getValueAsInt(long long& value)
666
#else
667
bool
668
QPDFObjectHandle::getValueAsInt(long long& value) const
669
#endif
670
1.29k
{
671
1.29k
    auto integer = asInteger();
672
1.29k
    if (integer == nullptr) {
673
11
        return false;
674
11
    }
675
1.28k
    value = integer->getVal();
676
1.28k
    return true;
677
1.29k
}
678
679
#ifndef QPDF_FUTURE
680
int
681
QPDFObjectHandle::getIntValueAsInt()
682
#else
683
int
684
QPDFObjectHandle::getIntValueAsInt() const
685
#endif
686
9.06k
{
687
9.06k
    int result = 0;
688
9.06k
    long long v = getIntValue();
689
9.06k
    if (v < INT_MIN) {
690
3
        QTC::TC("qpdf", "QPDFObjectHandle int returning INT_MIN");
691
3
        warnIfPossible("requested value of integer is too small; returning INT_MIN");
692
3
        result = INT_MIN;
693
9.05k
    } else if (v > INT_MAX) {
694
5
        QTC::TC("qpdf", "QPDFObjectHandle int returning INT_MAX");
695
5
        warnIfPossible("requested value of integer is too big; returning INT_MAX");
696
5
        result = INT_MAX;
697
9.05k
    } else {
698
9.05k
        result = static_cast<int>(v);
699
9.05k
    }
700
9.06k
    return result;
701
9.06k
}
702
703
#ifndef QPDF_FUTURE
704
bool
705
QPDFObjectHandle::getValueAsInt(int& value)
706
#else
707
bool
708
QPDFObjectHandle::getValueAsInt(int& value) const
709
#endif
710
0
{
711
0
    if (!isInteger()) {
712
0
        return false;
713
0
    }
714
0
    value = getIntValueAsInt();
715
0
    return true;
716
0
}
717
718
#ifndef QPDF_FUTURE
719
unsigned long long
720
QPDFObjectHandle::getUIntValue()
721
#else
722
unsigned long long
723
QPDFObjectHandle::getUIntValue() const
724
#endif
725
53.9k
{
726
53.9k
    long long v = getIntValue();
727
53.9k
    if (v < 0) {
728
12
        QTC::TC("qpdf", "QPDFObjectHandle uint returning 0");
729
12
        warnIfPossible("unsigned value request for negative number; returning 0");
730
12
        return 0;
731
53.9k
    } else {
732
53.9k
        return static_cast<unsigned long long>(v);
733
53.9k
    }
734
53.9k
}
735
736
#ifndef QPDF_FUTURE
737
bool
738
QPDFObjectHandle::getValueAsUInt(unsigned long long& value)
739
#else
740
bool
741
QPDFObjectHandle::getValueAsUInt(unsigned long long& value) const
742
#endif
743
0
{
744
0
    if (!isInteger()) {
745
0
        return false;
746
0
    }
747
0
    value = getUIntValue();
748
0
    return true;
749
0
}
750
751
#ifndef QPDF_FUTURE
752
unsigned int
753
QPDFObjectHandle::getUIntValueAsUInt()
754
#else
755
unsigned int
756
QPDFObjectHandle::getUIntValueAsUInt() const
757
#endif
758
0
{
759
0
    long long v = getIntValue();
760
0
    if (v < 0) {
761
0
        QTC::TC("qpdf", "QPDFObjectHandle uint uint returning 0");
762
0
        warnIfPossible("unsigned integer value request for negative number; returning 0");
763
0
        return 0;
764
0
    } else if (v > UINT_MAX) {
765
0
        QTC::TC("qpdf", "QPDFObjectHandle uint returning UINT_MAX");
766
0
        warnIfPossible("requested value of unsigned integer is too big; returning UINT_MAX");
767
0
        return UINT_MAX;
768
0
    } else {
769
0
        return static_cast<unsigned int>(v);
770
0
    }
771
0
}
772
773
#ifndef QPDF_FUTURE
774
bool
775
QPDFObjectHandle::getValueAsUInt(unsigned int& value)
776
#else
777
bool
778
QPDFObjectHandle::getValueAsUInt(unsigned int& value) const
779
#endif
780
0
{
781
0
    if (!isInteger()) {
782
0
        return false;
783
0
    }
784
0
    value = getUIntValueAsUInt();
785
0
    return true;
786
0
}
787
788
// Real accessors
789
790
#ifndef QPDF_FUTURE
791
std::string
792
QPDFObjectHandle::getRealValue()
793
#else
794
std::string
795
QPDFObjectHandle::getRealValue() const
796
#endif
797
0
{
798
0
    if (isReal()) {
799
0
        return obj->getStringValue();
800
0
    } else {
801
0
        typeWarning("real", "returning 0.0");
802
0
        QTC::TC("qpdf", "QPDFObjectHandle real returning 0.0");
803
0
        return "0.0";
804
0
    }
805
0
}
806
807
#ifndef QPDF_FUTURE
808
bool
809
QPDFObjectHandle::getValueAsReal(std::string& value)
810
#else
811
bool
812
QPDFObjectHandle::getValueAsReal(std::string& value) const
813
#endif
814
0
{
815
0
    if (!isReal()) {
816
0
        return false;
817
0
    }
818
0
    value = obj->getStringValue();
819
0
    return true;
820
0
}
821
822
// Name accessors
823
824
#ifndef QPDF_FUTURE
825
std::string
826
QPDFObjectHandle::getName()
827
#else
828
std::string
829
QPDFObjectHandle::getName() const
830
#endif
831
521k
{
832
521k
    if (isName()) {
833
521k
        return obj->getStringValue();
834
521k
    } else {
835
0
        typeWarning("name", "returning dummy name");
836
0
        QTC::TC("qpdf", "QPDFObjectHandle name returning dummy name");
837
0
        return "/QPDFFakeName";
838
0
    }
839
521k
}
840
841
#ifndef QPDF_FUTURE
842
bool
843
QPDFObjectHandle::getValueAsName(std::string& value)
844
#else
845
bool
846
QPDFObjectHandle::getValueAsName(std::string& value) const
847
#endif
848
0
{
849
0
    if (!isName()) {
850
0
        return false;
851
0
    }
852
0
    value = obj->getStringValue();
853
0
    return true;
854
0
}
855
856
// String accessors
857
858
#ifndef QPDF_FUTURE
859
std::string
860
QPDFObjectHandle::getStringValue()
861
#else
862
std::string
863
QPDFObjectHandle::getStringValue() const
864
#endif
865
7.56k
{
866
7.56k
    if (isString()) {
867
6.94k
        return obj->getStringValue();
868
6.94k
    } else {
869
625
        typeWarning("string", "returning empty string");
870
625
        QTC::TC("qpdf", "QPDFObjectHandle string returning empty string");
871
625
        return "";
872
625
    }
873
7.56k
}
874
875
#ifndef QPDF_FUTURE
876
bool
877
QPDFObjectHandle::getValueAsString(std::string& value)
878
#else
879
bool
880
QPDFObjectHandle::getValueAsString(std::string& value) const
881
#endif
882
0
{
883
0
    if (!isString()) {
884
0
        return false;
885
0
    }
886
0
    value = obj->getStringValue();
887
0
    return true;
888
0
}
889
890
#ifndef QPDF_FUTURE
891
std::string
892
QPDFObjectHandle::getUTF8Value()
893
#else
894
std::string
895
QPDFObjectHandle::getUTF8Value() const
896
#endif
897
0
{
898
0
    auto str = asString();
899
0
    if (str) {
900
0
        return str->getUTF8Val();
901
0
    } else {
902
0
        typeWarning("string", "returning empty string");
903
0
        QTC::TC("qpdf", "QPDFObjectHandle string returning empty utf8");
904
0
        return "";
905
0
    }
906
0
}
907
908
#ifndef QPDF_FUTURE
909
bool
910
QPDFObjectHandle::getValueAsUTF8(std::string& value)
911
#else
912
bool
913
QPDFObjectHandle::getValueAsUTF8(std::string& value) const
914
#endif
915
0
{
916
0
    auto str = asString();
917
0
    if (str == nullptr) {
918
0
        return false;
919
0
    }
920
0
    value = str->getUTF8Val();
921
0
    return true;
922
0
}
923
924
// Operator and Inline Image accessors
925
926
#ifndef QPDF_FUTURE
927
std::string
928
QPDFObjectHandle::getOperatorValue()
929
#else
930
std::string
931
QPDFObjectHandle::getOperatorValue() const
932
#endif
933
0
{
934
0
    if (isOperator()) {
935
0
        return obj->getStringValue();
936
0
    } else {
937
0
        typeWarning("operator", "returning fake value");
938
0
        QTC::TC("qpdf", "QPDFObjectHandle operator returning fake value");
939
0
        return "QPDFFAKE";
940
0
    }
941
0
}
942
943
#ifndef QPDF_FUTURE
944
bool
945
QPDFObjectHandle::getValueAsOperator(std::string& value)
946
#else
947
bool
948
QPDFObjectHandle::getValueAsOperator(std::string& value) const
949
#endif
950
0
{
951
0
    if (!isOperator()) {
952
0
        return false;
953
0
    }
954
0
    value = obj->getStringValue();
955
0
    return true;
956
0
}
957
958
#ifndef QPDF_FUTURE
959
std::string
960
QPDFObjectHandle::getInlineImageValue()
961
#else
962
std::string
963
QPDFObjectHandle::getInlineImageValue() const
964
#endif
965
0
{
966
0
    if (isInlineImage()) {
967
0
        return obj->getStringValue();
968
0
    } else {
969
0
        typeWarning("inlineimage", "returning empty data");
970
0
        QTC::TC("qpdf", "QPDFObjectHandle inlineimage returning empty data");
971
0
        return "";
972
0
    }
973
0
}
974
975
#ifndef QPDF_FUTURE
976
bool
977
QPDFObjectHandle::getValueAsInlineImage(std::string& value)
978
#else
979
bool
980
QPDFObjectHandle::getValueAsInlineImage(std::string& value) const
981
#endif
982
0
{
983
0
    if (!isInlineImage()) {
984
0
        return false;
985
0
    }
986
0
    value = obj->getStringValue();
987
0
    return true;
988
0
}
989
990
// Array accessors
991
992
QPDFObjectHandle::QPDFArrayItems
993
QPDFObjectHandle::aitems()
994
3.80k
{
995
3.80k
    return *this;
996
3.80k
}
997
998
#ifndef QPDF_FUTURE
999
int
1000
QPDFObjectHandle::getArrayNItems()
1001
#else
1002
int
1003
QPDFObjectHandle::getArrayNItems() const
1004
#endif
1005
396k
{
1006
396k
    if (auto array = asArray()) {
1007
395k
        return array->size();
1008
395k
    } else {
1009
424
        typeWarning("array", "treating as empty");
1010
424
        QTC::TC("qpdf", "QPDFObjectHandle array treating as empty");
1011
424
        return 0;
1012
424
    }
1013
396k
}
1014
1015
#ifndef QPDF_FUTURE
1016
QPDFObjectHandle
1017
QPDFObjectHandle::getArrayItem(int n)
1018
#else
1019
QPDFObjectHandle
1020
QPDFObjectHandle::getArrayItem(int n) const
1021
#endif
1022
2.01M
{
1023
2.01M
    if (auto array = asArray()) {
1024
2.01M
        if (auto result = array->at(n); result.obj != nullptr) {
1025
2.01M
            return result;
1026
2.01M
        } else {
1027
0
            objectWarning("returning null for out of bounds array access");
1028
0
            QTC::TC("qpdf", "QPDFObjectHandle array bounds");
1029
0
        }
1030
2.01M
    } else {
1031
236
        typeWarning("array", "returning null");
1032
236
        QTC::TC("qpdf", "QPDFObjectHandle array null for non-array");
1033
236
    }
1034
236
    static auto constexpr msg = " -> null returned from invalid array access"sv;
1035
236
    return QPDF_Null::create(obj, msg, "");
1036
2.01M
}
1037
1038
#ifndef QPDF_FUTURE
1039
bool
1040
QPDFObjectHandle::isRectangle()
1041
#else
1042
bool
1043
QPDFObjectHandle::isRectangle() const
1044
#endif
1045
29.2k
{
1046
29.2k
    if (auto array = asArray()) {
1047
111k
        for (int i = 0; i < 4; ++i) {
1048
89.2k
            if (auto item = array->at(i); !(item.obj && item.isNumber())) {
1049
478
                return false;
1050
478
            }
1051
89.2k
        }
1052
21.9k
        return array->size() == 4;
1053
22.4k
    }
1054
6.74k
    return false;
1055
29.2k
}
1056
1057
#ifndef QPDF_FUTURE
1058
bool
1059
QPDFObjectHandle::isMatrix()
1060
#else
1061
bool
1062
QPDFObjectHandle::isMatrix() const
1063
#endif
1064
0
{
1065
0
    if (auto array = asArray()) {
1066
0
        for (int i = 0; i < 6; ++i) {
1067
0
            if (auto item = array->at(i); !(item.obj && item.isNumber())) {
1068
0
                return false;
1069
0
            }
1070
0
        }
1071
0
        return array->size() == 6;
1072
0
    }
1073
0
    return false;
1074
0
}
1075
1076
#ifndef QPDF_FUTURE
1077
QPDFObjectHandle::Rectangle
1078
QPDFObjectHandle::getArrayAsRectangle()
1079
#else
1080
QPDFObjectHandle::Rectangle
1081
QPDFObjectHandle::getArrayAsRectangle() const
1082
#endif
1083
0
{
1084
0
    if (auto array = asArray()) {
1085
0
        if (array->size() != 4) {
1086
0
            return {};
1087
0
        }
1088
0
        double items[4];
1089
0
        for (int i = 0; i < 4; ++i) {
1090
0
            if (!array->at(i).getValueAsNumber(items[i])) {
1091
0
                return {};
1092
0
            }
1093
0
        }
1094
0
        return {
1095
0
            std::min(items[0], items[2]),
1096
0
            std::min(items[1], items[3]),
1097
0
            std::max(items[0], items[2]),
1098
0
            std::max(items[1], items[3])};
1099
0
    }
1100
0
    return {};
1101
0
}
1102
1103
#ifndef QPDF_FUTURE
1104
QPDFObjectHandle::Matrix
1105
QPDFObjectHandle::getArrayAsMatrix()
1106
#else
1107
QPDFObjectHandle::Matrix
1108
QPDFObjectHandle::getArrayAsMatrix() const
1109
#endif
1110
0
{
1111
0
    if (auto array = asArray()) {
1112
0
        if (array->size() != 6) {
1113
0
            return {};
1114
0
        }
1115
0
        double items[6];
1116
0
        for (int i = 0; i < 6; ++i) {
1117
0
            if (!array->at(i).getValueAsNumber(items[i])) {
1118
0
                return {};
1119
0
            }
1120
0
        }
1121
0
        return {items[0], items[1], items[2], items[3], items[4], items[5]};
1122
0
    }
1123
0
    return {};
1124
0
}
1125
1126
#ifndef QPDF_FUTURE
1127
std::vector<QPDFObjectHandle>
1128
QPDFObjectHandle::getArrayAsVector()
1129
#else
1130
std::vector<QPDFObjectHandle>
1131
QPDFObjectHandle::getArrayAsVector() const
1132
#endif
1133
164k
{
1134
164k
    auto array = asArray();
1135
164k
    if (array) {
1136
164k
        return array->getAsVector();
1137
164k
    } else {
1138
0
        typeWarning("array", "treating as empty");
1139
0
        QTC::TC("qpdf", "QPDFObjectHandle array treating as empty vector");
1140
0
    }
1141
0
    return {};
1142
164k
}
1143
1144
// Array mutators
1145
1146
void
1147
QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const& item)
1148
1.66k
{
1149
1.66k
    if (auto array = asArray()) {
1150
1.66k
        if (!array->setAt(n, item)) {
1151
0
            objectWarning("ignoring attempt to set out of bounds array item");
1152
0
            QTC::TC("qpdf", "QPDFObjectHandle set array bounds");
1153
0
        }
1154
1.66k
    } else {
1155
0
        typeWarning("array", "ignoring attempt to set item");
1156
0
        QTC::TC("qpdf", "QPDFObjectHandle array ignoring set item");
1157
0
    }
1158
1.66k
}
1159
void
1160
QPDFObjectHandle::setArrayFromVector(std::vector<QPDFObjectHandle> const& items)
1161
0
{
1162
0
    if (auto array = asArray()) {
1163
0
        array->setFromVector(items);
1164
0
    } else {
1165
0
        typeWarning("array", "ignoring attempt to replace items");
1166
0
        QTC::TC("qpdf", "QPDFObjectHandle array ignoring replace items");
1167
0
    }
1168
0
}
1169
1170
void
1171
QPDFObjectHandle::insertItem(int at, QPDFObjectHandle const& item)
1172
0
{
1173
0
    if (auto array = asArray()) {
1174
0
        if (!array->insert(at, item)) {
1175
0
            objectWarning("ignoring attempt to insert out of bounds array item");
1176
0
            QTC::TC("qpdf", "QPDFObjectHandle insert array bounds");
1177
0
        }
1178
0
    } else {
1179
0
        typeWarning("array", "ignoring attempt to insert item");
1180
0
        QTC::TC("qpdf", "QPDFObjectHandle array ignoring insert item");
1181
0
    }
1182
0
}
1183
1184
QPDFObjectHandle
1185
QPDFObjectHandle::insertItemAndGetNew(int at, QPDFObjectHandle const& item)
1186
0
{
1187
0
    insertItem(at, item);
1188
0
    return item;
1189
0
}
1190
1191
void
1192
QPDFObjectHandle::appendItem(QPDFObjectHandle const& item)
1193
0
{
1194
0
    if (auto array = asArray()) {
1195
0
        array->push_back(item);
1196
0
    } else {
1197
0
        typeWarning("array", "ignoring attempt to append item");
1198
0
        QTC::TC("qpdf", "QPDFObjectHandle array ignoring append item");
1199
0
    }
1200
0
}
1201
1202
QPDFObjectHandle
1203
QPDFObjectHandle::appendItemAndGetNew(QPDFObjectHandle const& item)
1204
0
{
1205
0
    appendItem(item);
1206
0
    return item;
1207
0
}
1208
1209
void
1210
QPDFObjectHandle::eraseItem(int at)
1211
44
{
1212
44
    if (auto array = asArray()) {
1213
22
        if (!array->erase(at)) {
1214
0
            objectWarning("ignoring attempt to erase out of bounds array item");
1215
0
            QTC::TC("qpdf", "QPDFObjectHandle erase array bounds");
1216
0
        }
1217
22
    } else {
1218
22
        typeWarning("array", "ignoring attempt to erase item");
1219
22
        QTC::TC("qpdf", "QPDFObjectHandle array ignoring erase item");
1220
22
    }
1221
44
}
1222
1223
QPDFObjectHandle
1224
QPDFObjectHandle::eraseItemAndGetOld(int at)
1225
0
{
1226
0
    auto array = asArray();
1227
0
    auto result = (array && at < array->size() && at >= 0) ? array->at(at) : newNull();
1228
0
    eraseItem(at);
1229
0
    return result;
1230
0
}
1231
1232
// Dictionary accessors
1233
1234
QPDFObjectHandle::QPDFDictItems
1235
QPDFObjectHandle::ditems()
1236
0
{
1237
0
    return {*this};
1238
0
}
1239
1240
#ifndef QPDF_FUTURE
1241
bool
1242
QPDFObjectHandle::hasKey(std::string const& key)
1243
#else
1244
bool
1245
QPDFObjectHandle::hasKey(std::string const& key) const
1246
#endif
1247
61.0k
{
1248
61.0k
    auto dict = asDictionary();
1249
61.0k
    if (dict) {
1250
60.4k
        return dict->hasKey(key);
1251
60.4k
    } else {
1252
635
        typeWarning("dictionary", "returning false for a key containment request");
1253
635
        QTC::TC("qpdf", "QPDFObjectHandle dictionary false for hasKey");
1254
635
        return false;
1255
635
    }
1256
61.0k
}
1257
1258
#ifndef QPDF_FUTURE
1259
QPDFObjectHandle
1260
QPDFObjectHandle::getKey(std::string const& key)
1261
#else
1262
QPDFObjectHandle
1263
QPDFObjectHandle::getKey(std::string const& key) const
1264
#endif
1265
2.81M
{
1266
2.81M
    if (auto dict = asDictionary()) {
1267
2.81M
        return dict->getKey(key);
1268
2.81M
    } else {
1269
0
        typeWarning("dictionary", "returning null for attempted key retrieval");
1270
0
        QTC::TC("qpdf", "QPDFObjectHandle dictionary null for getKey");
1271
0
        static auto constexpr msg = " -> null returned from getting key $VD from non-Dictionary"sv;
1272
0
        return QPDF_Null::create(obj, msg, "");
1273
0
    }
1274
2.81M
}
1275
1276
#ifndef QPDF_FUTURE
1277
QPDFObjectHandle
1278
QPDFObjectHandle::getKeyIfDict(std::string const& key)
1279
#else
1280
QPDFObjectHandle
1281
QPDFObjectHandle::getKeyIfDict(std::string const& key) const
1282
#endif
1283
0
{
1284
0
    return isNull() ? newNull() : getKey(key);
1285
0
}
1286
1287
#ifndef QPDF_FUTURE
1288
std::set<std::string>
1289
QPDFObjectHandle::getKeys()
1290
#else
1291
std::set<std::string>
1292
QPDFObjectHandle::getKeys() const
1293
#endif
1294
489k
{
1295
489k
    std::set<std::string> result;
1296
489k
    auto dict = asDictionary();
1297
489k
    if (dict) {
1298
489k
        result = dict->getKeys();
1299
489k
    } else {
1300
41
        typeWarning("dictionary", "treating as empty");
1301
41
        QTC::TC("qpdf", "QPDFObjectHandle dictionary empty set for getKeys");
1302
41
    }
1303
489k
    return result;
1304
489k
}
1305
1306
#ifndef QPDF_FUTURE
1307
std::map<std::string, QPDFObjectHandle>
1308
QPDFObjectHandle::getDictAsMap()
1309
#else
1310
std::map<std::string, QPDFObjectHandle>
1311
QPDFObjectHandle::getDictAsMap() const
1312
#endif
1313
358k
{
1314
358k
    std::map<std::string, QPDFObjectHandle> result;
1315
358k
    auto dict = asDictionary();
1316
358k
    if (dict) {
1317
358k
        result = dict->getAsMap();
1318
358k
    } else {
1319
0
        typeWarning("dictionary", "treating as empty");
1320
0
        QTC::TC("qpdf", "QPDFObjectHandle dictionary empty map for asMap");
1321
0
    }
1322
358k
    return result;
1323
358k
}
1324
1325
// Array and Name accessors
1326
#ifndef QPDF_FUTURE
1327
bool
1328
QPDFObjectHandle::isOrHasName(std::string const& value)
1329
#else
1330
bool
1331
QPDFObjectHandle::isOrHasName(std::string const& value) const
1332
#endif
1333
30.9k
{
1334
30.9k
    if (isNameAndEquals(value)) {
1335
0
        return true;
1336
30.9k
    } else if (isArray()) {
1337
9.22k
        for (auto& item: getArrayAsVector()) {
1338
9.22k
            if (item.isNameAndEquals(value)) {
1339
22
                return true;
1340
22
            }
1341
9.22k
        }
1342
3.16k
    }
1343
30.8k
    return false;
1344
30.9k
}
1345
1346
void
1347
QPDFObjectHandle::makeResourcesIndirect(QPDF& owning_qpdf)
1348
0
{
1349
0
    if (!isDictionary()) {
1350
0
        return;
1351
0
    }
1352
0
    for (auto const& i1: ditems()) {
1353
0
        QPDFObjectHandle sub = i1.second;
1354
0
        if (!sub.isDictionary()) {
1355
0
            continue;
1356
0
        }
1357
0
        for (auto const& i2: sub.ditems()) {
1358
0
            std::string const& key = i2.first;
1359
0
            QPDFObjectHandle val = i2.second;
1360
0
            if (!val.isIndirect()) {
1361
0
                sub.replaceKey(key, owning_qpdf.makeIndirectObject(val));
1362
0
            }
1363
0
        }
1364
0
    }
1365
0
}
1366
1367
void
1368
QPDFObjectHandle::mergeResources(
1369
    QPDFObjectHandle other, std::map<std::string, std::map<std::string, std::string>>* conflicts)
1370
0
{
1371
0
    if (!(isDictionary() && other.isDictionary())) {
1372
0
        QTC::TC("qpdf", "QPDFObjectHandle merge top type mismatch");
1373
0
        return;
1374
0
    }
1375
1376
0
    auto make_og_to_name = [](QPDFObjectHandle& dict,
1377
0
                              std::map<QPDFObjGen, std::string>& og_to_name) {
1378
0
        for (auto const& i: dict.ditems()) {
1379
0
            if (i.second.isIndirect()) {
1380
0
                og_to_name[i.second.getObjGen()] = i.first;
1381
0
            }
1382
0
        }
1383
0
    };
1384
1385
    // This algorithm is described in comments in QPDFObjectHandle.hh
1386
    // above the declaration of mergeResources.
1387
0
    for (auto const& o_top: other.ditems()) {
1388
0
        std::string const& rtype = o_top.first;
1389
0
        QPDFObjectHandle other_val = o_top.second;
1390
0
        if (hasKey(rtype)) {
1391
0
            QPDFObjectHandle this_val = getKey(rtype);
1392
0
            if (this_val.isDictionary() && other_val.isDictionary()) {
1393
0
                if (this_val.isIndirect()) {
1394
                    // Do this even if there are no keys. Various places in the code call
1395
                    // mergeResources with resource dictionaries that contain empty subdictionaries
1396
                    // just to get this shallow copy functionality.
1397
0
                    QTC::TC("qpdf", "QPDFObjectHandle replace with copy");
1398
0
                    this_val = replaceKeyAndGetNew(rtype, this_val.shallowCopy());
1399
0
                }
1400
0
                std::map<QPDFObjGen, std::string> og_to_name;
1401
0
                std::set<std::string> rnames;
1402
0
                int min_suffix = 1;
1403
0
                bool initialized_maps = false;
1404
0
                for (auto const& ov_iter: other_val.ditems()) {
1405
0
                    std::string const& key = ov_iter.first;
1406
0
                    QPDFObjectHandle rval = ov_iter.second;
1407
0
                    if (!this_val.hasKey(key)) {
1408
0
                        if (!rval.isIndirect()) {
1409
0
                            QTC::TC("qpdf", "QPDFObjectHandle merge shallow copy");
1410
0
                            rval = rval.shallowCopy();
1411
0
                        }
1412
0
                        this_val.replaceKey(key, rval);
1413
0
                    } else if (conflicts) {
1414
0
                        if (!initialized_maps) {
1415
0
                            make_og_to_name(this_val, og_to_name);
1416
0
                            rnames = this_val.getResourceNames();
1417
0
                            initialized_maps = true;
1418
0
                        }
1419
0
                        auto rval_og = rval.getObjGen();
1420
0
                        if (rval.isIndirect() && og_to_name.count(rval_og)) {
1421
0
                            QTC::TC("qpdf", "QPDFObjectHandle merge reuse");
1422
0
                            auto new_key = og_to_name[rval_og];
1423
0
                            if (new_key != key) {
1424
0
                                (*conflicts)[rtype][key] = new_key;
1425
0
                            }
1426
0
                        } else {
1427
0
                            QTC::TC("qpdf", "QPDFObjectHandle merge generate");
1428
0
                            std::string new_key =
1429
0
                                getUniqueResourceName(key + "_", min_suffix, &rnames);
1430
0
                            (*conflicts)[rtype][key] = new_key;
1431
0
                            this_val.replaceKey(new_key, rval);
1432
0
                        }
1433
0
                    }
1434
0
                }
1435
0
            } else if (this_val.isArray() && other_val.isArray()) {
1436
0
                std::set<std::string> scalars;
1437
0
                for (auto this_item: this_val.aitems()) {
1438
0
                    if (this_item.isScalar()) {
1439
0
                        scalars.insert(this_item.unparse());
1440
0
                    }
1441
0
                }
1442
0
                for (auto other_item: other_val.aitems()) {
1443
0
                    if (other_item.isScalar()) {
1444
0
                        if (scalars.count(other_item.unparse()) == 0) {
1445
0
                            QTC::TC("qpdf", "QPDFObjectHandle merge array");
1446
0
                            this_val.appendItem(other_item);
1447
0
                        } else {
1448
0
                            QTC::TC("qpdf", "QPDFObjectHandle merge array dup");
1449
0
                        }
1450
0
                    }
1451
0
                }
1452
0
            }
1453
0
        } else {
1454
0
            QTC::TC("qpdf", "QPDFObjectHandle merge copy from other");
1455
0
            replaceKey(rtype, other_val.shallowCopy());
1456
0
        }
1457
0
    }
1458
0
}
1459
1460
#ifndef QPDF_FUTURE
1461
std::set<std::string>
1462
QPDFObjectHandle::getResourceNames()
1463
#else
1464
std::set<std::string>
1465
QPDFObjectHandle::getResourceNames() const
1466
#endif
1467
0
{
1468
    // Return second-level dictionary keys
1469
0
    std::set<std::string> result;
1470
0
    if (!isDictionary()) {
1471
0
        return result;
1472
0
    }
1473
0
    for (auto const& key: getKeys()) {
1474
0
        QPDFObjectHandle val = getKey(key);
1475
0
        if (val.isDictionary()) {
1476
0
            for (auto const& val_key: val.getKeys()) {
1477
0
                result.insert(val_key);
1478
0
            }
1479
0
        }
1480
0
    }
1481
0
    return result;
1482
0
}
1483
1484
#ifndef QPDF_FUTURE
1485
std::string
1486
QPDFObjectHandle::getUniqueResourceName(
1487
    std::string const& prefix, int& min_suffix, std::set<std::string>* namesp)
1488
#else
1489
std::string
1490
QPDFObjectHandle::getUniqueResourceName(
1491
    std::string const& prefix, int& min_suffix, std::set<std::string>* namesp) const
1492
#endif
1493
1494
0
{
1495
0
    std::set<std::string> names = (namesp ? *namesp : getResourceNames());
1496
0
    int max_suffix = min_suffix + QIntC::to_int(names.size());
1497
0
    while (min_suffix <= max_suffix) {
1498
0
        std::string candidate = prefix + std::to_string(min_suffix);
1499
0
        if (names.count(candidate) == 0) {
1500
0
            return candidate;
1501
0
        }
1502
        // Increment after return; min_suffix should be the value
1503
        // used, not the next value.
1504
0
        ++min_suffix;
1505
0
    }
1506
    // This could only happen if there is a coding error.
1507
    // The number of candidates we test is more than the
1508
    // number of keys we're checking against.
1509
0
    throw std::logic_error("unable to find unconflicting name in"
1510
0
                           " QPDFObjectHandle::getUniqueResourceName");
1511
0
}
1512
1513
// Dictionary mutators
1514
1515
void
1516
QPDFObjectHandle::replaceKey(std::string const& key, QPDFObjectHandle const& value)
1517
33.1k
{
1518
33.1k
    auto dict = asDictionary();
1519
33.1k
    if (dict) {
1520
32.6k
        checkOwnership(value);
1521
32.6k
        dict->replaceKey(key, value);
1522
32.6k
    } else {
1523
515
        typeWarning("dictionary", "ignoring key replacement request");
1524
515
        QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring replaceKey");
1525
515
    }
1526
33.1k
}
1527
1528
QPDFObjectHandle
1529
QPDFObjectHandle::replaceKeyAndGetNew(std::string const& key, QPDFObjectHandle const& value)
1530
0
{
1531
0
    replaceKey(key, value);
1532
0
    return value;
1533
0
}
1534
1535
QPDFObjectHandle
1536
QPDFObjectHandle::replaceKeyAndGetOld(std::string const& key, QPDFObjectHandle const& value)
1537
0
{
1538
0
    QPDFObjectHandle old = removeKeyAndGetOld(key);
1539
0
    replaceKey(key, value);
1540
0
    return old;
1541
0
}
1542
1543
void
1544
QPDFObjectHandle::removeKey(std::string const& key)
1545
206k
{
1546
206k
    auto dict = asDictionary();
1547
206k
    if (dict) {
1548
206k
        dict->removeKey(key);
1549
206k
    } else {
1550
0
        typeWarning("dictionary", "ignoring key removal request");
1551
0
        QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring removeKey");
1552
0
    }
1553
206k
}
1554
1555
QPDFObjectHandle
1556
QPDFObjectHandle::removeKeyAndGetOld(std::string const& key)
1557
0
{
1558
0
    auto result = QPDFObjectHandle::newNull();
1559
0
    auto dict = asDictionary();
1560
0
    if (dict) {
1561
0
        result = dict->getKey(key);
1562
0
    }
1563
0
    removeKey(key);
1564
0
    return result;
1565
0
}
1566
1567
void
1568
QPDFObjectHandle::replaceOrRemoveKey(std::string const& key, QPDFObjectHandle const& value)
1569
0
{
1570
0
    replaceKey(key, value);
1571
0
}
1572
1573
// Stream accessors
1574
#ifndef QPDF_FUTURE
1575
QPDFObjectHandle
1576
QPDFObjectHandle::getDict()
1577
#else
1578
QPDFObjectHandle
1579
QPDFObjectHandle::getDict() const
1580
#endif
1581
246k
{
1582
246k
    return asStreamWithAssert()->getDict();
1583
246k
}
1584
1585
void
1586
QPDFObjectHandle::setFilterOnWrite(bool val)
1587
3.17k
{
1588
3.17k
    asStreamWithAssert()->setFilterOnWrite(val);
1589
3.17k
}
1590
1591
bool
1592
QPDFObjectHandle::getFilterOnWrite()
1593
87.8k
{
1594
87.8k
    return asStreamWithAssert()->getFilterOnWrite();
1595
87.8k
}
1596
1597
bool
1598
QPDFObjectHandle::isDataModified()
1599
170k
{
1600
170k
    return asStreamWithAssert()->isDataModified();
1601
170k
}
1602
1603
void
1604
QPDFObjectHandle::replaceDict(QPDFObjectHandle const& new_dict)
1605
0
{
1606
0
    asStreamWithAssert()->replaceDict(new_dict);
1607
0
}
1608
1609
std::shared_ptr<Buffer>
1610
QPDFObjectHandle::getStreamData(qpdf_stream_decode_level_e level)
1611
2.30k
{
1612
2.30k
    return asStreamWithAssert()->getStreamData(level);
1613
2.30k
}
1614
1615
std::shared_ptr<Buffer>
1616
QPDFObjectHandle::getRawStreamData()
1617
0
{
1618
0
    return asStreamWithAssert()->getRawStreamData();
1619
0
}
1620
1621
bool
1622
QPDFObjectHandle::pipeStreamData(
1623
    Pipeline* p,
1624
    bool* filtering_attempted,
1625
    int encode_flags,
1626
    qpdf_stream_decode_level_e decode_level,
1627
    bool suppress_warnings,
1628
    bool will_retry)
1629
0
{
1630
0
    return asStreamWithAssert()->pipeStreamData(
1631
0
        p, filtering_attempted, encode_flags, decode_level, suppress_warnings, will_retry);
1632
0
}
1633
1634
bool
1635
QPDFObjectHandle::pipeStreamData(
1636
    Pipeline* p,
1637
    int encode_flags,
1638
    qpdf_stream_decode_level_e decode_level,
1639
    bool suppress_warnings,
1640
    bool will_retry)
1641
90.9k
{
1642
90.9k
    bool filtering_attempted;
1643
90.9k
    asStreamWithAssert()->pipeStreamData(
1644
90.9k
        p, &filtering_attempted, encode_flags, decode_level, suppress_warnings, will_retry);
1645
90.9k
    return filtering_attempted;
1646
90.9k
}
1647
1648
bool
1649
QPDFObjectHandle::pipeStreamData(Pipeline* p, bool filter, bool normalize, bool compress)
1650
0
{
1651
0
    int encode_flags = 0;
1652
0
    qpdf_stream_decode_level_e decode_level = qpdf_dl_none;
1653
0
    if (filter) {
1654
0
        decode_level = qpdf_dl_generalized;
1655
0
        if (normalize) {
1656
0
            encode_flags |= qpdf_ef_normalize;
1657
0
        }
1658
0
        if (compress) {
1659
0
            encode_flags |= qpdf_ef_compress;
1660
0
        }
1661
0
    }
1662
0
    return pipeStreamData(p, encode_flags, decode_level, false);
1663
0
}
1664
1665
void
1666
QPDFObjectHandle::replaceStreamData(
1667
    std::shared_ptr<Buffer> data,
1668
    QPDFObjectHandle const& filter,
1669
    QPDFObjectHandle const& decode_parms)
1670
0
{
1671
0
    asStreamWithAssert()->replaceStreamData(data, filter, decode_parms);
1672
0
}
1673
1674
void
1675
QPDFObjectHandle::replaceStreamData(
1676
    std::string const& data, QPDFObjectHandle const& filter, QPDFObjectHandle const& decode_parms)
1677
0
{
1678
0
    auto b = std::make_shared<Buffer>(data.length());
1679
0
    unsigned char* bp = b->getBuffer();
1680
0
    if (bp) {
1681
0
        memcpy(bp, data.c_str(), data.length());
1682
0
    }
1683
0
    asStreamWithAssert()->replaceStreamData(b, filter, decode_parms);
1684
0
}
1685
1686
void
1687
QPDFObjectHandle::replaceStreamData(
1688
    std::shared_ptr<StreamDataProvider> provider,
1689
    QPDFObjectHandle const& filter,
1690
    QPDFObjectHandle const& decode_parms)
1691
0
{
1692
0
    asStreamWithAssert()->replaceStreamData(provider, filter, decode_parms);
1693
0
}
1694
1695
namespace
1696
{
1697
    class FunctionProvider: public QPDFObjectHandle::StreamDataProvider
1698
    {
1699
      public:
1700
        FunctionProvider(std::function<void(Pipeline*)> provider) :
1701
            StreamDataProvider(false),
1702
            p1(provider),
1703
            p2(nullptr)
1704
0
        {
1705
0
        }
1706
        FunctionProvider(std::function<bool(Pipeline*, bool, bool)> provider) :
1707
            StreamDataProvider(true),
1708
            p1(nullptr),
1709
            p2(provider)
1710
0
        {
1711
0
        }
1712
1713
        void
1714
        provideStreamData(QPDFObjGen const&, Pipeline* pipeline) override
1715
0
        {
1716
0
            p1(pipeline);
1717
0
        }
1718
1719
        bool
1720
        provideStreamData(
1721
            QPDFObjGen const&, Pipeline* pipeline, bool suppress_warnings, bool will_retry) override
1722
0
        {
1723
0
            return p2(pipeline, suppress_warnings, will_retry);
1724
0
        }
1725
1726
      private:
1727
        std::function<void(Pipeline*)> p1;
1728
        std::function<bool(Pipeline*, bool, bool)> p2;
1729
    };
1730
} // namespace
1731
1732
void
1733
QPDFObjectHandle::replaceStreamData(
1734
    std::function<void(Pipeline*)> provider,
1735
    QPDFObjectHandle const& filter,
1736
    QPDFObjectHandle const& decode_parms)
1737
0
{
1738
0
    auto sdp = std::shared_ptr<StreamDataProvider>(new FunctionProvider(provider));
1739
0
    asStreamWithAssert()->replaceStreamData(sdp, filter, decode_parms);
1740
0
}
1741
1742
void
1743
QPDFObjectHandle::replaceStreamData(
1744
    std::function<bool(Pipeline*, bool, bool)> provider,
1745
    QPDFObjectHandle const& filter,
1746
    QPDFObjectHandle const& decode_parms)
1747
0
{
1748
0
    auto sdp = std::shared_ptr<StreamDataProvider>(new FunctionProvider(provider));
1749
0
    asStreamWithAssert()->replaceStreamData(sdp, filter, decode_parms);
1750
0
}
1751
1752
std::map<std::string, QPDFObjectHandle>
1753
QPDFObjectHandle::getPageImages()
1754
0
{
1755
0
    return QPDFPageObjectHelper(*this).getImages();
1756
0
}
1757
1758
std::vector<QPDFObjectHandle>
1759
QPDFObjectHandle::arrayOrStreamToStreamArray(
1760
    std::string const& description, std::string& all_description)
1761
0
{
1762
0
    all_description = description;
1763
0
    std::vector<QPDFObjectHandle> result;
1764
0
    if (auto array = asArray()) {
1765
0
        int n_items = array->size();
1766
0
        for (int i = 0; i < n_items; ++i) {
1767
0
            QPDFObjectHandle item = array->at(i);
1768
0
            if (item.isStream()) {
1769
0
                result.push_back(item);
1770
0
            } else {
1771
0
                QTC::TC("qpdf", "QPDFObjectHandle non-stream in stream array");
1772
0
                warn(
1773
0
                    item.getOwningQPDF(),
1774
0
                    QPDFExc(
1775
0
                        qpdf_e_damaged_pdf,
1776
0
                        "",
1777
0
                        description + ": item index " + std::to_string(i) + " (from 0)",
1778
0
                        0,
1779
0
                        "ignoring non-stream in an array of streams"));
1780
0
            }
1781
0
        }
1782
0
    } else if (isStream()) {
1783
0
        result.push_back(*this);
1784
0
    } else if (!isNull()) {
1785
0
        warn(
1786
0
            getOwningQPDF(),
1787
0
            QPDFExc(
1788
0
                qpdf_e_damaged_pdf,
1789
0
                "",
1790
0
                description,
1791
0
                0,
1792
0
                " object is supposed to be a stream or an array of streams but is neither"));
1793
0
    }
1794
1795
0
    bool first = true;
1796
0
    for (auto const& item: result) {
1797
0
        if (first) {
1798
0
            first = false;
1799
0
        } else {
1800
0
            all_description += ",";
1801
0
        }
1802
0
        all_description += " stream " + item.getObjGen().unparse(' ');
1803
0
    }
1804
1805
0
    return result;
1806
0
}
1807
1808
std::vector<QPDFObjectHandle>
1809
QPDFObjectHandle::getPageContents()
1810
0
{
1811
0
    std::string description = "page object " + getObjGen().unparse(' ');
1812
0
    std::string all_description;
1813
0
    return this->getKey("/Contents").arrayOrStreamToStreamArray(description, all_description);
1814
0
}
1815
1816
void
1817
QPDFObjectHandle::addPageContents(QPDFObjectHandle new_contents, bool first)
1818
0
{
1819
0
    new_contents.assertStream();
1820
1821
0
    std::vector<QPDFObjectHandle> content_streams;
1822
0
    if (first) {
1823
0
        QTC::TC("qpdf", "QPDFObjectHandle prepend page contents");
1824
0
        content_streams.push_back(new_contents);
1825
0
    }
1826
0
    for (auto const& iter: getPageContents()) {
1827
0
        QTC::TC("qpdf", "QPDFObjectHandle append page contents");
1828
0
        content_streams.push_back(iter);
1829
0
    }
1830
0
    if (!first) {
1831
0
        content_streams.push_back(new_contents);
1832
0
    }
1833
1834
0
    this->replaceKey("/Contents", newArray(content_streams));
1835
0
}
1836
1837
void
1838
QPDFObjectHandle::rotatePage(int angle, bool relative)
1839
0
{
1840
0
    if ((angle % 90) != 0) {
1841
0
        throw std::runtime_error(
1842
0
            "QPDF::rotatePage called with an angle that is not a multiple of 90");
1843
0
    }
1844
0
    int new_angle = angle;
1845
0
    if (relative) {
1846
0
        int old_angle = 0;
1847
0
        QPDFObjectHandle cur_obj = *this;
1848
0
        QPDFObjGen::set visited;
1849
0
        while (visited.add(cur_obj)) {
1850
            // Don't get stuck in an infinite loop
1851
0
            if (cur_obj.getKey("/Rotate").getValueAsInt(old_angle)) {
1852
0
                break;
1853
0
            } else if (cur_obj.getKey("/Parent").isDictionary()) {
1854
0
                cur_obj = cur_obj.getKey("/Parent");
1855
0
            } else {
1856
0
                break;
1857
0
            }
1858
0
        }
1859
0
        QTC::TC("qpdf", "QPDFObjectHandle found old angle", visited.size() > 1 ? 0 : 1);
1860
0
        if ((old_angle % 90) != 0) {
1861
0
            old_angle = 0;
1862
0
        }
1863
0
        new_angle += old_angle;
1864
0
    }
1865
0
    new_angle = (new_angle + 360) % 360;
1866
    // Make this explicit even with new_angle == 0 since /Rotate can be inherited.
1867
0
    replaceKey("/Rotate", QPDFObjectHandle::newInteger(new_angle));
1868
0
}
1869
1870
void
1871
QPDFObjectHandle::coalesceContentStreams()
1872
0
{
1873
0
    QPDFObjectHandle contents = this->getKey("/Contents");
1874
0
    if (contents.isStream()) {
1875
0
        QTC::TC("qpdf", "QPDFObjectHandle coalesce called on stream");
1876
0
        return;
1877
0
    } else if (!contents.isArray()) {
1878
        // /Contents is optional for pages, and some very damaged files may have pages that are
1879
        // invalid in other ways.
1880
0
        return;
1881
0
    }
1882
    // Should not be possible for a page object to not have an owning PDF unless it was manually
1883
    // constructed in some incorrect way. However, it can happen in a PDF file whose page structure
1884
    // is direct, which is against spec but still possible to hand construct, as in fuzz issue
1885
    // 27393.
1886
0
    QPDF& qpdf = getQPDF("coalesceContentStreams called on object  with no associated PDF file");
1887
1888
0
    QPDFObjectHandle new_contents = newStream(&qpdf);
1889
0
    this->replaceKey("/Contents", new_contents);
1890
1891
0
    auto provider = std::shared_ptr<StreamDataProvider>(new CoalesceProvider(*this, contents));
1892
0
    new_contents.replaceStreamData(provider, newNull(), newNull());
1893
0
}
1894
1895
#ifndef QPDF_FUTURE
1896
std::string
1897
QPDFObjectHandle::unparse()
1898
#else
1899
std::string
1900
QPDFObjectHandle::unparse() const
1901
#endif
1902
35
{
1903
35
    if (this->isIndirect()) {
1904
35
        return getObjGen().unparse(' ') + " R";
1905
35
    } else {
1906
0
        return unparseResolved();
1907
0
    }
1908
35
}
1909
1910
#ifndef QPDF_FUTURE
1911
std::string
1912
QPDFObjectHandle::unparseResolved()
1913
#else
1914
std::string
1915
QPDFObjectHandle::unparseResolved() const
1916
#endif
1917
2.29M
{
1918
2.29M
    if (!obj) {
1919
0
        throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle");
1920
0
    }
1921
2.29M
    return obj->unparse();
1922
2.29M
}
1923
1924
#ifndef QPDF_FUTURE
1925
std::string
1926
QPDFObjectHandle::unparseBinary()
1927
#else
1928
std::string
1929
QPDFObjectHandle::unparseBinary() const
1930
#endif
1931
0
{
1932
0
    if (auto str = asString()) {
1933
0
        return str->unparse(true);
1934
0
    } else {
1935
0
        return unparse();
1936
0
    }
1937
0
}
1938
1939
// Deprecated versionless getJSON to be removed in qpdf 12
1940
#ifndef QPDF_FUTURE
1941
JSON
1942
QPDFObjectHandle::getJSON(bool dereference_indirect)
1943
#else
1944
JSON
1945
QPDFObjectHandle::getJSON(bool dereference_indirect) const
1946
#endif
1947
0
{
1948
0
    return getJSON(1, dereference_indirect);
1949
0
}
1950
1951
#ifndef QPDF_FUTURE
1952
JSON
1953
QPDFObjectHandle::getJSON(int json_version, bool dereference_indirect)
1954
#else
1955
JSON
1956
QPDFObjectHandle::getJSON(int json_version, bool dereference_indirect) const
1957
#endif
1958
0
{
1959
0
    if ((!dereference_indirect) && isIndirect()) {
1960
0
        return JSON::makeString(unparse());
1961
0
    } else if (!obj) {
1962
0
        throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle");
1963
0
    } else {
1964
0
        Pl_Buffer p{"json"};
1965
0
        JSON::Writer jw{&p, 0};
1966
0
        writeJSON(json_version, jw, dereference_indirect);
1967
0
        p.finish();
1968
0
        return JSON::parse(p.getString());
1969
0
    }
1970
0
}
1971
1972
#ifndef QPDF_FUTURE
1973
void
1974
QPDFObjectHandle::writeJSON(int json_version, JSON::Writer& p, bool dereference_indirect)
1975
#else
1976
void
1977
QPDFObjectHandle::writeJSON(int json_version, JSON::Writer& p, bool dereference_indirect) const
1978
#endif
1979
0
{
1980
0
    if (!dereference_indirect && isIndirect()) {
1981
0
        p << "\"" << getObjGen().unparse(' ') << " R\"";
1982
0
    } else if (!obj) {
1983
0
        throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle");
1984
0
    } else {
1985
0
        obj->writeJSON(json_version, p);
1986
0
    }
1987
0
}
1988
1989
#ifndef QPDF_FUTURE
1990
void
1991
QPDFObjectHandle::writeJSON(int json_version, Pipeline* p, bool dereference_indirect, size_t depth)
1992
#else
1993
void
1994
QPDFObjectHandle::writeJSON(
1995
    int json_version, Pipeline* p, bool dereference_indirect, size_t depth) const
1996
#endif
1997
0
{
1998
0
    JSON::Writer jw{p, depth};
1999
0
    writeJSON(json_version, jw, dereference_indirect);
2000
0
}
2001
2002
JSON
2003
QPDFObjectHandle::getStreamJSON(
2004
    int json_version,
2005
    qpdf_json_stream_data_e json_data,
2006
    qpdf_stream_decode_level_e decode_level,
2007
    Pipeline* p,
2008
    std::string const& data_filename)
2009
0
{
2010
0
    return asStreamWithAssert()->getStreamJSON(
2011
0
        json_version, json_data, decode_level, p, data_filename);
2012
0
}
2013
2014
QPDFObjectHandle
2015
QPDFObjectHandle::wrapInArray()
2016
0
{
2017
0
    if (isArray()) {
2018
0
        return *this;
2019
0
    }
2020
0
    QPDFObjectHandle result = QPDFObjectHandle::newArray();
2021
0
    result.appendItem(*this);
2022
0
    return result;
2023
0
}
2024
2025
QPDFObjectHandle
2026
QPDFObjectHandle::parse(std::string const& object_str, std::string const& object_description)
2027
4.63k
{
2028
4.63k
    return parse(nullptr, object_str, object_description);
2029
4.63k
}
2030
2031
QPDFObjectHandle
2032
QPDFObjectHandle::parse(
2033
    QPDF* context, std::string const& object_str, std::string const& object_description)
2034
4.63k
{
2035
4.63k
    auto input = std::shared_ptr<InputSource>(new BufferInputSource("parsed object", object_str));
2036
4.63k
    QPDFTokenizer tokenizer;
2037
4.63k
    bool empty = false;
2038
4.63k
    QPDFObjectHandle result = parse(input, object_description, tokenizer, empty, nullptr, context);
2039
4.63k
    size_t offset = QIntC::to_size(input->tell());
2040
4.63k
    while (offset < object_str.length()) {
2041
0
        if (!isspace(object_str.at(offset))) {
2042
0
            QTC::TC("qpdf", "QPDFObjectHandle trailing data in parse");
2043
0
            throw QPDFExc(
2044
0
                qpdf_e_damaged_pdf,
2045
0
                input->getName(),
2046
0
                object_description,
2047
0
                input->getLastOffset(),
2048
0
                "trailing data found parsing object from string");
2049
0
        }
2050
0
        ++offset;
2051
0
    }
2052
4.63k
    return result;
2053
4.63k
}
2054
2055
void
2056
QPDFObjectHandle::pipePageContents(Pipeline* p)
2057
0
{
2058
0
    std::string description = "page object " + getObjGen().unparse(' ');
2059
0
    std::string all_description;
2060
0
    this->getKey("/Contents").pipeContentStreams(p, description, all_description);
2061
0
}
2062
2063
void
2064
QPDFObjectHandle::pipeContentStreams(
2065
    Pipeline* p, std::string const& description, std::string& all_description)
2066
0
{
2067
0
    std::vector<QPDFObjectHandle> streams =
2068
0
        arrayOrStreamToStreamArray(description, all_description);
2069
0
    bool need_newline = false;
2070
0
    Pl_Buffer buf("concatenated content stream buffer");
2071
0
    for (auto stream: streams) {
2072
0
        if (need_newline) {
2073
0
            buf.writeCStr("\n");
2074
0
        }
2075
0
        LastChar lc(&buf);
2076
0
        if (!stream.pipeStreamData(&lc, 0, qpdf_dl_specialized)) {
2077
0
            QTC::TC("qpdf", "QPDFObjectHandle errors in parsecontent");
2078
0
            throw QPDFExc(
2079
0
                qpdf_e_damaged_pdf,
2080
0
                "content stream",
2081
0
                "content stream object " + stream.getObjGen().unparse(' '),
2082
0
                0,
2083
0
                "errors while decoding content stream");
2084
0
        }
2085
0
        lc.finish();
2086
0
        need_newline = (lc.getLastChar() != static_cast<unsigned char>('\n'));
2087
0
        QTC::TC("qpdf", "QPDFObjectHandle need_newline", need_newline ? 0 : 1);
2088
0
    }
2089
0
    p->writeString(buf.getString());
2090
0
    p->finish();
2091
0
}
2092
2093
void
2094
QPDFObjectHandle::parsePageContents(ParserCallbacks* callbacks)
2095
0
{
2096
0
    std::string description = "page object " + getObjGen().unparse(' ');
2097
0
    this->getKey("/Contents").parseContentStream_internal(description, callbacks);
2098
0
}
2099
2100
void
2101
QPDFObjectHandle::parseAsContents(ParserCallbacks* callbacks)
2102
0
{
2103
0
    std::string description = "object " + getObjGen().unparse(' ');
2104
0
    this->parseContentStream_internal(description, callbacks);
2105
0
}
2106
2107
void
2108
QPDFObjectHandle::filterPageContents(TokenFilter* filter, Pipeline* next)
2109
0
{
2110
0
    auto description = "token filter for page object " + getObjGen().unparse(' ');
2111
0
    Pl_QPDFTokenizer token_pipeline(description.c_str(), filter, next);
2112
0
    this->pipePageContents(&token_pipeline);
2113
0
}
2114
2115
void
2116
QPDFObjectHandle::filterAsContents(TokenFilter* filter, Pipeline* next)
2117
0
{
2118
0
    auto description = "token filter for object " + getObjGen().unparse(' ');
2119
0
    Pl_QPDFTokenizer token_pipeline(description.c_str(), filter, next);
2120
0
    this->pipeStreamData(&token_pipeline, 0, qpdf_dl_specialized);
2121
0
}
2122
2123
void
2124
QPDFObjectHandle::parseContentStream(QPDFObjectHandle stream_or_array, ParserCallbacks* callbacks)
2125
0
{
2126
0
    stream_or_array.parseContentStream_internal("content stream objects", callbacks);
2127
0
}
2128
2129
void
2130
QPDFObjectHandle::parseContentStream_internal(
2131
    std::string const& description, ParserCallbacks* callbacks)
2132
0
{
2133
0
    Pl_Buffer buf("concatenated stream data buffer");
2134
0
    std::string all_description;
2135
0
    pipeContentStreams(&buf, description, all_description);
2136
0
    auto stream_data = buf.getBufferSharedPointer();
2137
0
    callbacks->contentSize(stream_data->getSize());
2138
0
    try {
2139
0
        parseContentStream_data(stream_data, all_description, callbacks, getOwningQPDF());
2140
0
    } catch (TerminateParsing&) {
2141
0
        return;
2142
0
    }
2143
0
    callbacks->handleEOF();
2144
0
}
2145
2146
void
2147
QPDFObjectHandle::parseContentStream_data(
2148
    std::shared_ptr<Buffer> stream_data,
2149
    std::string const& description,
2150
    ParserCallbacks* callbacks,
2151
    QPDF* context)
2152
0
{
2153
0
    size_t stream_length = stream_data->getSize();
2154
0
    auto input =
2155
0
        std::shared_ptr<InputSource>(new BufferInputSource(description, stream_data.get()));
2156
0
    QPDFTokenizer tokenizer;
2157
0
    tokenizer.allowEOF();
2158
0
    bool empty = false;
2159
0
    while (QIntC::to_size(input->tell()) < stream_length) {
2160
        // Read a token and seek to the beginning. The offset we get from this process is the
2161
        // beginning of the next non-ignorable (space, comment) token. This way, the offset and
2162
        // don't including ignorable content.
2163
0
        tokenizer.readToken(input, "content", true);
2164
0
        qpdf_offset_t offset = input->getLastOffset();
2165
0
        input->seek(offset, SEEK_SET);
2166
0
        auto obj =
2167
0
            QPDFParser(input, "content", tokenizer, nullptr, context, false).parse(empty, true);
2168
0
        if (!obj.isInitialized()) {
2169
            // EOF
2170
0
            break;
2171
0
        }
2172
0
        size_t length = QIntC::to_size(input->tell() - offset);
2173
2174
0
        callbacks->handleObject(obj, QIntC::to_size(offset), length);
2175
0
        if (obj.isOperator() && (obj.getOperatorValue() == "ID")) {
2176
            // Discard next character; it is the space after ID that terminated the token.  Read
2177
            // until end of inline image.
2178
0
            char ch;
2179
0
            input->read(&ch, 1);
2180
0
            tokenizer.expectInlineImage(input);
2181
0
            QPDFTokenizer::Token t = tokenizer.readToken(input, description, true);
2182
0
            offset = input->getLastOffset();
2183
0
            length = QIntC::to_size(input->tell() - offset);
2184
0
            if (t.getType() == QPDFTokenizer::tt_bad) {
2185
0
                QTC::TC("qpdf", "QPDFObjectHandle EOF in inline image");
2186
0
                warn(
2187
0
                    context,
2188
0
                    QPDFExc(
2189
0
                        qpdf_e_damaged_pdf,
2190
0
                        input->getName(),
2191
0
                        "stream data",
2192
0
                        input->tell(),
2193
0
                        "EOF found while reading inline image"));
2194
0
            } else {
2195
0
                std::string inline_image = t.getValue();
2196
0
                QTC::TC("qpdf", "QPDFObjectHandle inline image token");
2197
0
                callbacks->handleObject(
2198
0
                    QPDFObjectHandle::newInlineImage(inline_image), QIntC::to_size(offset), length);
2199
0
            }
2200
0
        }
2201
0
    }
2202
0
}
2203
2204
void
2205
QPDFObjectHandle::addContentTokenFilter(std::shared_ptr<TokenFilter> filter)
2206
0
{
2207
0
    coalesceContentStreams();
2208
0
    this->getKey("/Contents").addTokenFilter(filter);
2209
0
}
2210
2211
void
2212
QPDFObjectHandle::addTokenFilter(std::shared_ptr<TokenFilter> filter)
2213
0
{
2214
0
    return asStreamWithAssert()->addTokenFilter(filter);
2215
0
}
2216
2217
QPDFObjectHandle
2218
QPDFObjectHandle::parse(
2219
    std::shared_ptr<InputSource> input,
2220
    std::string const& object_description,
2221
    QPDFTokenizer& tokenizer,
2222
    bool& empty,
2223
    StringDecrypter* decrypter,
2224
    QPDF* context)
2225
4.63k
{
2226
4.63k
    return QPDFParser(input, object_description, tokenizer, decrypter, context, false)
2227
4.63k
        .parse(empty, false);
2228
4.63k
}
2229
2230
#ifndef QPDF_FUTURE
2231
qpdf_offset_t
2232
QPDFObjectHandle::getParsedOffset()
2233
#else
2234
qpdf_offset_t
2235
QPDFObjectHandle::getParsedOffset() const
2236
#endif
2237
0
{
2238
0
    return obj ? obj->getParsedOffset() : -1;
2239
0
}
2240
2241
QPDFObjectHandle
2242
QPDFObjectHandle::newBool(bool value)
2243
0
{
2244
0
    return {QPDF_Bool::create(value)};
2245
0
}
2246
2247
QPDFObjectHandle
2248
QPDFObjectHandle::newNull()
2249
110
{
2250
110
    return {QPDF_Null::create()};
2251
110
}
2252
2253
QPDFObjectHandle
2254
QPDFObjectHandle::newInteger(long long value)
2255
1.83k
{
2256
1.83k
    return {QPDF_Integer::create(value)};
2257
1.83k
}
2258
2259
QPDFObjectHandle
2260
QPDFObjectHandle::newReal(std::string const& value)
2261
0
{
2262
0
    return {QPDF_Real::create(value)};
2263
0
}
2264
2265
QPDFObjectHandle
2266
QPDFObjectHandle::newReal(double value, int decimal_places, bool trim_trailing_zeroes)
2267
14.8k
{
2268
14.8k
    return {QPDF_Real::create(value, decimal_places, trim_trailing_zeroes)};
2269
14.8k
}
2270
2271
QPDFObjectHandle
2272
QPDFObjectHandle::newName(std::string const& name)
2273
0
{
2274
0
    return {QPDF_Name::create(name)};
2275
0
}
2276
2277
QPDFObjectHandle
2278
QPDFObjectHandle::newString(std::string const& str)
2279
0
{
2280
0
    return {QPDF_String::create(str)};
2281
0
}
2282
2283
QPDFObjectHandle
2284
QPDFObjectHandle::newUnicodeString(std::string const& utf8_str)
2285
0
{
2286
0
    return {QPDF_String::create_utf16(utf8_str)};
2287
0
}
2288
2289
QPDFObjectHandle
2290
QPDFObjectHandle::newOperator(std::string const& value)
2291
0
{
2292
0
    return {QPDF_Operator::create(value)};
2293
0
}
2294
2295
QPDFObjectHandle
2296
QPDFObjectHandle::newInlineImage(std::string const& value)
2297
0
{
2298
0
    return {QPDF_InlineImage::create(value)};
2299
0
}
2300
2301
QPDFObjectHandle
2302
QPDFObjectHandle::newArray()
2303
0
{
2304
0
    return newArray(std::vector<QPDFObjectHandle>());
2305
0
}
2306
2307
QPDFObjectHandle
2308
QPDFObjectHandle::newArray(std::vector<QPDFObjectHandle> const& items)
2309
5.61k
{
2310
5.61k
    return {QPDF_Array::create(items)};
2311
5.61k
}
2312
2313
QPDFObjectHandle
2314
QPDFObjectHandle::newArray(Rectangle const& rect)
2315
3.71k
{
2316
3.71k
    return newArray({newReal(rect.llx), newReal(rect.lly), newReal(rect.urx), newReal(rect.ury)});
2317
3.71k
}
2318
2319
QPDFObjectHandle
2320
QPDFObjectHandle::newArray(Matrix const& matrix)
2321
0
{
2322
0
    return newArray(
2323
0
        {newReal(matrix.a),
2324
0
         newReal(matrix.b),
2325
0
         newReal(matrix.c),
2326
0
         newReal(matrix.d),
2327
0
         newReal(matrix.e),
2328
0
         newReal(matrix.f)});
2329
0
}
2330
2331
QPDFObjectHandle
2332
QPDFObjectHandle::newArray(QPDFMatrix const& matrix)
2333
0
{
2334
0
    return newArray(
2335
0
        {newReal(matrix.a),
2336
0
         newReal(matrix.b),
2337
0
         newReal(matrix.c),
2338
0
         newReal(matrix.d),
2339
0
         newReal(matrix.e),
2340
0
         newReal(matrix.f)});
2341
0
}
2342
2343
QPDFObjectHandle
2344
QPDFObjectHandle::newFromRectangle(Rectangle const& rect)
2345
0
{
2346
0
    return newArray(rect);
2347
0
}
2348
2349
QPDFObjectHandle
2350
QPDFObjectHandle::newFromMatrix(Matrix const& m)
2351
0
{
2352
0
    return newArray(m);
2353
0
}
2354
2355
QPDFObjectHandle
2356
QPDFObjectHandle::newFromMatrix(QPDFMatrix const& m)
2357
0
{
2358
0
    return newArray(m);
2359
0
}
2360
2361
QPDFObjectHandle
2362
QPDFObjectHandle::newDictionary()
2363
0
{
2364
0
    return newDictionary(std::map<std::string, QPDFObjectHandle>());
2365
0
}
2366
2367
QPDFObjectHandle
2368
QPDFObjectHandle::newDictionary(std::map<std::string, QPDFObjectHandle> const& items)
2369
0
{
2370
0
    return {QPDF_Dictionary::create(items)};
2371
0
}
2372
2373
QPDFObjectHandle
2374
QPDFObjectHandle::newStream(QPDF* qpdf)
2375
0
{
2376
0
    if (qpdf == nullptr) {
2377
0
        throw std::runtime_error("attempt to create stream in null qpdf object");
2378
0
    }
2379
0
    QTC::TC("qpdf", "QPDFObjectHandle newStream");
2380
0
    return qpdf->newStream();
2381
0
}
2382
2383
QPDFObjectHandle
2384
QPDFObjectHandle::newStream(QPDF* qpdf, std::shared_ptr<Buffer> data)
2385
0
{
2386
0
    if (qpdf == nullptr) {
2387
0
        throw std::runtime_error("attempt to create stream in null qpdf object");
2388
0
    }
2389
0
    QTC::TC("qpdf", "QPDFObjectHandle newStream with data");
2390
0
    return qpdf->newStream(data);
2391
0
}
2392
2393
QPDFObjectHandle
2394
QPDFObjectHandle::newStream(QPDF* qpdf, std::string const& data)
2395
0
{
2396
0
    if (qpdf == nullptr) {
2397
0
        throw std::runtime_error("attempt to create stream in null qpdf object");
2398
0
    }
2399
0
    QTC::TC("qpdf", "QPDFObjectHandle newStream with string");
2400
0
    return qpdf->newStream(data);
2401
0
}
2402
2403
QPDFObjectHandle
2404
QPDFObjectHandle::newReserved(QPDF* qpdf)
2405
0
{
2406
0
    if (qpdf == nullptr) {
2407
0
        throw std::runtime_error("attempt to create reserved object in null qpdf object");
2408
0
    }
2409
0
    return qpdf->newReserved();
2410
0
}
2411
2412
void
2413
QPDFObjectHandle::setObjectDescription(QPDF* owning_qpdf, std::string const& object_description)
2414
0
{
2415
0
    if (obj) {
2416
0
        auto descr = std::make_shared<QPDFValue::Description>(object_description);
2417
0
        obj->setDescription(owning_qpdf, descr);
2418
0
    }
2419
0
}
2420
2421
#ifndef QPDF_FUTURE
2422
bool
2423
QPDFObjectHandle::hasObjectDescription()
2424
#else
2425
bool
2426
QPDFObjectHandle::hasObjectDescription() const
2427
#endif
2428
54.4k
{
2429
54.4k
    return obj && obj->hasDescription();
2430
54.4k
}
2431
2432
QPDFObjectHandle
2433
QPDFObjectHandle::shallowCopy()
2434
21.1k
{
2435
21.1k
    if (!obj) {
2436
0
        throw std::logic_error("operation attempted on uninitialized QPDFObjectHandle");
2437
0
    }
2438
21.1k
    return {obj->copy()};
2439
21.1k
}
2440
2441
QPDFObjectHandle
2442
QPDFObjectHandle::unsafeShallowCopy()
2443
368k
{
2444
368k
    if (!obj) {
2445
0
        throw std::logic_error("operation attempted on uninitialized QPDFObjectHandle");
2446
0
    }
2447
368k
    return {obj->copy(true)};
2448
368k
}
2449
2450
void
2451
QPDFObjectHandle::makeDirect(QPDFObjGen::set& visited, bool stop_at_streams)
2452
0
{
2453
0
    assertInitialized();
2454
2455
0
    auto cur_og = getObjGen();
2456
0
    if (!visited.add(cur_og)) {
2457
0
        QTC::TC("qpdf", "QPDFObjectHandle makeDirect loop");
2458
0
        throw std::runtime_error("loop detected while converting object from indirect to direct");
2459
0
    }
2460
2461
0
    if (isBool() || isInteger() || isName() || isNull() || isReal() || isString()) {
2462
0
        this->obj = obj->copy(true);
2463
0
    } else if (isArray()) {
2464
0
        std::vector<QPDFObjectHandle> items;
2465
0
        auto array = asArray();
2466
0
        int n = array->size();
2467
0
        for (int i = 0; i < n; ++i) {
2468
0
            items.push_back(array->at(i));
2469
0
            items.back().makeDirect(visited, stop_at_streams);
2470
0
        }
2471
0
        this->obj = QPDF_Array::create(items);
2472
0
    } else if (isDictionary()) {
2473
0
        std::map<std::string, QPDFObjectHandle> items;
2474
0
        auto dict = asDictionary();
2475
0
        for (auto const& key: getKeys()) {
2476
0
            items[key] = dict->getKey(key);
2477
0
            items[key].makeDirect(visited, stop_at_streams);
2478
0
        }
2479
0
        this->obj = QPDF_Dictionary::create(items);
2480
0
    } else if (isStream()) {
2481
0
        QTC::TC("qpdf", "QPDFObjectHandle copy stream", stop_at_streams ? 0 : 1);
2482
0
        if (!stop_at_streams) {
2483
0
            throw std::runtime_error("attempt to make a stream into a direct object");
2484
0
        }
2485
0
    } else if (isReserved()) {
2486
0
        throw std::logic_error(
2487
0
            "QPDFObjectHandle: attempting to make a reserved object handle direct");
2488
0
    } else {
2489
0
        throw std::logic_error("QPDFObjectHandle::makeDirectInternal: unknown object type");
2490
0
    }
2491
2492
0
    visited.erase(cur_og);
2493
0
}
2494
2495
QPDFObjectHandle
2496
QPDFObjectHandle::copyStream()
2497
0
{
2498
0
    assertStream();
2499
0
    QPDFObjectHandle result = newStream(this->getOwningQPDF());
2500
0
    QPDFObjectHandle dict = result.getDict();
2501
0
    QPDFObjectHandle old_dict = getDict();
2502
0
    for (auto& iter: QPDFDictItems(old_dict)) {
2503
0
        if (iter.second.isIndirect()) {
2504
0
            dict.replaceKey(iter.first, iter.second);
2505
0
        } else {
2506
0
            dict.replaceKey(iter.first, iter.second.shallowCopy());
2507
0
        }
2508
0
    }
2509
0
    QPDF::StreamCopier::copyStreamData(getOwningQPDF(), result, *this);
2510
0
    return result;
2511
0
}
2512
2513
void
2514
QPDFObjectHandle::makeDirect(bool allow_streams)
2515
0
{
2516
0
    QPDFObjGen::set visited;
2517
0
    makeDirect(visited, allow_streams);
2518
0
}
2519
2520
void
2521
QPDFObjectHandle::assertInitialized() const
2522
0
{
2523
0
    if (!isInitialized()) {
2524
0
        throw std::logic_error("operation attempted on uninitialized QPDFObjectHandle");
2525
0
    }
2526
0
}
2527
2528
void
2529
QPDFObjectHandle::typeWarning(char const* expected_type, std::string const& warning) const
2530
2.54k
{
2531
2.54k
    QPDF* context = nullptr;
2532
2.54k
    std::string description;
2533
    // Type checks above guarantee that the object has been dereferenced. Nevertheless, dereference
2534
    // throws exceptions in the test suite
2535
2.54k
    if (!obj) {
2536
0
        throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle");
2537
0
    }
2538
2.54k
    obj->getDescription(context, description);
2539
    // Null context handled by warn
2540
2.54k
    warn(
2541
2.54k
        context,
2542
2.54k
        QPDFExc(
2543
2.54k
            qpdf_e_object,
2544
2.54k
            "",
2545
2.54k
            description,
2546
2.54k
            0,
2547
2.54k
            std::string("operation for ") + expected_type + " attempted on object of type " +
2548
2.54k
                QPDFObjectHandle(*this).getTypeName() + ": " + warning));
2549
2.54k
}
2550
2551
#ifndef QPDF_FUTURE
2552
void
2553
QPDFObjectHandle::warnIfPossible(std::string const& warning)
2554
#else
2555
void
2556
QPDFObjectHandle::warnIfPossible(std::string const& warning) const
2557
#endif
2558
62.1k
{
2559
62.1k
    QPDF* context = nullptr;
2560
62.1k
    std::string description;
2561
62.1k
    if (obj && obj->getDescription(context, description)) {
2562
25.8k
        warn(context, QPDFExc(qpdf_e_damaged_pdf, "", description, 0, warning));
2563
36.2k
    } else {
2564
36.2k
        *QPDFLogger::defaultLogger()->getError() << warning << "\n";
2565
36.2k
    }
2566
62.1k
}
2567
2568
void
2569
QPDFObjectHandle::objectWarning(std::string const& warning) const
2570
0
{
2571
0
    QPDF* context = nullptr;
2572
0
    std::string description;
2573
    // Type checks above guarantee that the object is initialized.
2574
0
    obj->getDescription(context, description);
2575
    // Null context handled by warn
2576
0
    warn(context, QPDFExc(qpdf_e_object, "", description, 0, warning));
2577
0
}
2578
2579
void
2580
QPDFObjectHandle::assertType(char const* type_name, bool istype) const
2581
601k
{
2582
601k
    if (!istype) {
2583
0
        throw std::runtime_error(
2584
0
            std::string("operation for ") + type_name + " attempted on object of type " +
2585
0
            QPDFObjectHandle(*this).getTypeName());
2586
0
    }
2587
601k
}
2588
2589
#ifndef QPDF_FUTURE
2590
void
2591
QPDFObjectHandle::assertNull()
2592
#else
2593
void
2594
QPDFObjectHandle::assertNull() const
2595
#endif
2596
0
{
2597
0
    assertType("null", isNull());
2598
0
}
2599
2600
#ifndef QPDF_FUTURE
2601
void
2602
QPDFObjectHandle::assertBool()
2603
#else
2604
void
2605
QPDFObjectHandle::assertBool() const
2606
#endif
2607
0
{
2608
0
    assertType("boolean", isBool());
2609
0
}
2610
2611
#ifndef QPDF_FUTURE
2612
void
2613
QPDFObjectHandle::assertInteger()
2614
#else
2615
void
2616
QPDFObjectHandle::assertInteger() const
2617
#endif
2618
0
{
2619
0
    assertType("integer", isInteger());
2620
0
}
2621
2622
#ifndef QPDF_FUTURE
2623
void
2624
QPDFObjectHandle::assertReal()
2625
#else
2626
void
2627
QPDFObjectHandle::assertReal() const
2628
#endif
2629
0
{
2630
0
    assertType("real", isReal());
2631
0
}
2632
2633
#ifndef QPDF_FUTURE
2634
void
2635
QPDFObjectHandle::assertName()
2636
#else
2637
void
2638
QPDFObjectHandle::assertName() const
2639
#endif
2640
0
{
2641
0
    assertType("name", isName());
2642
0
}
2643
2644
#ifndef QPDF_FUTURE
2645
void
2646
QPDFObjectHandle::assertString()
2647
#else
2648
void
2649
QPDFObjectHandle::assertString() const
2650
#endif
2651
0
{
2652
0
    assertType("string", isString());
2653
0
}
2654
2655
#ifndef QPDF_FUTURE
2656
void
2657
QPDFObjectHandle::assertOperator()
2658
#else
2659
void
2660
QPDFObjectHandle::assertOperator() const
2661
#endif
2662
0
{
2663
0
    assertType("operator", isOperator());
2664
0
}
2665
2666
#ifndef QPDF_FUTURE
2667
void
2668
QPDFObjectHandle::assertInlineImage()
2669
#else
2670
void
2671
QPDFObjectHandle::assertInlineImage() const
2672
#endif
2673
0
{
2674
0
    assertType("inlineimage", isInlineImage());
2675
0
}
2676
2677
#ifndef QPDF_FUTURE
2678
void
2679
QPDFObjectHandle::assertArray()
2680
#else
2681
void
2682
QPDFObjectHandle::assertArray() const
2683
#endif
2684
0
{
2685
0
    assertType("array", isArray());
2686
0
}
2687
2688
#ifndef QPDF_FUTURE
2689
void
2690
QPDFObjectHandle::assertDictionary()
2691
#else
2692
void
2693
QPDFObjectHandle::assertDictionary() const
2694
#endif
2695
0
{
2696
0
    assertType("dictionary", isDictionary());
2697
0
}
2698
2699
#ifndef QPDF_FUTURE
2700
void
2701
QPDFObjectHandle::assertStream()
2702
#else
2703
void
2704
QPDFObjectHandle::assertStream() const
2705
#endif
2706
0
{
2707
0
    assertType("stream", isStream());
2708
0
}
2709
2710
#ifndef QPDF_FUTURE
2711
void
2712
QPDFObjectHandle::assertReserved()
2713
#else
2714
void
2715
QPDFObjectHandle::assertReserved() const
2716
#endif
2717
0
{
2718
0
    assertType("reserved", isReserved());
2719
0
}
2720
2721
#ifndef QPDF_FUTURE
2722
void
2723
QPDFObjectHandle::assertIndirect()
2724
#else
2725
void
2726
QPDFObjectHandle::assertIndirect() const
2727
#endif
2728
0
{
2729
0
    if (!isIndirect()) {
2730
0
        throw std::logic_error("operation for indirect object attempted on direct object");
2731
0
    }
2732
0
}
2733
2734
#ifndef QPDF_FUTURE
2735
void
2736
QPDFObjectHandle::assertScalar()
2737
#else
2738
void
2739
QPDFObjectHandle::assertScalar() const
2740
#endif
2741
0
{
2742
0
    assertType("scalar", isScalar());
2743
0
}
2744
2745
#ifndef QPDF_FUTURE
2746
void
2747
QPDFObjectHandle::assertNumber()
2748
#else
2749
void
2750
QPDFObjectHandle::assertNumber() const
2751
#endif
2752
0
{
2753
0
    assertType("number", isNumber());
2754
0
}
2755
2756
#ifndef QPDF_FUTURE
2757
bool
2758
QPDFObjectHandle::isPageObject()
2759
#else
2760
bool
2761
QPDFObjectHandle::isPageObject() const
2762
#endif
2763
0
{
2764
    // See comments in QPDFObjectHandle.hh.
2765
0
    if (getOwningQPDF() == nullptr) {
2766
0
        return false;
2767
0
    }
2768
    // getAllPages repairs /Type when traversing the page tree.
2769
0
    getOwningQPDF()->getAllPages();
2770
0
    return isDictionaryOfType("/Page");
2771
0
}
2772
2773
#ifndef QPDF_FUTURE
2774
bool
2775
QPDFObjectHandle::isPagesObject()
2776
#else
2777
bool
2778
QPDFObjectHandle::isPagesObject() const
2779
#endif
2780
0
{
2781
0
    if (getOwningQPDF() == nullptr) {
2782
0
        return false;
2783
0
    }
2784
    // getAllPages repairs /Type when traversing the page tree.
2785
0
    getOwningQPDF()->getAllPages();
2786
0
    return isDictionaryOfType("/Pages");
2787
0
}
2788
2789
#ifndef QPDF_FUTURE
2790
bool
2791
QPDFObjectHandle::isFormXObject()
2792
#else
2793
bool
2794
QPDFObjectHandle::isFormXObject() const
2795
#endif
2796
0
{
2797
0
    return isStreamOfType("", "/Form");
2798
0
}
2799
2800
#ifndef QPDF_FUTURE
2801
bool
2802
QPDFObjectHandle::isImage(bool exclude_imagemask)
2803
#else
2804
bool
2805
QPDFObjectHandle::isImage(bool exclude_imagemask) const
2806
#endif
2807
0
{
2808
0
    return (
2809
0
        isStreamOfType("", "/Image") &&
2810
0
        ((!exclude_imagemask) ||
2811
0
         (!(getDict().getKey("/ImageMask").isBool() &&
2812
0
            getDict().getKey("/ImageMask").getBoolValue()))));
2813
0
}
2814
2815
void
2816
QPDFObjectHandle::checkOwnership(QPDFObjectHandle const& item) const
2817
32.6k
{
2818
32.6k
    auto qpdf = getOwningQPDF();
2819
32.6k
    auto item_qpdf = item.getOwningQPDF();
2820
32.6k
    if ((qpdf != nullptr) && (item_qpdf != nullptr) && (qpdf != item_qpdf)) {
2821
0
        QTC::TC("qpdf", "QPDFObjectHandle check ownership");
2822
0
        throw std::logic_error("Attempting to add an object from a different QPDF. Use "
2823
0
                               "QPDF::copyForeignObject to add objects from another file.");
2824
0
    }
2825
32.6k
}
2826
2827
#ifndef QPDF_FUTURE
2828
void
2829
QPDFObjectHandle::assertPageObject()
2830
#else
2831
void
2832
QPDFObjectHandle::assertPageObject() const
2833
#endif
2834
0
{
2835
0
    if (!isPageObject()) {
2836
0
        throw std::runtime_error("page operation called on non-Page object");
2837
0
    }
2838
0
}
2839
2840
void
2841
QPDFObjectHandle::warn(QPDF* qpdf, QPDFExc const& e)
2842
28.3k
{
2843
    // If parsing on behalf of a QPDF object and want to give a warning, we can warn through the
2844
    // object. If parsing for some other reason, such as an explicit creation of an object from a
2845
    // string, then just throw the exception.
2846
28.3k
    if (qpdf) {
2847
27.8k
        qpdf->warn(e);
2848
27.8k
    } else {
2849
470
        throw e;
2850
470
    }
2851
28.3k
}
2852
2853
QPDFObjectHandle::QPDFDictItems::QPDFDictItems(QPDFObjectHandle const& oh) :
2854
    oh(oh)
2855
0
{
2856
0
}
2857
2858
QPDFObjectHandle::QPDFDictItems::iterator&
2859
QPDFObjectHandle::QPDFDictItems::iterator::operator++()
2860
0
{
2861
0
    ++m->iter;
2862
0
    updateIValue();
2863
0
    return *this;
2864
0
}
2865
2866
QPDFObjectHandle::QPDFDictItems::iterator&
2867
QPDFObjectHandle::QPDFDictItems::iterator::operator--()
2868
0
{
2869
0
    --m->iter;
2870
0
    updateIValue();
2871
0
    return *this;
2872
0
}
2873
2874
QPDFObjectHandle::QPDFDictItems::iterator::reference
2875
QPDFObjectHandle::QPDFDictItems::iterator::operator*()
2876
0
{
2877
0
    updateIValue();
2878
0
    return this->ivalue;
2879
0
}
2880
2881
QPDFObjectHandle::QPDFDictItems::iterator::pointer
2882
QPDFObjectHandle::QPDFDictItems::iterator::operator->()
2883
0
{
2884
0
    updateIValue();
2885
0
    return &this->ivalue;
2886
0
}
2887
2888
bool
2889
QPDFObjectHandle::QPDFDictItems::iterator::operator==(iterator const& other) const
2890
0
{
2891
0
    if (m->is_end && other.m->is_end) {
2892
0
        return true;
2893
0
    }
2894
0
    if (m->is_end || other.m->is_end) {
2895
0
        return false;
2896
0
    }
2897
0
    return (this->ivalue.first == other.ivalue.first);
2898
0
}
2899
2900
QPDFObjectHandle::QPDFDictItems::iterator::iterator(QPDFObjectHandle& oh, bool for_begin) :
2901
    m(new Members(oh, for_begin))
2902
0
{
2903
0
    updateIValue();
2904
0
}
2905
2906
void
2907
QPDFObjectHandle::QPDFDictItems::iterator::updateIValue()
2908
0
{
2909
0
    m->is_end = (m->iter == m->keys.end());
2910
0
    if (m->is_end) {
2911
0
        this->ivalue.first = "";
2912
0
        this->ivalue.second = QPDFObjectHandle();
2913
0
    } else {
2914
0
        this->ivalue.first = *(m->iter);
2915
0
        this->ivalue.second = m->oh.getKey(this->ivalue.first);
2916
0
    }
2917
0
}
2918
2919
QPDFObjectHandle::QPDFDictItems::iterator::Members::Members(QPDFObjectHandle& oh, bool for_begin) :
2920
    oh(oh)
2921
0
{
2922
0
    this->keys = oh.getKeys();
2923
0
    this->iter = for_begin ? this->keys.begin() : this->keys.end();
2924
0
}
2925
2926
QPDFObjectHandle::QPDFDictItems::iterator
2927
QPDFObjectHandle::QPDFDictItems::begin()
2928
0
{
2929
0
    return {oh, true};
2930
0
}
2931
2932
QPDFObjectHandle::QPDFDictItems::iterator
2933
QPDFObjectHandle::QPDFDictItems::end()
2934
0
{
2935
0
    return {oh, false};
2936
0
}
2937
2938
QPDFObjectHandle::QPDFArrayItems::QPDFArrayItems(QPDFObjectHandle const& oh) :
2939
    oh(oh)
2940
3.80k
{
2941
3.80k
}
2942
2943
QPDFObjectHandle::QPDFArrayItems::iterator&
2944
QPDFObjectHandle::QPDFArrayItems::iterator::operator++()
2945
70.5k
{
2946
70.5k
    if (!m->is_end) {
2947
70.5k
        ++m->item_number;
2948
70.5k
        updateIValue();
2949
70.5k
    }
2950
70.5k
    return *this;
2951
70.5k
}
2952
2953
QPDFObjectHandle::QPDFArrayItems::iterator&
2954
QPDFObjectHandle::QPDFArrayItems::iterator::operator--()
2955
0
{
2956
0
    if (m->item_number > 0) {
2957
0
        --m->item_number;
2958
0
        updateIValue();
2959
0
    }
2960
0
    return *this;
2961
0
}
2962
2963
QPDFObjectHandle::QPDFArrayItems::iterator::reference
2964
QPDFObjectHandle::QPDFArrayItems::iterator::operator*()
2965
70.6k
{
2966
70.6k
    updateIValue();
2967
70.6k
    return this->ivalue;
2968
70.6k
}
2969
2970
QPDFObjectHandle::QPDFArrayItems::iterator::pointer
2971
QPDFObjectHandle::QPDFArrayItems::iterator::operator->()
2972
0
{
2973
0
    updateIValue();
2974
0
    return &this->ivalue;
2975
0
}
2976
2977
bool
2978
QPDFObjectHandle::QPDFArrayItems::iterator::operator==(iterator const& other) const
2979
74.3k
{
2980
74.3k
    return (m->item_number == other.m->item_number);
2981
74.3k
}
2982
2983
QPDFObjectHandle::QPDFArrayItems::iterator::iterator(QPDFObjectHandle& oh, bool for_begin) :
2984
    m(new Members(oh, for_begin))
2985
7.61k
{
2986
7.61k
    updateIValue();
2987
7.61k
}
2988
2989
void
2990
QPDFObjectHandle::QPDFArrayItems::iterator::updateIValue()
2991
148k
{
2992
148k
    m->is_end = (m->item_number >= m->oh.getArrayNItems());
2993
148k
    if (m->is_end) {
2994
7.53k
        this->ivalue = QPDFObjectHandle();
2995
141k
    } else {
2996
141k
        this->ivalue = m->oh.getArrayItem(m->item_number);
2997
141k
    }
2998
148k
}
2999
3000
QPDFObjectHandle::QPDFArrayItems::iterator::Members::Members(QPDFObjectHandle& oh, bool for_begin) :
3001
    oh(oh)
3002
7.61k
{
3003
7.61k
    this->item_number = for_begin ? 0 : oh.getArrayNItems();
3004
7.61k
}
3005
3006
QPDFObjectHandle::QPDFArrayItems::iterator
3007
QPDFObjectHandle::QPDFArrayItems::begin()
3008
3.80k
{
3009
3.80k
    return {oh, true};
3010
3.80k
}
3011
3012
QPDFObjectHandle::QPDFArrayItems::iterator
3013
QPDFObjectHandle::QPDFArrayItems::end()
3014
3.80k
{
3015
3.80k
    return {oh, false};
3016
3.80k
}
3017
3018
QPDFObjGen
3019
QPDFObjectHandle::getObjGen() const
3020
12.5M
{
3021
12.5M
    return isInitialized() ? obj->getObjGen() : QPDFObjGen();
3022
12.5M
}
3023
3024
// Indirect object accessors
3025
QPDF*
3026
QPDFObjectHandle::getOwningQPDF() const
3027
122k
{
3028
122k
    return isInitialized() ? this->obj->getQPDF() : nullptr;
3029
122k
}
3030
3031
QPDF&
3032
QPDFObjectHandle::getQPDF(std::string const& error_msg) const
3033
0
{
3034
0
    auto result = isInitialized() ? this->obj->getQPDF() : nullptr;
3035
0
    if (result == nullptr) {
3036
0
        throw std::runtime_error(
3037
0
            error_msg.empty() ? "attempt to use a null qpdf object" : error_msg);
3038
0
    }
3039
0
    return *result;
3040
0
}
3041
3042
void
3043
QPDFObjectHandle::setParsedOffset(qpdf_offset_t offset)
3044
0
{
3045
    // This is called during parsing on newly created direct objects,
3046
    // so we can't call dereference() here.
3047
0
    if (isInitialized()) {
3048
0
        this->obj->setParsedOffset(offset);
3049
0
    }
3050
0
}
3051
3052
QPDFObjectHandle
3053
operator""_qpdf(char const* v, size_t len)
3054
4.63k
{
3055
4.63k
    return QPDFObjectHandle::parse(std::string(v, len), "QPDFObjectHandle literal");
3056
4.63k
}