Coverage Report

Created: 2024-09-08 06:06

/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
0
{
228
0
    return this->obj == rhs.obj;
229
0
}
230
#else
231
bool
232
QPDFObjectHandle::isSameObjectAs(QPDFObjectHandle const& rhs) const noexcept
233
{
234
    return this->obj == rhs.obj;
235
}
236
#endif
237
void
238
QPDFObjectHandle::disconnect()
239
876k
{
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
876k
    if (obj && !isIndirect()) {
244
719k
        this->obj->disconnect();
245
719k
    }
246
876k
}
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
3.19M
{
256
3.19M
    return obj ? obj->getResolvedTypeCode() : ::ot_uninitialized;
257
3.19M
}
258
259
#ifndef QPDF_FUTURE
260
char const*
261
QPDFObjectHandle::getTypeName()
262
#else
263
char const*
264
QPDFObjectHandle::getTypeName() const
265
#endif
266
1.19k
{
267
1.19k
    static constexpr std::array<char const*, 15> tn{
268
1.19k
        "uninitialized",
269
1.19k
        "reserved",
270
1.19k
        "null",
271
1.19k
        "boolean",
272
1.19k
        "integer",
273
1.19k
        "real",
274
1.19k
        "string",
275
1.19k
        "name",
276
1.19k
        "array",
277
1.19k
        "dictionary",
278
1.19k
        "stream",
279
1.19k
        "operator",
280
1.19k
        "inline-image",
281
1.19k
        "unresolved",
282
1.19k
        "destroyed"};
283
1.19k
    return obj ? tn[getTypeCode()] : "uninitialized";
284
1.19k
}
285
286
QPDF_Array*
287
QPDFObjectHandle::asArray() const
288
3.39M
{
289
3.39M
    return obj ? obj->as<QPDF_Array>() : nullptr;
290
3.39M
}
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.03M
{
301
3.03M
    return obj ? obj->as<QPDF_Dictionary>() : nullptr;
302
3.03M
}
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
59.9k
{
313
59.9k
    return obj ? obj->as<QPDF_Integer>() : nullptr;
314
59.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
451k
{
349
451k
    return obj ? obj->as<QPDF_Stream>() : nullptr;
350
451k
}
351
352
QPDF_Stream*
353
QPDFObjectHandle::asStreamWithAssert() const
354
451k
{
355
451k
    auto stream = asStream();
356
451k
    assertType("stream", stream);
357
451k
    return stream;
358
451k
}
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
489
{
385
489
    return obj && obj->getResolvedTypeCode() == ::ot_boolean;
386
489
}
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
2.46M
{
404
2.46M
    return obj && obj->getResolvedTypeCode() == ::ot_null;
405
2.46M
}
406
407
#ifndef QPDF_FUTURE
408
bool
409
QPDFObjectHandle::isInteger()
410
#else
411
bool
412
QPDFObjectHandle::isInteger() const
413
#endif
414
120k
{
415
120k
    return obj && obj->getResolvedTypeCode() == ::ot_integer;
416
120k
}
417
418
#ifndef QPDF_FUTURE
419
bool
420
QPDFObjectHandle::isReal()
421
#else
422
bool
423
QPDFObjectHandle::isReal() const
424
#endif
425
3.73k
{
426
3.73k
    return obj && obj->getResolvedTypeCode() == ::ot_real;
427
3.73k
}
428
429
#ifndef QPDF_FUTURE
430
bool
431
QPDFObjectHandle::isNumber()
432
#else
433
bool
434
QPDFObjectHandle::isNumber() const
435
#endif
436
62.0k
{
437
62.0k
    return (isInteger() || isReal());
438
62.0k
}
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.14M
{
482
1.14M
    return obj && obj->getResolvedTypeCode() == ::ot_name;
483
1.14M
}
484
485
#ifndef QPDF_FUTURE
486
bool
487
QPDFObjectHandle::isString()
488
#else
489
bool
490
QPDFObjectHandle::isString() const
491
#endif
492
78.2k
{
493
78.2k
    return obj && obj->getResolvedTypeCode() == ::ot_string;
494
78.2k
}
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.78M
{
526
3.78M
    return obj && obj->getResolvedTypeCode() == ::ot_array;
527
3.78M
}
528
529
#ifndef QPDF_FUTURE
530
bool
531
QPDFObjectHandle::isDictionary()
532
#else
533
bool
534
QPDFObjectHandle::isDictionary() const
535
#endif
536
7.29M
{
537
7.29M
    return obj && obj->getResolvedTypeCode() == ::ot_dictionary;
538
7.29M
}
539
540
#ifndef QPDF_FUTURE
541
bool
542
QPDFObjectHandle::isStream()
543
#else
544
bool
545
QPDFObjectHandle::isStream() const
546
#endif
547
3.74M
{
548
3.74M
    return obj && obj->getResolvedTypeCode() == ::ot_stream;
549
3.74M
}
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
489
{
570
489
    return isBool() || isInteger() || isName() || isNull() || isReal() || isString();
571
489
}
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
629k
{
581
629k
    return isName() && (getName() == name);
582
629k
}
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
3.45M
{
592
3.45M
    return isDictionary() && (type.empty() || getKey("/Type").isNameAndEquals(type)) &&
593
3.45M
        (subtype.empty() || getKey("/Subtype").isNameAndEquals(subtype));
594
3.45M
}
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
45.1k
{
604
45.1k
    return isStream() && getDict().isDictionaryOfType(type, subtype);
605
45.1k
}
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
58.6k
{
653
58.6k
    auto integer = asInteger();
654
58.6k
    if (integer) {
655
58.5k
        return integer->getVal();
656
58.5k
    } else {
657
40
        typeWarning("integer", "returning 0");
658
40
        QTC::TC("qpdf", "QPDFObjectHandle integer returning 0");
659
40
        return 0;
660
40
    }
661
58.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.32k
{
671
1.32k
    auto integer = asInteger();
672
1.32k
    if (integer == nullptr) {
673
23
        return false;
674
23
    }
675
1.30k
    value = integer->getVal();
676
1.30k
    return true;
677
1.32k
}
678
679
#ifndef QPDF_FUTURE
680
int
681
QPDFObjectHandle::getIntValueAsInt()
682
#else
683
int
684
QPDFObjectHandle::getIntValueAsInt() const
685
#endif
686
11.1k
{
687
11.1k
    int result = 0;
688
11.1k
    long long v = getIntValue();
689
11.1k
    if (v < INT_MIN) {
690
1
        QTC::TC("qpdf", "QPDFObjectHandle int returning INT_MIN");
691
1
        warnIfPossible("requested value of integer is too small; returning INT_MIN");
692
1
        result = INT_MIN;
693
11.1k
    } 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
11.1k
    } else {
698
11.1k
        result = static_cast<int>(v);
699
11.1k
    }
700
11.1k
    return result;
701
11.1k
}
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
46.2k
{
726
46.2k
    long long v = getIntValue();
727
46.2k
    if (v < 0) {
728
18
        QTC::TC("qpdf", "QPDFObjectHandle uint returning 0");
729
18
        warnIfPossible("unsigned value request for negative number; returning 0");
730
18
        return 0;
731
46.2k
    } else {
732
46.2k
        return static_cast<unsigned long long>(v);
733
46.2k
    }
734
46.2k
}
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
400k
{
832
400k
    if (isName()) {
833
400k
        return obj->getStringValue();
834
400k
    } 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
400k
}
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
77.8k
{
866
77.8k
    if (isString()) {
867
77.7k
        return obj->getStringValue();
868
77.7k
    } else {
869
114
        typeWarning("string", "returning empty string");
870
114
        QTC::TC("qpdf", "QPDFObjectHandle string returning empty string");
871
114
        return "";
872
114
    }
873
77.8k
}
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.47k
{
995
3.47k
    return *this;
996
3.47k
}
997
998
#ifndef QPDF_FUTURE
999
int
1000
QPDFObjectHandle::getArrayNItems()
1001
#else
1002
int
1003
QPDFObjectHandle::getArrayNItems() const
1004
#endif
1005
251k
{
1006
251k
    if (auto array = asArray()) {
1007
251k
        return array->size();
1008
251k
    } else {
1009
38
        typeWarning("array", "treating as empty");
1010
38
        QTC::TC("qpdf", "QPDFObjectHandle array treating as empty");
1011
38
        return 0;
1012
38
    }
1013
251k
}
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.99M
{
1023
2.99M
    if (auto array = asArray()) {
1024
2.99M
        if (auto result = array->at(n); result.obj != nullptr) {
1025
2.99M
            return result;
1026
2.99M
        } else {
1027
13
            objectWarning("returning null for out of bounds array access");
1028
13
            QTC::TC("qpdf", "QPDFObjectHandle array bounds");
1029
13
        }
1030
2.99M
    } else {
1031
75
        typeWarning("array", "returning null");
1032
75
        QTC::TC("qpdf", "QPDFObjectHandle array null for non-array");
1033
75
    }
1034
88
    static auto constexpr msg = " -> null returned from invalid array access"sv;
1035
88
    return QPDF_Null::create(obj, msg, "");
1036
2.99M
}
1037
1038
#ifndef QPDF_FUTURE
1039
bool
1040
QPDFObjectHandle::isRectangle()
1041
#else
1042
bool
1043
QPDFObjectHandle::isRectangle() const
1044
#endif
1045
21.3k
{
1046
21.3k
    if (auto array = asArray()) {
1047
77.4k
        for (int i = 0; i < 4; ++i) {
1048
62.1k
            if (auto item = array->at(i); !(item.obj && item.isNumber())) {
1049
293
                return false;
1050
293
            }
1051
62.1k
        }
1052
15.3k
        return array->size() == 4;
1053
15.6k
    }
1054
5.71k
    return false;
1055
21.3k
}
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
131k
{
1134
131k
    auto array = asArray();
1135
131k
    if (array) {
1136
131k
        return array->getAsVector();
1137
131k
    } 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
131k
}
1143
1144
// Array mutators
1145
1146
void
1147
QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const& item)
1148
1.10k
{
1149
1.10k
    if (auto array = asArray()) {
1150
1.10k
        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.10k
    } else {
1155
0
        typeWarning("array", "ignoring attempt to set item");
1156
0
        QTC::TC("qpdf", "QPDFObjectHandle array ignoring set item");
1157
0
    }
1158
1.10k
}
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
30
{
1212
30
    if (auto array = asArray()) {
1213
15
        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
15
    } else {
1218
15
        typeWarning("array", "ignoring attempt to erase item");
1219
15
        QTC::TC("qpdf", "QPDFObjectHandle array ignoring erase item");
1220
15
    }
1221
30
}
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
50.7k
{
1248
50.7k
    auto dict = asDictionary();
1249
50.7k
    if (dict) {
1250
50.2k
        return dict->hasKey(key);
1251
50.2k
    } else {
1252
497
        typeWarning("dictionary", "returning false for a key containment request");
1253
497
        QTC::TC("qpdf", "QPDFObjectHandle dictionary false for hasKey");
1254
497
        return false;
1255
497
    }
1256
50.7k
}
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.13M
{
1266
2.13M
    if (auto dict = asDictionary()) {
1267
2.13M
        return dict->getKey(key);
1268
2.13M
    } 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.13M
}
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
318k
{
1295
318k
    std::set<std::string> result;
1296
318k
    auto dict = asDictionary();
1297
318k
    if (dict) {
1298
318k
        result = dict->getKeys();
1299
318k
    } else {
1300
2
        typeWarning("dictionary", "treating as empty");
1301
2
        QTC::TC("qpdf", "QPDFObjectHandle dictionary empty set for getKeys");
1302
2
    }
1303
318k
    return result;
1304
318k
}
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
307k
{
1314
307k
    std::map<std::string, QPDFObjectHandle> result;
1315
307k
    auto dict = asDictionary();
1316
307k
    if (dict) {
1317
307k
        result = dict->getAsMap();
1318
307k
    } else {
1319
0
        typeWarning("dictionary", "treating as empty");
1320
0
        QTC::TC("qpdf", "QPDFObjectHandle dictionary empty map for asMap");
1321
0
    }
1322
307k
    return result;
1323
307k
}
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
22.4k
{
1334
22.4k
    if (isNameAndEquals(value)) {
1335
0
        return true;
1336
22.4k
    } else if (isArray()) {
1337
5.70k
        for (auto& item: getArrayAsVector()) {
1338
5.70k
            if (item.isNameAndEquals(value)) {
1339
15
                return true;
1340
15
            }
1341
5.70k
        }
1342
2.43k
    }
1343
22.4k
    return false;
1344
22.4k
}
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
34.3k
{
1518
34.3k
    auto dict = asDictionary();
1519
34.3k
    if (dict) {
1520
33.9k
        checkOwnership(value);
1521
33.9k
        dict->replaceKey(key, value);
1522
33.9k
    } else {
1523
391
        typeWarning("dictionary", "ignoring key replacement request");
1524
391
        QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring replaceKey");
1525
391
    }
1526
34.3k
}
1527
1528
QPDFObjectHandle
1529
QPDFObjectHandle::replaceKeyAndGetNew(std::string const& key, QPDFObjectHandle const& value)
1530
5.20k
{
1531
5.20k
    replaceKey(key, value);
1532
5.20k
    return value;
1533
5.20k
}
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
191k
{
1546
191k
    auto dict = asDictionary();
1547
191k
    if (dict) {
1548
191k
        dict->removeKey(key);
1549
191k
    } else {
1550
0
        typeWarning("dictionary", "ignoring key removal request");
1551
0
        QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring removeKey");
1552
0
    }
1553
191k
}
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
177k
{
1582
177k
    return asStreamWithAssert()->getDict();
1583
177k
}
1584
1585
void
1586
QPDFObjectHandle::setFilterOnWrite(bool val)
1587
2.45k
{
1588
2.45k
    asStreamWithAssert()->setFilterOnWrite(val);
1589
2.45k
}
1590
1591
bool
1592
QPDFObjectHandle::getFilterOnWrite()
1593
67.6k
{
1594
67.6k
    return asStreamWithAssert()->getFilterOnWrite();
1595
67.6k
}
1596
1597
bool
1598
QPDFObjectHandle::isDataModified()
1599
130k
{
1600
130k
    return asStreamWithAssert()->isDataModified();
1601
130k
}
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
3.18k
{
1612
3.18k
    return asStreamWithAssert()->getStreamData(level);
1613
3.18k
}
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
70.1k
{
1642
70.1k
    bool filtering_attempted;
1643
70.1k
    asStreamWithAssert()->pipeStreamData(
1644
70.1k
        p, &filtering_attempted, encode_flags, decode_level, suppress_warnings, will_retry);
1645
70.1k
    return filtering_attempted;
1646
70.1k
}
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
34
{
1903
34
    if (this->isIndirect()) {
1904
34
        return getObjGen().unparse(' ') + " R";
1905
34
    } else {
1906
0
        return unparseResolved();
1907
0
    }
1908
34
}
1909
1910
#ifndef QPDF_FUTURE
1911
std::string
1912
QPDFObjectHandle::unparseResolved()
1913
#else
1914
std::string
1915
QPDFObjectHandle::unparseResolved() const
1916
#endif
1917
2.64M
{
1918
2.64M
    if (!obj) {
1919
0
        throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle");
1920
0
    }
1921
2.64M
    return obj->unparse();
1922
2.64M
}
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
8.99k
{
2028
8.99k
    return parse(nullptr, object_str, object_description);
2029
8.99k
}
2030
2031
QPDFObjectHandle
2032
QPDFObjectHandle::parse(
2033
    QPDF* context, std::string const& object_str, std::string const& object_description)
2034
8.99k
{
2035
8.99k
    auto input = std::shared_ptr<InputSource>(new BufferInputSource("parsed object", object_str));
2036
8.99k
    QPDFTokenizer tokenizer;
2037
8.99k
    bool empty = false;
2038
8.99k
    QPDFObjectHandle result = parse(input, object_description, tokenizer, empty, nullptr, context);
2039
8.99k
    size_t offset = QIntC::to_size(input->tell());
2040
8.99k
    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
8.99k
    return result;
2053
8.99k
}
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
8.99k
{
2226
8.99k
    return QPDFParser(input, object_description, tokenizer, decrypter, context, false)
2227
8.99k
        .parse(empty, false);
2228
8.99k
}
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
75
{
2250
75
    return {QPDF_Null::create()};
2251
75
}
2252
2253
QPDFObjectHandle
2254
QPDFObjectHandle::newInteger(long long value)
2255
1.42k
{
2256
1.42k
    return {QPDF_Integer::create(value)};
2257
1.42k
}
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
11.4k
{
2268
11.4k
    return {QPDF_Real::create(value, decimal_places, trim_trailing_zeroes)};
2269
11.4k
}
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
4.39k
{
2310
4.39k
    return {QPDF_Array::create(items)};
2311
4.39k
}
2312
2313
QPDFObjectHandle
2314
QPDFObjectHandle::newArray(Rectangle const& rect)
2315
2.85k
{
2316
2.85k
    return newArray({newReal(rect.llx), newReal(rect.lly), newReal(rect.urx), newReal(rect.ury)});
2317
2.85k
}
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
5.20k
{
2364
5.20k
    return newDictionary(std::map<std::string, QPDFObjectHandle>());
2365
5.20k
}
2366
2367
QPDFObjectHandle
2368
QPDFObjectHandle::newDictionary(std::map<std::string, QPDFObjectHandle> const& items)
2369
5.20k
{
2370
5.20k
    return {QPDF_Dictionary::create(items)};
2371
5.20k
}
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
47.0k
{
2429
47.0k
    return obj && obj->hasDescription();
2430
47.0k
}
2431
2432
QPDFObjectHandle
2433
QPDFObjectHandle::shallowCopy()
2434
8.05k
{
2435
8.05k
    if (!obj) {
2436
0
        throw std::logic_error("operation attempted on uninitialized QPDFObjectHandle");
2437
0
    }
2438
8.05k
    return {obj->copy()};
2439
8.05k
}
2440
2441
QPDFObjectHandle
2442
QPDFObjectHandle::unsafeShallowCopy()
2443
318k
{
2444
318k
    if (!obj) {
2445
0
        throw std::logic_error("operation attempted on uninitialized QPDFObjectHandle");
2446
0
    }
2447
318k
    return {obj->copy(true)};
2448
318k
}
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
1.17k
{
2531
1.17k
    QPDF* context = nullptr;
2532
1.17k
    std::string description;
2533
    // Type checks above guarantee that the object has been dereferenced. Nevertheless, dereference
2534
    // throws exceptions in the test suite
2535
1.17k
    if (!obj) {
2536
0
        throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle");
2537
0
    }
2538
1.17k
    obj->getDescription(context, description);
2539
    // Null context handled by warn
2540
1.17k
    warn(
2541
1.17k
        context,
2542
1.17k
        QPDFExc(
2543
1.17k
            qpdf_e_object,
2544
1.17k
            "",
2545
1.17k
            description,
2546
1.17k
            0,
2547
1.17k
            std::string("operation for ") + expected_type + " attempted on object of type " +
2548
1.17k
                QPDFObjectHandle(*this).getTypeName() + ": " + warning));
2549
1.17k
}
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
34.8k
{
2559
34.8k
    QPDF* context = nullptr;
2560
34.8k
    std::string description;
2561
34.8k
    if (obj && obj->getDescription(context, description)) {
2562
19.6k
        warn(context, QPDFExc(qpdf_e_damaged_pdf, "", description, 0, warning));
2563
19.6k
    } else {
2564
15.1k
        *QPDFLogger::defaultLogger()->getError() << warning << "\n";
2565
15.1k
    }
2566
34.8k
}
2567
2568
void
2569
QPDFObjectHandle::objectWarning(std::string const& warning) const
2570
13
{
2571
13
    QPDF* context = nullptr;
2572
13
    std::string description;
2573
    // Type checks above guarantee that the object is initialized.
2574
13
    obj->getDescription(context, description);
2575
    // Null context handled by warn
2576
13
    warn(context, QPDFExc(qpdf_e_object, "", description, 0, warning));
2577
13
}
2578
2579
void
2580
QPDFObjectHandle::assertType(char const* type_name, bool istype) const
2581
451k
{
2582
451k
    if (!istype) {
2583
18
        throw std::runtime_error(
2584
18
            std::string("operation for ") + type_name + " attempted on object of type " +
2585
18
            QPDFObjectHandle(*this).getTypeName());
2586
18
    }
2587
451k
}
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
33.9k
{
2818
33.9k
    auto qpdf = getOwningQPDF();
2819
33.9k
    auto item_qpdf = item.getOwningQPDF();
2820
33.9k
    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
33.9k
}
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
20.8k
{
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
20.8k
    if (qpdf) {
2847
20.7k
        qpdf->warn(e);
2848
20.7k
    } else {
2849
73
        throw e;
2850
73
    }
2851
20.8k
}
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.47k
{
2941
3.47k
}
2942
2943
QPDFObjectHandle::QPDFArrayItems::iterator&
2944
QPDFObjectHandle::QPDFArrayItems::iterator::operator++()
2945
41.7k
{
2946
41.7k
    if (!m->is_end) {
2947
41.7k
        ++m->item_number;
2948
41.7k
        updateIValue();
2949
41.7k
    }
2950
41.7k
    return *this;
2951
41.7k
}
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
41.7k
{
2966
41.7k
    updateIValue();
2967
41.7k
    return this->ivalue;
2968
41.7k
}
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
45.2k
{
2980
45.2k
    return (m->item_number == other.m->item_number);
2981
45.2k
}
2982
2983
QPDFObjectHandle::QPDFArrayItems::iterator::iterator(QPDFObjectHandle& oh, bool for_begin) :
2984
    m(new Members(oh, for_begin))
2985
6.94k
{
2986
6.94k
    updateIValue();
2987
6.94k
}
2988
2989
void
2990
QPDFObjectHandle::QPDFArrayItems::iterator::updateIValue()
2991
90.4k
{
2992
90.4k
    m->is_end = (m->item_number >= m->oh.getArrayNItems());
2993
90.4k
    if (m->is_end) {
2994
6.88k
        this->ivalue = QPDFObjectHandle();
2995
83.5k
    } else {
2996
83.5k
        this->ivalue = m->oh.getArrayItem(m->item_number);
2997
83.5k
    }
2998
90.4k
}
2999
3000
QPDFObjectHandle::QPDFArrayItems::iterator::Members::Members(QPDFObjectHandle& oh, bool for_begin) :
3001
    oh(oh)
3002
6.94k
{
3003
6.94k
    this->item_number = for_begin ? 0 : oh.getArrayNItems();
3004
6.94k
}
3005
3006
QPDFObjectHandle::QPDFArrayItems::iterator
3007
QPDFObjectHandle::QPDFArrayItems::begin()
3008
3.47k
{
3009
3.47k
    return {oh, true};
3010
3.47k
}
3011
3012
QPDFObjectHandle::QPDFArrayItems::iterator
3013
QPDFObjectHandle::QPDFArrayItems::end()
3014
3.47k
{
3015
3.47k
    return {oh, false};
3016
3.47k
}
3017
3018
QPDFObjGen
3019
QPDFObjectHandle::getObjGen() const
3020
13.3M
{
3021
13.3M
    return isInitialized() ? obj->getObjGen() : QPDFObjGen();
3022
13.3M
}
3023
3024
// Indirect object accessors
3025
QPDF*
3026
QPDFObjectHandle::getOwningQPDF() const
3027
140k
{
3028
140k
    return isInitialized() ? this->obj->getQPDF() : nullptr;
3029
140k
}
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
3.78k
{
3055
3.78k
    return QPDFObjectHandle::parse(std::string(v, len), "QPDFObjectHandle literal");
3056
3.78k
}