Commit 9685176ceae9049205783597752b09298631fbfd

Authored by m-holger
Committed by GitHub
2 parents 3fcf5696 1b7c8727

Merge pull request #1372 from m-holger/object

Refactor QPDFObject / QPDFObjectHandle
Showing 61 changed files with 2705 additions and 2989 deletions
include/qpdf/Constants.h
@@ -126,6 +126,7 @@ enum qpdf_object_type_e { @@ -126,6 +126,7 @@ enum qpdf_object_type_e {
126 /* Object types internal to qpdf */ 126 /* Object types internal to qpdf */
127 ot_unresolved, 127 ot_unresolved,
128 ot_destroyed, 128 ot_destroyed,
  129 + ot_reference,
129 }; 130 };
130 131
131 /* Write Parameters. See QPDFWriter.hh for details. */ 132 /* Write Parameters. See QPDFWriter.hh for details. */
include/qpdf/ObjectHandle.hh 0 → 100644
  1 +// Copyright (c) 2005-2021 Jay Berkenbilt
  2 +// Copyright (c) 2022-2025 Jay Berkenbilt and Manfred Holger
  3 +//
  4 +// This file is part of qpdf.
  5 +//
  6 +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
  7 +// in compliance with the License. You may obtain a copy of the License at
  8 +//
  9 +// http://www.apache.org/licenses/LICENSE-2.0
  10 +//
  11 +// Unless required by applicable law or agreed to in writing, software distributed under the License
  12 +// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
  13 +// or implied. See the License for the specific language governing permissions and limitations under
  14 +// the License.
  15 +//
  16 +// Versions of qpdf prior to version 7 were released under the terms of version 2.0 of the Artistic
  17 +// License. At your option, you may continue to consider qpdf to be licensed under those terms.
  18 +// Please see the manual for additional information.
  19 +
  20 +#ifndef OBJECTHANDLE_HH
  21 +#define OBJECTHANDLE_HH
  22 +
  23 +#include <qpdf/Constants.h>
  24 +#include <qpdf/DLL.h>
  25 +#include <qpdf/QPDFObjGen.hh>
  26 +#include <qpdf/Types.h>
  27 +
  28 +#include <cstdint>
  29 +#include <memory>
  30 +
  31 +class QPDF;
  32 +class QPDF_Dictionary;
  33 +class QPDFObject;
  34 +class QPDFObjectHandle;
  35 +
  36 +namespace qpdf
  37 +{
  38 + class Array;
  39 + class BaseDictionary;
  40 + class Dictionary;
  41 + class Stream;
  42 +
  43 + enum typed : std::uint8_t { strict = 0, any_flag = 1, optional = 2, any = 3, error = 4 };
  44 +
  45 + // Basehandle is only used as a base-class for QPDFObjectHandle like classes. Currently the only
  46 + // methods exposed in public API are operators to convert derived objects to QPDFObjectHandle,
  47 + // QPDFObjGen and bool.
  48 + class BaseHandle
  49 + {
  50 + public:
  51 + explicit inline operator bool() const;
  52 + inline operator QPDFObjectHandle() const;
  53 + operator QPDFObjGen() const;
  54 +
  55 + // The rest of the header file is for qpdf internal use only.
  56 +
  57 + inline QPDFObjGen id_gen() const;
  58 + inline bool indirect() const;
  59 + inline bool null() const;
  60 + inline QPDF* qpdf() const;
  61 + inline qpdf_object_type_e raw_type_code() const;
  62 + inline qpdf_object_type_e type_code() const;
  63 +
  64 + protected:
  65 + BaseHandle() = default;
  66 + BaseHandle(std::shared_ptr<QPDFObject> const& obj) :
  67 + obj(obj) {};
  68 + BaseHandle(std::shared_ptr<QPDFObject>&& obj) :
  69 + obj(std::move(obj)) {};
  70 + BaseHandle(BaseHandle const&) = default;
  71 + BaseHandle& operator=(BaseHandle const&) = default;
  72 + BaseHandle(BaseHandle&&) = default;
  73 + BaseHandle& operator=(BaseHandle&&) = default;
  74 + ~BaseHandle() = default;
  75 +
  76 + template <typename T>
  77 + T* as() const;
  78 +
  79 + std::shared_ptr<QPDFObject> obj;
  80 + };
  81 +
  82 +} // namespace qpdf
  83 +
  84 +#endif // QPDFOBJECTHANDLE_HH
include/qpdf/QPDF.hh
@@ -798,9 +798,10 @@ class QPDF @@ -798,9 +798,10 @@ class QPDF
798 { 798 {
799 friend class QPDFObject; 799 friend class QPDFObject;
800 friend class QPDF_Unresolved; 800 friend class QPDF_Unresolved;
  801 + friend class qpdf::BaseHandle;
801 802
802 private: 803 private:
803 - static QPDFObject* 804 + static std::shared_ptr<QPDFObject> const&
804 resolved(QPDF* qpdf, QPDFObjGen og) 805 resolved(QPDF* qpdf, QPDFObjGen og)
805 { 806 {
806 return qpdf->resolve(og); 807 return qpdf->resolve(og);
@@ -854,6 +855,7 @@ class QPDF @@ -854,6 +855,7 @@ class QPDF
854 class Pipe 855 class Pipe
855 { 856 {
856 friend class QPDF_Stream; 857 friend class QPDF_Stream;
  858 + friend class qpdf::Stream;
857 859
858 private: 860 private:
859 static bool 861 static bool
@@ -1071,7 +1073,7 @@ class QPDF @@ -1071,7 +1073,7 @@ class QPDF
1071 QPDFObjGen exp_og, 1073 QPDFObjGen exp_og,
1072 QPDFObjGen& og, 1074 QPDFObjGen& og,
1073 bool skip_cache_if_in_xref); 1075 bool skip_cache_if_in_xref);
1074 - QPDFObject* resolve(QPDFObjGen og); 1076 + std::shared_ptr<QPDFObject> const& resolve(QPDFObjGen og);
1075 void resolveObjectsInStream(int obj_stream_number); 1077 void resolveObjectsInStream(int obj_stream_number);
1076 void stopOnError(std::string const& message); 1078 void stopOnError(std::string const& message);
1077 QPDFObjGen nextObjGen(); 1079 QPDFObjGen nextObjGen();
@@ -1086,7 +1088,8 @@ class QPDF @@ -1086,7 +1088,8 @@ class QPDF
1086 QPDFObjGen og, 1088 QPDFObjGen og,
1087 std::shared_ptr<QPDFObject> const& object, 1089 std::shared_ptr<QPDFObject> const& object,
1088 qpdf_offset_t end_before_space, 1090 qpdf_offset_t end_before_space,
1089 - qpdf_offset_t end_after_space); 1091 + qpdf_offset_t end_after_space,
  1092 + bool destroy = true);
1090 static QPDFExc damagedPDF( 1093 static QPDFExc damagedPDF(
1091 InputSource& input, 1094 InputSource& input,
1092 std::string const& object, 1095 std::string const& object,
include/qpdf/QPDFObjGen.hh
@@ -130,12 +130,6 @@ class QPDFObjGen @@ -130,12 +130,6 @@ class QPDFObjGen
130 } 130 }
131 131
132 QPDF_DLL 132 QPDF_DLL
133 - bool add(QPDFObjectHandle const& oh);  
134 -  
135 - QPDF_DLL  
136 - bool add(QPDFObjectHelper const& oh);  
137 -  
138 - QPDF_DLL  
139 void 133 void
140 erase(QPDFObjGen og) 134 erase(QPDFObjGen og)
141 { 135 {
@@ -143,12 +137,6 @@ class QPDFObjGen @@ -143,12 +137,6 @@ class QPDFObjGen
143 std::set<QPDFObjGen>::erase(og); 137 std::set<QPDFObjGen>::erase(og);
144 } 138 }
145 } 139 }
146 -  
147 - QPDF_DLL  
148 - void erase(QPDFObjectHandle const& oh);  
149 -  
150 - QPDF_DLL  
151 - void erase(QPDFObjectHelper const& oh);  
152 }; 140 };
153 141
154 private: 142 private:
include/qpdf/QPDFObjectHandle.hh
@@ -37,6 +37,7 @@ @@ -37,6 +37,7 @@
37 #include <qpdf/Buffer.hh> 37 #include <qpdf/Buffer.hh>
38 #include <qpdf/InputSource.hh> 38 #include <qpdf/InputSource.hh>
39 #include <qpdf/JSON.hh> 39 #include <qpdf/JSON.hh>
  40 +#include <qpdf/ObjectHandle.hh>
40 #include <qpdf/QPDFObjGen.hh> 41 #include <qpdf/QPDFObjGen.hh>
41 #include <qpdf/QPDFTokenizer.hh> 42 #include <qpdf/QPDFTokenizer.hh>
42 43
@@ -55,13 +56,14 @@ class QPDF_Reserved; @@ -55,13 +56,14 @@ class QPDF_Reserved;
55 class QPDF_Stream; 56 class QPDF_Stream;
56 class QPDF_String; 57 class QPDF_String;
57 class QPDFObject; 58 class QPDFObject;
  59 +class QPDFObjectHandle;
58 class QPDFTokenizer; 60 class QPDFTokenizer;
59 class QPDFExc; 61 class QPDFExc;
60 class Pl_QPDFTokenizer; 62 class Pl_QPDFTokenizer;
61 class QPDFMatrix; 63 class QPDFMatrix;
62 class QPDFParser; 64 class QPDFParser;
63 65
64 -class QPDFObjectHandle 66 +class QPDFObjectHandle final: public qpdf::BaseHandle
65 { 67 {
66 friend class QPDFParser; 68 friend class QPDFParser;
67 69
@@ -290,25 +292,19 @@ class QPDFObjectHandle @@ -290,25 +292,19 @@ class QPDFObjectHandle
290 QPDFObjectHandle(QPDFObjectHandle const&) = default; 292 QPDFObjectHandle(QPDFObjectHandle const&) = default;
291 QPDF_DLL 293 QPDF_DLL
292 QPDFObjectHandle& operator=(QPDFObjectHandle const&) = default; 294 QPDFObjectHandle& operator=(QPDFObjectHandle const&) = default;
293 -  
294 QPDF_DLL 295 QPDF_DLL
295 QPDFObjectHandle(QPDFObjectHandle&&) = default; 296 QPDFObjectHandle(QPDFObjectHandle&&) = default;
296 QPDF_DLL 297 QPDF_DLL
297 QPDFObjectHandle& operator=(QPDFObjectHandle&&) = default; 298 QPDFObjectHandle& operator=(QPDFObjectHandle&&) = default;
298 299
299 - // Return true if the QPDFObjectHandle is initialized. This allows object handles to be used in  
300 - // if statements with initializer.  
301 - QPDF_DLL  
302 - explicit inline operator bool() const noexcept;  
303 -  
304 - [[deprecated("use operator bool()")]] QPDF_DLL inline bool isInitialized() const noexcept; 300 + [[deprecated("use operator bool()")]] QPDF_DLL inline bool isInitialized() const;
305 301
306 // This method returns true if the QPDFObjectHandle objects point to exactly the same underlying 302 // This method returns true if the QPDFObjectHandle objects point to exactly the same underlying
307 // object, meaning that changes to one are reflected in the other, or "if you paint one, the 303 // object, meaning that changes to one are reflected in the other, or "if you paint one, the
308 // other one changes color." This does not perform a structural comparison of the contents of 304 // other one changes color." This does not perform a structural comparison of the contents of
309 // the objects. 305 // the objects.
310 QPDF_DLL 306 QPDF_DLL
311 - bool isSameObjectAs(QPDFObjectHandle const&) const noexcept; 307 + bool isSameObjectAs(QPDFObjectHandle const&) const;
312 308
313 // Return type code and type name of underlying object. These are useful for doing rapid type 309 // Return type code and type name of underlying object. These are useful for doing rapid type
314 // tests (like switch statements) or for testing and debugging. 310 // tests (like switch statements) or for testing and debugging.
@@ -1258,8 +1254,7 @@ class QPDFObjectHandle @@ -1258,8 +1254,7 @@ class QPDFObjectHandle
1258 // Provide access to specific classes for recursive disconnected(). 1254 // Provide access to specific classes for recursive disconnected().
1259 class DisconnectAccess 1255 class DisconnectAccess
1260 { 1256 {
1261 - friend class QPDF_Dictionary;  
1262 - friend class QPDF_Stream; 1257 + friend class QPDFObject;
1263 1258
1264 private: 1259 private:
1265 static void 1260 static void
@@ -1329,7 +1324,11 @@ class QPDFObjectHandle @@ -1329,7 +1324,11 @@ class QPDFObjectHandle
1329 // The following methods do not form part of the public API and are for internal use only. 1324 // The following methods do not form part of the public API and are for internal use only.
1330 1325
1331 QPDFObjectHandle(std::shared_ptr<QPDFObject> const& obj) : 1326 QPDFObjectHandle(std::shared_ptr<QPDFObject> const& obj) :
1332 - obj(obj) 1327 + qpdf::BaseHandle(obj)
  1328 + {
  1329 + }
  1330 + QPDFObjectHandle(std::shared_ptr<QPDFObject>&& obj) :
  1331 + qpdf::BaseHandle(std::move(obj))
1333 { 1332 {
1334 } 1333 }
1335 std::shared_ptr<QPDFObject> 1334 std::shared_ptr<QPDFObject>
@@ -1355,21 +1354,11 @@ class QPDFObjectHandle @@ -1355,21 +1354,11 @@ class QPDFObjectHandle
1355 1354
1356 void writeJSON(int json_version, JSON::Writer& p, bool dereference_indirect = false) const; 1355 void writeJSON(int json_version, JSON::Writer& p, bool dereference_indirect = false) const;
1357 1356
1358 - private:  
1359 - QPDF_Array* asArray() const;  
1360 - QPDF_Bool* asBool() const;  
1361 - QPDF_Dictionary* asDictionary() const;  
1362 - QPDF_InlineImage* asInlineImage() const;  
1363 - QPDF_Integer* asInteger() const;  
1364 - QPDF_Name* asName() const;  
1365 - QPDF_Null* asNull() const;  
1366 - QPDF_Operator* asOperator() const;  
1367 - QPDF_Real* asReal() const;  
1368 - QPDF_Reserved* asReserved() const;  
1369 - QPDF_Stream* asStream() const;  
1370 - QPDF_Stream* asStreamWithAssert() const;  
1371 - QPDF_String* asString() const; 1357 + inline qpdf::Array as_array(qpdf::typed options = qpdf::typed::any) const;
  1358 + inline qpdf::Dictionary as_dictionary(qpdf::typed options = qpdf::typed::any) const;
  1359 + inline qpdf::Stream as_stream(qpdf::typed options = qpdf::typed::strict) const;
1372 1360
  1361 + private:
1373 void typeWarning(char const* expected_type, std::string const& warning) const; 1362 void typeWarning(char const* expected_type, std::string const& warning) const;
1374 void objectWarning(std::string const& warning) const; 1363 void objectWarning(std::string const& warning) const;
1375 void assertType(char const* type_name, bool istype) const; 1364 void assertType(char const* type_name, bool istype) const;
@@ -1386,10 +1375,6 @@ class QPDFObjectHandle @@ -1386,10 +1375,6 @@ class QPDFObjectHandle
1386 arrayOrStreamToStreamArray(std::string const& description, std::string& all_description); 1375 arrayOrStreamToStreamArray(std::string const& description, std::string& all_description);
1387 static void warn(QPDF*, QPDFExc const&); 1376 static void warn(QPDF*, QPDFExc const&);
1388 void checkOwnership(QPDFObjectHandle const&) const; 1377 void checkOwnership(QPDFObjectHandle const&) const;
1389 -  
1390 - // Moving members of QPDFObjectHandle into a smart pointer incurs a substantial performance  
1391 - // penalty since QPDFObjectHandle objects are copied around so frequently.  
1392 - std::shared_ptr<QPDFObject> obj;  
1393 }; 1378 };
1394 1379
1395 #ifndef QPDF_NO_QPDF_STRING 1380 #ifndef QPDF_NO_QPDF_STRING
@@ -1606,6 +1591,22 @@ class QPDFObjectHandle::QPDFArrayItems @@ -1606,6 +1591,22 @@ class QPDFObjectHandle::QPDFArrayItems
1606 QPDFObjectHandle oh; 1591 QPDFObjectHandle oh;
1607 }; 1592 };
1608 1593
  1594 +namespace qpdf
  1595 +{
  1596 + inline BaseHandle::
  1597 + operator bool() const
  1598 + {
  1599 + return static_cast<bool>(obj);
  1600 + }
  1601 +
  1602 + inline BaseHandle::
  1603 + operator QPDFObjectHandle() const
  1604 + {
  1605 + return {obj};
  1606 + }
  1607 +
  1608 +} // namespace qpdf
  1609 +
1609 inline int 1610 inline int
1610 QPDFObjectHandle::getObjectID() const 1611 QPDFObjectHandle::getObjectID() const
1611 { 1612 {
@@ -1625,15 +1626,9 @@ QPDFObjectHandle::isIndirect() const @@ -1625,15 +1626,9 @@ QPDFObjectHandle::isIndirect() const
1625 } 1626 }
1626 1627
1627 inline bool 1628 inline bool
1628 -QPDFObjectHandle::isInitialized() const noexcept 1629 +QPDFObjectHandle::isInitialized() const
1629 { 1630 {
1630 return obj != nullptr; 1631 return obj != nullptr;
1631 } 1632 }
1632 1633
1633 -inline QPDFObjectHandle::  
1634 -operator bool() const noexcept  
1635 -{  
1636 - return static_cast<bool>(obj);  
1637 -}  
1638 -  
1639 #endif // QPDFOBJECTHANDLE_HH 1634 #endif // QPDFOBJECTHANDLE_HH
include/qpdf/QPDFObjectHandle_future.hh deleted
include/qpdf/QPDFObjectHelper.hh
@@ -31,13 +31,12 @@ @@ -31,13 +31,12 @@
31 // underlying QPDF objects unless there is a specific comment in a specific helper method that says 31 // underlying QPDF objects unless there is a specific comment in a specific helper method that says
32 // otherwise. The pattern of using helper objects was introduced to allow creation of higher level 32 // otherwise. The pattern of using helper objects was introduced to allow creation of higher level
33 // helper functions without polluting the public interface of QPDFObjectHandle. 33 // helper functions without polluting the public interface of QPDFObjectHandle.
34 -  
35 -class QPDF_DLL_CLASS QPDFObjectHelper 34 +class QPDF_DLL_CLASS QPDFObjectHelper: public qpdf::BaseHandle
36 { 35 {
37 public: 36 public:
38 QPDF_DLL 37 QPDF_DLL
39 QPDFObjectHelper(QPDFObjectHandle oh) : 38 QPDFObjectHelper(QPDFObjectHandle oh) :
40 - oh(oh) 39 + qpdf::BaseHandle(oh.getObj())
41 { 40 {
42 } 41 }
43 QPDF_DLL 42 QPDF_DLL
@@ -46,17 +45,29 @@ class QPDF_DLL_CLASS QPDFObjectHelper @@ -46,17 +45,29 @@ class QPDF_DLL_CLASS QPDFObjectHelper
46 QPDFObjectHandle 45 QPDFObjectHandle
47 getObjectHandle() 46 getObjectHandle()
48 { 47 {
49 - return this->oh; 48 + return {obj};
50 } 49 }
51 QPDF_DLL 50 QPDF_DLL
52 QPDFObjectHandle const 51 QPDFObjectHandle const
53 getObjectHandle() const 52 getObjectHandle() const
54 { 53 {
55 - return this->oh; 54 + return {obj};
56 } 55 }
57 56
58 protected: 57 protected:
59 - QPDFObjectHandle oh; 58 + QPDF_DLL_PRIVATE
  59 + QPDFObjectHandle
  60 + oh()
  61 + {
  62 + return {obj};
  63 + }
  64 + QPDF_DLL_PRIVATE
  65 + QPDFObjectHandle const
  66 + oh() const
  67 + {
  68 + return {obj};
  69 + }
  70 + QPDFObjectHandle oh_;
60 }; 71 };
61 72
62 #endif // QPDFOBJECTHELPER_HH 73 #endif // QPDFOBJECTHELPER_HH
libqpdf/CMakeLists.txt
@@ -76,7 +76,6 @@ set(libqpdf_SOURCES @@ -76,7 +76,6 @@ set(libqpdf_SOURCES
76 QPDFObject.cc 76 QPDFObject.cc
77 QPDFObjectHandle.cc 77 QPDFObjectHandle.cc
78 QPDFObjectHelper.cc 78 QPDFObjectHelper.cc
79 - QPDFObjGen.cc  
80 QPDFOutlineDocumentHelper.cc 79 QPDFOutlineDocumentHelper.cc
81 QPDFOutlineObjectHelper.cc 80 QPDFOutlineObjectHelper.cc
82 QPDFPageDocumentHelper.cc 81 QPDFPageDocumentHelper.cc
@@ -87,23 +86,12 @@ set(libqpdf_SOURCES @@ -87,23 +86,12 @@ set(libqpdf_SOURCES
87 QPDFSystemError.cc 86 QPDFSystemError.cc
88 QPDFTokenizer.cc 87 QPDFTokenizer.cc
89 QPDFUsage.cc 88 QPDFUsage.cc
90 - QPDFValue.cc  
91 QPDFWriter.cc 89 QPDFWriter.cc
92 QPDFXRefEntry.cc 90 QPDFXRefEntry.cc
93 QPDF_Array.cc 91 QPDF_Array.cc
94 - QPDF_Bool.cc  
95 - QPDF_Destroyed.cc  
96 QPDF_Dictionary.cc 92 QPDF_Dictionary.cc
97 - QPDF_InlineImage.cc  
98 - QPDF_Integer.cc  
99 - QPDF_Name.cc  
100 - QPDF_Null.cc  
101 - QPDF_Operator.cc  
102 - QPDF_Real.cc  
103 - QPDF_Reserved.cc  
104 QPDF_Stream.cc 93 QPDF_Stream.cc
105 QPDF_String.cc 94 QPDF_String.cc
106 - QPDF_Unresolved.cc  
107 QPDF_encryption.cc 95 QPDF_encryption.cc
108 QPDF_json.cc 96 QPDF_json.cc
109 QPDF_linearization.cc 97 QPDF_linearization.cc
libqpdf/ContentNormalizer.cc
1 #include <qpdf/ContentNormalizer.hh> 1 #include <qpdf/ContentNormalizer.hh>
2 2
3 -#include <qpdf/QPDF_Name.hh> 3 +#include <qpdf/QPDFObjectHandle_private.hh>
4 #include <qpdf/QUtil.hh> 4 #include <qpdf/QUtil.hh>
5 5
  6 +using namespace qpdf;
  7 +
6 ContentNormalizer::ContentNormalizer() : 8 ContentNormalizer::ContentNormalizer() :
7 any_bad_tokens(false), 9 any_bad_tokens(false),
8 last_token_was_bad(false) 10 last_token_was_bad(false)
@@ -55,7 +57,7 @@ ContentNormalizer::handleToken(QPDFTokenizer::Token const&amp; token) @@ -55,7 +57,7 @@ ContentNormalizer::handleToken(QPDFTokenizer::Token const&amp; token)
55 break; 57 break;
56 58
57 case QPDFTokenizer::tt_name: 59 case QPDFTokenizer::tt_name:
58 - write(QPDF_Name::normalizeName(token.getValue())); 60 + write(Name::normalize(token.getValue()));
59 break; 61 break;
60 62
61 default: 63 default:
libqpdf/QPDF.cc
@@ -17,14 +17,9 @@ @@ -17,14 +17,9 @@
17 #include <qpdf/Pipeline.hh> 17 #include <qpdf/Pipeline.hh>
18 #include <qpdf/QPDFExc.hh> 18 #include <qpdf/QPDFExc.hh>
19 #include <qpdf/QPDFLogger.hh> 19 #include <qpdf/QPDFLogger.hh>
  20 +#include <qpdf/QPDFObjectHandle_private.hh>
20 #include <qpdf/QPDFObject_private.hh> 21 #include <qpdf/QPDFObject_private.hh>
21 #include <qpdf/QPDFParser.hh> 22 #include <qpdf/QPDFParser.hh>
22 -#include <qpdf/QPDF_Array.hh>  
23 -#include <qpdf/QPDF_Dictionary.hh>  
24 -#include <qpdf/QPDF_Null.hh>  
25 -#include <qpdf/QPDF_Reserved.hh>  
26 -#include <qpdf/QPDF_Stream.hh>  
27 -#include <qpdf/QPDF_Unresolved.hh>  
28 #include <qpdf/QTC.hh> 23 #include <qpdf/QTC.hh>
29 #include <qpdf/QUtil.hh> 24 #include <qpdf/QUtil.hh>
30 25
@@ -298,7 +293,7 @@ void @@ -298,7 +293,7 @@ void
298 QPDF::registerStreamFilter( 293 QPDF::registerStreamFilter(
299 std::string const& filter_name, std::function<std::shared_ptr<QPDFStreamFilter>()> factory) 294 std::string const& filter_name, std::function<std::shared_ptr<QPDFStreamFilter>()> factory)
300 { 295 {
301 - QPDF_Stream::registerStreamFilter(filter_name, factory); 296 + qpdf::Stream::registerStreamFilter(filter_name, factory);
302 } 297 }
303 298
304 void 299 void
@@ -1565,7 +1560,7 @@ QPDF::readStream(QPDFObjectHandle&amp; object, QPDFObjGen og, qpdf_offset_t offset) @@ -1565,7 +1560,7 @@ QPDF::readStream(QPDFObjectHandle&amp; object, QPDFObjGen og, qpdf_offset_t offset)
1565 throw; 1560 throw;
1566 } 1561 }
1567 } 1562 }
1568 - object = {QPDF_Stream::create(this, og, object, stream_offset, length)}; 1563 + object = QPDFObjectHandle(qpdf::Stream(*this, og, object, stream_offset, length));
1569 } 1564 }
1570 1565
1571 void 1566 void
@@ -1872,11 +1867,11 @@ QPDF::readObjectAtOffset( @@ -1872,11 +1867,11 @@ QPDF::readObjectAtOffset(
1872 return oh; 1867 return oh;
1873 } 1868 }
1874 1869
1875 -QPDFObject* 1870 +std::shared_ptr<QPDFObject> const&
1876 QPDF::resolve(QPDFObjGen og) 1871 QPDF::resolve(QPDFObjGen og)
1877 { 1872 {
1878 if (!isUnresolved(og)) { 1873 if (!isUnresolved(og)) {
1879 - return m->obj_cache[og].object.get(); 1874 + return m->obj_cache[og].object;
1880 } 1875 }
1881 1876
1882 if (m->resolving.count(og)) { 1877 if (m->resolving.count(og)) {
@@ -1884,8 +1879,8 @@ QPDF::resolve(QPDFObjGen og) @@ -1884,8 +1879,8 @@ QPDF::resolve(QPDFObjGen og)
1884 // has to be resolved during object parsing, such as stream length. 1879 // has to be resolved during object parsing, such as stream length.
1885 QTC::TC("qpdf", "QPDF recursion loop in resolve"); 1880 QTC::TC("qpdf", "QPDF recursion loop in resolve");
1886 warn(damagedPDF("", "loop detected resolving object " + og.unparse(' '))); 1881 warn(damagedPDF("", "loop detected resolving object " + og.unparse(' ')));
1887 - updateCache(og, QPDF_Null::create(), -1, -1);  
1888 - return m->obj_cache[og].object.get(); 1882 + updateCache(og, QPDFObject::create<QPDF_Null>(), -1, -1);
  1883 + return m->obj_cache[og].object;
1889 } 1884 }
1890 ResolveRecorder rr(this, og); 1885 ResolveRecorder rr(this, og);
1891 1886
@@ -1921,12 +1916,12 @@ QPDF::resolve(QPDFObjGen og) @@ -1921,12 +1916,12 @@ QPDF::resolve(QPDFObjGen og)
1921 if (isUnresolved(og)) { 1916 if (isUnresolved(og)) {
1922 // PDF spec says unknown objects resolve to the null object. 1917 // PDF spec says unknown objects resolve to the null object.
1923 QTC::TC("qpdf", "QPDF resolve failure to null"); 1918 QTC::TC("qpdf", "QPDF resolve failure to null");
1924 - updateCache(og, QPDF_Null::create(), -1, -1); 1919 + updateCache(og, QPDFObject::create<QPDF_Null>(), -1, -1);
1925 } 1920 }
1926 1921
1927 - auto result(m->obj_cache[og].object); 1922 + auto& result(m->obj_cache[og].object);
1928 result->setDefaultDescription(this, og); 1923 result->setDefaultDescription(this, og);
1929 - return result.get(); 1924 + return result;
1930 } 1925 }
1931 1926
1932 void 1927 void
@@ -2034,12 +2029,13 @@ QPDF::updateCache( @@ -2034,12 +2029,13 @@ QPDF::updateCache(
2034 QPDFObjGen og, 2029 QPDFObjGen og,
2035 std::shared_ptr<QPDFObject> const& object, 2030 std::shared_ptr<QPDFObject> const& object,
2036 qpdf_offset_t end_before_space, 2031 qpdf_offset_t end_before_space,
2037 - qpdf_offset_t end_after_space) 2032 + qpdf_offset_t end_after_space,
  2033 + bool destroy)
2038 { 2034 {
2039 object->setObjGen(this, og); 2035 object->setObjGen(this, og);
2040 if (isCached(og)) { 2036 if (isCached(og)) {
2041 auto& cache = m->obj_cache[og]; 2037 auto& cache = m->obj_cache[og];
2042 - cache.object->assign(object); 2038 + object->move_to(cache.object, destroy);
2043 cache.end_before_space = end_before_space; 2039 cache.end_before_space = end_before_space;
2044 cache.end_after_space = end_after_space; 2040 cache.end_after_space = end_after_space;
2045 } else { 2041 } else {
@@ -2089,20 +2085,20 @@ QPDF::makeIndirectObject(QPDFObjectHandle oh) @@ -2089,20 +2085,20 @@ QPDF::makeIndirectObject(QPDFObjectHandle oh)
2089 QPDFObjectHandle 2085 QPDFObjectHandle
2090 QPDF::newReserved() 2086 QPDF::newReserved()
2091 { 2087 {
2092 - return makeIndirectFromQPDFObject(QPDF_Reserved::create()); 2088 + return makeIndirectFromQPDFObject(QPDFObject::create<QPDF_Reserved>());
2093 } 2089 }
2094 2090
2095 QPDFObjectHandle 2091 QPDFObjectHandle
2096 QPDF::newIndirectNull() 2092 QPDF::newIndirectNull()
2097 { 2093 {
2098 - return makeIndirectFromQPDFObject(QPDF_Null::create()); 2094 + return makeIndirectFromQPDFObject(QPDFObject::create<QPDF_Null>());
2099 } 2095 }
2100 2096
2101 QPDFObjectHandle 2097 QPDFObjectHandle
2102 QPDF::newStream() 2098 QPDF::newStream()
2103 { 2099 {
2104 - return makeIndirectFromQPDFObject(  
2105 - QPDF_Stream::create(this, nextObjGen(), QPDFObjectHandle::newDictionary(), 0, 0)); 2100 + return makeIndirectObject(
  2101 + qpdf::Stream(*this, nextObjGen(), QPDFObjectHandle::newDictionary(), 0, 0));
2106 } 2102 }
2107 2103
2108 QPDFObjectHandle 2104 QPDFObjectHandle
@@ -2130,12 +2126,13 @@ QPDF::getObjectForParser(int id, int gen, bool parse_pdf) @@ -2130,12 +2126,13 @@ QPDF::getObjectForParser(int id, int gen, bool parse_pdf)
2130 return iter->second.object; 2126 return iter->second.object;
2131 } 2127 }
2132 if (m->xref_table.count(og) || !m->parsed) { 2128 if (m->xref_table.count(og) || !m->parsed) {
2133 - return m->obj_cache.insert({og, QPDF_Unresolved::create(this, og)}).first->second.object; 2129 + return m->obj_cache.insert({og, QPDFObject::create<QPDF_Unresolved>(this, og)})
  2130 + .first->second.object;
2134 } 2131 }
2135 if (parse_pdf) { 2132 if (parse_pdf) {
2136 - return QPDF_Null::create(); 2133 + return QPDFObject::create<QPDF_Null>();
2137 } 2134 }
2138 - return m->obj_cache.insert({og, QPDF_Null::create(this, og)}).first->second.object; 2135 + return m->obj_cache.insert({og, QPDFObject::create<QPDF_Null>(this, og)}).first->second.object;
2139 } 2136 }
2140 2137
2141 std::shared_ptr<QPDFObject> 2138 std::shared_ptr<QPDFObject>
@@ -2145,8 +2142,9 @@ QPDF::getObjectForJSON(int id, int gen) @@ -2145,8 +2142,9 @@ QPDF::getObjectForJSON(int id, int gen)
2145 auto [it, inserted] = m->obj_cache.try_emplace(og); 2142 auto [it, inserted] = m->obj_cache.try_emplace(og);
2146 auto& obj = it->second.object; 2143 auto& obj = it->second.object;
2147 if (inserted) { 2144 if (inserted) {
2148 - obj = (m->parsed && !m->xref_table.count(og)) ? QPDF_Null::create(this, og)  
2149 - : QPDF_Unresolved::create(this, og); 2145 + obj = (m->parsed && !m->xref_table.count(og))
  2146 + ? QPDFObject::create<QPDF_Null>(this, og)
  2147 + : QPDFObject::create<QPDF_Unresolved>(this, og);
2150 } 2148 }
2151 return obj; 2149 return obj;
2152 } 2150 }
@@ -2157,9 +2155,10 @@ QPDF::getObject(QPDFObjGen og) @@ -2157,9 +2155,10 @@ QPDF::getObject(QPDFObjGen og)
2157 if (auto it = m->obj_cache.find(og); it != m->obj_cache.end()) { 2155 if (auto it = m->obj_cache.find(og); it != m->obj_cache.end()) {
2158 return {it->second.object}; 2156 return {it->second.object};
2159 } else if (m->parsed && !m->xref_table.count(og)) { 2157 } else if (m->parsed && !m->xref_table.count(og)) {
2160 - return QPDF_Null::create(); 2158 + return QPDFObject::create<QPDF_Null>();
2161 } else { 2159 } else {
2162 - auto result = m->obj_cache.try_emplace(og, QPDF_Unresolved::create(this, og), -1, -1); 2160 + auto result =
  2161 + m->obj_cache.try_emplace(og, QPDFObject::create<QPDF_Unresolved>(this, og), -1, -1);
2163 return {result.first->second.object}; 2162 return {result.first->second.object};
2164 } 2163 }
2165 } 2164 }
@@ -2195,7 +2194,7 @@ QPDF::replaceObject(QPDFObjGen og, QPDFObjectHandle oh) @@ -2195,7 +2194,7 @@ QPDF::replaceObject(QPDFObjGen og, QPDFObjectHandle oh)
2195 QTC::TC("qpdf", "QPDF replaceObject called with indirect object"); 2194 QTC::TC("qpdf", "QPDF replaceObject called with indirect object");
2196 throw std::logic_error("QPDF::replaceObject called with indirect object handle"); 2195 throw std::logic_error("QPDF::replaceObject called with indirect object handle");
2197 } 2196 }
2198 - updateCache(og, oh.getObj(), -1, -1); 2197 + updateCache(og, oh.getObj(), -1, -1, false);
2199 } 2198 }
2200 2199
2201 void 2200 void
@@ -2204,7 +2203,7 @@ QPDF::removeObject(QPDFObjGen og) @@ -2204,7 +2203,7 @@ QPDF::removeObject(QPDFObjGen og)
2204 m->xref_table.erase(og); 2203 m->xref_table.erase(og);
2205 if (auto cached = m->obj_cache.find(og); cached != m->obj_cache.end()) { 2204 if (auto cached = m->obj_cache.find(og); cached != m->obj_cache.end()) {
2206 // Take care of any object handles that may be floating around. 2205 // Take care of any object handles that may be floating around.
2207 - cached->second.object->assign(QPDF_Null::create()); 2206 + cached->second.object->assign_null();
2208 cached->second.object->setObjGen(nullptr, QPDFObjGen()); 2207 cached->second.object->setObjGen(nullptr, QPDFObjGen());
2209 m->obj_cache.erase(cached); 2208 m->obj_cache.erase(cached);
2210 } 2209 }
@@ -2347,14 +2346,15 @@ QPDF::reserveObjects(QPDFObjectHandle foreign, ObjCopier&amp; obj_copier, bool top) @@ -2347,14 +2346,15 @@ QPDF::reserveObjects(QPDFObjectHandle foreign, ObjCopier&amp; obj_copier, bool top)
2347 2346
2348 if (foreign_tc == ::ot_array) { 2347 if (foreign_tc == ::ot_array) {
2349 QTC::TC("qpdf", "QPDF reserve array"); 2348 QTC::TC("qpdf", "QPDF reserve array");
2350 - int n = foreign.getArrayNItems();  
2351 - for (int i = 0; i < n; ++i) {  
2352 - reserveObjects(foreign.getArrayItem(i), obj_copier, false); 2349 + for (auto const& item: foreign.as_array()) {
  2350 + reserveObjects(item, obj_copier, false);
2353 } 2351 }
2354 } else if (foreign_tc == ::ot_dictionary) { 2352 } else if (foreign_tc == ::ot_dictionary) {
2355 QTC::TC("qpdf", "QPDF reserve dictionary"); 2353 QTC::TC("qpdf", "QPDF reserve dictionary");
2356 - for (auto const& key: foreign.getKeys()) {  
2357 - reserveObjects(foreign.getKey(key), obj_copier, false); 2354 + for (auto const& item: foreign.as_dictionary()) {
  2355 + if (!item.second.null()) {
  2356 + reserveObjects(item.second, obj_copier, false);
  2357 + }
2358 } 2358 }
2359 } else if (foreign_tc == ::ot_stream) { 2359 } else if (foreign_tc == ::ot_stream) {
2360 QTC::TC("qpdf", "QPDF reserve stream"); 2360 QTC::TC("qpdf", "QPDF reserve stream");
@@ -2383,30 +2383,26 @@ QPDF::replaceForeignIndirectObjects(QPDFObjectHandle foreign, ObjCopier&amp; obj_cop @@ -2383,30 +2383,26 @@ QPDF::replaceForeignIndirectObjects(QPDFObjectHandle foreign, ObjCopier&amp; obj_cop
2383 } else if (foreign_tc == ::ot_array) { 2383 } else if (foreign_tc == ::ot_array) {
2384 QTC::TC("qpdf", "QPDF replace array"); 2384 QTC::TC("qpdf", "QPDF replace array");
2385 result = QPDFObjectHandle::newArray(); 2385 result = QPDFObjectHandle::newArray();
2386 - int n = foreign.getArrayNItems();  
2387 - for (int i = 0; i < n; ++i) {  
2388 - result.appendItem(  
2389 - // line-break  
2390 - replaceForeignIndirectObjects(foreign.getArrayItem(i), obj_copier, false)); 2386 + for (auto const& item: foreign.as_array()) {
  2387 + result.appendItem(replaceForeignIndirectObjects(item, obj_copier, false));
2391 } 2388 }
2392 } else if (foreign_tc == ::ot_dictionary) { 2389 } else if (foreign_tc == ::ot_dictionary) {
2393 QTC::TC("qpdf", "QPDF replace dictionary"); 2390 QTC::TC("qpdf", "QPDF replace dictionary");
2394 result = QPDFObjectHandle::newDictionary(); 2391 result = QPDFObjectHandle::newDictionary();
2395 - std::set<std::string> keys = foreign.getKeys();  
2396 - for (auto const& iter: keys) {  
2397 - result.replaceKey(  
2398 - iter, replaceForeignIndirectObjects(foreign.getKey(iter), obj_copier, false)); 2392 + for (auto const& [key, value]: foreign.as_dictionary()) {
  2393 + if (!value.null()) {
  2394 + result.replaceKey(key, replaceForeignIndirectObjects(value, obj_copier, false));
  2395 + }
2399 } 2396 }
2400 } else if (foreign_tc == ::ot_stream) { 2397 } else if (foreign_tc == ::ot_stream) {
2401 QTC::TC("qpdf", "QPDF replace stream"); 2398 QTC::TC("qpdf", "QPDF replace stream");
2402 result = obj_copier.object_map[foreign.getObjGen()]; 2399 result = obj_copier.object_map[foreign.getObjGen()];
2403 - result.assertStream();  
2404 QPDFObjectHandle dict = result.getDict(); 2400 QPDFObjectHandle dict = result.getDict();
2405 QPDFObjectHandle old_dict = foreign.getDict(); 2401 QPDFObjectHandle old_dict = foreign.getDict();
2406 - std::set<std::string> keys = old_dict.getKeys();  
2407 - for (auto const& iter: keys) {  
2408 - dict.replaceKey(  
2409 - iter, replaceForeignIndirectObjects(old_dict.getKey(iter), obj_copier, false)); 2402 + for (auto const& [key, value]: old_dict.as_dictionary()) {
  2403 + if (!value.null()) {
  2404 + dict.replaceKey(key, replaceForeignIndirectObjects(value, obj_copier, false));
  2405 + }
2410 } 2406 }
2411 copyStreamData(result, foreign); 2407 copyStreamData(result, foreign);
2412 } else { 2408 } else {
@@ -2442,13 +2438,11 @@ QPDF::copyStreamData(QPDFObjectHandle result, QPDFObjectHandle foreign) @@ -2442,13 +2438,11 @@ QPDF::copyStreamData(QPDFObjectHandle result, QPDFObjectHandle foreign)
2442 QPDF& foreign_stream_qpdf = 2438 QPDF& foreign_stream_qpdf =
2443 foreign.getQPDF("unable to retrieve owning qpdf from foreign stream"); 2439 foreign.getQPDF("unable to retrieve owning qpdf from foreign stream");
2444 2440
2445 - auto stream = foreign.getObjectPtr()->as<QPDF_Stream>();  
2446 - if (stream == nullptr) {  
2447 - throw std::logic_error(  
2448 - "unable to retrieve underlying"  
2449 - " stream object from foreign stream"); 2441 + auto stream = foreign.as_stream();
  2442 + if (!stream) {
  2443 + throw std::logic_error("unable to retrieve underlying stream object from foreign stream");
2450 } 2444 }
2451 - std::shared_ptr<Buffer> stream_buffer = stream->getStreamDataBuffer(); 2445 + std::shared_ptr<Buffer> stream_buffer = stream.getStreamDataBuffer();
2452 if ((foreign_stream_qpdf.m->immediate_copy_from) && (stream_buffer == nullptr)) { 2446 if ((foreign_stream_qpdf.m->immediate_copy_from) && (stream_buffer == nullptr)) {
2453 // Pull the stream data into a buffer before attempting the copy operation. Do it on the 2447 // Pull the stream data into a buffer before attempting the copy operation. Do it on the
2454 // source stream so that if the source stream is copied multiple times, we don't have to 2448 // source stream so that if the source stream is copied multiple times, we don't have to
@@ -2458,10 +2452,10 @@ QPDF::copyStreamData(QPDFObjectHandle result, QPDFObjectHandle foreign) @@ -2458,10 +2452,10 @@ QPDF::copyStreamData(QPDFObjectHandle result, QPDFObjectHandle foreign)
2458 foreign.getRawStreamData(), 2452 foreign.getRawStreamData(),
2459 old_dict.getKey("/Filter"), 2453 old_dict.getKey("/Filter"),
2460 old_dict.getKey("/DecodeParms")); 2454 old_dict.getKey("/DecodeParms"));
2461 - stream_buffer = stream->getStreamDataBuffer(); 2455 + stream_buffer = stream.getStreamDataBuffer();
2462 } 2456 }
2463 std::shared_ptr<QPDFObjectHandle::StreamDataProvider> stream_provider = 2457 std::shared_ptr<QPDFObjectHandle::StreamDataProvider> stream_provider =
2464 - stream->getStreamDataProvider(); 2458 + stream.getStreamDataProvider();
2465 if (stream_buffer.get()) { 2459 if (stream_buffer.get()) {
2466 QTC::TC("qpdf", "QPDF copy foreign stream with buffer"); 2460 QTC::TC("qpdf", "QPDF copy foreign stream with buffer");
2467 result.replaceStreamData( 2461 result.replaceStreamData(
@@ -2476,9 +2470,9 @@ QPDF::copyStreamData(QPDFObjectHandle result, QPDFObjectHandle foreign) @@ -2476,9 +2470,9 @@ QPDF::copyStreamData(QPDFObjectHandle result, QPDFObjectHandle foreign)
2476 auto foreign_stream_data = std::make_shared<ForeignStreamData>( 2470 auto foreign_stream_data = std::make_shared<ForeignStreamData>(
2477 foreign_stream_qpdf.m->encp, 2471 foreign_stream_qpdf.m->encp,
2478 foreign_stream_qpdf.m->file, 2472 foreign_stream_qpdf.m->file,
2479 - foreign.getObjGen(),  
2480 - stream->getParsedOffset(),  
2481 - stream->getLength(), 2473 + foreign,
  2474 + foreign.getParsedOffset(),
  2475 + stream.getLength(),
2482 dict); 2476 dict);
2483 m->copied_stream_data_provider->registerForeignStream(local_og, foreign_stream_data); 2477 m->copied_stream_data_provider->registerForeignStream(local_og, foreign_stream_data);
2484 result.replaceStreamData( 2478 result.replaceStreamData(
@@ -2692,30 +2686,32 @@ QPDF::getCompressibleObjGens() @@ -2692,30 +2686,32 @@ QPDF::getCompressibleObjGens()
2692 } 2686 }
2693 } 2687 }
2694 if (obj.isStream()) { 2688 if (obj.isStream()) {
2695 - QPDFObjectHandle dict = obj.getDict();  
2696 - std::set<std::string> keys = dict.getKeys();  
2697 - for (auto iter = keys.rbegin(); iter != keys.rend(); ++iter) {  
2698 - std::string const& key = *iter;  
2699 - QPDFObjectHandle value = dict.getKey(key);  
2700 - if (key == "/Length") {  
2701 - // omit stream lengths  
2702 - if (value.isIndirect()) {  
2703 - QTC::TC("qpdf", "QPDF exclude indirect length"); 2689 + auto dict = obj.getDict().as_dictionary();
  2690 + auto end = dict.crend();
  2691 + for (auto iter = dict.crbegin(); iter != end; ++iter) {
  2692 + std::string const& key = iter->first;
  2693 + QPDFObjectHandle const& value = iter->second;
  2694 + if (!value.null()) {
  2695 + if (key == "/Length") {
  2696 + // omit stream lengths
  2697 + if (value.isIndirect()) {
  2698 + QTC::TC("qpdf", "QPDF exclude indirect length");
  2699 + }
  2700 + } else {
  2701 + queue.emplace_back(value);
2704 } 2702 }
2705 - } else {  
2706 - queue.push_back(value);  
2707 } 2703 }
2708 } 2704 }
2709 } else if (obj.isDictionary()) { 2705 } else if (obj.isDictionary()) {
2710 - std::set<std::string> keys = obj.getKeys();  
2711 - for (auto iter = keys.rbegin(); iter != keys.rend(); ++iter) {  
2712 - queue.push_back(obj.getKey(*iter));  
2713 - }  
2714 - } else if (obj.isArray()) {  
2715 - int n = obj.getArrayNItems();  
2716 - for (int i = 1; i <= n; ++i) {  
2717 - queue.push_back(obj.getArrayItem(n - i)); 2706 + auto dict = obj.as_dictionary();
  2707 + auto end = dict.crend();
  2708 + for (auto iter = dict.crbegin(); iter != end; ++iter) {
  2709 + if (!iter->second.null()) {
  2710 + queue.emplace_back(iter->second);
  2711 + }
2718 } 2712 }
  2713 + } else if (auto items = obj.as_array()) {
  2714 + queue.insert(queue.end(), items.crbegin(), items.crend());
2719 } 2715 }
2720 } 2716 }
2721 2717
libqpdf/QPDFAcroFormDocumentHelper.cc
1 #include <qpdf/QPDFAcroFormDocumentHelper.hh> 1 #include <qpdf/QPDFAcroFormDocumentHelper.hh>
2 2
3 #include <qpdf/Pl_Buffer.hh> 3 #include <qpdf/Pl_Buffer.hh>
  4 +#include <qpdf/QPDFObjectHandle_private.hh>
4 #include <qpdf/QPDFPageDocumentHelper.hh> 5 #include <qpdf/QPDFPageDocumentHelper.hh>
5 #include <qpdf/QTC.hh> 6 #include <qpdf/QTC.hh>
6 #include <qpdf/QUtil.hh> 7 #include <qpdf/QUtil.hh>
7 #include <qpdf/ResourceFinder.hh> 8 #include <qpdf/ResourceFinder.hh>
8 9
  10 +using namespace qpdf;
  11 +
9 QPDFAcroFormDocumentHelper::Members::Members() : 12 QPDFAcroFormDocumentHelper::Members::Members() :
10 cache_valid(false) 13 cache_valid(false)
11 { 14 {
@@ -238,26 +241,25 @@ QPDFAcroFormDocumentHelper::analyze() @@ -238,26 +241,25 @@ QPDFAcroFormDocumentHelper::analyze()
238 return; 241 return;
239 } 242 }
240 m->cache_valid = true; 243 m->cache_valid = true;
241 - QPDFObjectHandle acroform = this->qpdf.getRoot().getKey("/AcroForm"); 244 + QPDFObjectHandle acroform = qpdf.getRoot().getKey("/AcroForm");
242 if (!(acroform.isDictionary() && acroform.hasKey("/Fields"))) { 245 if (!(acroform.isDictionary() && acroform.hasKey("/Fields"))) {
243 return; 246 return;
244 } 247 }
245 QPDFObjectHandle fields = acroform.getKey("/Fields"); 248 QPDFObjectHandle fields = acroform.getKey("/Fields");
246 - if (!fields.isArray()) { 249 + if (auto fa = fields.as_array(strict)) {
  250 + // Traverse /AcroForm to find annotations and map them bidirectionally to fields.
  251 +
  252 + QPDFObjGen::set visited;
  253 + QPDFObjectHandle null(QPDFObjectHandle::newNull());
  254 + for (auto const& field: fa) {
  255 + traverseField(field, null, 0, visited);
  256 + }
  257 + } else {
247 QTC::TC("qpdf", "QPDFAcroFormDocumentHelper fields not array"); 258 QTC::TC("qpdf", "QPDFAcroFormDocumentHelper fields not array");
248 acroform.warnIfPossible("/Fields key of /AcroForm dictionary is not an array; ignoring"); 259 acroform.warnIfPossible("/Fields key of /AcroForm dictionary is not an array; ignoring");
249 fields = QPDFObjectHandle::newArray(); 260 fields = QPDFObjectHandle::newArray();
250 } 261 }
251 262
252 - // Traverse /AcroForm to find annotations and map them bidirectionally to fields.  
253 -  
254 - QPDFObjGen::set visited;  
255 - int nfields = fields.getArrayNItems();  
256 - QPDFObjectHandle null(QPDFObjectHandle::newNull());  
257 - for (int i = 0; i < nfields; ++i) {  
258 - traverseField(fields.getArrayItem(i), null, 0, visited);  
259 - }  
260 -  
261 // All Widget annotations should have been encountered by traversing /AcroForm, but in case any 263 // All Widget annotations should have been encountered by traversing /AcroForm, but in case any
262 // weren't, find them by walking through pages, and treat any widget annotation that is not 264 // weren't, find them by walking through pages, and treat any widget annotation that is not
263 // associated with a field as its own field. This just ensures that requesting the field for any 265 // associated with a field as its own field. This just ensures that requesting the field for any
@@ -324,12 +326,10 @@ QPDFAcroFormDocumentHelper::traverseField( @@ -324,12 +326,10 @@ QPDFAcroFormDocumentHelper::traverseField(
324 326
325 bool is_annotation = false; 327 bool is_annotation = false;
326 bool is_field = (0 == depth); 328 bool is_field = (0 == depth);
327 - QPDFObjectHandle kids = field.getKey("/Kids");  
328 - if (kids.isArray()) { 329 + if (auto a = field.getKey("/Kids").as_array(strict)) {
329 is_field = true; 330 is_field = true;
330 - int nkids = kids.getArrayNItems();  
331 - for (int k = 0; k < nkids; ++k) {  
332 - traverseField(kids.getArrayItem(k), field, 1 + depth, visited); 331 + for (auto const& item: a) {
  332 + traverseField(item, field, 1 + depth, visited);
333 } 333 }
334 } else { 334 } else {
335 if (field.hasKey("/Parent")) { 335 if (field.hasKey("/Parent")) {
@@ -975,17 +975,14 @@ QPDFAcroFormDocumentHelper::transformAnnotations( @@ -975,17 +975,14 @@ QPDFAcroFormDocumentHelper::transformAnnotations(
975 auto replace_stream = [](auto& dict, auto& key, auto& old) { 975 auto replace_stream = [](auto& dict, auto& key, auto& old) {
976 return dict.replaceKeyAndGetNew(key, old.copyStream()); 976 return dict.replaceKeyAndGetNew(key, old.copyStream());
977 }; 977 };
978 - if (apdict.isDictionary()) {  
979 - for (auto& ap: apdict.ditems()) {  
980 - if (ap.second.isStream()) {  
981 - streams.push_back(replace_stream(apdict, ap.first, ap.second));  
982 - } else if (ap.second.isDictionary()) {  
983 - for (auto& ap2: ap.second.ditems()) {  
984 - if (ap2.second.isStream()) {  
985 - streams.push_back(  
986 - // line-break  
987 - replace_stream(ap.second, ap2.first, ap2.second));  
988 - } 978 +
  979 + for (auto& [key1, value1]: apdict.as_dictionary()) {
  980 + if (value1.isStream()) {
  981 + streams.emplace_back(replace_stream(apdict, key1, value1));
  982 + } else {
  983 + for (auto& [key2, value2]: value1.as_dictionary()) {
  984 + if (value2.isStream()) {
  985 + streams.emplace_back(replace_stream(value1, key2, value2));
989 } 986 }
990 } 987 }
991 } 988 }
libqpdf/QPDFAnnotationObjectHelper.cc
@@ -13,27 +13,27 @@ QPDFAnnotationObjectHelper::QPDFAnnotationObjectHelper(QPDFObjectHandle oh) : @@ -13,27 +13,27 @@ QPDFAnnotationObjectHelper::QPDFAnnotationObjectHelper(QPDFObjectHandle oh) :
13 std::string 13 std::string
14 QPDFAnnotationObjectHelper::getSubtype() 14 QPDFAnnotationObjectHelper::getSubtype()
15 { 15 {
16 - return this->oh.getKey("/Subtype").getName(); 16 + return oh().getKey("/Subtype").getName();
17 } 17 }
18 18
19 QPDFObjectHandle::Rectangle 19 QPDFObjectHandle::Rectangle
20 QPDFAnnotationObjectHelper::getRect() 20 QPDFAnnotationObjectHelper::getRect()
21 { 21 {
22 - return this->oh.getKey("/Rect").getArrayAsRectangle(); 22 + return oh().getKey("/Rect").getArrayAsRectangle();
23 } 23 }
24 24
25 QPDFObjectHandle 25 QPDFObjectHandle
26 QPDFAnnotationObjectHelper::getAppearanceDictionary() 26 QPDFAnnotationObjectHelper::getAppearanceDictionary()
27 { 27 {
28 - return this->oh.getKey("/AP"); 28 + return oh().getKey("/AP");
29 } 29 }
30 30
31 std::string 31 std::string
32 QPDFAnnotationObjectHelper::getAppearanceState() 32 QPDFAnnotationObjectHelper::getAppearanceState()
33 { 33 {
34 - if (this->oh.getKey("/AS").isName()) { 34 + if (oh().getKey("/AS").isName()) {
35 QTC::TC("qpdf", "QPDFAnnotationObjectHelper AS present"); 35 QTC::TC("qpdf", "QPDFAnnotationObjectHelper AS present");
36 - return this->oh.getKey("/AS").getName(); 36 + return oh().getKey("/AS").getName();
37 } 37 }
38 QTC::TC("qpdf", "QPDFAnnotationObjectHelper AS absent"); 38 QTC::TC("qpdf", "QPDFAnnotationObjectHelper AS absent");
39 return ""; 39 return "";
@@ -42,7 +42,7 @@ QPDFAnnotationObjectHelper::getAppearanceState() @@ -42,7 +42,7 @@ QPDFAnnotationObjectHelper::getAppearanceState()
42 int 42 int
43 QPDFAnnotationObjectHelper::getFlags() 43 QPDFAnnotationObjectHelper::getFlags()
44 { 44 {
45 - QPDFObjectHandle flags_obj = this->oh.getKey("/F"); 45 + QPDFObjectHandle flags_obj = oh().getKey("/F");
46 return flags_obj.isInteger() ? flags_obj.getIntValueAsInt() : 0; 46 return flags_obj.isInteger() ? flags_obj.getIntValueAsInt() : 0;
47 } 47 }
48 48
@@ -143,7 +143,7 @@ QPDFAnnotationObjectHelper::getPageContentForAppearance( @@ -143,7 +143,7 @@ QPDFAnnotationObjectHelper::getPageContentForAppearance(
143 143
144 // 3. Apply the rotation to A as computed above to get the final appearance matrix. 144 // 3. Apply the rotation to A as computed above to get the final appearance matrix.
145 145
146 - QPDFObjectHandle rect_obj = this->oh.getKey("/Rect"); 146 + QPDFObjectHandle rect_obj = oh().getKey("/Rect");
147 QPDFObjectHandle as = getAppearanceStream("/N").getDict(); 147 QPDFObjectHandle as = getAppearanceStream("/N").getDict();
148 QPDFObjectHandle bbox_obj = as.getKey("/BBox"); 148 QPDFObjectHandle bbox_obj = as.getKey("/BBox");
149 QPDFObjectHandle matrix_obj = as.getKey("/Matrix"); 149 QPDFObjectHandle matrix_obj = as.getKey("/Matrix");
libqpdf/QPDFEFStreamObjectHelper.cc
@@ -16,7 +16,7 @@ QPDFEFStreamObjectHelper::QPDFEFStreamObjectHelper(QPDFObjectHandle oh) : @@ -16,7 +16,7 @@ QPDFEFStreamObjectHelper::QPDFEFStreamObjectHelper(QPDFObjectHandle oh) :
16 QPDFObjectHandle 16 QPDFObjectHandle
17 QPDFEFStreamObjectHelper::getParam(std::string const& pkey) 17 QPDFEFStreamObjectHelper::getParam(std::string const& pkey)
18 { 18 {
19 - auto params = this->oh.getDict().getKey("/Params"); 19 + auto params = oh().getDict().getKey("/Params");
20 if (params.isDictionary()) { 20 if (params.isDictionary()) {
21 return params.getKey(pkey); 21 return params.getKey(pkey);
22 } 22 }
@@ -26,10 +26,9 @@ QPDFEFStreamObjectHelper::getParam(std::string const&amp; pkey) @@ -26,10 +26,9 @@ QPDFEFStreamObjectHelper::getParam(std::string const&amp; pkey)
26 void 26 void
27 QPDFEFStreamObjectHelper::setParam(std::string const& pkey, QPDFObjectHandle const& pval) 27 QPDFEFStreamObjectHelper::setParam(std::string const& pkey, QPDFObjectHandle const& pval)
28 { 28 {
29 - auto params = this->oh.getDict().getKey("/Params"); 29 + auto params = oh().getDict().getKey("/Params");
30 if (!params.isDictionary()) { 30 if (!params.isDictionary()) {
31 - params =  
32 - this->oh.getDict().replaceKeyAndGetNew("/Params", QPDFObjectHandle::newDictionary()); 31 + params = oh().getDict().replaceKeyAndGetNew("/Params", QPDFObjectHandle::newDictionary());
33 } 32 }
34 params.replaceKey(pkey, pval); 33 params.replaceKey(pkey, pval);
35 } 34 }
@@ -67,7 +66,7 @@ QPDFEFStreamObjectHelper::getSize() @@ -67,7 +66,7 @@ QPDFEFStreamObjectHelper::getSize()
67 std::string 66 std::string
68 QPDFEFStreamObjectHelper::getSubtype() 67 QPDFEFStreamObjectHelper::getSubtype()
69 { 68 {
70 - auto val = this->oh.getDict().getKey("/Subtype"); 69 + auto val = oh().getDict().getKey("/Subtype");
71 if (val.isName()) { 70 if (val.isName()) {
72 auto n = val.getName(); 71 auto n = val.getName();
73 if (n.length() > 1) { 72 if (n.length() > 1) {
@@ -124,7 +123,7 @@ QPDFEFStreamObjectHelper::setModDate(std::string const&amp; date) @@ -124,7 +123,7 @@ QPDFEFStreamObjectHelper::setModDate(std::string const&amp; date)
124 QPDFEFStreamObjectHelper& 123 QPDFEFStreamObjectHelper&
125 QPDFEFStreamObjectHelper::setSubtype(std::string const& subtype) 124 QPDFEFStreamObjectHelper::setSubtype(std::string const& subtype)
126 { 125 {
127 - this->oh.getDict().replaceKey("/Subtype", QPDFObjectHandle::newName("/" + subtype)); 126 + oh().getDict().replaceKey("/Subtype", QPDFObjectHandle::newName("/" + subtype));
128 return *this; 127 return *this;
129 } 128 }
130 129
libqpdf/QPDFFileSpecObjectHelper.cc
@@ -25,7 +25,7 @@ std::string @@ -25,7 +25,7 @@ std::string
25 QPDFFileSpecObjectHelper::getDescription() 25 QPDFFileSpecObjectHelper::getDescription()
26 { 26 {
27 std::string result; 27 std::string result;
28 - auto desc = this->oh.getKey("/Desc"); 28 + auto desc = oh().getKey("/Desc");
29 if (desc.isString()) { 29 if (desc.isString()) {
30 result = desc.getUTF8Value(); 30 result = desc.getUTF8Value();
31 } 31 }
@@ -36,7 +36,7 @@ std::string @@ -36,7 +36,7 @@ std::string
36 QPDFFileSpecObjectHelper::getFilename() 36 QPDFFileSpecObjectHelper::getFilename()
37 { 37 {
38 for (auto const& i: name_keys) { 38 for (auto const& i: name_keys) {
39 - auto k = this->oh.getKey(i); 39 + auto k = oh().getKey(i);
40 if (k.isString()) { 40 if (k.isString()) {
41 return k.getUTF8Value(); 41 return k.getUTF8Value();
42 } 42 }
@@ -49,7 +49,7 @@ QPDFFileSpecObjectHelper::getFilenames() @@ -49,7 +49,7 @@ QPDFFileSpecObjectHelper::getFilenames()
49 { 49 {
50 std::map<std::string, std::string> result; 50 std::map<std::string, std::string> result;
51 for (auto const& i: name_keys) { 51 for (auto const& i: name_keys) {
52 - auto k = this->oh.getKey(i); 52 + auto k = oh().getKey(i);
53 if (k.isString()) { 53 if (k.isString()) {
54 result[i] = k.getUTF8Value(); 54 result[i] = k.getUTF8Value();
55 } 55 }
@@ -60,7 +60,7 @@ QPDFFileSpecObjectHelper::getFilenames() @@ -60,7 +60,7 @@ QPDFFileSpecObjectHelper::getFilenames()
60 QPDFObjectHandle 60 QPDFObjectHandle
61 QPDFFileSpecObjectHelper::getEmbeddedFileStream(std::string const& key) 61 QPDFFileSpecObjectHelper::getEmbeddedFileStream(std::string const& key)
62 { 62 {
63 - auto ef = this->oh.getKey("/EF"); 63 + auto ef = oh().getKey("/EF");
64 if (!ef.isDictionary()) { 64 if (!ef.isDictionary()) {
65 return QPDFObjectHandle::newNull(); 65 return QPDFObjectHandle::newNull();
66 } 66 }
@@ -79,7 +79,7 @@ QPDFFileSpecObjectHelper::getEmbeddedFileStream(std::string const&amp; key) @@ -79,7 +79,7 @@ QPDFFileSpecObjectHelper::getEmbeddedFileStream(std::string const&amp; key)
79 QPDFObjectHandle 79 QPDFObjectHandle
80 QPDFFileSpecObjectHelper::getEmbeddedFileStreams() 80 QPDFFileSpecObjectHelper::getEmbeddedFileStreams()
81 { 81 {
82 - return this->oh.getKey("/EF"); 82 + return oh().getKey("/EF");
83 } 83 }
84 84
85 QPDFFileSpecObjectHelper 85 QPDFFileSpecObjectHelper
@@ -110,7 +110,7 @@ QPDFFileSpecObjectHelper::createFileSpec( @@ -110,7 +110,7 @@ QPDFFileSpecObjectHelper::createFileSpec(
110 QPDFFileSpecObjectHelper& 110 QPDFFileSpecObjectHelper&
111 QPDFFileSpecObjectHelper::setDescription(std::string const& desc) 111 QPDFFileSpecObjectHelper::setDescription(std::string const& desc)
112 { 112 {
113 - this->oh.replaceKey("/Desc", QPDFObjectHandle::newUnicodeString(desc)); 113 + oh().replaceKey("/Desc", QPDFObjectHandle::newUnicodeString(desc));
114 return *this; 114 return *this;
115 } 115 }
116 116
@@ -119,13 +119,13 @@ QPDFFileSpecObjectHelper::setFilename( @@ -119,13 +119,13 @@ QPDFFileSpecObjectHelper::setFilename(
119 std::string const& unicode_name, std::string const& compat_name) 119 std::string const& unicode_name, std::string const& compat_name)
120 { 120 {
121 auto uf = QPDFObjectHandle::newUnicodeString(unicode_name); 121 auto uf = QPDFObjectHandle::newUnicodeString(unicode_name);
122 - this->oh.replaceKey("/UF", uf); 122 + oh().replaceKey("/UF", uf);
123 if (compat_name.empty()) { 123 if (compat_name.empty()) {
124 QTC::TC("qpdf", "QPDFFileSpecObjectHelper empty compat_name"); 124 QTC::TC("qpdf", "QPDFFileSpecObjectHelper empty compat_name");
125 - this->oh.replaceKey("/F", uf); 125 + oh().replaceKey("/F", uf);
126 } else { 126 } else {
127 QTC::TC("qpdf", "QPDFFileSpecObjectHelper non-empty compat_name"); 127 QTC::TC("qpdf", "QPDFFileSpecObjectHelper non-empty compat_name");
128 - this->oh.replaceKey("/F", QPDFObjectHandle::newString(compat_name)); 128 + oh().replaceKey("/F", QPDFObjectHandle::newString(compat_name));
129 } 129 }
130 return *this; 130 return *this;
131 } 131 }
libqpdf/QPDFFormFieldObjectHelper.cc
@@ -4,6 +4,7 @@ @@ -4,6 +4,7 @@
4 #include <qpdf/QIntC.hh> 4 #include <qpdf/QIntC.hh>
5 #include <qpdf/QPDFAcroFormDocumentHelper.hh> 5 #include <qpdf/QPDFAcroFormDocumentHelper.hh>
6 #include <qpdf/QPDFAnnotationObjectHelper.hh> 6 #include <qpdf/QPDFAnnotationObjectHelper.hh>
  7 +#include <qpdf/QPDFObjectHandle_private.hh>
7 #include <qpdf/QTC.hh> 8 #include <qpdf/QTC.hh>
8 #include <qpdf/QUtil.hh> 9 #include <qpdf/QUtil.hh>
9 #include <cstdlib> 10 #include <cstdlib>
@@ -23,19 +24,19 @@ QPDFFormFieldObjectHelper::QPDFFormFieldObjectHelper() : @@ -23,19 +24,19 @@ QPDFFormFieldObjectHelper::QPDFFormFieldObjectHelper() :
23 bool 24 bool
24 QPDFFormFieldObjectHelper::isNull() 25 QPDFFormFieldObjectHelper::isNull()
25 { 26 {
26 - return this->oh.isNull(); 27 + return oh().isNull();
27 } 28 }
28 29
29 QPDFFormFieldObjectHelper 30 QPDFFormFieldObjectHelper
30 QPDFFormFieldObjectHelper::getParent() 31 QPDFFormFieldObjectHelper::getParent()
31 { 32 {
32 - return this->oh.getKey("/Parent"); // may be null 33 + return oh().getKey("/Parent"); // may be null
33 } 34 }
34 35
35 QPDFFormFieldObjectHelper 36 QPDFFormFieldObjectHelper
36 QPDFFormFieldObjectHelper::getTopLevelField(bool* is_different) 37 QPDFFormFieldObjectHelper::getTopLevelField(bool* is_different)
37 { 38 {
38 - auto top_field = this->oh; 39 + auto top_field = oh();
39 QPDFObjGen::set seen; 40 QPDFObjGen::set seen;
40 while (seen.add(top_field) && !top_field.getKeyIfDict("/Parent").isNull()) { 41 while (seen.add(top_field) && !top_field.getKeyIfDict("/Parent").isNull()) {
41 top_field = top_field.getKey("/Parent"); 42 top_field = top_field.getKey("/Parent");
@@ -51,7 +52,7 @@ QPDFFormFieldObjectHelper::getFieldFromAcroForm(std::string const&amp; name) @@ -51,7 +52,7 @@ QPDFFormFieldObjectHelper::getFieldFromAcroForm(std::string const&amp; name)
51 { 52 {
52 QPDFObjectHandle result = QPDFObjectHandle::newNull(); 53 QPDFObjectHandle result = QPDFObjectHandle::newNull();
53 // Fields are supposed to be indirect, so this should work. 54 // Fields are supposed to be indirect, so this should work.
54 - QPDF* q = this->oh.getOwningQPDF(); 55 + QPDF* q = oh().getOwningQPDF();
55 if (!q) { 56 if (!q) {
56 return result; 57 return result;
57 } 58 }
@@ -65,7 +66,7 @@ QPDFFormFieldObjectHelper::getFieldFromAcroForm(std::string const&amp; name) @@ -65,7 +66,7 @@ QPDFFormFieldObjectHelper::getFieldFromAcroForm(std::string const&amp; name)
65 QPDFObjectHandle 66 QPDFObjectHandle
66 QPDFFormFieldObjectHelper::getInheritableFieldValue(std::string const& name) 67 QPDFFormFieldObjectHelper::getInheritableFieldValue(std::string const& name)
67 { 68 {
68 - QPDFObjectHandle node = this->oh; 69 + QPDFObjectHandle node = oh();
69 if (!node.isDictionary()) { 70 if (!node.isDictionary()) {
70 return QPDFObjectHandle::newNull(); 71 return QPDFObjectHandle::newNull();
71 } 72 }
@@ -116,7 +117,7 @@ std::string @@ -116,7 +117,7 @@ std::string
116 QPDFFormFieldObjectHelper::getFullyQualifiedName() 117 QPDFFormFieldObjectHelper::getFullyQualifiedName()
117 { 118 {
118 std::string result; 119 std::string result;
119 - QPDFObjectHandle node = this->oh; 120 + QPDFObjectHandle node = oh();
120 QPDFObjGen::set seen; 121 QPDFObjGen::set seen;
121 while (!node.isNull() && seen.add(node)) { 122 while (!node.isNull() && seen.add(node)) {
122 if (node.getKey("/T").isString()) { 123 if (node.getKey("/T").isString()) {
@@ -135,8 +136,8 @@ std::string @@ -135,8 +136,8 @@ std::string
135 QPDFFormFieldObjectHelper::getPartialName() 136 QPDFFormFieldObjectHelper::getPartialName()
136 { 137 {
137 std::string result; 138 std::string result;
138 - if (this->oh.getKey("/T").isString()) {  
139 - result = this->oh.getKey("/T").getUTF8Value(); 139 + if (oh().getKey("/T").isString()) {
  140 + result = oh().getKey("/T").getUTF8Value();
140 } 141 }
141 return result; 142 return result;
142 } 143 }
@@ -144,9 +145,9 @@ QPDFFormFieldObjectHelper::getPartialName() @@ -144,9 +145,9 @@ QPDFFormFieldObjectHelper::getPartialName()
144 std::string 145 std::string
145 QPDFFormFieldObjectHelper::getAlternativeName() 146 QPDFFormFieldObjectHelper::getAlternativeName()
146 { 147 {
147 - if (this->oh.getKey("/TU").isString()) { 148 + if (oh().getKey("/TU").isString()) {
148 QTC::TC("qpdf", "QPDFFormFieldObjectHelper TU present"); 149 QTC::TC("qpdf", "QPDFFormFieldObjectHelper TU present");
149 - return this->oh.getKey("/TU").getUTF8Value(); 150 + return oh().getKey("/TU").getUTF8Value();
150 } 151 }
151 QTC::TC("qpdf", "QPDFFormFieldObjectHelper TU absent"); 152 QTC::TC("qpdf", "QPDFFormFieldObjectHelper TU absent");
152 return getFullyQualifiedName(); 153 return getFullyQualifiedName();
@@ -155,9 +156,9 @@ QPDFFormFieldObjectHelper::getAlternativeName() @@ -155,9 +156,9 @@ QPDFFormFieldObjectHelper::getAlternativeName()
155 std::string 156 std::string
156 QPDFFormFieldObjectHelper::getMappingName() 157 QPDFFormFieldObjectHelper::getMappingName()
157 { 158 {
158 - if (this->oh.getKey("/TM").isString()) { 159 + if (oh().getKey("/TM").isString()) {
159 QTC::TC("qpdf", "QPDFFormFieldObjectHelper TM present"); 160 QTC::TC("qpdf", "QPDFFormFieldObjectHelper TM present");
160 - return this->oh.getKey("/TM").getUTF8Value(); 161 + return oh().getKey("/TM").getUTF8Value();
161 } 162 }
162 QTC::TC("qpdf", "QPDFFormFieldObjectHelper TM absent"); 163 QTC::TC("qpdf", "QPDFFormFieldObjectHelper TM absent");
163 return getAlternativeName(); 164 return getAlternativeName();
@@ -271,14 +272,9 @@ QPDFFormFieldObjectHelper::getChoices() @@ -271,14 +272,9 @@ QPDFFormFieldObjectHelper::getChoices()
271 if (!isChoice()) { 272 if (!isChoice()) {
272 return result; 273 return result;
273 } 274 }
274 - QPDFObjectHandle opt = getInheritableFieldValue("/Opt");  
275 - if (opt.isArray()) {  
276 - int n = opt.getArrayNItems();  
277 - for (int i = 0; i < n; ++i) {  
278 - QPDFObjectHandle item = opt.getArrayItem(i);  
279 - if (item.isString()) {  
280 - result.push_back(item.getUTF8Value());  
281 - } 275 + for (auto const& item: getInheritableFieldValue("/Opt").as_array()) {
  276 + if (item.isString()) {
  277 + result.emplace_back(item.getUTF8Value());
282 } 278 }
283 } 279 }
284 return result; 280 return result;
@@ -287,13 +283,13 @@ QPDFFormFieldObjectHelper::getChoices() @@ -287,13 +283,13 @@ QPDFFormFieldObjectHelper::getChoices()
287 void 283 void
288 QPDFFormFieldObjectHelper::setFieldAttribute(std::string const& key, QPDFObjectHandle value) 284 QPDFFormFieldObjectHelper::setFieldAttribute(std::string const& key, QPDFObjectHandle value)
289 { 285 {
290 - this->oh.replaceKey(key, value); 286 + oh().replaceKey(key, value);
291 } 287 }
292 288
293 void 289 void
294 QPDFFormFieldObjectHelper::setFieldAttribute(std::string const& key, std::string const& utf8_value) 290 QPDFFormFieldObjectHelper::setFieldAttribute(std::string const& key, std::string const& utf8_value)
295 { 291 {
296 - this->oh.replaceKey(key, QPDFObjectHandle::newUnicodeString(utf8_value)); 292 + oh().replaceKey(key, QPDFObjectHandle::newUnicodeString(utf8_value));
297 } 293 }
298 294
299 void 295 void
@@ -310,18 +306,18 @@ QPDFFormFieldObjectHelper::setV(QPDFObjectHandle value, bool need_appearances) @@ -310,18 +306,18 @@ QPDFFormFieldObjectHelper::setV(QPDFObjectHandle value, bool need_appearances)
310 setCheckBoxValue((name != "/Off")); 306 setCheckBoxValue((name != "/Off"));
311 } 307 }
312 if (!okay) { 308 if (!okay) {
313 - this->oh.warnIfPossible( 309 + oh().warnIfPossible(
314 "ignoring attempt to set a checkbox field to a value whose type is not name"); 310 "ignoring attempt to set a checkbox field to a value whose type is not name");
315 } 311 }
316 } else if (isRadioButton()) { 312 } else if (isRadioButton()) {
317 if (value.isName()) { 313 if (value.isName()) {
318 setRadioButtonValue(value); 314 setRadioButtonValue(value);
319 } else { 315 } else {
320 - this->oh.warnIfPossible( 316 + oh().warnIfPossible(
321 "ignoring attempt to set a radio button field to an object that is not a name"); 317 "ignoring attempt to set a radio button field to an object that is not a name");
322 } 318 }
323 } else if (isPushbutton()) { 319 } else if (isPushbutton()) {
324 - this->oh.warnIfPossible("ignoring attempt set the value of a pushbutton field"); 320 + oh().warnIfPossible("ignoring attempt set the value of a pushbutton field");
325 } 321 }
326 return; 322 return;
327 } 323 }
@@ -331,7 +327,7 @@ QPDFFormFieldObjectHelper::setV(QPDFObjectHandle value, bool need_appearances) @@ -331,7 +327,7 @@ QPDFFormFieldObjectHelper::setV(QPDFObjectHandle value, bool need_appearances)
331 setFieldAttribute("/V", value); 327 setFieldAttribute("/V", value);
332 } 328 }
333 if (need_appearances) { 329 if (need_appearances) {
334 - QPDF& qpdf = this->oh.getQPDF( 330 + QPDF& qpdf = oh().getQPDF(
335 "QPDFFormFieldObjectHelper::setV called with need_appearances = " 331 "QPDFFormFieldObjectHelper::setV called with need_appearances = "
336 "true on an object that is not associated with an owning QPDF"); 332 "true on an object that is not associated with an owning QPDF");
337 QPDFAcroFormDocumentHelper(qpdf).setNeedAppearances(true); 333 QPDFAcroFormDocumentHelper(qpdf).setNeedAppearances(true);
@@ -355,7 +351,7 @@ QPDFFormFieldObjectHelper::setRadioButtonValue(QPDFObjectHandle name) @@ -355,7 +351,7 @@ QPDFFormFieldObjectHelper::setRadioButtonValue(QPDFObjectHandle name)
355 // its /AP (i.e. its normal appearance stream dictionary), set /AS to name; otherwise, if /Off 351 // its /AP (i.e. its normal appearance stream dictionary), set /AS to name; otherwise, if /Off
356 // is a member, set /AS to /Off. 352 // is a member, set /AS to /Off.
357 // Note that we never turn on /NeedAppearances when setting a radio button field. 353 // Note that we never turn on /NeedAppearances when setting a radio button field.
358 - QPDFObjectHandle parent = this->oh.getKey("/Parent"); 354 + QPDFObjectHandle parent = oh().getKey("/Parent");
359 if (parent.isDictionary() && parent.getKey("/Parent").isNull()) { 355 if (parent.isDictionary() && parent.getKey("/Parent").isNull()) {
360 QPDFFormFieldObjectHelper ph(parent); 356 QPDFFormFieldObjectHelper ph(parent);
361 if (ph.isRadioButton()) { 357 if (ph.isRadioButton()) {
@@ -366,32 +362,23 @@ QPDFFormFieldObjectHelper::setRadioButtonValue(QPDFObjectHandle name) @@ -366,32 +362,23 @@ QPDFFormFieldObjectHelper::setRadioButtonValue(QPDFObjectHandle name)
366 } 362 }
367 } 363 }
368 364
369 - QPDFObjectHandle kids = this->oh.getKey("/Kids"); 365 + QPDFObjectHandle kids = oh().getKey("/Kids");
370 if (!(isRadioButton() && parent.isNull() && kids.isArray())) { 366 if (!(isRadioButton() && parent.isNull() && kids.isArray())) {
371 - this->oh.warnIfPossible(  
372 - "don't know how to set the value"  
373 - " of this field as a radio button"); 367 + oh().warnIfPossible("don't know how to set the value of this field as a radio button");
374 return; 368 return;
375 } 369 }
376 setFieldAttribute("/V", name); 370 setFieldAttribute("/V", name);
377 - int nkids = kids.getArrayNItems();  
378 - for (int i = 0; i < nkids; ++i) {  
379 - QPDFObjectHandle kid = kids.getArrayItem(i); 371 + for (auto const& kid: kids.as_array()) {
380 QPDFObjectHandle AP = kid.getKey("/AP"); 372 QPDFObjectHandle AP = kid.getKey("/AP");
381 QPDFObjectHandle annot; 373 QPDFObjectHandle annot;
382 - if (AP.isNull()) { 374 + if (AP.null()) {
383 // The widget may be below. If there is more than one, just find the first one. 375 // The widget may be below. If there is more than one, just find the first one.
384 - QPDFObjectHandle grandkids = kid.getKey("/Kids");  
385 - if (grandkids.isArray()) {  
386 - int ngrandkids = grandkids.getArrayNItems();  
387 - for (int j = 0; j < ngrandkids; ++j) {  
388 - QPDFObjectHandle grandkid = grandkids.getArrayItem(j);  
389 - AP = grandkid.getKey("/AP");  
390 - if (!AP.isNull()) {  
391 - QTC::TC("qpdf", "QPDFFormFieldObjectHelper radio button grandkid");  
392 - annot = grandkid;  
393 - break;  
394 - } 376 + for (auto const& grandkid: kid.getKey("/Kids").as_array()) {
  377 + AP = grandkid.getKey("/AP");
  378 + if (!AP.null()) {
  379 + QTC::TC("qpdf", "QPDFFormFieldObjectHelper radio button grandkid");
  380 + annot = grandkid;
  381 + break;
395 } 382 }
396 } 383 }
397 } else { 384 } else {
@@ -399,7 +386,7 @@ QPDFFormFieldObjectHelper::setRadioButtonValue(QPDFObjectHandle name) @@ -399,7 +386,7 @@ QPDFFormFieldObjectHelper::setRadioButtonValue(QPDFObjectHandle name)
399 } 386 }
400 if (!annot) { 387 if (!annot) {
401 QTC::TC("qpdf", "QPDFObjectHandle broken radio button"); 388 QTC::TC("qpdf", "QPDFObjectHandle broken radio button");
402 - this->oh.warnIfPossible("unable to set the value of this radio button"); 389 + oh().warnIfPossible("unable to set the value of this radio button");
403 continue; 390 continue;
404 } 391 }
405 if (AP.isDictionary() && AP.getKey("/N").isDictionary() && 392 if (AP.isDictionary() && AP.getKey("/N").isDictionary() &&
@@ -416,39 +403,32 @@ QPDFFormFieldObjectHelper::setRadioButtonValue(QPDFObjectHandle name) @@ -416,39 +403,32 @@ QPDFFormFieldObjectHelper::setRadioButtonValue(QPDFObjectHandle name)
416 void 403 void
417 QPDFFormFieldObjectHelper::setCheckBoxValue(bool value) 404 QPDFFormFieldObjectHelper::setCheckBoxValue(bool value)
418 { 405 {
419 - QPDFObjectHandle AP = this->oh.getKey("/AP"); 406 + QPDFObjectHandle AP = oh().getKey("/AP");
420 QPDFObjectHandle annot; 407 QPDFObjectHandle annot;
421 - if (AP.isNull()) { 408 + if (AP.null()) {
422 // The widget may be below. If there is more than one, just 409 // The widget may be below. If there is more than one, just
423 // find the first one. 410 // find the first one.
424 - QPDFObjectHandle kids = this->oh.getKey("/Kids");  
425 - if (kids.isArray()) {  
426 - int nkids = kids.getArrayNItems();  
427 - for (int i = 0; i < nkids; ++i) {  
428 - QPDFObjectHandle kid = kids.getArrayItem(i);  
429 - AP = kid.getKey("/AP");  
430 - if (!AP.isNull()) {  
431 - QTC::TC("qpdf", "QPDFFormFieldObjectHelper checkbox kid widget");  
432 - annot = kid;  
433 - break;  
434 - } 411 + QPDFObjectHandle kids = oh().getKey("/Kids");
  412 + for (auto const& kid: oh().getKey("/Kids").as_array(qpdf::strict)) {
  413 + AP = kid.getKey("/AP");
  414 + if (!AP.null()) {
  415 + QTC::TC("qpdf", "QPDFFormFieldObjectHelper checkbox kid widget");
  416 + annot = kid;
  417 + break;
435 } 418 }
436 } 419 }
437 } else { 420 } else {
438 - annot = this->oh; 421 + annot = oh();
439 } 422 }
440 std::string on_value; 423 std::string on_value;
441 if (value) { 424 if (value) {
442 // Set the "on" value to the first value in the appearance stream's normal state dictionary 425 // Set the "on" value to the first value in the appearance stream's normal state dictionary
443 // that isn't /Off. If not found, fall back to /Yes. 426 // that isn't /Off. If not found, fall back to /Yes.
444 if (AP.isDictionary()) { 427 if (AP.isDictionary()) {
445 - auto N = AP.getKey("/N");  
446 - if (N.isDictionary()) {  
447 - for (auto const& iter: N.ditems()) {  
448 - if (iter.first != "/Off") {  
449 - on_value = iter.first;  
450 - break;  
451 - } 428 + for (auto const& item: AP.getKey("/N").as_dictionary()) {
  429 + if (item.first != "/Off") {
  430 + on_value = item.first;
  431 + break;
452 } 432 }
453 } 433 }
454 } 434 }
@@ -462,7 +442,7 @@ QPDFFormFieldObjectHelper::setCheckBoxValue(bool value) @@ -462,7 +442,7 @@ QPDFFormFieldObjectHelper::setCheckBoxValue(bool value)
462 setFieldAttribute("/V", name); 442 setFieldAttribute("/V", name);
463 if (!annot) { 443 if (!annot) {
464 QTC::TC("qpdf", "QPDFObjectHandle broken checkbox"); 444 QTC::TC("qpdf", "QPDFObjectHandle broken checkbox");
465 - this->oh.warnIfPossible("unable to set the value of this checkbox"); 445 + oh().warnIfPossible("unable to set the value of this checkbox");
466 return; 446 return;
467 } 447 }
468 QTC::TC("qpdf", "QPDFFormFieldObjectHelper set checkbox AS"); 448 QTC::TC("qpdf", "QPDFFormFieldObjectHelper set checkbox AS");
@@ -775,7 +755,7 @@ QPDFFormFieldObjectHelper::generateTextAppearance(QPDFAnnotationObjectHelper&amp; ao @@ -775,7 +755,7 @@ QPDFFormFieldObjectHelper::generateTextAppearance(QPDFAnnotationObjectHelper&amp; ao
775 "<< /Resources << /ProcSet [ /PDF /Text ] >>" 755 "<< /Resources << /ProcSet [ /PDF /Text ] >>"
776 " /Type /XObject /Subtype /Form >>"); 756 " /Type /XObject /Subtype /Form >>");
777 dict.replaceKey("/BBox", QPDFObjectHandle::newFromRectangle(bbox)); 757 dict.replaceKey("/BBox", QPDFObjectHandle::newFromRectangle(bbox));
778 - AS = QPDFObjectHandle::newStream(this->oh.getOwningQPDF(), "/Tx BMC\nEMC\n"); 758 + AS = QPDFObjectHandle::newStream(oh().getOwningQPDF(), "/Tx BMC\nEMC\n");
779 AS.replaceDict(dict); 759 AS.replaceDict(dict);
780 QPDFObjectHandle AP = aoh.getAppearanceDictionary(); 760 QPDFObjectHandle AP = aoh.getAppearanceDictionary();
781 if (AP.isNull()) { 761 if (AP.isNull()) {
libqpdf/QPDFJob.cc
@@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
19 #include <qpdf/QPDFEmbeddedFileDocumentHelper.hh> 19 #include <qpdf/QPDFEmbeddedFileDocumentHelper.hh>
20 #include <qpdf/QPDFExc.hh> 20 #include <qpdf/QPDFExc.hh>
21 #include <qpdf/QPDFLogger.hh> 21 #include <qpdf/QPDFLogger.hh>
  22 +#include <qpdf/QPDFObjectHandle_private.hh>
22 #include <qpdf/QPDFOutlineDocumentHelper.hh> 23 #include <qpdf/QPDFOutlineDocumentHelper.hh>
23 #include <qpdf/QPDFPageDocumentHelper.hh> 24 #include <qpdf/QPDFPageDocumentHelper.hh>
24 #include <qpdf/QPDFPageLabelDocumentHelper.hh> 25 #include <qpdf/QPDFPageLabelDocumentHelper.hh>
@@ -916,10 +917,13 @@ QPDFJob::doListAttachments(QPDF&amp; pdf) @@ -916,10 +917,13 @@ QPDFJob::doListAttachments(QPDF&amp; pdf)
916 v << " " << i2.first << " -> " << i2.second << "\n"; 917 v << " " << i2.first << " -> " << i2.second << "\n";
917 } 918 }
918 v << " all data streams:\n"; 919 v << " all data streams:\n";
919 - for (auto const& i2: efoh->getEmbeddedFileStreams().ditems()) {  
920 - auto efs = QPDFEFStreamObjectHelper(i2.second);  
921 - v << " " << i2.first << " -> "  
922 - << efs.getObjectHandle().getObjGen().unparse(',') << "\n"; 920 + for (auto const& [key2, value2]: efoh->getEmbeddedFileStreams().as_dictionary()) {
  921 + if (value2.null()) {
  922 + continue;
  923 + }
  924 + auto efs = QPDFEFStreamObjectHelper(value2);
  925 + v << " " << key2 << " -> " << efs.getObjectHandle().getObjGen().unparse(',')
  926 + << "\n";
923 v << " creation date: " << efs.getCreationDate() << "\n" 927 v << " creation date: " << efs.getCreationDate() << "\n"
924 << " modification date: " << efs.getModDate() << "\n" 928 << " modification date: " << efs.getModDate() << "\n"
925 << " mime type: " << efs.getSubtype() << "\n" 929 << " mime type: " << efs.getSubtype() << "\n"
@@ -1338,9 +1342,12 @@ QPDFJob::doJSONAttachments(Pipeline* p, bool&amp; first, QPDF&amp; pdf) @@ -1338,9 +1342,12 @@ QPDFJob::doJSONAttachments(Pipeline* p, bool&amp; first, QPDF&amp; pdf)
1338 j_names.addDictionaryMember(i2.first, JSON::makeString(i2.second)); 1342 j_names.addDictionaryMember(i2.first, JSON::makeString(i2.second));
1339 } 1343 }
1340 auto j_streams = j_details.addDictionaryMember("streams", JSON::makeDictionary()); 1344 auto j_streams = j_details.addDictionaryMember("streams", JSON::makeDictionary());
1341 - for (auto const& i2: fsoh->getEmbeddedFileStreams().ditems()) {  
1342 - auto efs = QPDFEFStreamObjectHelper(i2.second);  
1343 - auto j_stream = j_streams.addDictionaryMember(i2.first, JSON::makeDictionary()); 1345 + for (auto const& [key2, value2]: fsoh->getEmbeddedFileStreams().as_dictionary()) {
  1346 + if (value2.null()) {
  1347 + continue;
  1348 + }
  1349 + auto efs = QPDFEFStreamObjectHelper(value2);
  1350 + auto j_stream = j_streams.addDictionaryMember(key2, JSON::makeDictionary());
1344 j_stream.addDictionaryMember( 1351 j_stream.addDictionaryMember(
1345 "creationdate", null_or_string(to_iso8601(efs.getCreationDate()))); 1352 "creationdate", null_or_string(to_iso8601(efs.getCreationDate())));
1346 j_stream.addDictionaryMember( 1353 j_stream.addDictionaryMember(
@@ -2347,12 +2354,10 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF&amp; pdf) @@ -2347,12 +2354,10 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF&amp; pdf)
2347 return true; 2354 return true;
2348 } 2355 }
2349 } 2356 }
2350 - if (xobject.isDictionary()) {  
2351 - for (auto const& k: xobject.getKeys()) {  
2352 - QPDFObjectHandle xobj = xobject.getKey(k);  
2353 - if (xobj.isFormXObject()) {  
2354 - queue.push_back(xobj);  
2355 - } 2357 +
  2358 + for (auto const& xobj: xobject.as_dictionary()) {
  2359 + if (xobj.second.isFormXObject()) {
  2360 + queue.emplace_back(xobj.second);
2356 } 2361 }
2357 } 2362 }
2358 } 2363 }
libqpdf/QPDFObjGen.cc deleted
1 -#include <qpdf/QPDFObjGen.hh>  
2 -  
3 -#include <qpdf/QPDFObjectHandle.hh>  
4 -#include <qpdf/QPDFObjectHelper.hh>  
5 -#include <qpdf/QPDFObject_private.hh>  
6 -  
7 -#include <stdexcept>  
8 -  
9 -bool  
10 -QPDFObjGen::set::add(QPDFObjectHandle const& oh)  
11 -{  
12 - if (auto* ptr = oh.getObjectPtr()) {  
13 - return add(ptr->getObjGen());  
14 - } else {  
15 - throw std::logic_error(  
16 - "attempt to retrieve QPDFObjGen from uninitialized QPDFObjectHandle");  
17 - return false;  
18 - }  
19 -}  
20 -  
21 -bool  
22 -QPDFObjGen::set::add(QPDFObjectHelper const& helper)  
23 -{  
24 - if (auto* ptr = helper.getObjectHandle().getObjectPtr()) {  
25 - return add(ptr->getObjGen());  
26 - } else {  
27 - throw std::logic_error(  
28 - "attempt to retrieve QPDFObjGen from uninitialized QPDFObjectHandle");  
29 - return false;  
30 - }  
31 -}  
32 -  
33 -void  
34 -QPDFObjGen::set::erase(QPDFObjectHandle const& oh)  
35 -{  
36 - if (auto* ptr = oh.getObjectPtr()) {  
37 - erase(ptr->getObjGen());  
38 - } else {  
39 - throw std::logic_error(  
40 - "attempt to retrieve QPDFObjGen from uninitialized QPDFObjectHandle");  
41 - }  
42 -}  
43 -  
44 -void  
45 -QPDFObjGen::set::erase(QPDFObjectHelper const& helper)  
46 -{  
47 - if (auto* ptr = helper.getObjectHandle().getObjectPtr()) {  
48 - erase(ptr->getObjGen());  
49 - } else {  
50 - throw std::logic_error(  
51 - "attempt to retrieve QPDFObjGen from uninitialized QPDFObjectHandle");  
52 - }  
53 -}  
libqpdf/QPDFObject.cc
1 #include <qpdf/QPDFObject_private.hh> 1 #include <qpdf/QPDFObject_private.hh>
2 2
3 -#include <qpdf/QPDF.hh>  
4 -#include <qpdf/QPDF_Destroyed.hh>  
5 -  
6 -void  
7 -QPDFObject::destroy() 3 +std::string
  4 +QPDFObject::getDescription()
8 { 5 {
9 - value = QPDF_Destroyed::getInstance(); 6 + if (object_description) {
  7 + switch (object_description->index()) {
  8 + case 0:
  9 + {
  10 + // Simple template string
  11 + auto description = std::get<0>(*object_description);
  12 +
  13 + if (auto pos = description.find("$OG"); pos != std::string::npos) {
  14 + description.replace(pos, 3, og.unparse(' '));
  15 + }
  16 + if (auto pos = description.find("$PO"); pos != std::string::npos) {
  17 + qpdf_offset_t shift = (getTypeCode() == ::ot_dictionary) ? 2
  18 + : (getTypeCode() == ::ot_array) ? 1
  19 + : 0;
  20 +
  21 + description.replace(pos, 3, std::to_string(parsed_offset + shift));
  22 + }
  23 + return description;
  24 + }
  25 + case 1:
  26 + {
  27 + // QPDF::JSONReactor generated description
  28 + auto j_descr = std::get<1>(*object_description);
  29 + return (
  30 + *j_descr.input + (j_descr.object.empty() ? "" : ", " + j_descr.object) +
  31 + " at offset " + std::to_string(parsed_offset));
  32 + }
  33 + case 2:
  34 + {
  35 + // Child object description
  36 + auto j_descr = std::get<2>(*object_description);
  37 + std::string result;
  38 + if (auto p = j_descr.parent.lock()) {
  39 + result = p->getDescription();
  40 + }
  41 + result += j_descr.static_descr;
  42 + if (auto pos = result.find("$VD"); pos != std::string::npos) {
  43 + result.replace(pos, 3, j_descr.var_descr);
  44 + }
  45 + return result;
  46 + }
  47 + }
  48 + } else if (og.isIndirect()) {
  49 + return "object " + og.unparse(' ');
  50 + }
  51 + return {};
10 } 52 }
libqpdf/QPDFObjectHandle.cc
1 -#include <qpdf/QPDFObjectHandle.hh> 1 +#include <qpdf/QPDFObjectHandle_private.hh>
2 2
3 #include <qpdf/BufferInputSource.hh> 3 #include <qpdf/BufferInputSource.hh>
4 #include <qpdf/JSON_writer.hh> 4 #include <qpdf/JSON_writer.hh>
@@ -11,19 +11,6 @@ @@ -11,19 +11,6 @@
11 #include <qpdf/QPDFObject_private.hh> 11 #include <qpdf/QPDFObject_private.hh>
12 #include <qpdf/QPDFPageObjectHelper.hh> 12 #include <qpdf/QPDFPageObjectHelper.hh>
13 #include <qpdf/QPDFParser.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 14
28 #include <qpdf/QIntC.hh> 15 #include <qpdf/QIntC.hh>
29 #include <qpdf/QTC.hh> 16 #include <qpdf/QTC.hh>
@@ -38,6 +25,13 @@ @@ -38,6 +25,13 @@
38 #include <stdexcept> 25 #include <stdexcept>
39 26
40 using namespace std::literals; 27 using namespace std::literals;
  28 +using namespace qpdf;
  29 +
  30 +BaseHandle::
  31 +operator QPDFObjGen() const
  32 +{
  33 + return obj ? obj->getObjGen() : QPDFObjGen();
  34 +}
41 35
42 namespace 36 namespace
43 { 37 {
@@ -221,8 +215,441 @@ LastChar::getLastChar() @@ -221,8 +215,441 @@ LastChar::getLastChar()
221 return this->last_char; 215 return this->last_char;
222 } 216 }
223 217
  218 +std::pair<bool, bool>
  219 +Name::analyzeJSONEncoding(const std::string& name)
  220 +{
  221 + int tail = 0; // Number of continuation characters expected.
  222 + bool tail2 = false; // Potential overlong 3 octet utf-8.
  223 + bool tail3 = false; // potential overlong 4 octet
  224 + bool needs_escaping = false;
  225 + for (auto const& it: name) {
  226 + auto c = static_cast<unsigned char>(it);
  227 + if (tail) {
  228 + if ((c & 0xc0) != 0x80) {
  229 + return {false, false};
  230 + }
  231 + if (tail2) {
  232 + if ((c & 0xe0) == 0x80) {
  233 + return {false, false};
  234 + }
  235 + tail2 = false;
  236 + } else if (tail3) {
  237 + if ((c & 0xf0) == 0x80) {
  238 + return {false, false};
  239 + }
  240 + tail3 = false;
  241 + }
  242 + tail--;
  243 + } else if (c < 0x80) {
  244 + if (!needs_escaping) {
  245 + needs_escaping = !((c > 34 && c != '\\') || c == ' ' || c == 33);
  246 + }
  247 + } else if ((c & 0xe0) == 0xc0) {
  248 + if ((c & 0xfe) == 0xc0) {
  249 + return {false, false};
  250 + }
  251 + tail = 1;
  252 + } else if ((c & 0xf0) == 0xe0) {
  253 + tail2 = (c == 0xe0);
  254 + tail = 2;
  255 + } else if ((c & 0xf8) == 0xf0) {
  256 + tail3 = (c == 0xf0);
  257 + tail = 3;
  258 + } else {
  259 + return {false, false};
  260 + }
  261 + }
  262 + return {tail == 0, !needs_escaping};
  263 +}
  264 +
  265 +std::string
  266 +Name::normalize(std::string const& name)
  267 +{
  268 + if (name.empty()) {
  269 + return name;
  270 + }
  271 + std::string result;
  272 + result += name.at(0);
  273 + for (size_t i = 1; i < name.length(); ++i) {
  274 + char ch = name.at(i);
  275 + // Don't use locale/ctype here; follow PDF spec guidelines.
  276 + if (ch == '\0') {
  277 + // QPDFTokenizer embeds a null character to encode an invalid #.
  278 + result += "#";
  279 + } else if (
  280 + ch < 33 || ch == '#' || ch == '/' || ch == '(' || ch == ')' || ch == '{' || ch == '}' ||
  281 + ch == '<' || ch == '>' || ch == '[' || ch == ']' || ch == '%' || ch > 126) {
  282 + result += QUtil::hex_encode_char(ch);
  283 + } else {
  284 + result += ch;
  285 + }
  286 + }
  287 + return result;
  288 +}
  289 +
  290 +std::shared_ptr<QPDFObject>
  291 +QPDFObject::copy(bool shallow)
  292 +{
  293 + switch (getResolvedTypeCode()) {
  294 + case ::ot_uninitialized:
  295 + throw std::logic_error("QPDFObjectHandle: attempting to copy an uninitialized object");
  296 + return {}; // does not return
  297 + case ::ot_reserved:
  298 + return create<QPDF_Reserved>();
  299 + case ::ot_null:
  300 + return create<QPDF_Null>();
  301 + case ::ot_boolean:
  302 + return create<QPDF_Bool>(std::get<QPDF_Bool>(value).val);
  303 + case ::ot_integer:
  304 + return create<QPDF_Integer>(std::get<QPDF_Integer>(value).val);
  305 + case ::ot_real:
  306 + return create<QPDF_Real>(std::get<QPDF_Real>(value).val);
  307 + case ::ot_string:
  308 + return create<QPDF_String>(std::get<QPDF_String>(value).val);
  309 + case ::ot_name:
  310 + return create<QPDF_Name>(std::get<QPDF_Name>(value).name);
  311 + case ::ot_array:
  312 + {
  313 + auto const& a = std::get<QPDF_Array>(value);
  314 + if (shallow) {
  315 + return QPDFObject::create<QPDF_Array>(a);
  316 + } else {
  317 + QTC::TC("qpdf", "QPDF_Array copy", a.sp ? 0 : 1);
  318 + if (a.sp) {
  319 + QPDF_Array result;
  320 + result.sp = std::make_unique<QPDF_Array::Sparse>();
  321 + result.sp->size = a.sp->size;
  322 + for (auto const& [idx, oh]: a.sp->elements) {
  323 + result.sp->elements[idx] = oh.indirect() ? oh : oh.getObj()->copy();
  324 + }
  325 + return QPDFObject::create<QPDF_Array>(std::move(result));
  326 + } else {
  327 + std::vector<QPDFObjectHandle> result;
  328 + result.reserve(a.elements.size());
  329 + for (auto const& element: a.elements) {
  330 + result.emplace_back(
  331 + element ? (element.indirect() ? element : element.getObj()->copy())
  332 + : element);
  333 + }
  334 + return QPDFObject::create<QPDF_Array>(std::move(result), false);
  335 + }
  336 + }
  337 + }
  338 + case ::ot_dictionary:
  339 + {
  340 + auto const& d = std::get<QPDF_Dictionary>(value);
  341 + if (shallow) {
  342 + return QPDFObject::create<QPDF_Dictionary>(d.items);
  343 + } else {
  344 + std::map<std::string, QPDFObjectHandle> new_items;
  345 + for (auto const& [key, val]: d.items) {
  346 + new_items[key] = val.indirect() ? val : val.getObj()->copy();
  347 + }
  348 + return QPDFObject::create<QPDF_Dictionary>(new_items);
  349 + }
  350 + }
  351 + case ::ot_stream:
  352 + QTC::TC("qpdf", "QPDF_Stream ERR shallow copy stream");
  353 + throw std::runtime_error("stream objects cannot be cloned");
  354 + return {}; // does not return
  355 + case ::ot_operator:
  356 + return create<QPDF_Operator>(std::get<QPDF_Operator>(value).val);
  357 + case ::ot_inlineimage:
  358 + return create<QPDF_InlineImage>(std::get<QPDF_InlineImage>(value).val);
  359 + case ::ot_unresolved:
  360 + throw std::logic_error("QPDFObjectHandle: attempting to unparse a reserved object");
  361 + return {}; // does not return
  362 + case ::ot_destroyed:
  363 + throw std::logic_error("attempted to shallow copy QPDFObjectHandle from destroyed QPDF");
  364 + return {}; // does not return
  365 + case ::ot_reference:
  366 + return qpdf->getObject(og).getObj();
  367 + }
  368 + return {}; // unreachable
  369 +}
  370 +
  371 +std::string
  372 +QPDFObject::unparse()
  373 +{
  374 + switch (getResolvedTypeCode()) {
  375 + case ::ot_uninitialized:
  376 + throw std::logic_error("QPDFObjectHandle: attempting to unparse an uninitialized object");
  377 + return ""; // does not return
  378 + case ::ot_reserved:
  379 + throw std::logic_error("QPDFObjectHandle: attempting to unparse a reserved object");
  380 + return ""; // does not return
  381 + case ::ot_null:
  382 + return "null";
  383 + case ::ot_boolean:
  384 + return std::get<QPDF_Bool>(value).val ? "true" : "false";
  385 + case ::ot_integer:
  386 + return std::to_string(std::get<QPDF_Integer>(value).val);
  387 + case ::ot_real:
  388 + return std::get<QPDF_Real>(value).val;
  389 + case ::ot_string:
  390 + return std::get<QPDF_String>(value).unparse(false);
  391 + case ::ot_name:
  392 + return Name::normalize(std::get<QPDF_Name>(value).name);
  393 + case ::ot_array:
  394 + {
  395 + auto const& a = std::get<QPDF_Array>(value);
  396 + std::string result = "[ ";
  397 + if (a.sp) {
  398 + int next = 0;
  399 + for (auto& item: a.sp->elements) {
  400 + int key = item.first;
  401 + for (int j = next; j < key; ++j) {
  402 + result += "null ";
  403 + }
  404 + auto item_og = item.second.id_gen();
  405 + result += item_og.isIndirect() ? item_og.unparse(' ') + " R "
  406 + : item.second.getObj()->unparse() + " ";
  407 + next = ++key;
  408 + }
  409 + for (int j = next; j < a.sp->size; ++j) {
  410 + result += "null ";
  411 + }
  412 + } else {
  413 + for (auto const& item: a.elements) {
  414 + auto item_og = item.id_gen();
  415 + result += item_og.isIndirect() ? item_og.unparse(' ') + " R "
  416 + : item.getObj()->unparse() + " ";
  417 + }
  418 + }
  419 + result += "]";
  420 + return result;
  421 + }
  422 + case ::ot_dictionary:
  423 + {
  424 + auto const& items = std::get<QPDF_Dictionary>(value).items;
  425 + std::string result = "<< ";
  426 + for (auto& iter: items) {
  427 + if (!iter.second.null()) {
  428 + result += Name::normalize(iter.first) + " " + iter.second.unparse() + " ";
  429 + }
  430 + }
  431 + result += ">>";
  432 + return result;
  433 + }
  434 + case ::ot_stream:
  435 + return og.unparse(' ') + " R";
  436 + case ::ot_operator:
  437 + return std::get<QPDF_Operator>(value).val;
  438 + case ::ot_inlineimage:
  439 + return std::get<QPDF_InlineImage>(value).val;
  440 + case ::ot_unresolved:
  441 + throw std::logic_error("QPDFObjectHandle: attempting to unparse a unresolved object");
  442 + return ""; // does not return
  443 + case ::ot_destroyed:
  444 + throw std::logic_error("attempted to unparse a QPDFObjectHandle from a destroyed QPDF");
  445 + return ""; // does not return
  446 + case ::ot_reference:
  447 + return og.unparse(' ') + " R";
  448 + }
  449 + return {}; // unreachable
  450 +}
  451 +
  452 +void
  453 +QPDFObject::write_json(int json_version, JSON::Writer& p)
  454 +{
  455 + switch (getResolvedTypeCode()) {
  456 + case ::ot_uninitialized:
  457 + throw std::logic_error(
  458 + "QPDFObjectHandle: attempting to get JSON from a uninitialized object");
  459 + break; // unreachable
  460 + case ::ot_null:
  461 + case ::ot_operator:
  462 + case ::ot_inlineimage:
  463 + p << "null";
  464 + break;
  465 + case ::ot_boolean:
  466 + p << std::get<QPDF_Bool>(value).val;
  467 + break;
  468 + case ::ot_integer:
  469 + p << std::to_string(std::get<QPDF_Integer>(value).val);
  470 + break;
  471 + case ::ot_real:
  472 + {
  473 + auto const& val = std::get<QPDF_Real>(value).val;
  474 + if (val.length() == 0) {
  475 + // Can't really happen...
  476 + p << "0";
  477 + } else if (val.at(0) == '.') {
  478 + p << "0" << val;
  479 + } else if (val.length() >= 2 && val.at(0) == '-' && val.at(1) == '.') {
  480 + p << "-0." << val.substr(2);
  481 + } else {
  482 + p << val;
  483 + }
  484 + if (val.back() == '.') {
  485 + p << "0";
  486 + }
  487 + }
  488 + break;
  489 + case ::ot_string:
  490 + std::get<QPDF_String>(value).writeJSON(json_version, p);
  491 + break;
  492 + case ::ot_name:
  493 + {
  494 + auto const& n = std::get<QPDF_Name>(value);
  495 + // For performance reasons this code is duplicated in QPDF_Dictionary::writeJSON. When
  496 + // updating this method make sure QPDF_Dictionary is also update.
  497 + if (json_version == 1) {
  498 + p << "\"" << JSON::Writer::encode_string(Name::normalize(n.name)) << "\"";
  499 + } else {
  500 + if (auto res = Name::analyzeJSONEncoding(n.name); res.first) {
  501 + if (res.second) {
  502 + p << "\"" << n.name << "\"";
  503 + } else {
  504 + p << "\"" << JSON::Writer::encode_string(n.name) << "\"";
  505 + }
  506 + } else {
  507 + p << "\"n:" << JSON::Writer::encode_string(Name::normalize(n.name)) << "\"";
  508 + }
  509 + }
  510 + }
  511 + break;
  512 + case ::ot_array:
  513 + {
  514 + auto const& a = std::get<QPDF_Array>(value);
  515 + p.writeStart('[');
  516 + if (a.sp) {
  517 + int next = 0;
  518 + for (auto& item: a.sp->elements) {
  519 + int key = item.first;
  520 + for (int j = next; j < key; ++j) {
  521 + p.writeNext() << "null";
  522 + }
  523 + p.writeNext();
  524 + auto item_og = item.second.getObj()->getObjGen();
  525 + if (item_og.isIndirect()) {
  526 + p << "\"" << item_og.unparse(' ') << " R\"";
  527 + } else {
  528 + item.second.getObj()->write_json(json_version, p);
  529 + }
  530 + next = ++key;
  531 + }
  532 + for (int j = next; j < a.sp->size; ++j) {
  533 + p.writeNext() << "null";
  534 + }
  535 + } else {
  536 + for (auto const& item: a.elements) {
  537 + p.writeNext();
  538 + auto item_og = item.getObj()->getObjGen();
  539 + if (item_og.isIndirect()) {
  540 + p << "\"" << item_og.unparse(' ') << " R\"";
  541 + } else {
  542 + item.getObj()->write_json(json_version, p);
  543 + }
  544 + }
  545 + }
  546 + p.writeEnd(']');
  547 + }
  548 + break;
  549 + case ::ot_dictionary:
  550 + {
  551 + auto const& d = std::get<QPDF_Dictionary>(value);
  552 + p.writeStart('{');
  553 + for (auto& iter: d.items) {
  554 + if (!iter.second.null()) {
  555 + p.writeNext();
  556 + if (json_version == 1) {
  557 + p << "\"" << JSON::Writer::encode_string(Name::normalize(iter.first))
  558 + << "\": ";
  559 + } else if (auto res = Name::analyzeJSONEncoding(iter.first); res.first) {
  560 + if (res.second) {
  561 + p << "\"" << iter.first << "\": ";
  562 + } else {
  563 + p << "\"" << JSON::Writer::encode_string(iter.first) << "\": ";
  564 + }
  565 + } else {
  566 + p << "\"n:" << JSON::Writer::encode_string(Name::normalize(iter.first))
  567 + << "\": ";
  568 + }
  569 + iter.second.writeJSON(json_version, p);
  570 + }
  571 + }
  572 + p.writeEnd('}');
  573 + }
  574 + break;
  575 + case ::ot_stream:
  576 + std::get<QPDF_Stream>(value).m->stream_dict.writeJSON(json_version, p);
  577 + break;
  578 + case ::ot_reference:
  579 + p << "\"" << getObjGen().unparse(' ') << " R\"";
  580 + break;
  581 + default:
  582 + throw std::logic_error("attempted to write an unsuitable object as JSON");
  583 + }
  584 +}
  585 +
  586 +void
  587 +QPDFObject::disconnect()
  588 +{
  589 + // Disconnect an object from its owning QPDF. This is called by QPDF's destructor.
  590 +
  591 + switch (getTypeCode()) {
  592 + case ::ot_array:
  593 + {
  594 + auto& a = std::get<QPDF_Array>(value);
  595 + if (a.sp) {
  596 + for (auto& item: a.sp->elements) {
  597 + auto& obj = item.second;
  598 + if (!obj.indirect()) {
  599 + obj.getObj()->disconnect();
  600 + }
  601 + }
  602 + } else {
  603 + for (auto& obj: a.elements) {
  604 + if (!obj.indirect()) {
  605 + obj.getObj()->disconnect();
  606 + }
  607 + }
  608 + }
  609 + }
  610 + break;
  611 + case ::ot_dictionary:
  612 + for (auto& iter: std::get<QPDF_Dictionary>(value).items) {
  613 + QPDFObjectHandle::DisconnectAccess::disconnect(iter.second);
  614 + }
  615 + break;
  616 + case ::ot_stream:
  617 + {
  618 + auto& s = std::get<QPDF_Stream>(value);
  619 + s.m->stream_provider = nullptr;
  620 + QPDFObjectHandle::DisconnectAccess::disconnect(s.m->stream_dict);
  621 + }
  622 + break;
  623 + default:
  624 + break;
  625 + }
  626 + qpdf = nullptr;
  627 + og = QPDFObjGen();
  628 +}
  629 +std::string
  630 +QPDFObject::getStringValue() const
  631 +{
  632 + switch (getResolvedTypeCode()) {
  633 + case ::ot_real:
  634 + return std::get<QPDF_Real>(value).val;
  635 + case ::ot_string:
  636 + return std::get<QPDF_String>(value).val;
  637 + case ::ot_name:
  638 + return std::get<QPDF_Name>(value).name;
  639 + case ::ot_operator:
  640 + return std::get<QPDF_Operator>(value).val;
  641 + case ::ot_inlineimage:
  642 + return std::get<QPDF_InlineImage>(value).val;
  643 + case ::ot_reference:
  644 + return std::get<QPDF_Reference>(value).obj->getStringValue();
  645 + default:
  646 + throw std::logic_error("Internal error in QPDFObject::getStringValue");
  647 + }
  648 + return ""; // unreachable
  649 +}
  650 +
224 bool 651 bool
225 -QPDFObjectHandle::isSameObjectAs(QPDFObjectHandle const& rhs) const noexcept 652 +QPDFObjectHandle::isSameObjectAs(QPDFObjectHandle const& rhs) const
226 { 653 {
227 return this->obj == rhs.obj; 654 return this->obj == rhs.obj;
228 } 655 }
@@ -246,7 +673,7 @@ QPDFObjectHandle::getTypeCode() const @@ -246,7 +673,7 @@ QPDFObjectHandle::getTypeCode() const
246 char const* 673 char const*
247 QPDFObjectHandle::getTypeName() const 674 QPDFObjectHandle::getTypeName() const
248 { 675 {
249 - static constexpr std::array<char const*, 15> tn{ 676 + static constexpr std::array<char const*, 16> tn{
250 "uninitialized", 677 "uninitialized",
251 "reserved", 678 "reserved",
252 "null", 679 "null",
@@ -261,100 +688,21 @@ QPDFObjectHandle::getTypeName() const @@ -261,100 +688,21 @@ QPDFObjectHandle::getTypeName() const
261 "operator", 688 "operator",
262 "inline-image", 689 "inline-image",
263 "unresolved", 690 "unresolved",
264 - "destroyed"}; 691 + "destroyed",
  692 + "reference"};
265 return obj ? tn[getTypeCode()] : "uninitialized"; 693 return obj ? tn[getTypeCode()] : "uninitialized";
266 } 694 }
267 695
268 -QPDF_Array*  
269 -QPDFObjectHandle::asArray() const  
270 -{  
271 - return obj ? obj->as<QPDF_Array>() : nullptr;  
272 -}  
273 -  
274 -QPDF_Bool*  
275 -QPDFObjectHandle::asBool() const  
276 -{  
277 - return obj ? obj->as<QPDF_Bool>() : nullptr;  
278 -}  
279 -  
280 -QPDF_Dictionary*  
281 -QPDFObjectHandle::asDictionary() const  
282 -{  
283 - return obj ? obj->as<QPDF_Dictionary>() : nullptr;  
284 -}  
285 -  
286 -QPDF_InlineImage*  
287 -QPDFObjectHandle::asInlineImage() const  
288 -{  
289 - return obj ? obj->as<QPDF_InlineImage>() : nullptr;  
290 -}  
291 -  
292 -QPDF_Integer*  
293 -QPDFObjectHandle::asInteger() const  
294 -{  
295 - return obj ? obj->as<QPDF_Integer>() : nullptr;  
296 -}  
297 -  
298 -QPDF_Name*  
299 -QPDFObjectHandle::asName() const  
300 -{  
301 - return obj ? obj->as<QPDF_Name>() : nullptr;  
302 -}  
303 -  
304 -QPDF_Null*  
305 -QPDFObjectHandle::asNull() const  
306 -{  
307 - return obj ? obj->as<QPDF_Null>() : nullptr;  
308 -}  
309 -  
310 -QPDF_Operator*  
311 -QPDFObjectHandle::asOperator() const  
312 -{  
313 - return obj ? obj->as<QPDF_Operator>() : nullptr;  
314 -}  
315 -  
316 -QPDF_Real*  
317 -QPDFObjectHandle::asReal() const  
318 -{  
319 - return obj ? obj->as<QPDF_Real>() : nullptr;  
320 -}  
321 -  
322 -QPDF_Reserved*  
323 -QPDFObjectHandle::asReserved() const  
324 -{  
325 - return obj ? obj->as<QPDF_Reserved>() : nullptr;  
326 -}  
327 -  
328 -QPDF_Stream*  
329 -QPDFObjectHandle::asStream() const  
330 -{  
331 - return obj ? obj->as<QPDF_Stream>() : nullptr;  
332 -}  
333 -  
334 -QPDF_Stream*  
335 -QPDFObjectHandle::asStreamWithAssert() const  
336 -{  
337 - auto stream = asStream();  
338 - assertType("stream", stream);  
339 - return stream;  
340 -}  
341 -  
342 -QPDF_String*  
343 -QPDFObjectHandle::asString() const  
344 -{  
345 - return obj ? obj->as<QPDF_String>() : nullptr;  
346 -}  
347 -  
348 bool 696 bool
349 QPDFObjectHandle::isDestroyed() const 697 QPDFObjectHandle::isDestroyed() const
350 { 698 {
351 - return obj && obj->getResolvedTypeCode() == ::ot_destroyed; 699 + return type_code() == ::ot_destroyed;
352 } 700 }
353 701
354 bool 702 bool
355 QPDFObjectHandle::isBool() const 703 QPDFObjectHandle::isBool() const
356 { 704 {
357 - return obj && obj->getResolvedTypeCode() == ::ot_boolean; 705 + return type_code() == ::ot_boolean;
358 } 706 }
359 707
360 bool 708 bool
@@ -362,25 +710,25 @@ QPDFObjectHandle::isDirectNull() const @@ -362,25 +710,25 @@ QPDFObjectHandle::isDirectNull() const
362 { 710 {
363 // Don't call dereference() -- this is a const method, and we know 711 // Don't call dereference() -- this is a const method, and we know
364 // objid == 0, so there's nothing to resolve. 712 // objid == 0, so there's nothing to resolve.
365 - return (obj && getObjectID() == 0 && obj->getTypeCode() == ::ot_null); 713 + return !indirect() && raw_type_code() == ::ot_null;
366 } 714 }
367 715
368 bool 716 bool
369 QPDFObjectHandle::isNull() const 717 QPDFObjectHandle::isNull() const
370 { 718 {
371 - return obj && obj->getResolvedTypeCode() == ::ot_null; 719 + return type_code() == ::ot_null;
372 } 720 }
373 721
374 bool 722 bool
375 QPDFObjectHandle::isInteger() const 723 QPDFObjectHandle::isInteger() const
376 { 724 {
377 - return obj && obj->getResolvedTypeCode() == ::ot_integer; 725 + return type_code() == ::ot_integer;
378 } 726 }
379 727
380 bool 728 bool
381 QPDFObjectHandle::isReal() const 729 QPDFObjectHandle::isReal() const
382 { 730 {
383 - return obj && obj->getResolvedTypeCode() == ::ot_real; 731 + return type_code() == ::ot_real;
384 } 732 }
385 733
386 bool 734 bool
@@ -416,49 +764,49 @@ QPDFObjectHandle::getValueAsNumber(double&amp; value) const @@ -416,49 +764,49 @@ QPDFObjectHandle::getValueAsNumber(double&amp; value) const
416 bool 764 bool
417 QPDFObjectHandle::isName() const 765 QPDFObjectHandle::isName() const
418 { 766 {
419 - return obj && obj->getResolvedTypeCode() == ::ot_name; 767 + return type_code() == ::ot_name;
420 } 768 }
421 769
422 bool 770 bool
423 QPDFObjectHandle::isString() const 771 QPDFObjectHandle::isString() const
424 { 772 {
425 - return obj && obj->getResolvedTypeCode() == ::ot_string; 773 + return type_code() == ::ot_string;
426 } 774 }
427 775
428 bool 776 bool
429 QPDFObjectHandle::isOperator() const 777 QPDFObjectHandle::isOperator() const
430 { 778 {
431 - return obj && obj->getResolvedTypeCode() == ::ot_operator; 779 + return type_code() == ::ot_operator;
432 } 780 }
433 781
434 bool 782 bool
435 QPDFObjectHandle::isInlineImage() const 783 QPDFObjectHandle::isInlineImage() const
436 { 784 {
437 - return obj && obj->getResolvedTypeCode() == ::ot_inlineimage; 785 + return type_code() == ::ot_inlineimage;
438 } 786 }
439 787
440 bool 788 bool
441 QPDFObjectHandle::isArray() const 789 QPDFObjectHandle::isArray() const
442 { 790 {
443 - return obj && obj->getResolvedTypeCode() == ::ot_array; 791 + return type_code() == ::ot_array;
444 } 792 }
445 793
446 bool 794 bool
447 QPDFObjectHandle::isDictionary() const 795 QPDFObjectHandle::isDictionary() const
448 { 796 {
449 - return obj && obj->getResolvedTypeCode() == ::ot_dictionary; 797 + return type_code() == ::ot_dictionary;
450 } 798 }
451 799
452 bool 800 bool
453 QPDFObjectHandle::isStream() const 801 QPDFObjectHandle::isStream() const
454 { 802 {
455 - return obj && obj->getResolvedTypeCode() == ::ot_stream; 803 + return type_code() == ::ot_stream;
456 } 804 }
457 805
458 bool 806 bool
459 QPDFObjectHandle::isReserved() const 807 QPDFObjectHandle::isReserved() const
460 { 808 {
461 - return obj && obj->getResolvedTypeCode() == ::ot_reserved; 809 + return type_code() == ::ot_reserved;
462 } 810 }
463 811
464 bool 812 bool
@@ -491,9 +839,8 @@ QPDFObjectHandle::isStreamOfType(std::string const&amp; type, std::string const&amp; sub @@ -491,9 +839,8 @@ QPDFObjectHandle::isStreamOfType(std::string const&amp; type, std::string const&amp; sub
491 bool 839 bool
492 QPDFObjectHandle::getBoolValue() const 840 QPDFObjectHandle::getBoolValue() const
493 { 841 {
494 - auto boolean = asBool();  
495 - if (boolean) {  
496 - return boolean->getVal(); 842 + if (auto boolean = as<QPDF_Bool>()) {
  843 + return boolean->val;
497 } else { 844 } else {
498 typeWarning("boolean", "returning false"); 845 typeWarning("boolean", "returning false");
499 QTC::TC("qpdf", "QPDFObjectHandle boolean returning false"); 846 QTC::TC("qpdf", "QPDFObjectHandle boolean returning false");
@@ -504,12 +851,11 @@ QPDFObjectHandle::getBoolValue() const @@ -504,12 +851,11 @@ QPDFObjectHandle::getBoolValue() const
504 bool 851 bool
505 QPDFObjectHandle::getValueAsBool(bool& value) const 852 QPDFObjectHandle::getValueAsBool(bool& value) const
506 { 853 {
507 - auto boolean = asBool();  
508 - if (boolean == nullptr) {  
509 - return false; 854 + if (auto boolean = as<QPDF_Bool>()) {
  855 + value = boolean->val;
  856 + return true;
510 } 857 }
511 - value = boolean->getVal();  
512 - return true; 858 + return false;
513 } 859 }
514 860
515 // Integer accessors 861 // Integer accessors
@@ -517,9 +863,8 @@ QPDFObjectHandle::getValueAsBool(bool&amp; value) const @@ -517,9 +863,8 @@ QPDFObjectHandle::getValueAsBool(bool&amp; value) const
517 long long 863 long long
518 QPDFObjectHandle::getIntValue() const 864 QPDFObjectHandle::getIntValue() const
519 { 865 {
520 - auto integer = asInteger();  
521 - if (integer) {  
522 - return integer->getVal(); 866 + if (auto integer = as<QPDF_Integer>()) {
  867 + return integer->val;
523 } else { 868 } else {
524 typeWarning("integer", "returning 0"); 869 typeWarning("integer", "returning 0");
525 QTC::TC("qpdf", "QPDFObjectHandle integer returning 0"); 870 QTC::TC("qpdf", "QPDFObjectHandle integer returning 0");
@@ -530,12 +875,11 @@ QPDFObjectHandle::getIntValue() const @@ -530,12 +875,11 @@ QPDFObjectHandle::getIntValue() const
530 bool 875 bool
531 QPDFObjectHandle::getValueAsInt(long long& value) const 876 QPDFObjectHandle::getValueAsInt(long long& value) const
532 { 877 {
533 - auto integer = asInteger();  
534 - if (integer == nullptr) {  
535 - return false; 878 + if (auto integer = as<QPDF_Integer>()) {
  879 + value = integer->val;
  880 + return true;
536 } 881 }
537 - value = integer->getVal();  
538 - return true; 882 + return false;
539 } 883 }
540 884
541 int 885 int
@@ -692,8 +1036,7 @@ QPDFObjectHandle::getValueAsString(std::string&amp; value) const @@ -692,8 +1036,7 @@ QPDFObjectHandle::getValueAsString(std::string&amp; value) const
692 std::string 1036 std::string
693 QPDFObjectHandle::getUTF8Value() const 1037 QPDFObjectHandle::getUTF8Value() const
694 { 1038 {
695 - auto str = asString();  
696 - if (str) { 1039 + if (auto str = as<QPDF_String>()) {
697 return str->getUTF8Val(); 1040 return str->getUTF8Val();
698 } else { 1041 } else {
699 typeWarning("string", "returning empty string"); 1042 typeWarning("string", "returning empty string");
@@ -705,12 +1048,11 @@ QPDFObjectHandle::getUTF8Value() const @@ -705,12 +1048,11 @@ QPDFObjectHandle::getUTF8Value() const
705 bool 1048 bool
706 QPDFObjectHandle::getValueAsUTF8(std::string& value) const 1049 QPDFObjectHandle::getValueAsUTF8(std::string& value) const
707 { 1050 {
708 - auto str = asString();  
709 - if (str == nullptr) {  
710 - return false; 1051 + if (auto str = as<QPDF_String>()) {
  1052 + value = str->getUTF8Val();
  1053 + return true;
711 } 1054 }
712 - value = str->getUTF8Val();  
713 - return true; 1055 + return false;
714 } 1056 }
715 1057
716 // Operator and Inline Image accessors 1058 // Operator and Inline Image accessors
@@ -759,7 +1101,7 @@ QPDFObjectHandle::getValueAsInlineImage(std::string&amp; value) const @@ -759,7 +1101,7 @@ QPDFObjectHandle::getValueAsInlineImage(std::string&amp; value) const
759 return true; 1101 return true;
760 } 1102 }
761 1103
762 -// Array accessors 1104 +// Array accessors and mutators are in QPDF_Array.cc
763 1105
764 QPDFObjectHandle::QPDFArrayItems 1106 QPDFObjectHandle::QPDFArrayItems
765 QPDFObjectHandle::aitems() 1107 QPDFObjectHandle::aitems()
@@ -767,207 +1109,7 @@ QPDFObjectHandle::aitems() @@ -767,207 +1109,7 @@ QPDFObjectHandle::aitems()
767 return *this; 1109 return *this;
768 } 1110 }
769 1111
770 -int  
771 -QPDFObjectHandle::getArrayNItems() const  
772 -{  
773 - if (auto array = asArray()) {  
774 - return array->size();  
775 - } else {  
776 - typeWarning("array", "treating as empty");  
777 - QTC::TC("qpdf", "QPDFObjectHandle array treating as empty");  
778 - return 0;  
779 - }  
780 -}  
781 -  
782 -QPDFObjectHandle  
783 -QPDFObjectHandle::getArrayItem(int n) const  
784 -{  
785 - if (auto array = asArray()) {  
786 - auto result = array->at(n);  
787 - if (result.first) {  
788 - return result.second;  
789 - } else {  
790 - objectWarning("returning null for out of bounds array access");  
791 - QTC::TC("qpdf", "QPDFObjectHandle array bounds");  
792 - }  
793 - } else {  
794 - typeWarning("array", "returning null");  
795 - QTC::TC("qpdf", "QPDFObjectHandle array null for non-array");  
796 - }  
797 - static auto constexpr msg = " -> null returned from invalid array access"sv;  
798 - return QPDF_Null::create(obj, msg, "");  
799 -}  
800 -  
801 -bool  
802 -QPDFObjectHandle::isRectangle() const  
803 -{  
804 - if (auto array = asArray()) {  
805 - for (int i = 0; i < 4; ++i) {  
806 - if (auto item = array->at(i).second; !item.isNumber()) {  
807 - return false;  
808 - }  
809 - }  
810 - return array->size() == 4;  
811 - }  
812 - return false;  
813 -}  
814 -  
815 -bool  
816 -QPDFObjectHandle::isMatrix() const  
817 -{  
818 - if (auto array = asArray()) {  
819 - for (int i = 0; i < 6; ++i) {  
820 - if (auto item = array->at(i).second; !item.isNumber()) {  
821 - return false;  
822 - }  
823 - }  
824 - return array->size() == 6;  
825 - }  
826 - return false;  
827 -}  
828 -  
829 -QPDFObjectHandle::Rectangle  
830 -QPDFObjectHandle::getArrayAsRectangle() const  
831 -{  
832 - if (auto array = asArray()) {  
833 - if (array->size() != 4) {  
834 - return {};  
835 - }  
836 - double items[4];  
837 - for (int i = 0; i < 4; ++i) {  
838 - if (auto item = array->at(i).second; !item.getValueAsNumber(items[i])) {  
839 - return {};  
840 - }  
841 - }  
842 - return {  
843 - std::min(items[0], items[2]),  
844 - std::min(items[1], items[3]),  
845 - std::max(items[0], items[2]),  
846 - std::max(items[1], items[3])};  
847 - }  
848 - return {};  
849 -}  
850 -  
851 -QPDFObjectHandle::Matrix  
852 -QPDFObjectHandle::getArrayAsMatrix() const  
853 -{  
854 - if (auto array = asArray()) {  
855 - if (array->size() != 6) {  
856 - return {};  
857 - }  
858 - double items[6];  
859 - for (int i = 0; i < 6; ++i) {  
860 - if (auto item = array->at(i).second; !item.getValueAsNumber(items[i])) {  
861 - return {};  
862 - }  
863 - }  
864 - return {items[0], items[1], items[2], items[3], items[4], items[5]};  
865 - }  
866 - return {};  
867 -}  
868 -  
869 -std::vector<QPDFObjectHandle>  
870 -QPDFObjectHandle::getArrayAsVector() const  
871 -{  
872 - auto array = asArray();  
873 - if (array) {  
874 - return array->getAsVector();  
875 - } else {  
876 - typeWarning("array", "treating as empty");  
877 - QTC::TC("qpdf", "QPDFObjectHandle array treating as empty vector");  
878 - }  
879 - return {};  
880 -}  
881 -  
882 -// Array mutators  
883 -  
884 -void  
885 -QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const& item)  
886 -{  
887 - if (auto array = asArray()) {  
888 - if (!array->setAt(n, item)) {  
889 - objectWarning("ignoring attempt to set out of bounds array item");  
890 - QTC::TC("qpdf", "QPDFObjectHandle set array bounds");  
891 - }  
892 - } else {  
893 - typeWarning("array", "ignoring attempt to set item");  
894 - QTC::TC("qpdf", "QPDFObjectHandle array ignoring set item");  
895 - }  
896 -}  
897 -void  
898 -QPDFObjectHandle::setArrayFromVector(std::vector<QPDFObjectHandle> const& items)  
899 -{  
900 - if (auto array = asArray()) {  
901 - array->setFromVector(items);  
902 - } else {  
903 - typeWarning("array", "ignoring attempt to replace items");  
904 - QTC::TC("qpdf", "QPDFObjectHandle array ignoring replace items");  
905 - }  
906 -}  
907 -  
908 -void  
909 -QPDFObjectHandle::insertItem(int at, QPDFObjectHandle const& item)  
910 -{  
911 - if (auto array = asArray()) {  
912 - if (!array->insert(at, item)) {  
913 - objectWarning("ignoring attempt to insert out of bounds array item");  
914 - QTC::TC("qpdf", "QPDFObjectHandle insert array bounds");  
915 - }  
916 - } else {  
917 - typeWarning("array", "ignoring attempt to insert item");  
918 - QTC::TC("qpdf", "QPDFObjectHandle array ignoring insert item");  
919 - }  
920 -}  
921 -  
922 -QPDFObjectHandle  
923 -QPDFObjectHandle::insertItemAndGetNew(int at, QPDFObjectHandle const& item)  
924 -{  
925 - insertItem(at, item);  
926 - return item;  
927 -}  
928 -  
929 -void  
930 -QPDFObjectHandle::appendItem(QPDFObjectHandle const& item)  
931 -{  
932 - if (auto array = asArray()) {  
933 - array->push_back(item);  
934 - } else {  
935 - typeWarning("array", "ignoring attempt to append item");  
936 - QTC::TC("qpdf", "QPDFObjectHandle array ignoring append item");  
937 - }  
938 -}  
939 -  
940 -QPDFObjectHandle  
941 -QPDFObjectHandle::appendItemAndGetNew(QPDFObjectHandle const& item)  
942 -{  
943 - appendItem(item);  
944 - return item;  
945 -}  
946 -  
947 -void  
948 -QPDFObjectHandle::eraseItem(int at)  
949 -{  
950 - if (auto array = asArray()) {  
951 - if (!array->erase(at)) {  
952 - objectWarning("ignoring attempt to erase out of bounds array item");  
953 - QTC::TC("qpdf", "QPDFObjectHandle erase array bounds");  
954 - }  
955 - } else {  
956 - typeWarning("array", "ignoring attempt to erase item");  
957 - QTC::TC("qpdf", "QPDFObjectHandle array ignoring erase item");  
958 - }  
959 -}  
960 -  
961 -QPDFObjectHandle  
962 -QPDFObjectHandle::eraseItemAndGetOld(int at)  
963 -{  
964 - auto array = asArray();  
965 - auto result = (array && at < array->size() && at >= 0) ? array->at(at).second : newNull();  
966 - eraseItem(at);  
967 - return result;  
968 -}  
969 -  
970 -// Dictionary accessors 1112 +// Dictionary accessors are in QPDF_Dictionary.cc
971 1113
972 QPDFObjectHandle::QPDFDictItems 1114 QPDFObjectHandle::QPDFDictItems
973 QPDFObjectHandle::ditems() 1115 QPDFObjectHandle::ditems()
@@ -975,66 +1117,6 @@ QPDFObjectHandle::ditems() @@ -975,66 +1117,6 @@ QPDFObjectHandle::ditems()
975 return {*this}; 1117 return {*this};
976 } 1118 }
977 1119
978 -bool  
979 -QPDFObjectHandle::hasKey(std::string const& key) const  
980 -{  
981 - auto dict = asDictionary();  
982 - if (dict) {  
983 - return dict->hasKey(key);  
984 - } else {  
985 - typeWarning("dictionary", "returning false for a key containment request");  
986 - QTC::TC("qpdf", "QPDFObjectHandle dictionary false for hasKey");  
987 - return false;  
988 - }  
989 -}  
990 -  
991 -QPDFObjectHandle  
992 -QPDFObjectHandle::getKey(std::string const& key) const  
993 -{  
994 - if (auto dict = asDictionary()) {  
995 - return dict->getKey(key);  
996 - } else {  
997 - typeWarning("dictionary", "returning null for attempted key retrieval");  
998 - QTC::TC("qpdf", "QPDFObjectHandle dictionary null for getKey");  
999 - static auto constexpr msg = " -> null returned from getting key $VD from non-Dictionary"sv;  
1000 - return QPDF_Null::create(obj, msg, "");  
1001 - }  
1002 -}  
1003 -  
1004 -QPDFObjectHandle  
1005 -QPDFObjectHandle::getKeyIfDict(std::string const& key) const  
1006 -{  
1007 - return isNull() ? newNull() : getKey(key);  
1008 -}  
1009 -  
1010 -std::set<std::string>  
1011 -QPDFObjectHandle::getKeys() const  
1012 -{  
1013 - std::set<std::string> result;  
1014 - auto dict = asDictionary();  
1015 - if (dict) {  
1016 - result = dict->getKeys();  
1017 - } else {  
1018 - typeWarning("dictionary", "treating as empty");  
1019 - QTC::TC("qpdf", "QPDFObjectHandle dictionary empty set for getKeys");  
1020 - }  
1021 - return result;  
1022 -}  
1023 -  
1024 -std::map<std::string, QPDFObjectHandle>  
1025 -QPDFObjectHandle::getDictAsMap() const  
1026 -{  
1027 - std::map<std::string, QPDFObjectHandle> result;  
1028 - auto dict = asDictionary();  
1029 - if (dict) {  
1030 - result = dict->getAsMap();  
1031 - } else {  
1032 - typeWarning("dictionary", "treating as empty");  
1033 - QTC::TC("qpdf", "QPDFObjectHandle dictionary empty map for asMap");  
1034 - }  
1035 - return result;  
1036 -}  
1037 -  
1038 // Array and Name accessors 1120 // Array and Name accessors
1039 1121
1040 bool 1122 bool
@@ -1055,19 +1137,10 @@ QPDFObjectHandle::isOrHasName(std::string const&amp; value) const @@ -1055,19 +1137,10 @@ QPDFObjectHandle::isOrHasName(std::string const&amp; value) const
1055 void 1137 void
1056 QPDFObjectHandle::makeResourcesIndirect(QPDF& owning_qpdf) 1138 QPDFObjectHandle::makeResourcesIndirect(QPDF& owning_qpdf)
1057 { 1139 {
1058 - if (!isDictionary()) {  
1059 - return;  
1060 - }  
1061 - for (auto const& i1: ditems()) {  
1062 - QPDFObjectHandle sub = i1.second;  
1063 - if (!sub.isDictionary()) {  
1064 - continue;  
1065 - }  
1066 - for (auto const& i2: sub.ditems()) {  
1067 - std::string const& key = i2.first;  
1068 - QPDFObjectHandle val = i2.second;  
1069 - if (!val.isIndirect()) {  
1070 - sub.replaceKey(key, owning_qpdf.makeIndirectObject(val)); 1140 + for (auto const& i1: as_dictionary()) {
  1141 + for (auto& i2: i1.second.as_dictionary()) {
  1142 + if (!i2.second.null() && !i2.second.isIndirect()) {
  1143 + i2.second = owning_qpdf.makeIndirectObject(i2.second);
1071 } 1144 }
1072 } 1145 }
1073 } 1146 }
@@ -1084,18 +1157,17 @@ QPDFObjectHandle::mergeResources( @@ -1084,18 +1157,17 @@ QPDFObjectHandle::mergeResources(
1084 1157
1085 auto make_og_to_name = [](QPDFObjectHandle& dict, 1158 auto make_og_to_name = [](QPDFObjectHandle& dict,
1086 std::map<QPDFObjGen, std::string>& og_to_name) { 1159 std::map<QPDFObjGen, std::string>& og_to_name) {
1087 - for (auto const& i: dict.ditems()) {  
1088 - if (i.second.isIndirect()) {  
1089 - og_to_name[i.second.getObjGen()] = i.first; 1160 + for (auto const& [key, value]: dict.as_dictionary()) {
  1161 + if (!value.null() && value.isIndirect()) {
  1162 + og_to_name.insert_or_assign(value.getObjGen(), key);
1090 } 1163 }
1091 } 1164 }
1092 }; 1165 };
1093 1166
1094 // This algorithm is described in comments in QPDFObjectHandle.hh 1167 // This algorithm is described in comments in QPDFObjectHandle.hh
1095 // above the declaration of mergeResources. 1168 // above the declaration of mergeResources.
1096 - for (auto const& o_top: other.ditems()) {  
1097 - std::string const& rtype = o_top.first;  
1098 - QPDFObjectHandle other_val = o_top.second; 1169 + for (auto const& [rtype, value1]: other.as_dictionary()) {
  1170 + auto other_val = value1;
1099 if (hasKey(rtype)) { 1171 if (hasKey(rtype)) {
1100 QPDFObjectHandle this_val = getKey(rtype); 1172 QPDFObjectHandle this_val = getKey(rtype);
1101 if (this_val.isDictionary() && other_val.isDictionary()) { 1173 if (this_val.isDictionary() && other_val.isDictionary()) {
@@ -1110,9 +1182,8 @@ QPDFObjectHandle::mergeResources( @@ -1110,9 +1182,8 @@ QPDFObjectHandle::mergeResources(
1110 std::set<std::string> rnames; 1182 std::set<std::string> rnames;
1111 int min_suffix = 1; 1183 int min_suffix = 1;
1112 bool initialized_maps = false; 1184 bool initialized_maps = false;
1113 - for (auto const& ov_iter: other_val.ditems()) {  
1114 - std::string const& key = ov_iter.first;  
1115 - QPDFObjectHandle rval = ov_iter.second; 1185 + for (auto const& [key, value2]: other_val.as_dictionary()) {
  1186 + QPDFObjectHandle rval = value2;
1116 if (!this_val.hasKey(key)) { 1187 if (!this_val.hasKey(key)) {
1117 if (!rval.isIndirect()) { 1188 if (!rval.isIndirect()) {
1118 QTC::TC("qpdf", "QPDFObjectHandle merge shallow copy"); 1189 QTC::TC("qpdf", "QPDFObjectHandle merge shallow copy");
@@ -1171,14 +1242,10 @@ QPDFObjectHandle::getResourceNames() const @@ -1171,14 +1242,10 @@ QPDFObjectHandle::getResourceNames() const
1171 { 1242 {
1172 // Return second-level dictionary keys 1243 // Return second-level dictionary keys
1173 std::set<std::string> result; 1244 std::set<std::string> result;
1174 - if (!isDictionary()) {  
1175 - return result;  
1176 - }  
1177 - for (auto const& key: getKeys()) {  
1178 - QPDFObjectHandle val = getKey(key);  
1179 - if (val.isDictionary()) {  
1180 - for (auto const& val_key: val.getKeys()) {  
1181 - result.insert(val_key); 1245 + for (auto const& item: as_dictionary(strict)) {
  1246 + for (auto const& [key2, val2]: item.second.as_dictionary(strict)) {
  1247 + if (!val2.null()) {
  1248 + result.insert(key2);
1182 } 1249 }
1183 } 1250 }
1184 } 1251 }
@@ -1208,234 +1275,9 @@ QPDFObjectHandle::getUniqueResourceName( @@ -1208,234 +1275,9 @@ QPDFObjectHandle::getUniqueResourceName(
1208 " QPDFObjectHandle::getUniqueResourceName"); 1275 " QPDFObjectHandle::getUniqueResourceName");
1209 } 1276 }
1210 1277
1211 -// Dictionary mutators  
1212 -  
1213 -void  
1214 -QPDFObjectHandle::replaceKey(std::string const& key, QPDFObjectHandle const& value)  
1215 -{  
1216 - auto dict = asDictionary();  
1217 - if (dict) {  
1218 - checkOwnership(value);  
1219 - dict->replaceKey(key, value);  
1220 - } else {  
1221 - typeWarning("dictionary", "ignoring key replacement request");  
1222 - QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring replaceKey");  
1223 - }  
1224 -}  
1225 -  
1226 -QPDFObjectHandle  
1227 -QPDFObjectHandle::replaceKeyAndGetNew(std::string const& key, QPDFObjectHandle const& value)  
1228 -{  
1229 - replaceKey(key, value);  
1230 - return value;  
1231 -}  
1232 -  
1233 -QPDFObjectHandle  
1234 -QPDFObjectHandle::replaceKeyAndGetOld(std::string const& key, QPDFObjectHandle const& value)  
1235 -{  
1236 - QPDFObjectHandle old = removeKeyAndGetOld(key);  
1237 - replaceKey(key, value);  
1238 - return old;  
1239 -}  
1240 -  
1241 -void  
1242 -QPDFObjectHandle::removeKey(std::string const& key)  
1243 -{  
1244 - auto dict = asDictionary();  
1245 - if (dict) {  
1246 - dict->removeKey(key);  
1247 - } else {  
1248 - typeWarning("dictionary", "ignoring key removal request");  
1249 - QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring removeKey");  
1250 - }  
1251 -}  
1252 -  
1253 -QPDFObjectHandle  
1254 -QPDFObjectHandle::removeKeyAndGetOld(std::string const& key)  
1255 -{  
1256 - auto result = QPDFObjectHandle::newNull();  
1257 - auto dict = asDictionary();  
1258 - if (dict) {  
1259 - result = dict->getKey(key);  
1260 - }  
1261 - removeKey(key);  
1262 - return result;  
1263 -}  
1264 -  
1265 -// Stream accessors  
1266 -  
1267 -QPDFObjectHandle  
1268 -QPDFObjectHandle::getDict() const  
1269 -{  
1270 - return asStreamWithAssert()->getDict();  
1271 -}  
1272 -  
1273 -void  
1274 -QPDFObjectHandle::setFilterOnWrite(bool val)  
1275 -{  
1276 - asStreamWithAssert()->setFilterOnWrite(val);  
1277 -}  
1278 -  
1279 -bool  
1280 -QPDFObjectHandle::getFilterOnWrite()  
1281 -{  
1282 - return asStreamWithAssert()->getFilterOnWrite();  
1283 -}  
1284 -  
1285 -bool  
1286 -QPDFObjectHandle::isDataModified()  
1287 -{  
1288 - return asStreamWithAssert()->isDataModified();  
1289 -}  
1290 -  
1291 -void  
1292 -QPDFObjectHandle::replaceDict(QPDFObjectHandle const& new_dict)  
1293 -{  
1294 - asStreamWithAssert()->replaceDict(new_dict);  
1295 -}  
1296 -  
1297 -std::shared_ptr<Buffer>  
1298 -QPDFObjectHandle::getStreamData(qpdf_stream_decode_level_e level)  
1299 -{  
1300 - return asStreamWithAssert()->getStreamData(level);  
1301 -}  
1302 -  
1303 -std::shared_ptr<Buffer>  
1304 -QPDFObjectHandle::getRawStreamData()  
1305 -{  
1306 - return asStreamWithAssert()->getRawStreamData();  
1307 -}  
1308 -  
1309 -bool  
1310 -QPDFObjectHandle::pipeStreamData(  
1311 - Pipeline* p,  
1312 - bool* filtering_attempted,  
1313 - int encode_flags,  
1314 - qpdf_stream_decode_level_e decode_level,  
1315 - bool suppress_warnings,  
1316 - bool will_retry)  
1317 -{  
1318 - return asStreamWithAssert()->pipeStreamData(  
1319 - p, filtering_attempted, encode_flags, decode_level, suppress_warnings, will_retry);  
1320 -}  
1321 -  
1322 -bool  
1323 -QPDFObjectHandle::pipeStreamData(  
1324 - Pipeline* p,  
1325 - int encode_flags,  
1326 - qpdf_stream_decode_level_e decode_level,  
1327 - bool suppress_warnings,  
1328 - bool will_retry)  
1329 -{  
1330 - bool filtering_attempted;  
1331 - asStreamWithAssert()->pipeStreamData(  
1332 - p, &filtering_attempted, encode_flags, decode_level, suppress_warnings, will_retry);  
1333 - return filtering_attempted;  
1334 -}  
1335 -  
1336 -bool  
1337 -QPDFObjectHandle::pipeStreamData(Pipeline* p, bool filter, bool normalize, bool compress)  
1338 -{  
1339 - int encode_flags = 0;  
1340 - qpdf_stream_decode_level_e decode_level = qpdf_dl_none;  
1341 - if (filter) {  
1342 - decode_level = qpdf_dl_generalized;  
1343 - if (normalize) {  
1344 - encode_flags |= qpdf_ef_normalize;  
1345 - }  
1346 - if (compress) {  
1347 - encode_flags |= qpdf_ef_compress;  
1348 - }  
1349 - }  
1350 - return pipeStreamData(p, encode_flags, decode_level, false);  
1351 -}  
1352 -  
1353 -void  
1354 -QPDFObjectHandle::replaceStreamData(  
1355 - std::shared_ptr<Buffer> data,  
1356 - QPDFObjectHandle const& filter,  
1357 - QPDFObjectHandle const& decode_parms)  
1358 -{  
1359 - asStreamWithAssert()->replaceStreamData(data, filter, decode_parms);  
1360 -}  
1361 -  
1362 -void  
1363 -QPDFObjectHandle::replaceStreamData(  
1364 - std::string const& data, QPDFObjectHandle const& filter, QPDFObjectHandle const& decode_parms)  
1365 -{  
1366 - auto b = std::make_shared<Buffer>(data.length());  
1367 - unsigned char* bp = b->getBuffer();  
1368 - if (bp) {  
1369 - memcpy(bp, data.c_str(), data.length());  
1370 - }  
1371 - asStreamWithAssert()->replaceStreamData(b, filter, decode_parms);  
1372 -}  
1373 -  
1374 -void  
1375 -QPDFObjectHandle::replaceStreamData(  
1376 - std::shared_ptr<StreamDataProvider> provider,  
1377 - QPDFObjectHandle const& filter,  
1378 - QPDFObjectHandle const& decode_parms)  
1379 -{  
1380 - asStreamWithAssert()->replaceStreamData(provider, filter, decode_parms);  
1381 -}  
1382 -  
1383 -namespace  
1384 -{  
1385 - class FunctionProvider: public QPDFObjectHandle::StreamDataProvider  
1386 - {  
1387 - public:  
1388 - FunctionProvider(std::function<void(Pipeline*)> provider) :  
1389 - StreamDataProvider(false),  
1390 - p1(provider),  
1391 - p2(nullptr)  
1392 - {  
1393 - }  
1394 - FunctionProvider(std::function<bool(Pipeline*, bool, bool)> provider) :  
1395 - StreamDataProvider(true),  
1396 - p1(nullptr),  
1397 - p2(provider)  
1398 - {  
1399 - } 1278 +// Dictionary mutators are in QPDF_Dictionary.cc
1400 1279
1401 - void  
1402 - provideStreamData(QPDFObjGen const&, Pipeline* pipeline) override  
1403 - {  
1404 - p1(pipeline);  
1405 - }  
1406 -  
1407 - bool  
1408 - provideStreamData(  
1409 - QPDFObjGen const&, Pipeline* pipeline, bool suppress_warnings, bool will_retry) override  
1410 - {  
1411 - return p2(pipeline, suppress_warnings, will_retry);  
1412 - }  
1413 -  
1414 - private:  
1415 - std::function<void(Pipeline*)> p1;  
1416 - std::function<bool(Pipeline*, bool, bool)> p2;  
1417 - };  
1418 -} // namespace  
1419 -  
1420 -void  
1421 -QPDFObjectHandle::replaceStreamData(  
1422 - std::function<void(Pipeline*)> provider,  
1423 - QPDFObjectHandle const& filter,  
1424 - QPDFObjectHandle const& decode_parms)  
1425 -{  
1426 - auto sdp = std::shared_ptr<StreamDataProvider>(new FunctionProvider(provider));  
1427 - asStreamWithAssert()->replaceStreamData(sdp, filter, decode_parms);  
1428 -}  
1429 -  
1430 -void  
1431 -QPDFObjectHandle::replaceStreamData(  
1432 - std::function<bool(Pipeline*, bool, bool)> provider,  
1433 - QPDFObjectHandle const& filter,  
1434 - QPDFObjectHandle const& decode_parms)  
1435 -{  
1436 - auto sdp = std::shared_ptr<StreamDataProvider>(new FunctionProvider(provider));  
1437 - asStreamWithAssert()->replaceStreamData(sdp, filter, decode_parms);  
1438 -} 1280 +// Stream accessors are in QPDF_Stream.cc
1439 1281
1440 std::map<std::string, QPDFObjectHandle> 1282 std::map<std::string, QPDFObjectHandle>
1441 QPDFObjectHandle::getPageImages() 1283 QPDFObjectHandle::getPageImages()
@@ -1449,12 +1291,12 @@ QPDFObjectHandle::arrayOrStreamToStreamArray( @@ -1449,12 +1291,12 @@ QPDFObjectHandle::arrayOrStreamToStreamArray(
1449 { 1291 {
1450 all_description = description; 1292 all_description = description;
1451 std::vector<QPDFObjectHandle> result; 1293 std::vector<QPDFObjectHandle> result;
1452 - if (auto array = asArray()) {  
1453 - int n_items = array->size(); 1294 + if (auto array = as_array(strict)) {
  1295 + int n_items = array.size();
1454 for (int i = 0; i < n_items; ++i) { 1296 for (int i = 0; i < n_items; ++i) {
1455 - QPDFObjectHandle item = array->at(i).second; 1297 + QPDFObjectHandle item = array.at(i).second;
1456 if (item.isStream()) { 1298 if (item.isStream()) {
1457 - result.push_back(item); 1299 + result.emplace_back(item);
1458 } else { 1300 } else {
1459 QTC::TC("qpdf", "QPDFObjectHandle non-stream in stream array"); 1301 QTC::TC("qpdf", "QPDFObjectHandle non-stream in stream array");
1460 warn( 1302 warn(
@@ -1468,7 +1310,7 @@ QPDFObjectHandle::arrayOrStreamToStreamArray( @@ -1468,7 +1310,7 @@ QPDFObjectHandle::arrayOrStreamToStreamArray(
1468 } 1310 }
1469 } 1311 }
1470 } else if (isStream()) { 1312 } else if (isStream()) {
1471 - result.push_back(*this); 1313 + result.emplace_back(*this);
1472 } else if (!isNull()) { 1314 } else if (!isNull()) {
1473 warn( 1315 warn(
1474 getOwningQPDF(), 1316 getOwningQPDF(),
@@ -1602,7 +1444,7 @@ QPDFObjectHandle::unparseResolved() const @@ -1602,7 +1444,7 @@ QPDFObjectHandle::unparseResolved() const
1602 std::string 1444 std::string
1603 QPDFObjectHandle::unparseBinary() const 1445 QPDFObjectHandle::unparseBinary() const
1604 { 1446 {
1605 - if (auto str = asString()) { 1447 + if (auto str = as<QPDF_String>()) {
1606 return str->unparse(true); 1448 return str->unparse(true);
1607 } else { 1449 } else {
1608 return unparse(); 1450 return unparse();
@@ -1633,7 +1475,7 @@ QPDFObjectHandle::writeJSON(int json_version, JSON::Writer&amp; p, bool dereference_ @@ -1633,7 +1475,7 @@ QPDFObjectHandle::writeJSON(int json_version, JSON::Writer&amp; p, bool dereference_
1633 } else if (!obj) { 1475 } else if (!obj) {
1634 throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle"); 1476 throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle");
1635 } else { 1477 } else {
1636 - obj->writeJSON(json_version, p); 1478 + obj->write_json(json_version, p);
1637 } 1479 }
1638 } 1480 }
1639 1481
@@ -1645,18 +1487,6 @@ QPDFObjectHandle::writeJSON( @@ -1645,18 +1487,6 @@ QPDFObjectHandle::writeJSON(
1645 writeJSON(json_version, jw, dereference_indirect); 1487 writeJSON(json_version, jw, dereference_indirect);
1646 } 1488 }
1647 1489
1648 -JSON  
1649 -QPDFObjectHandle::getStreamJSON(  
1650 - int json_version,  
1651 - qpdf_json_stream_data_e json_data,  
1652 - qpdf_stream_decode_level_e decode_level,  
1653 - Pipeline* p,  
1654 - std::string const& data_filename)  
1655 -{  
1656 - return asStreamWithAssert()->getStreamJSON(  
1657 - json_version, json_data, decode_level, p, data_filename);  
1658 -}  
1659 -  
1660 QPDFObjectHandle 1490 QPDFObjectHandle
1661 QPDFObjectHandle::wrapInArray() 1491 QPDFObjectHandle::wrapInArray()
1662 { 1492 {
@@ -1857,7 +1687,7 @@ QPDFObjectHandle::addContentTokenFilter(std::shared_ptr&lt;TokenFilter&gt; filter) @@ -1857,7 +1687,7 @@ QPDFObjectHandle::addContentTokenFilter(std::shared_ptr&lt;TokenFilter&gt; filter)
1857 void 1687 void
1858 QPDFObjectHandle::addTokenFilter(std::shared_ptr<TokenFilter> filter) 1688 QPDFObjectHandle::addTokenFilter(std::shared_ptr<TokenFilter> filter)
1859 { 1689 {
1860 - return asStreamWithAssert()->addTokenFilter(filter); 1690 + return as_stream(error).addTokenFilter(filter);
1861 } 1691 }
1862 1692
1863 QPDFObjectHandle 1693 QPDFObjectHandle
@@ -1882,43 +1712,43 @@ QPDFObjectHandle::getParsedOffset() const @@ -1882,43 +1712,43 @@ QPDFObjectHandle::getParsedOffset() const
1882 QPDFObjectHandle 1712 QPDFObjectHandle
1883 QPDFObjectHandle::newBool(bool value) 1713 QPDFObjectHandle::newBool(bool value)
1884 { 1714 {
1885 - return {QPDF_Bool::create(value)}; 1715 + return {QPDFObject::create<QPDF_Bool>(value)};
1886 } 1716 }
1887 1717
1888 QPDFObjectHandle 1718 QPDFObjectHandle
1889 QPDFObjectHandle::newNull() 1719 QPDFObjectHandle::newNull()
1890 { 1720 {
1891 - return {QPDF_Null::create()}; 1721 + return {QPDFObject::create<QPDF_Null>()};
1892 } 1722 }
1893 1723
1894 QPDFObjectHandle 1724 QPDFObjectHandle
1895 QPDFObjectHandle::newInteger(long long value) 1725 QPDFObjectHandle::newInteger(long long value)
1896 { 1726 {
1897 - return {QPDF_Integer::create(value)}; 1727 + return {QPDFObject::create<QPDF_Integer>(value)};
1898 } 1728 }
1899 1729
1900 QPDFObjectHandle 1730 QPDFObjectHandle
1901 QPDFObjectHandle::newReal(std::string const& value) 1731 QPDFObjectHandle::newReal(std::string const& value)
1902 { 1732 {
1903 - return {QPDF_Real::create(value)}; 1733 + return {QPDFObject::create<QPDF_Real>(value)};
1904 } 1734 }
1905 1735
1906 QPDFObjectHandle 1736 QPDFObjectHandle
1907 QPDFObjectHandle::newReal(double value, int decimal_places, bool trim_trailing_zeroes) 1737 QPDFObjectHandle::newReal(double value, int decimal_places, bool trim_trailing_zeroes)
1908 { 1738 {
1909 - return {QPDF_Real::create(value, decimal_places, trim_trailing_zeroes)}; 1739 + return {QPDFObject::create<QPDF_Real>(value, decimal_places, trim_trailing_zeroes)};
1910 } 1740 }
1911 1741
1912 QPDFObjectHandle 1742 QPDFObjectHandle
1913 QPDFObjectHandle::newName(std::string const& name) 1743 QPDFObjectHandle::newName(std::string const& name)
1914 { 1744 {
1915 - return {QPDF_Name::create(name)}; 1745 + return {QPDFObject::create<QPDF_Name>(name)};
1916 } 1746 }
1917 1747
1918 QPDFObjectHandle 1748 QPDFObjectHandle
1919 QPDFObjectHandle::newString(std::string const& str) 1749 QPDFObjectHandle::newString(std::string const& str)
1920 { 1750 {
1921 - return {QPDF_String::create(str)}; 1751 + return {QPDFObject::create<QPDF_String>(str)};
1922 } 1752 }
1923 1753
1924 QPDFObjectHandle 1754 QPDFObjectHandle
@@ -1930,13 +1760,13 @@ QPDFObjectHandle::newUnicodeString(std::string const&amp; utf8_str) @@ -1930,13 +1760,13 @@ QPDFObjectHandle::newUnicodeString(std::string const&amp; utf8_str)
1930 QPDFObjectHandle 1760 QPDFObjectHandle
1931 QPDFObjectHandle::newOperator(std::string const& value) 1761 QPDFObjectHandle::newOperator(std::string const& value)
1932 { 1762 {
1933 - return {QPDF_Operator::create(value)}; 1763 + return {QPDFObject::create<QPDF_Operator>(value)};
1934 } 1764 }
1935 1765
1936 QPDFObjectHandle 1766 QPDFObjectHandle
1937 QPDFObjectHandle::newInlineImage(std::string const& value) 1767 QPDFObjectHandle::newInlineImage(std::string const& value)
1938 { 1768 {
1939 - return {QPDF_InlineImage::create(value)}; 1769 + return {QPDFObject::create<QPDF_InlineImage>(value)};
1940 } 1770 }
1941 1771
1942 QPDFObjectHandle 1772 QPDFObjectHandle
@@ -1948,7 +1778,7 @@ QPDFObjectHandle::newArray() @@ -1948,7 +1778,7 @@ QPDFObjectHandle::newArray()
1948 QPDFObjectHandle 1778 QPDFObjectHandle
1949 QPDFObjectHandle::newArray(std::vector<QPDFObjectHandle> const& items) 1779 QPDFObjectHandle::newArray(std::vector<QPDFObjectHandle> const& items)
1950 { 1780 {
1951 - return {QPDF_Array::create(items)}; 1781 + return {QPDFObject::create<QPDF_Array>(items)};
1952 } 1782 }
1953 1783
1954 QPDFObjectHandle 1784 QPDFObjectHandle
@@ -2008,7 +1838,7 @@ QPDFObjectHandle::newDictionary() @@ -2008,7 +1838,7 @@ QPDFObjectHandle::newDictionary()
2008 QPDFObjectHandle 1838 QPDFObjectHandle
2009 QPDFObjectHandle::newDictionary(std::map<std::string, QPDFObjectHandle> const& items) 1839 QPDFObjectHandle::newDictionary(std::map<std::string, QPDFObjectHandle> const& items)
2010 { 1840 {
2011 - return {QPDF_Dictionary::create(items)}; 1841 + return {QPDFObject::create<QPDF_Dictionary>(items)};
2012 } 1842 }
2013 1843
2014 QPDFObjectHandle 1844 QPDFObjectHandle
@@ -2054,7 +1884,7 @@ void @@ -2054,7 +1884,7 @@ void
2054 QPDFObjectHandle::setObjectDescription(QPDF* owning_qpdf, std::string const& object_description) 1884 QPDFObjectHandle::setObjectDescription(QPDF* owning_qpdf, std::string const& object_description)
2055 { 1885 {
2056 if (obj) { 1886 if (obj) {
2057 - auto descr = std::make_shared<QPDFValue::Description>(object_description); 1887 + auto descr = std::make_shared<QPDFObject::Description>(object_description);
2058 obj->setDescription(owning_qpdf, descr); 1888 obj->setDescription(owning_qpdf, descr);
2059 } 1889 }
2060 } 1890 }
@@ -2096,23 +1926,22 @@ QPDFObjectHandle::makeDirect(QPDFObjGen::set&amp; visited, bool stop_at_streams) @@ -2096,23 +1926,22 @@ QPDFObjectHandle::makeDirect(QPDFObjGen::set&amp; visited, bool stop_at_streams)
2096 1926
2097 if (isBool() || isInteger() || isName() || isNull() || isReal() || isString()) { 1927 if (isBool() || isInteger() || isName() || isNull() || isReal() || isString()) {
2098 this->obj = obj->copy(true); 1928 this->obj = obj->copy(true);
2099 - } else if (isArray()) { 1929 + } else if (auto a = as_array(strict)) {
2100 std::vector<QPDFObjectHandle> items; 1930 std::vector<QPDFObjectHandle> items;
2101 - auto array = asArray();  
2102 - int n = array->size();  
2103 - for (int i = 0; i < n; ++i) {  
2104 - items.push_back(array->at(i).second); 1931 + for (auto const& item: a) {
  1932 + items.emplace_back(item);
2105 items.back().makeDirect(visited, stop_at_streams); 1933 items.back().makeDirect(visited, stop_at_streams);
2106 } 1934 }
2107 - this->obj = QPDF_Array::create(items); 1935 + this->obj = QPDFObject::create<QPDF_Array>(items);
2108 } else if (isDictionary()) { 1936 } else if (isDictionary()) {
2109 std::map<std::string, QPDFObjectHandle> items; 1937 std::map<std::string, QPDFObjectHandle> items;
2110 - auto dict = asDictionary();  
2111 - for (auto const& key: getKeys()) {  
2112 - items[key] = dict->getKey(key);  
2113 - items[key].makeDirect(visited, stop_at_streams); 1938 + for (auto const& [key, value]: as_dictionary(strict)) {
  1939 + if (!value.null()) {
  1940 + items.insert({key, value});
  1941 + items[key].makeDirect(visited, stop_at_streams);
  1942 + }
2114 } 1943 }
2115 - this->obj = QPDF_Dictionary::create(items); 1944 + this->obj = QPDFObject::create<QPDF_Dictionary>(items);
2116 } else if (isStream()) { 1945 } else if (isStream()) {
2117 QTC::TC("qpdf", "QPDFObjectHandle copy stream", stop_at_streams ? 0 : 1); 1946 QTC::TC("qpdf", "QPDFObjectHandle copy stream", stop_at_streams ? 0 : 1);
2118 if (!stop_at_streams) { 1947 if (!stop_at_streams) {
@@ -2349,19 +2178,6 @@ QPDFObjectHandle::isImage(bool exclude_imagemask) const @@ -2349,19 +2178,6 @@ QPDFObjectHandle::isImage(bool exclude_imagemask) const
2349 } 2178 }
2350 2179
2351 void 2180 void
2352 -QPDFObjectHandle::checkOwnership(QPDFObjectHandle const& item) const  
2353 -{  
2354 - auto qpdf = getOwningQPDF();  
2355 - auto item_qpdf = item.getOwningQPDF();  
2356 - if ((qpdf != nullptr) && (item_qpdf != nullptr) && (qpdf != item_qpdf)) {  
2357 - QTC::TC("qpdf", "QPDFObjectHandle check ownership");  
2358 - throw std::logic_error(  
2359 - "Attempting to add an object from a different QPDF. Use "  
2360 - "QPDF::copyForeignObject to add objects from another file.");  
2361 - }  
2362 -}  
2363 -  
2364 -void  
2365 QPDFObjectHandle::assertPageObject() const 2181 QPDFObjectHandle::assertPageObject() const
2366 { 2182 {
2367 if (!isPageObject()) { 2183 if (!isPageObject()) {
libqpdf/QPDFOutlineObjectHelper.cc
@@ -9,8 +9,8 @@ QPDFOutlineObjectHelper::Members::Members(QPDFOutlineDocumentHelper&amp; dh) : @@ -9,8 +9,8 @@ QPDFOutlineObjectHelper::Members::Members(QPDFOutlineDocumentHelper&amp; dh) :
9 } 9 }
10 10
11 QPDFOutlineObjectHelper::QPDFOutlineObjectHelper( 11 QPDFOutlineObjectHelper::QPDFOutlineObjectHelper(
12 - QPDFObjectHandle oh, QPDFOutlineDocumentHelper& dh, int depth) :  
13 - QPDFObjectHelper(oh), 12 + QPDFObjectHandle a_oh, QPDFOutlineDocumentHelper& dh, int depth) :
  13 + QPDFObjectHelper(a_oh),
14 m(new Members(dh)) 14 m(new Members(dh))
15 { 15 {
16 if (depth > 50) { 16 if (depth > 50) {
@@ -18,13 +18,13 @@ QPDFOutlineObjectHelper::QPDFOutlineObjectHelper( @@ -18,13 +18,13 @@ QPDFOutlineObjectHelper::QPDFOutlineObjectHelper(
18 // to 1. 18 // to 1.
19 return; 19 return;
20 } 20 }
21 - if (QPDFOutlineDocumentHelper::Accessor::checkSeen(m->dh, this->oh.getObjGen())) { 21 + if (QPDFOutlineDocumentHelper::Accessor::checkSeen(m->dh, a_oh.getObjGen())) {
22 QTC::TC("qpdf", "QPDFOutlineObjectHelper loop"); 22 QTC::TC("qpdf", "QPDFOutlineObjectHelper loop");
23 return; 23 return;
24 } 24 }
25 25
26 QPDFObjGen::set children; 26 QPDFObjGen::set children;
27 - QPDFObjectHandle cur = oh.getKey("/First"); 27 + QPDFObjectHandle cur = a_oh.getKey("/First");
28 while (!cur.isNull() && cur.isIndirect() && children.add(cur)) { 28 while (!cur.isNull() && cur.isIndirect() && children.add(cur)) {
29 QPDFOutlineObjectHelper new_ooh(cur, dh, 1 + depth); 29 QPDFOutlineObjectHelper new_ooh(cur, dh, 1 + depth);
30 new_ooh.m->parent = std::make_shared<QPDFOutlineObjectHelper>(*this); 30 new_ooh.m->parent = std::make_shared<QPDFOutlineObjectHelper>(*this);
@@ -50,11 +50,11 @@ QPDFOutlineObjectHelper::getDest() @@ -50,11 +50,11 @@ QPDFOutlineObjectHelper::getDest()
50 { 50 {
51 QPDFObjectHandle dest; 51 QPDFObjectHandle dest;
52 QPDFObjectHandle A; 52 QPDFObjectHandle A;
53 - if (this->oh.hasKey("/Dest")) { 53 + if (oh().hasKey("/Dest")) {
54 QTC::TC("qpdf", "QPDFOutlineObjectHelper direct dest"); 54 QTC::TC("qpdf", "QPDFOutlineObjectHelper direct dest");
55 - dest = this->oh.getKey("/Dest"); 55 + dest = oh().getKey("/Dest");
56 } else if ( 56 } else if (
57 - (A = this->oh.getKey("/A")).isDictionary() && A.getKey("/S").isName() && 57 + (A = oh().getKey("/A")).isDictionary() && A.getKey("/S").isName() &&
58 (A.getKey("/S").getName() == "/GoTo") && A.hasKey("/D")) { 58 (A.getKey("/S").getName() == "/GoTo") && A.hasKey("/D")) {
59 QTC::TC("qpdf", "QPDFOutlineObjectHelper action dest"); 59 QTC::TC("qpdf", "QPDFOutlineObjectHelper action dest");
60 dest = A.getKey("/D"); 60 dest = A.getKey("/D");
@@ -85,8 +85,8 @@ int @@ -85,8 +85,8 @@ int
85 QPDFOutlineObjectHelper::getCount() 85 QPDFOutlineObjectHelper::getCount()
86 { 86 {
87 int count = 0; 87 int count = 0;
88 - if (this->oh.hasKey("/Count")) {  
89 - count = this->oh.getKey("/Count").getIntValueAsInt(); 88 + if (oh().hasKey("/Count")) {
  89 + count = oh().getKey("/Count").getIntValueAsInt();
90 } 90 }
91 return count; 91 return count;
92 } 92 }
@@ -95,8 +95,8 @@ std::string @@ -95,8 +95,8 @@ std::string
95 QPDFOutlineObjectHelper::getTitle() 95 QPDFOutlineObjectHelper::getTitle()
96 { 96 {
97 std::string result; 97 std::string result;
98 - if (this->oh.hasKey("/Title")) {  
99 - result = this->oh.getKey("/Title").getUTF8Value(); 98 + if (oh().hasKey("/Title")) {
  99 + result = oh().getKey("/Title").getUTF8Value();
100 } 100 }
101 return result; 101 return result;
102 } 102 }
libqpdf/QPDFPageObjectHelper.cc
@@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
7 #include <qpdf/QPDFAcroFormDocumentHelper.hh> 7 #include <qpdf/QPDFAcroFormDocumentHelper.hh>
8 #include <qpdf/QPDFExc.hh> 8 #include <qpdf/QPDFExc.hh>
9 #include <qpdf/QPDFMatrix.hh> 9 #include <qpdf/QPDFMatrix.hh>
  10 +#include <qpdf/QPDFObjectHandle_private.hh>
10 #include <qpdf/QTC.hh> 11 #include <qpdf/QTC.hh>
11 #include <qpdf/QUtil.hh> 12 #include <qpdf/QUtil.hh>
12 #include <qpdf/ResourceFinder.hh> 13 #include <qpdf/ResourceFinder.hh>
@@ -72,9 +73,12 @@ InlineImageTracker::convertIIDict(QPDFObjectHandle odict) @@ -72,9 +73,12 @@ InlineImageTracker::convertIIDict(QPDFObjectHandle odict)
72 QPDFObjectHandle dict = QPDFObjectHandle::newDictionary(); 73 QPDFObjectHandle dict = QPDFObjectHandle::newDictionary();
73 dict.replaceKey("/Type", QPDFObjectHandle::newName("/XObject")); 74 dict.replaceKey("/Type", QPDFObjectHandle::newName("/XObject"));
74 dict.replaceKey("/Subtype", QPDFObjectHandle::newName("/Image")); 75 dict.replaceKey("/Subtype", QPDFObjectHandle::newName("/Image"));
75 - std::set<std::string> keys = odict.getKeys();  
76 - for (auto key: keys) {  
77 - QPDFObjectHandle value = odict.getKey(key); 76 + for (auto const& [k, v]: odict.as_dictionary()) {
  77 + if (v.null()) {
  78 + continue;
  79 + }
  80 + auto key = k;
  81 + auto value = v;
78 if (key == "/BPC") { 82 if (key == "/BPC") {
79 key = "/BitsPerComponent"; 83 key = "/BitsPerComponent";
80 } else if (key == "/CS") { 84 } else if (key == "/CS") {
@@ -227,9 +231,9 @@ QPDFPageObjectHelper::getAttribute( @@ -227,9 +231,9 @@ QPDFPageObjectHelper::getAttribute(
227 std::function<QPDFObjectHandle()> get_fallback, 231 std::function<QPDFObjectHandle()> get_fallback,
228 bool copy_if_fallback) 232 bool copy_if_fallback)
229 { 233 {
230 - const bool is_form_xobject = this->oh.isFormXObject(); 234 + const bool is_form_xobject = oh().isFormXObject();
231 bool inherited = false; 235 bool inherited = false;
232 - auto dict = is_form_xobject ? oh.getDict() : oh; 236 + auto dict = is_form_xobject ? oh().getDict() : oh();
233 auto result = dict.getKey(name); 237 auto result = dict.getKey(name);
234 238
235 if (!is_form_xobject && result.isNull() && 239 if (!is_form_xobject && result.isNull() &&
@@ -324,23 +328,24 @@ QPDFPageObjectHelper::forEachXObject( @@ -324,23 +328,24 @@ QPDFPageObjectHelper::forEachXObject(
324 QTC::TC( 328 QTC::TC(
325 "qpdf", 329 "qpdf",
326 "QPDFPageObjectHelper::forEachXObject", 330 "QPDFPageObjectHelper::forEachXObject",
327 - recursive ? (this->oh.isFormXObject() ? 0 : 1) : (this->oh.isFormXObject() ? 2 : 3)); 331 + recursive ? (oh().isFormXObject() ? 0 : 1) : (oh().isFormXObject() ? 2 : 3));
328 QPDFObjGen::set seen; 332 QPDFObjGen::set seen;
329 std::list<QPDFPageObjectHelper> queue; 333 std::list<QPDFPageObjectHelper> queue;
330 - queue.push_back(*this); 334 + queue.emplace_back(*this);
331 while (!queue.empty()) { 335 while (!queue.empty()) {
332 auto& ph = queue.front(); 336 auto& ph = queue.front();
333 if (seen.add(ph)) { 337 if (seen.add(ph)) {
334 auto xobj_dict = ph.getAttribute("/Resources", false).getKeyIfDict("/XObject"); 338 auto xobj_dict = ph.getAttribute("/Resources", false).getKeyIfDict("/XObject");
335 - if (xobj_dict.isDictionary()) {  
336 - for (auto const& key: xobj_dict.getKeys()) {  
337 - QPDFObjectHandle obj = xobj_dict.getKey(key);  
338 - if ((!selector) || selector(obj)) {  
339 - action(obj, xobj_dict, key);  
340 - }  
341 - if (recursive && obj.isFormXObject()) {  
342 - queue.emplace_back(obj);  
343 - } 339 + for (auto const& [key, value]: xobj_dict.as_dictionary()) {
  340 + if (value.null()) {
  341 + continue;
  342 + }
  343 + auto obj = value;
  344 + if ((!selector) || selector(obj)) {
  345 + action(obj, xobj_dict, key);
  346 + }
  347 + if (recursive && obj.isFormXObject()) {
  348 + queue.emplace_back(obj);
344 } 349 }
345 } 350 }
346 } 351 }
@@ -402,28 +407,27 @@ QPDFPageObjectHelper::externalizeInlineImages(size_t min_size, bool shallow) @@ -402,28 +407,27 @@ QPDFPageObjectHelper::externalizeInlineImages(size_t min_size, bool shallow)
402 // Calling mergeResources also ensures that /XObject becomes direct and is not shared with 407 // Calling mergeResources also ensures that /XObject becomes direct and is not shared with
403 // other pages. 408 // other pages.
404 resources.mergeResources("<< /XObject << >> >>"_qpdf); 409 resources.mergeResources("<< /XObject << >> >>"_qpdf);
405 - InlineImageTracker iit(this->oh.getOwningQPDF(), min_size, resources); 410 + InlineImageTracker iit(oh().getOwningQPDF(), min_size, resources);
406 Pl_Buffer b("new page content"); 411 Pl_Buffer b("new page content");
407 bool filtered = false; 412 bool filtered = false;
408 try { 413 try {
409 filterContents(&iit, &b); 414 filterContents(&iit, &b);
410 filtered = true; 415 filtered = true;
411 } catch (std::exception& e) { 416 } catch (std::exception& e) {
412 - this->oh.warnIfPossible( 417 + oh().warnIfPossible(
413 std::string("Unable to filter content stream: ") + e.what() + 418 std::string("Unable to filter content stream: ") + e.what() +
414 - "; not attempting to externalize inline images"  
415 - " from this stream"); 419 + "; not attempting to externalize inline images from this stream");
416 } 420 }
417 if (filtered && iit.any_images) { 421 if (filtered && iit.any_images) {
418 - if (this->oh.isFormXObject()) {  
419 - this->oh.replaceStreamData( 422 + if (oh().isFormXObject()) {
  423 + oh().replaceStreamData(
420 b.getBufferSharedPointer(), 424 b.getBufferSharedPointer(),
421 QPDFObjectHandle::newNull(), 425 QPDFObjectHandle::newNull(),
422 QPDFObjectHandle::newNull()); 426 QPDFObjectHandle::newNull());
423 } else { 427 } else {
424 - this->oh.replaceKey( 428 + oh().replaceKey(
425 "/Contents", 429 "/Contents",
426 - QPDFObjectHandle::newStream(&this->oh.getQPDF(), b.getBufferSharedPointer())); 430 + QPDFObjectHandle::newStream(&oh().getQPDF(), b.getBufferSharedPointer()));
427 } 431 }
428 } 432 }
429 } else { 433 } else {
@@ -439,7 +443,7 @@ std::vector&lt;QPDFAnnotationObjectHelper&gt; @@ -439,7 +443,7 @@ std::vector&lt;QPDFAnnotationObjectHelper&gt;
439 QPDFPageObjectHelper::getAnnotations(std::string const& only_subtype) 443 QPDFPageObjectHelper::getAnnotations(std::string const& only_subtype)
440 { 444 {
441 std::vector<QPDFAnnotationObjectHelper> result; 445 std::vector<QPDFAnnotationObjectHelper> result;
442 - QPDFObjectHandle annots = this->oh.getKey("/Annots"); 446 + QPDFObjectHandle annots = oh().getKey("/Annots");
443 if (annots.isArray()) { 447 if (annots.isArray()) {
444 int nannots = annots.getArrayNItems(); 448 int nannots = annots.getArrayNItems();
445 for (int i = 0; i < nannots; ++i) { 449 for (int i = 0; i < nannots; ++i) {
@@ -455,25 +459,25 @@ QPDFPageObjectHelper::getAnnotations(std::string const&amp; only_subtype) @@ -455,25 +459,25 @@ QPDFPageObjectHelper::getAnnotations(std::string const&amp; only_subtype)
455 std::vector<QPDFObjectHandle> 459 std::vector<QPDFObjectHandle>
456 QPDFPageObjectHelper::getPageContents() 460 QPDFPageObjectHelper::getPageContents()
457 { 461 {
458 - return this->oh.getPageContents(); 462 + return oh().getPageContents();
459 } 463 }
460 464
461 void 465 void
462 QPDFPageObjectHelper::addPageContents(QPDFObjectHandle contents, bool first) 466 QPDFPageObjectHelper::addPageContents(QPDFObjectHandle contents, bool first)
463 { 467 {
464 - this->oh.addPageContents(contents, first); 468 + oh().addPageContents(contents, first);
465 } 469 }
466 470
467 void 471 void
468 QPDFPageObjectHelper::rotatePage(int angle, bool relative) 472 QPDFPageObjectHelper::rotatePage(int angle, bool relative)
469 { 473 {
470 - this->oh.rotatePage(angle, relative); 474 + oh().rotatePage(angle, relative);
471 } 475 }
472 476
473 void 477 void
474 QPDFPageObjectHelper::coalesceContentStreams() 478 QPDFPageObjectHelper::coalesceContentStreams()
475 { 479 {
476 - this->oh.coalesceContentStreams(); 480 + oh().coalesceContentStreams();
477 } 481 }
478 482
479 void 483 void
@@ -485,10 +489,10 @@ QPDFPageObjectHelper::parsePageContents(QPDFObjectHandle::ParserCallbacks* callb @@ -485,10 +489,10 @@ QPDFPageObjectHelper::parsePageContents(QPDFObjectHandle::ParserCallbacks* callb
485 void 489 void
486 QPDFPageObjectHelper::parseContents(QPDFObjectHandle::ParserCallbacks* callbacks) 490 QPDFPageObjectHelper::parseContents(QPDFObjectHandle::ParserCallbacks* callbacks)
487 { 491 {
488 - if (this->oh.isFormXObject()) {  
489 - this->oh.parseAsContents(callbacks); 492 + if (oh().isFormXObject()) {
  493 + oh().parseAsContents(callbacks);
490 } else { 494 } else {
491 - this->oh.parsePageContents(callbacks); 495 + oh().parsePageContents(callbacks);
492 } 496 }
493 } 497 }
494 498
@@ -501,10 +505,10 @@ QPDFPageObjectHelper::filterPageContents(QPDFObjectHandle::TokenFilter* filter, @@ -501,10 +505,10 @@ QPDFPageObjectHelper::filterPageContents(QPDFObjectHandle::TokenFilter* filter,
501 void 505 void
502 QPDFPageObjectHelper::filterContents(QPDFObjectHandle::TokenFilter* filter, Pipeline* next) 506 QPDFPageObjectHelper::filterContents(QPDFObjectHandle::TokenFilter* filter, Pipeline* next)
503 { 507 {
504 - if (this->oh.isFormXObject()) {  
505 - this->oh.filterAsContents(filter, next); 508 + if (oh().isFormXObject()) {
  509 + oh().filterAsContents(filter, next);
506 } else { 510 } else {
507 - this->oh.filterPageContents(filter, next); 511 + oh().filterPageContents(filter, next);
508 } 512 }
509 } 513 }
510 514
@@ -517,10 +521,10 @@ QPDFPageObjectHelper::pipePageContents(Pipeline* p) @@ -517,10 +521,10 @@ QPDFPageObjectHelper::pipePageContents(Pipeline* p)
517 void 521 void
518 QPDFPageObjectHelper::pipeContents(Pipeline* p) 522 QPDFPageObjectHelper::pipeContents(Pipeline* p)
519 { 523 {
520 - if (this->oh.isFormXObject()) {  
521 - this->oh.pipeStreamData(p, 0, qpdf_dl_specialized); 524 + if (oh().isFormXObject()) {
  525 + oh().pipeStreamData(p, 0, qpdf_dl_specialized);
522 } else { 526 } else {
523 - this->oh.pipePageContents(p); 527 + oh().pipePageContents(p);
524 } 528 }
525 } 529 }
526 530
@@ -528,10 +532,10 @@ void @@ -528,10 +532,10 @@ void
528 QPDFPageObjectHelper::addContentTokenFilter( 532 QPDFPageObjectHelper::addContentTokenFilter(
529 std::shared_ptr<QPDFObjectHandle::TokenFilter> token_filter) 533 std::shared_ptr<QPDFObjectHandle::TokenFilter> token_filter)
530 { 534 {
531 - if (this->oh.isFormXObject()) {  
532 - this->oh.addTokenFilter(token_filter); 535 + if (oh().isFormXObject()) {
  536 + oh().addTokenFilter(token_filter);
533 } else { 537 } else {
534 - this->oh.addContentTokenFilter(token_filter); 538 + oh().addContentTokenFilter(token_filter);
535 } 539 }
536 } 540 }
537 541
@@ -539,30 +543,28 @@ bool @@ -539,30 +543,28 @@ bool
539 QPDFPageObjectHelper::removeUnreferencedResourcesHelper( 543 QPDFPageObjectHelper::removeUnreferencedResourcesHelper(
540 QPDFPageObjectHelper ph, std::set<std::string>& unresolved) 544 QPDFPageObjectHelper ph, std::set<std::string>& unresolved)
541 { 545 {
542 - bool is_page = (!ph.oh.isFormXObject()); 546 + bool is_page = (!ph.oh().isFormXObject());
543 if (!is_page) { 547 if (!is_page) {
544 QTC::TC("qpdf", "QPDFPageObjectHelper filter form xobject"); 548 QTC::TC("qpdf", "QPDFPageObjectHelper filter form xobject");
545 } 549 }
546 550
547 ResourceFinder rf; 551 ResourceFinder rf;
548 try { 552 try {
549 - auto q = ph.oh.getOwningQPDF(); 553 + auto q = ph.oh().getOwningQPDF();
550 size_t before_nw = (q ? q->numWarnings() : 0); 554 size_t before_nw = (q ? q->numWarnings() : 0);
551 ph.parseContents(&rf); 555 ph.parseContents(&rf);
552 size_t after_nw = (q ? q->numWarnings() : 0); 556 size_t after_nw = (q ? q->numWarnings() : 0);
553 if (after_nw > before_nw) { 557 if (after_nw > before_nw) {
554 - ph.oh.warnIfPossible( 558 + ph.oh().warnIfPossible(
555 "Bad token found while scanning content stream; " 559 "Bad token found while scanning content stream; "
556 - "not attempting to remove unreferenced objects from"  
557 - " this object"); 560 + "not attempting to remove unreferenced objects from this object");
558 return false; 561 return false;
559 } 562 }
560 } catch (std::exception& e) { 563 } catch (std::exception& e) {
561 QTC::TC("qpdf", "QPDFPageObjectHelper bad token finding names"); 564 QTC::TC("qpdf", "QPDFPageObjectHelper bad token finding names");
562 - ph.oh.warnIfPossible( 565 + ph.oh().warnIfPossible(
563 std::string("Unable to parse content stream: ") + e.what() + 566 std::string("Unable to parse content stream: ") + e.what() +
564 - "; not attempting to remove unreferenced objects"  
565 - " from this object"); 567 + "; not attempting to remove unreferenced objects from this object");
566 return false; 568 return false;
567 } 569 }
568 570
@@ -646,7 +648,7 @@ QPDFPageObjectHelper::removeUnreferencedResources() @@ -646,7 +648,7 @@ QPDFPageObjectHelper::removeUnreferencedResources()
646 any_failures = true; 648 any_failures = true;
647 } 649 }
648 }); 650 });
649 - if (this->oh.isFormXObject() || (!any_failures)) { 651 + if (oh().isFormXObject() || (!any_failures)) {
650 removeUnreferencedResourcesHelper(*this, unresolved); 652 removeUnreferencedResourcesHelper(*this, unresolved);
651 } 653 }
652 } 654 }
@@ -654,9 +656,8 @@ QPDFPageObjectHelper::removeUnreferencedResources() @@ -654,9 +656,8 @@ QPDFPageObjectHelper::removeUnreferencedResources()
654 QPDFPageObjectHelper 656 QPDFPageObjectHelper
655 QPDFPageObjectHelper::shallowCopyPage() 657 QPDFPageObjectHelper::shallowCopyPage()
656 { 658 {
657 - QPDF& qpdf =  
658 - this->oh.getQPDF("QPDFPageObjectHelper::shallowCopyPage called with a direct object");  
659 - QPDFObjectHandle new_page = this->oh.shallowCopy(); 659 + QPDF& qpdf = oh().getQPDF("QPDFPageObjectHelper::shallowCopyPage called with a direct object");
  660 + QPDFObjectHandle new_page = oh().shallowCopy();
660 return {qpdf.makeIndirectObject(new_page)}; 661 return {qpdf.makeIndirectObject(new_page)};
661 } 662 }
662 663
@@ -707,7 +708,7 @@ QPDFObjectHandle @@ -707,7 +708,7 @@ QPDFObjectHandle
707 QPDFPageObjectHelper::getFormXObjectForPage(bool handle_transformations) 708 QPDFPageObjectHelper::getFormXObjectForPage(bool handle_transformations)
708 { 709 {
709 auto result = 710 auto result =
710 - this->oh.getQPDF("QPDFPageObjectHelper::getFormXObjectForPage called with a direct object") 711 + oh().getQPDF("QPDFPageObjectHelper::getFormXObjectForPage called with a direct object")
711 .newStream(); 712 .newStream();
712 QPDFObjectHandle newdict = result.getDict(); 713 QPDFObjectHandle newdict = result.getDict();
713 newdict.replaceKey("/Type", QPDFObjectHandle::newName("/XObject")); 714 newdict.replaceKey("/Type", QPDFObjectHandle::newName("/XObject"));
@@ -716,13 +717,13 @@ QPDFPageObjectHelper::getFormXObjectForPage(bool handle_transformations) @@ -716,13 +717,13 @@ QPDFPageObjectHelper::getFormXObjectForPage(bool handle_transformations)
716 newdict.replaceKey("/Group", getAttribute("/Group", false).shallowCopy()); 717 newdict.replaceKey("/Group", getAttribute("/Group", false).shallowCopy());
717 QPDFObjectHandle bbox = getTrimBox(false).shallowCopy(); 718 QPDFObjectHandle bbox = getTrimBox(false).shallowCopy();
718 if (!bbox.isRectangle()) { 719 if (!bbox.isRectangle()) {
719 - this->oh.warnIfPossible( 720 + oh().warnIfPossible(
720 "bounding box is invalid; form" 721 "bounding box is invalid; form"
721 " XObject created from page will not work"); 722 " XObject created from page will not work");
722 } 723 }
723 newdict.replaceKey("/BBox", bbox); 724 newdict.replaceKey("/BBox", bbox);
724 auto provider = 725 auto provider =
725 - std::shared_ptr<QPDFObjectHandle::StreamDataProvider>(new ContentProvider(this->oh)); 726 + std::shared_ptr<QPDFObjectHandle::StreamDataProvider>(new ContentProvider(oh()));
726 result.replaceStreamData(provider, QPDFObjectHandle::newNull(), QPDFObjectHandle::newNull()); 727 result.replaceStreamData(provider, QPDFObjectHandle::newNull(), QPDFObjectHandle::newNull());
727 QPDFObjectHandle rotate_obj = getAttribute("/Rotate", false); 728 QPDFObjectHandle rotate_obj = getAttribute("/Rotate", false);
728 QPDFObjectHandle scale_obj = getAttribute("/UserUnit", false); 729 QPDFObjectHandle scale_obj = getAttribute("/UserUnit", false);
@@ -863,9 +864,8 @@ QPDFPageObjectHelper::placeFormXObject( @@ -863,9 +864,8 @@ QPDFPageObjectHelper::placeFormXObject(
863 void 864 void
864 QPDFPageObjectHelper::flattenRotation(QPDFAcroFormDocumentHelper* afdh) 865 QPDFPageObjectHelper::flattenRotation(QPDFAcroFormDocumentHelper* afdh)
865 { 866 {
866 - QPDF& qpdf =  
867 - this->oh.getQPDF("QPDFPageObjectHelper::flattenRotation called with a direct object");  
868 - auto rotate_oh = this->oh.getKey("/Rotate"); 867 + QPDF& qpdf = oh().getQPDF("QPDFPageObjectHelper::flattenRotation called with a direct object");
  868 + auto rotate_oh = oh().getKey("/Rotate");
869 int rotate = 0; 869 int rotate = 0;
870 if (rotate_oh.isInteger()) { 870 if (rotate_oh.isInteger()) {
871 rotate = rotate_oh.getIntValueAsInt(); 871 rotate = rotate_oh.getIntValueAsInt();
@@ -873,7 +873,7 @@ QPDFPageObjectHelper::flattenRotation(QPDFAcroFormDocumentHelper* afdh) @@ -873,7 +873,7 @@ QPDFPageObjectHelper::flattenRotation(QPDFAcroFormDocumentHelper* afdh)
873 if (!((rotate == 90) || (rotate == 180) || (rotate == 270))) { 873 if (!((rotate == 90) || (rotate == 180) || (rotate == 270))) {
874 return; 874 return;
875 } 875 }
876 - auto mediabox = this->oh.getKey("/MediaBox"); 876 + auto mediabox = oh().getKey("/MediaBox");
877 if (!mediabox.isRectangle()) { 877 if (!mediabox.isRectangle()) {
878 return; 878 return;
879 } 879 }
@@ -887,7 +887,7 @@ QPDFPageObjectHelper::flattenRotation(QPDFAcroFormDocumentHelper* afdh) @@ -887,7 +887,7 @@ QPDFPageObjectHelper::flattenRotation(QPDFAcroFormDocumentHelper* afdh)
887 "/ArtBox", 887 "/ArtBox",
888 }; 888 };
889 for (auto const& boxkey: boxes) { 889 for (auto const& boxkey: boxes) {
890 - auto box = this->oh.getKey(boxkey); 890 + auto box = oh().getKey(boxkey);
891 if (!box.isRectangle()) { 891 if (!box.isRectangle()) {
892 continue; 892 continue;
893 } 893 }
@@ -930,7 +930,7 @@ QPDFPageObjectHelper::flattenRotation(QPDFAcroFormDocumentHelper* afdh) @@ -930,7 +930,7 @@ QPDFPageObjectHelper::flattenRotation(QPDFAcroFormDocumentHelper* afdh)
930 break; 930 break;
931 } 931 }
932 932
933 - this->oh.replaceKey(boxkey, QPDFObjectHandle::newFromRectangle(new_rect)); 933 + oh().replaceKey(boxkey, QPDFObjectHandle::newFromRectangle(new_rect));
934 } 934 }
935 935
936 // When we rotate the page, pivot about the point 0, 0 and then translate so the page is visible 936 // When we rotate the page, pivot about the point 0, 0 and then translate so the page is visible
@@ -962,16 +962,16 @@ QPDFPageObjectHelper::flattenRotation(QPDFAcroFormDocumentHelper* afdh) @@ -962,16 +962,16 @@ QPDFPageObjectHelper::flattenRotation(QPDFAcroFormDocumentHelper* afdh)
962 break; 962 break;
963 } 963 }
964 std::string cm_str = std::string("q\n") + cm.unparse() + " cm\n"; 964 std::string cm_str = std::string("q\n") + cm.unparse() + " cm\n";
965 - this->oh.addPageContents(QPDFObjectHandle::newStream(&qpdf, cm_str), true);  
966 - this->oh.addPageContents(qpdf.newStream("\nQ\n"), false);  
967 - this->oh.removeKey("/Rotate"); 965 + oh().addPageContents(QPDFObjectHandle::newStream(&qpdf, cm_str), true);
  966 + oh().addPageContents(qpdf.newStream("\nQ\n"), false);
  967 + oh().removeKey("/Rotate");
968 QPDFObjectHandle rotate_obj = getAttribute("/Rotate", false); 968 QPDFObjectHandle rotate_obj = getAttribute("/Rotate", false);
969 if (!rotate_obj.isNull()) { 969 if (!rotate_obj.isNull()) {
970 QTC::TC("qpdf", "QPDFPageObjectHelper flatten inherit rotate"); 970 QTC::TC("qpdf", "QPDFPageObjectHelper flatten inherit rotate");
971 - this->oh.replaceKey("/Rotate", QPDFObjectHandle::newInteger(0)); 971 + oh().replaceKey("/Rotate", QPDFObjectHandle::newInteger(0));
972 } 972 }
973 973
974 - QPDFObjectHandle annots = this->oh.getKey("/Annots"); 974 + QPDFObjectHandle annots = oh().getKey("/Annots");
975 if (annots.isArray()) { 975 if (annots.isArray()) {
976 std::vector<QPDFObjectHandle> new_annots; 976 std::vector<QPDFObjectHandle> new_annots;
977 std::vector<QPDFObjectHandle> new_fields; 977 std::vector<QPDFObjectHandle> new_fields;
@@ -986,7 +986,7 @@ QPDFPageObjectHelper::flattenRotation(QPDFAcroFormDocumentHelper* afdh) @@ -986,7 +986,7 @@ QPDFPageObjectHelper::flattenRotation(QPDFAcroFormDocumentHelper* afdh)
986 for (auto const& f: new_fields) { 986 for (auto const& f: new_fields) {
987 afdh->addFormField(QPDFFormFieldObjectHelper(f)); 987 afdh->addFormField(QPDFFormFieldObjectHelper(f));
988 } 988 }
989 - this->oh.replaceKey("/Annots", QPDFObjectHandle::newArray(new_annots)); 989 + oh().replaceKey("/Annots", QPDFObjectHandle::newArray(new_annots));
990 } 990 }
991 } 991 }
992 992
@@ -1005,7 +1005,7 @@ QPDFPageObjectHelper::copyAnnotations( @@ -1005,7 +1005,7 @@ QPDFPageObjectHelper::copyAnnotations(
1005 QPDF& from_qpdf = from_page.getObjectHandle().getQPDF( 1005 QPDF& from_qpdf = from_page.getObjectHandle().getQPDF(
1006 "QPDFPageObjectHelper::copyAnnotations: from page is a direct object"); 1006 "QPDFPageObjectHelper::copyAnnotations: from page is a direct object");
1007 QPDF& this_qpdf = 1007 QPDF& this_qpdf =
1008 - this->oh.getQPDF("QPDFPageObjectHelper::copyAnnotations: this page is a direct object"); 1008 + oh().getQPDF("QPDFPageObjectHelper::copyAnnotations: this page is a direct object");
1009 1009
1010 std::vector<QPDFObjectHandle> new_annots; 1010 std::vector<QPDFObjectHandle> new_annots;
1011 std::vector<QPDFObjectHandle> new_fields; 1011 std::vector<QPDFObjectHandle> new_fields;
@@ -1032,9 +1032,9 @@ QPDFPageObjectHelper::copyAnnotations( @@ -1032,9 +1032,9 @@ QPDFPageObjectHelper::copyAnnotations(
1032 afdh->transformAnnotations( 1032 afdh->transformAnnotations(
1033 old_annots, new_annots, new_fields, old_fields, cm, &from_qpdf, from_afdh); 1033 old_annots, new_annots, new_fields, old_fields, cm, &from_qpdf, from_afdh);
1034 afdh->addAndRenameFormFields(new_fields); 1034 afdh->addAndRenameFormFields(new_fields);
1035 - auto annots = this->oh.getKey("/Annots"); 1035 + auto annots = oh().getKey("/Annots");
1036 if (!annots.isArray()) { 1036 if (!annots.isArray()) {
1037 - annots = this->oh.replaceKeyAndGetNew("/Annots", QPDFObjectHandle::newArray()); 1037 + annots = oh().replaceKeyAndGetNew("/Annots", QPDFObjectHandle::newArray());
1038 } 1038 }
1039 for (auto const& annot: new_annots) { 1039 for (auto const& annot: new_annots) {
1040 annots.appendItem(annot); 1040 annots.appendItem(annot);
libqpdf/QPDFParser.cc
@@ -4,18 +4,6 @@ @@ -4,18 +4,6 @@
4 #include <qpdf/QPDFObjGen.hh> 4 #include <qpdf/QPDFObjGen.hh>
5 #include <qpdf/QPDFObjectHandle.hh> 5 #include <qpdf/QPDFObjectHandle.hh>
6 #include <qpdf/QPDFObject_private.hh> 6 #include <qpdf/QPDFObject_private.hh>
7 -#include <qpdf/QPDF_Array.hh>  
8 -#include <qpdf/QPDF_Bool.hh>  
9 -#include <qpdf/QPDF_Dictionary.hh>  
10 -#include <qpdf/QPDF_InlineImage.hh>  
11 -#include <qpdf/QPDF_Integer.hh>  
12 -#include <qpdf/QPDF_Name.hh>  
13 -#include <qpdf/QPDF_Null.hh>  
14 -#include <qpdf/QPDF_Operator.hh>  
15 -#include <qpdf/QPDF_Real.hh>  
16 -#include <qpdf/QPDF_Reserved.hh>  
17 -#include <qpdf/QPDF_Stream.hh>  
18 -#include <qpdf/QPDF_String.hh>  
19 #include <qpdf/QTC.hh> 7 #include <qpdf/QTC.hh>
20 #include <qpdf/QUtil.hh> 8 #include <qpdf/QUtil.hh>
21 9
@@ -47,27 +35,27 @@ QPDFParser::parse(bool&amp; empty, bool content_stream) @@ -47,27 +35,27 @@ QPDFParser::parse(bool&amp; empty, bool content_stream)
47 } 35 }
48 QTC::TC("qpdf", "QPDFParser eof in parse"); 36 QTC::TC("qpdf", "QPDFParser eof in parse");
49 warn("unexpected EOF"); 37 warn("unexpected EOF");
50 - return {QPDF_Null::create()}; 38 + return {QPDFObject::create<QPDF_Null>()};
51 39
52 case QPDFTokenizer::tt_bad: 40 case QPDFTokenizer::tt_bad:
53 QTC::TC("qpdf", "QPDFParser bad token in parse"); 41 QTC::TC("qpdf", "QPDFParser bad token in parse");
54 - return {QPDF_Null::create()}; 42 + return {QPDFObject::create<QPDF_Null>()};
55 43
56 case QPDFTokenizer::tt_brace_open: 44 case QPDFTokenizer::tt_brace_open:
57 case QPDFTokenizer::tt_brace_close: 45 case QPDFTokenizer::tt_brace_close:
58 QTC::TC("qpdf", "QPDFParser bad brace"); 46 QTC::TC("qpdf", "QPDFParser bad brace");
59 warn("treating unexpected brace token as null"); 47 warn("treating unexpected brace token as null");
60 - return {QPDF_Null::create()}; 48 + return {QPDFObject::create<QPDF_Null>()};
61 49
62 case QPDFTokenizer::tt_array_close: 50 case QPDFTokenizer::tt_array_close:
63 QTC::TC("qpdf", "QPDFParser bad array close"); 51 QTC::TC("qpdf", "QPDFParser bad array close");
64 warn("treating unexpected array close token as null"); 52 warn("treating unexpected array close token as null");
65 - return {QPDF_Null::create()}; 53 + return {QPDFObject::create<QPDF_Null>()};
66 54
67 case QPDFTokenizer::tt_dict_close: 55 case QPDFTokenizer::tt_dict_close:
68 QTC::TC("qpdf", "QPDFParser bad dictionary close"); 56 QTC::TC("qpdf", "QPDFParser bad dictionary close");
69 warn("unexpected dictionary close token"); 57 warn("unexpected dictionary close token");
70 - return {QPDF_Null::create()}; 58 + return {QPDFObject::create<QPDF_Null>()};
71 59
72 case QPDFTokenizer::tt_array_open: 60 case QPDFTokenizer::tt_array_open:
73 case QPDFTokenizer::tt_dict_open: 61 case QPDFTokenizer::tt_dict_open:
@@ -82,7 +70,7 @@ QPDFParser::parse(bool&amp; empty, bool content_stream) @@ -82,7 +70,7 @@ QPDFParser::parse(bool&amp; empty, bool content_stream)
82 return withDescription<QPDF_Bool>(tokenizer.getValue() == "true"); 70 return withDescription<QPDF_Bool>(tokenizer.getValue() == "true");
83 71
84 case QPDFTokenizer::tt_null: 72 case QPDFTokenizer::tt_null:
85 - return {QPDF_Null::create()}; 73 + return {QPDFObject::create<QPDF_Null>()};
86 74
87 case QPDFTokenizer::tt_integer: 75 case QPDFTokenizer::tt_integer:
88 return withDescription<QPDF_Integer>(QUtil::string_to_ll(tokenizer.getValue().c_str())); 76 return withDescription<QPDF_Integer>(QUtil::string_to_ll(tokenizer.getValue().c_str()));
@@ -103,7 +91,7 @@ QPDFParser::parse(bool&amp; empty, bool content_stream) @@ -103,7 +91,7 @@ QPDFParser::parse(bool&amp; empty, bool content_stream)
103 // not move the input source's offset. 91 // not move the input source's offset.
104 input.seek(input.getLastOffset(), SEEK_SET); 92 input.seek(input.getLastOffset(), SEEK_SET);
105 empty = true; 93 empty = true;
106 - return {QPDF_Null::create()}; 94 + return {QPDFObject::create<QPDF_Null>()};
107 } else { 95 } else {
108 QTC::TC("qpdf", "QPDFParser treat word as string"); 96 QTC::TC("qpdf", "QPDFParser treat word as string");
109 warn("unknown token while reading object; treating as string"); 97 warn("unknown token while reading object; treating as string");
@@ -122,7 +110,7 @@ QPDFParser::parse(bool&amp; empty, bool content_stream) @@ -122,7 +110,7 @@ QPDFParser::parse(bool&amp; empty, bool content_stream)
122 110
123 default: 111 default:
124 warn("treating unknown token type as null while reading object"); 112 warn("treating unknown token type as null while reading object");
125 - return {QPDF_Null::create()}; 113 + return {QPDFObject::create<QPDF_Null>()};
126 } 114 }
127 } 115 }
128 116
@@ -194,12 +182,12 @@ QPDFParser::parseRemainder(bool content_stream) @@ -194,12 +182,12 @@ QPDFParser::parseRemainder(bool content_stream)
194 } 182 }
195 QTC::TC("qpdf", "QPDFParser eof in parseRemainder"); 183 QTC::TC("qpdf", "QPDFParser eof in parseRemainder");
196 warn("unexpected EOF"); 184 warn("unexpected EOF");
197 - return {QPDF_Null::create()}; 185 + return {QPDFObject::create<QPDF_Null>()};
198 186
199 case QPDFTokenizer::tt_bad: 187 case QPDFTokenizer::tt_bad:
200 QTC::TC("qpdf", "QPDFParser bad token in parseRemainder"); 188 QTC::TC("qpdf", "QPDFParser bad token in parseRemainder");
201 if (tooManyBadTokens()) { 189 if (tooManyBadTokens()) {
202 - return {QPDF_Null::create()}; 190 + return {QPDFObject::create<QPDF_Null>()};
203 } 191 }
204 addNull(); 192 addNull();
205 continue; 193 continue;
@@ -209,7 +197,7 @@ QPDFParser::parseRemainder(bool content_stream) @@ -209,7 +197,7 @@ QPDFParser::parseRemainder(bool content_stream)
209 QTC::TC("qpdf", "QPDFParser bad brace in parseRemainder"); 197 QTC::TC("qpdf", "QPDFParser bad brace in parseRemainder");
210 warn("treating unexpected brace token as null"); 198 warn("treating unexpected brace token as null");
211 if (tooManyBadTokens()) { 199 if (tooManyBadTokens()) {
212 - return {QPDF_Null::create()}; 200 + return {QPDFObject::create<QPDF_Null>()};
213 } 201 }
214 addNull(); 202 addNull();
215 continue; 203 continue;
@@ -218,10 +206,12 @@ QPDFParser::parseRemainder(bool content_stream) @@ -218,10 +206,12 @@ QPDFParser::parseRemainder(bool content_stream)
218 if (bad_count && !max_bad_count) { 206 if (bad_count && !max_bad_count) {
219 // Trigger warning. 207 // Trigger warning.
220 (void)tooManyBadTokens(); 208 (void)tooManyBadTokens();
221 - return {QPDF_Null::create()}; 209 + return {QPDFObject::create<QPDF_Null>()};
222 } 210 }
223 if (frame->state == st_array) { 211 if (frame->state == st_array) {
224 - auto object = QPDF_Array::create(std::move(frame->olist), frame->null_count > 100); 212 + auto object = frame->null_count > 100
  213 + ? QPDFObject::create<QPDF_Array>(std::move(frame->olist), true)
  214 + : QPDFObject::create<QPDF_Array>(std::move(frame->olist));
225 setDescription(object, frame->offset - 1); 215 setDescription(object, frame->offset - 1);
226 // The `offset` points to the next of "[". Set the rewind offset to point to the 216 // The `offset` points to the next of "[". Set the rewind offset to point to the
227 // beginning of "[". This has been explicitly tested with whitespace surrounding the 217 // beginning of "[". This has been explicitly tested with whitespace surrounding the
@@ -237,7 +227,7 @@ QPDFParser::parseRemainder(bool content_stream) @@ -237,7 +227,7 @@ QPDFParser::parseRemainder(bool content_stream)
237 QTC::TC("qpdf", "QPDFParser bad array close in parseRemainder"); 227 QTC::TC("qpdf", "QPDFParser bad array close in parseRemainder");
238 warn("treating unexpected array close token as null"); 228 warn("treating unexpected array close token as null");
239 if (tooManyBadTokens()) { 229 if (tooManyBadTokens()) {
240 - return {QPDF_Null::create()}; 230 + return {QPDFObject::create<QPDF_Null>()};
241 } 231 }
242 addNull(); 232 addNull();
243 } 233 }
@@ -247,7 +237,7 @@ QPDFParser::parseRemainder(bool content_stream) @@ -247,7 +237,7 @@ QPDFParser::parseRemainder(bool content_stream)
247 if (bad_count && !max_bad_count) { 237 if (bad_count && !max_bad_count) {
248 // Trigger warning. 238 // Trigger warning.
249 (void)tooManyBadTokens(); 239 (void)tooManyBadTokens();
250 - return {QPDF_Null::create()}; 240 + return {QPDFObject::create<QPDF_Null>()};
251 } 241 }
252 if (frame->state <= st_dictionary_value) { 242 if (frame->state <= st_dictionary_value) {
253 // Attempt to recover more or less gracefully from invalid dictionaries. 243 // Attempt to recover more or less gracefully from invalid dictionaries.
@@ -258,7 +248,7 @@ QPDFParser::parseRemainder(bool content_stream) @@ -258,7 +248,7 @@ QPDFParser::parseRemainder(bool content_stream)
258 warn( 248 warn(
259 frame->offset, 249 frame->offset,
260 "dictionary ended prematurely; using null as value for last key"); 250 "dictionary ended prematurely; using null as value for last key");
261 - dict[frame->key] = QPDF_Null::create(); 251 + dict[frame->key] = QPDFObject::create<QPDF_Null>();
262 } 252 }
263 253
264 if (!frame->olist.empty()) { 254 if (!frame->olist.empty()) {
@@ -271,7 +261,7 @@ QPDFParser::parseRemainder(bool content_stream) @@ -271,7 +261,7 @@ QPDFParser::parseRemainder(bool content_stream)
271 dict["/Contents"] = QPDFObjectHandle::newString(frame->contents_string); 261 dict["/Contents"] = QPDFObjectHandle::newString(frame->contents_string);
272 dict["/Contents"].setParsedOffset(frame->contents_offset); 262 dict["/Contents"].setParsedOffset(frame->contents_offset);
273 } 263 }
274 - auto object = QPDF_Dictionary::create(std::move(dict)); 264 + auto object = QPDFObject::create<QPDF_Dictionary>(std::move(dict));
275 setDescription(object, frame->offset - 2); 265 setDescription(object, frame->offset - 2);
276 // The `offset` points to the next of "<<". Set the rewind offset to point to the 266 // The `offset` points to the next of "<<". Set the rewind offset to point to the
277 // beginning of "<<". This has been explicitly tested with whitespace surrounding 267 // beginning of "<<". This has been explicitly tested with whitespace surrounding
@@ -287,7 +277,7 @@ QPDFParser::parseRemainder(bool content_stream) @@ -287,7 +277,7 @@ QPDFParser::parseRemainder(bool content_stream)
287 QTC::TC("qpdf", "QPDFParser bad dictionary close in parseRemainder"); 277 QTC::TC("qpdf", "QPDFParser bad dictionary close in parseRemainder");
288 warn("unexpected dictionary close token"); 278 warn("unexpected dictionary close token");
289 if (tooManyBadTokens()) { 279 if (tooManyBadTokens()) {
290 - return {QPDF_Null::create()}; 280 + return {QPDFObject::create<QPDF_Null>()};
291 } 281 }
292 addNull(); 282 addNull();
293 } 283 }
@@ -298,7 +288,7 @@ QPDFParser::parseRemainder(bool content_stream) @@ -298,7 +288,7 @@ QPDFParser::parseRemainder(bool content_stream)
298 if (stack.size() > 499) { 288 if (stack.size() > 499) {
299 QTC::TC("qpdf", "QPDFParser too deep"); 289 QTC::TC("qpdf", "QPDFParser too deep");
300 warn("ignoring excessively deeply nested data structure"); 290 warn("ignoring excessively deeply nested data structure");
301 - return {QPDF_Null::create()}; 291 + return {QPDFObject::create<QPDF_Null>()};
302 } else { 292 } else {
303 b_contents = false; 293 b_contents = false;
304 stack.emplace_back( 294 stack.emplace_back(
@@ -350,7 +340,7 @@ QPDFParser::parseRemainder(bool content_stream) @@ -350,7 +340,7 @@ QPDFParser::parseRemainder(bool content_stream)
350 QTC::TC("qpdf", "QPDFParser treat word as string in parseRemainder"); 340 QTC::TC("qpdf", "QPDFParser treat word as string in parseRemainder");
351 warn("unknown token while reading object; treating as string"); 341 warn("unknown token while reading object; treating as string");
352 if (tooManyBadTokens()) { 342 if (tooManyBadTokens()) {
353 - return {QPDF_Null::create()}; 343 + return {QPDFObject::create<QPDF_Null>()};
354 } 344 }
355 addScalar<QPDF_String>(tokenizer.getValue()); 345 addScalar<QPDF_String>(tokenizer.getValue());
356 } 346 }
@@ -377,7 +367,7 @@ QPDFParser::parseRemainder(bool content_stream) @@ -377,7 +367,7 @@ QPDFParser::parseRemainder(bool content_stream)
377 default: 367 default:
378 warn("treating unknown token type as null while reading object"); 368 warn("treating unknown token type as null while reading object");
379 if (tooManyBadTokens()) { 369 if (tooManyBadTokens()) {
380 - return {QPDF_Null::create()}; 370 + return {QPDFObject::create<QPDF_Null>()};
381 } 371 }
382 addNull(); 372 addNull();
383 } 373 }
@@ -402,7 +392,7 @@ QPDFParser::add(std::shared_ptr&lt;QPDFObject&gt;&amp;&amp; obj) @@ -402,7 +392,7 @@ QPDFParser::add(std::shared_ptr&lt;QPDFObject&gt;&amp;&amp; obj)
402 void 392 void
403 QPDFParser::addNull() 393 QPDFParser::addNull()
404 { 394 {
405 - const static ObjectPtr null_obj = QPDF_Null::create(); 395 + const static ObjectPtr null_obj = QPDFObject::create<QPDF_Null>();
406 396
407 if (frame->state != st_dictionary_value) { 397 if (frame->state != st_dictionary_value) {
408 // If state is st_dictionary_key then there is a missing key. Push onto olist for 398 // If state is st_dictionary_key then there is a missing key. Push onto olist for
@@ -420,7 +410,7 @@ QPDFParser::addNull() @@ -420,7 +410,7 @@ QPDFParser::addNull()
420 void 410 void
421 QPDFParser::addInt(int count) 411 QPDFParser::addInt(int count)
422 { 412 {
423 - auto obj = QPDF_Integer::create(int_buffer[count % 2]); 413 + auto obj = QPDFObject::create<QPDF_Integer>(int_buffer[count % 2]);
424 obj->setDescription(context, description, last_offset_buffer[count % 2]); 414 obj->setDescription(context, description, last_offset_buffer[count % 2]);
425 add(std::move(obj)); 415 add(std::move(obj));
426 } 416 }
@@ -435,7 +425,7 @@ QPDFParser::addScalar(Args&amp;&amp;... args) @@ -435,7 +425,7 @@ QPDFParser::addScalar(Args&amp;&amp;... args)
435 max_bad_count = 0; 425 max_bad_count = 0;
436 return; 426 return;
437 } 427 }
438 - auto obj = T::create(args...); 428 + auto obj = QPDFObject::create<T>(std::forward<Args>(args)...);
439 obj->setDescription(context, description, input.getLastOffset()); 429 obj->setDescription(context, description, input.getLastOffset());
440 add(std::move(obj)); 430 add(std::move(obj));
441 } 431 }
@@ -444,7 +434,7 @@ template &lt;typename T, typename... Args&gt; @@ -444,7 +434,7 @@ template &lt;typename T, typename... Args&gt;
444 QPDFObjectHandle 434 QPDFObjectHandle
445 QPDFParser::withDescription(Args&&... args) 435 QPDFParser::withDescription(Args&&... args)
446 { 436 {
447 - auto obj = T::create(args...); 437 + auto obj = QPDFObject::create<T>(std::forward<Args>(args)...);
448 obj->setDescription(context, description, start); 438 obj->setDescription(context, description, start);
449 return {obj}; 439 return {obj};
450 } 440 }
@@ -462,8 +452,8 @@ QPDFParser::fixMissingKeys() @@ -462,8 +452,8 @@ QPDFParser::fixMissingKeys()
462 { 452 {
463 std::set<std::string> names; 453 std::set<std::string> names;
464 for (auto& obj: frame->olist) { 454 for (auto& obj: frame->olist) {
465 - if (obj->getTypeCode() == ::ot_name) {  
466 - names.insert(obj->getStringValue()); 455 + if (obj.getObj()->getTypeCode() == ::ot_name) {
  456 + names.insert(obj.getObj()->getStringValue());
467 } 457 }
468 } 458 }
469 int next_fake_key = 1; 459 int next_fake_key = 1;
libqpdf/QPDFValue.cc deleted
1 -#include <qpdf/QPDFValue.hh>  
2 -  
3 -#include <qpdf/QPDFObject_private.hh>  
4 -  
5 -std::shared_ptr<QPDFObject>  
6 -QPDFValue::do_create(QPDFValue* object)  
7 -{  
8 - std::shared_ptr<QPDFObject> obj(new QPDFObject());  
9 - obj->value = std::shared_ptr<QPDFValue>(object);  
10 - return obj;  
11 -}  
12 -  
13 -std::string  
14 -QPDFValue::getDescription()  
15 -{  
16 - if (object_description) {  
17 - switch (object_description->index()) {  
18 - case 0:  
19 - {  
20 - // Simple template string  
21 - auto description = std::get<0>(*object_description);  
22 -  
23 - if (auto pos = description.find("$OG"); pos != std::string::npos) {  
24 - description.replace(pos, 3, og.unparse(' '));  
25 - }  
26 - if (auto pos = description.find("$PO"); pos != std::string::npos) {  
27 - qpdf_offset_t shift = (type_code == ::ot_dictionary) ? 2  
28 - : (type_code == ::ot_array) ? 1  
29 - : 0;  
30 -  
31 - description.replace(pos, 3, std::to_string(parsed_offset + shift));  
32 - }  
33 - return description;  
34 - }  
35 - case 1:  
36 - {  
37 - // QPDF::JSONReactor generated description  
38 - auto j_descr = std::get<1>(*object_description);  
39 - return (  
40 - *j_descr.input + (j_descr.object.empty() ? "" : ", " + j_descr.object) +  
41 - " at offset " + std::to_string(parsed_offset));  
42 - }  
43 - case 2:  
44 - {  
45 - // Child object description  
46 - auto j_descr = std::get<2>(*object_description);  
47 - std::string result;  
48 - if (auto p = j_descr.parent.lock()) {  
49 - result = p->getDescription();  
50 - }  
51 - result += j_descr.static_descr;  
52 - if (auto pos = result.find("$VD"); pos != std::string::npos) {  
53 - result.replace(pos, 3, j_descr.var_descr);  
54 - }  
55 - return result;  
56 - }  
57 - }  
58 - } else if (og.isIndirect()) {  
59 - return "object " + og.unparse(' ');  
60 - }  
61 - return {};  
62 -}  
libqpdf/QPDFWriter.cc
@@ -15,9 +15,8 @@ @@ -15,9 +15,8 @@
15 #include <qpdf/Pl_StdioFile.hh> 15 #include <qpdf/Pl_StdioFile.hh>
16 #include <qpdf/QIntC.hh> 16 #include <qpdf/QIntC.hh>
17 #include <qpdf/QPDF.hh> 17 #include <qpdf/QPDF.hh>
18 -#include <qpdf/QPDFObjectHandle.hh>  
19 -#include <qpdf/QPDF_Name.hh>  
20 -#include <qpdf/QPDF_String.hh> 18 +#include <qpdf/QPDFObjectHandle_private.hh>
  19 +#include <qpdf/QPDFObject_private.hh>
21 #include <qpdf/QTC.hh> 20 #include <qpdf/QTC.hh>
22 #include <qpdf/QUtil.hh> 21 #include <qpdf/QUtil.hh>
23 #include <qpdf/RC4.hh> 22 #include <qpdf/RC4.hh>
@@ -27,6 +26,7 @@ @@ -27,6 +26,7 @@
27 #include <stdexcept> 26 #include <stdexcept>
28 27
29 using namespace std::literals; 28 using namespace std::literals;
  29 +using namespace qpdf;
30 30
31 QPDFWriter::ProgressReporter::~ProgressReporter() // NOLINT (modernize-use-equals-default) 31 QPDFWriter::ProgressReporter::~ProgressReporter() // NOLINT (modernize-use-equals-default)
32 { 32 {
@@ -1129,12 +1129,12 @@ QPDFWriter::enqueueObject(QPDFObjectHandle object) @@ -1129,12 +1129,12 @@ QPDFWriter::enqueueObject(QPDFObjectHandle object)
1129 return; 1129 return;
1130 } else if (!m->linearized) { 1130 } else if (!m->linearized) {
1131 if (object.isArray()) { 1131 if (object.isArray()) {
1132 - for (auto& item: object.getArrayAsVector()) { 1132 + for (auto& item: object.as_array()) {
1133 enqueueObject(item); 1133 enqueueObject(item);
1134 } 1134 }
1135 - } else if (object.isDictionary()) {  
1136 - for (auto& item: object.getDictAsMap()) {  
1137 - if (!item.second.isNull()) { 1135 + } else if (auto d = object.as_dictionary()) {
  1136 + for (auto const& item: d) {
  1137 + if (!item.second.null()) {
1138 enqueueObject(item.second); 1138 enqueueObject(item.second);
1139 } 1139 }
1140 } 1140 }
@@ -1173,10 +1173,13 @@ QPDFWriter::writeTrailer( @@ -1173,10 +1173,13 @@ QPDFWriter::writeTrailer(
1173 writeString(" /Size "); 1173 writeString(" /Size ");
1174 writeString(std::to_string(size)); 1174 writeString(std::to_string(size));
1175 } else { 1175 } else {
1176 - for (auto const& key: trailer.getKeys()) { 1176 + for (auto const& [key, value]: trailer.as_dictionary()) {
  1177 + if (value.null()) {
  1178 + continue;
  1179 + }
1177 writeStringQDF(" "); 1180 writeStringQDF(" ");
1178 writeStringNoQDF(" "); 1181 writeStringNoQDF(" ");
1179 - writeString(QPDF_Name::normalizeName(key)); 1182 + writeString(Name::normalize(key));
1180 writeString(" "); 1183 writeString(" ");
1181 if (key == "/Size") { 1184 if (key == "/Size") {
1182 writeString(std::to_string(size)); 1185 writeString(std::to_string(size));
@@ -1187,7 +1190,7 @@ QPDFWriter::writeTrailer( @@ -1187,7 +1190,7 @@ QPDFWriter::writeTrailer(
1187 writePad(QIntC::to_size(pos - m->pipeline->getCount() + 21)); 1190 writePad(QIntC::to_size(pos - m->pipeline->getCount() + 21));
1188 } 1191 }
1189 } else { 1192 } else {
1190 - unparseChild(trailer.getKey(key), 1, 0); 1193 + unparseChild(value, 1, 0);
1191 } 1194 }
1192 writeStringQDF("\n"); 1195 writeStringQDF("\n");
1193 } 1196 }
@@ -1347,7 +1350,7 @@ QPDFWriter::unparseObject( @@ -1347,7 +1350,7 @@ QPDFWriter::unparseObject(
1347 // [ in the /H key of the linearization parameter dictionary. We'll do this unconditionally 1350 // [ in the /H key of the linearization parameter dictionary. We'll do this unconditionally
1348 // for all arrays because it looks nicer and doesn't make the files that much bigger. 1351 // for all arrays because it looks nicer and doesn't make the files that much bigger.
1349 writeString("["); 1352 writeString("[");
1350 - for (auto const& item: object.getArrayAsVector()) { 1353 + for (auto const& item: object.as_array()) {
1351 writeString(indent); 1354 writeString(indent);
1352 writeStringQDF(" "); 1355 writeStringQDF(" ");
1353 unparseChild(item, level + 1, child_flags); 1356 unparseChild(item, level + 1, child_flags);
@@ -1498,20 +1501,18 @@ QPDFWriter::unparseObject( @@ -1498,20 +1501,18 @@ QPDFWriter::unparseObject(
1498 1501
1499 writeString("<<"); 1502 writeString("<<");
1500 1503
1501 - for (auto& item: object.getDictAsMap()) {  
1502 - if (!item.second.isNull()) {  
1503 - auto const& key = item.first; 1504 + for (auto const& [key, value]: object.as_dictionary()) {
  1505 + if (!value.null()) {
1504 writeString(indent); 1506 writeString(indent);
1505 writeStringQDF(" "); 1507 writeStringQDF(" ");
1506 - writeString(QPDF_Name::normalizeName(key)); 1508 + writeString(Name::normalize(key));
1507 writeString(" "); 1509 writeString(" ");
1508 if (key == "/Contents" && object.isDictionaryOfType("/Sig") && 1510 if (key == "/Contents" && object.isDictionaryOfType("/Sig") &&
1509 object.hasKey("/ByteRange")) { 1511 object.hasKey("/ByteRange")) {
1510 QTC::TC("qpdf", "QPDFWriter no encryption sig contents"); 1512 QTC::TC("qpdf", "QPDFWriter no encryption sig contents");
1511 - unparseChild(  
1512 - item.second, level + 1, child_flags | f_hex_string | f_no_encryption); 1513 + unparseChild(value, level + 1, child_flags | f_hex_string | f_no_encryption);
1513 } else { 1514 } else {
1514 - unparseChild(item.second, level + 1, child_flags); 1515 + unparseChild(value, level + 1, child_flags);
1515 } 1516 }
1516 } 1517 }
1517 } 1518 }
@@ -1891,12 +1892,10 @@ QPDFWriter::generateID() @@ -1891,12 +1892,10 @@ QPDFWriter::generateID()
1891 } 1892 }
1892 seed += " QPDF "; 1893 seed += " QPDF ";
1893 if (trailer.hasKey("/Info")) { 1894 if (trailer.hasKey("/Info")) {
1894 - QPDFObjectHandle info = trailer.getKey("/Info");  
1895 - for (auto const& key: info.getKeys()) {  
1896 - QPDFObjectHandle obj = info.getKey(key);  
1897 - if (obj.isString()) { 1895 + for (auto const& item: trailer.getKey("/Info").as_dictionary()) {
  1896 + if (item.second.isString()) {
1898 seed += " "; 1897 seed += " ";
1899 - seed += obj.getStringValue(); 1898 + seed += item.second.getStringValue();
1900 } 1899 }
1901 } 1900 }
1902 } 1901 }
@@ -1922,8 +1921,7 @@ QPDFWriter::generateID() @@ -1922,8 +1921,7 @@ QPDFWriter::generateID()
1922 void 1921 void
1923 QPDFWriter::initializeSpecialStreams() 1922 QPDFWriter::initializeSpecialStreams()
1924 { 1923 {
1925 - // Mark all page content streams in case we are filtering or  
1926 - // normalizing. 1924 + // Mark all page content streams in case we are filtering or normalizing.
1927 std::vector<QPDFObjectHandle> pages = m->pdf.getAllPages(); 1925 std::vector<QPDFObjectHandle> pages = m->pdf.getAllPages();
1928 int num = 0; 1926 int num = 0;
1929 for (auto& page: pages) { 1927 for (auto& page: pages) {
@@ -2939,8 +2937,10 @@ QPDFWriter::enqueueObjectsStandard() @@ -2939,8 +2937,10 @@ QPDFWriter::enqueueObjectsStandard()
2939 2937
2940 // Next place any other objects referenced from the trailer dictionary into the queue, handling 2938 // Next place any other objects referenced from the trailer dictionary into the queue, handling
2941 // direct objects recursively. Root is already there, so enqueuing it a second time is a no-op. 2939 // direct objects recursively. Root is already there, so enqueuing it a second time is a no-op.
2942 - for (auto const& key: trailer.getKeys()) {  
2943 - enqueueObject(trailer.getKey(key)); 2940 + for (auto& item: trailer.as_dictionary()) {
  2941 + if (!item.second.null()) {
  2942 + enqueueObject(item.second);
  2943 + }
2944 } 2944 }
2945 } 2945 }
2946 2946
@@ -2962,9 +2962,11 @@ QPDFWriter::enqueueObjectsPCLm() @@ -2962,9 +2962,11 @@ QPDFWriter::enqueueObjectsPCLm()
2962 2962
2963 // enqueue all the strips for each page 2963 // enqueue all the strips for each page
2964 QPDFObjectHandle strips = page.getKey("/Resources").getKey("/XObject"); 2964 QPDFObjectHandle strips = page.getKey("/Resources").getKey("/XObject");
2965 - for (auto const& image: strips.getKeys()) {  
2966 - enqueueObject(strips.getKey(image));  
2967 - enqueueObject(QPDFObjectHandle::newStream(&m->pdf, image_transform_content)); 2965 + for (auto& image: strips.as_dictionary()) {
  2966 + if (!image.second.null()) {
  2967 + enqueueObject(image.second);
  2968 + enqueueObject(QPDFObjectHandle::newStream(&m->pdf, image_transform_content));
  2969 + }
2968 } 2970 }
2969 } 2971 }
2970 2972
libqpdf/QPDF_Array.cc
1 -#include <qpdf/QPDF_Array.hh> 1 +#include <qpdf/QPDFObjectHandle_private.hh>
2 2
3 -#include <qpdf/JSON_writer.hh>  
4 -#include <qpdf/QPDFObjectHandle.hh>  
5 -#include <qpdf/QPDFObject_private.hh>  
6 #include <qpdf/QTC.hh> 3 #include <qpdf/QTC.hh>
7 4
  5 +using namespace std::literals;
  6 +using namespace qpdf;
  7 +
8 static const QPDFObjectHandle null_oh = QPDFObjectHandle::newNull(); 8 static const QPDFObjectHandle null_oh = QPDFObjectHandle::newNull();
9 9
10 inline void 10 inline void
11 -QPDF_Array::checkOwnership(QPDFObjectHandle const& item) const  
12 -{  
13 - if (auto obj = item.getObjectPtr()) {  
14 - if (qpdf) {  
15 - if (auto item_qpdf = obj->getQPDF()) {  
16 - if (qpdf != item_qpdf) {  
17 - throw std::logic_error(  
18 - "Attempting to add an object from a different QPDF. Use "  
19 - "QPDF::copyForeignObject to add objects from another file.");  
20 - }  
21 - }  
22 - }  
23 - } else { 11 +Array::checkOwnership(QPDFObjectHandle const& item) const
  12 +{
  13 + if (!item) {
24 throw std::logic_error("Attempting to add an uninitialized object to a QPDF_Array."); 14 throw std::logic_error("Attempting to add an uninitialized object to a QPDF_Array.");
25 } 15 }
  16 + if (qpdf() && item.qpdf() && qpdf() != item.qpdf()) {
  17 + throw std::logic_error(
  18 + "Attempting to add an object from a different QPDF. Use "
  19 + "QPDF::copyForeignObject to add objects from another file.");
  20 + }
26 } 21 }
27 22
28 -QPDF_Array::QPDF_Array() :  
29 - QPDFValue(::ot_array)  
30 -{  
31 -}  
32 -  
33 -QPDF_Array::QPDF_Array(QPDF_Array const& other) :  
34 - QPDFValue(::ot_array),  
35 - sp(other.sp ? std::make_unique<Sparse>(*other.sp) : nullptr)  
36 -{  
37 -}  
38 -  
39 -QPDF_Array::QPDF_Array(std::vector<QPDFObjectHandle> const& v) :  
40 - QPDFValue(::ot_array)  
41 -{  
42 - setFromVector(v);  
43 -}  
44 -  
45 -QPDF_Array::QPDF_Array(std::vector<std::shared_ptr<QPDFObject>>&& v, bool sparse) :  
46 - QPDFValue(::ot_array) 23 +QPDF_Array::QPDF_Array(std::vector<QPDFObjectHandle>&& v, bool sparse)
47 { 24 {
48 if (sparse) { 25 if (sparse) {
49 sp = std::make_unique<Sparse>(); 26 sp = std::make_unique<Sparse>();
50 - for (auto&& item: v) {  
51 - if (item->getTypeCode() != ::ot_null || item->getObjGen().isIndirect()) { 27 + for (auto& item: v) {
  28 + if (item.raw_type_code() != ::ot_null || item.indirect()) {
52 sp->elements[sp->size] = std::move(item); 29 sp->elements[sp->size] = std::move(item);
53 } 30 }
54 ++sp->size; 31 ++sp->size;
@@ -58,191 +35,184 @@ QPDF_Array::QPDF_Array(std::vector&lt;std::shared_ptr&lt;QPDFObject&gt;&gt;&amp;&amp; v, bool sparse @@ -58,191 +35,184 @@ QPDF_Array::QPDF_Array(std::vector&lt;std::shared_ptr&lt;QPDFObject&gt;&gt;&amp;&amp; v, bool sparse
58 } 35 }
59 } 36 }
60 37
61 -std::shared_ptr<QPDFObject>  
62 -QPDF_Array::create(std::vector<QPDFObjectHandle> const& items) 38 +QPDF_Array*
  39 +Array::array() const
63 { 40 {
64 - return do_create(new QPDF_Array(items)); 41 + if (auto a = as<QPDF_Array>()) {
  42 + return a;
  43 + }
  44 +
  45 + throw std::runtime_error("Expected an array but found a non-array object");
  46 + return nullptr; // unreachable
65 } 47 }
66 48
67 -std::shared_ptr<QPDFObject>  
68 -QPDF_Array::create(std::vector<std::shared_ptr<QPDFObject>>&& items, bool sparse) 49 +Array::iterator
  50 +Array::begin()
69 { 51 {
70 - return do_create(new QPDF_Array(std::move(items), sparse)); 52 + if (auto a = as<QPDF_Array>()) {
  53 + if (!a->sp) {
  54 + return a->elements.begin();
  55 + }
  56 + if (!sp_elements) {
  57 + sp_elements = std::make_unique<std::vector<QPDFObjectHandle>>(getAsVector());
  58 + }
  59 + return sp_elements->begin();
  60 + }
  61 + return {};
71 } 62 }
72 63
73 -std::shared_ptr<QPDFObject>  
74 -QPDF_Array::copy(bool shallow) 64 +Array::iterator
  65 +Array::end()
75 { 66 {
76 - if (shallow) {  
77 - return do_create(new QPDF_Array(*this));  
78 - } else {  
79 - QTC::TC("qpdf", "QPDF_Array copy", sp ? 0 : 1);  
80 - if (sp) {  
81 - auto* result = new QPDF_Array();  
82 - result->sp = std::make_unique<Sparse>();  
83 - result->sp->size = sp->size;  
84 - for (auto const& element: sp->elements) {  
85 - auto const& obj = element.second;  
86 - result->sp->elements[element.first] =  
87 - obj->getObjGen().isIndirect() ? obj : obj->copy();  
88 - }  
89 - return do_create(result);  
90 - } else {  
91 - std::vector<std::shared_ptr<QPDFObject>> result;  
92 - result.reserve(elements.size());  
93 - for (auto const& element: elements) {  
94 - result.push_back(  
95 - element ? (element->getObjGen().isIndirect() ? element : element->copy())  
96 - : element);  
97 - }  
98 - return create(std::move(result), false); 67 + if (auto a = as<QPDF_Array>()) {
  68 + if (!a->sp) {
  69 + return a->elements.end();
99 } 70 }
  71 + if (!sp_elements) {
  72 + sp_elements = std::make_unique<std::vector<QPDFObjectHandle>>(getAsVector());
  73 + }
  74 + return sp_elements->end();
100 } 75 }
  76 + return {};
101 } 77 }
102 78
103 -void  
104 -QPDF_Array::disconnect() 79 +Array::const_iterator
  80 +Array::cbegin()
105 { 81 {
106 - if (sp) {  
107 - for (auto& item: sp->elements) {  
108 - auto& obj = item.second;  
109 - if (!obj->getObjGen().isIndirect()) {  
110 - obj->disconnect();  
111 - } 82 + if (auto a = as<QPDF_Array>()) {
  83 + if (!a->sp) {
  84 + return a->elements.cbegin();
112 } 85 }
113 - } else {  
114 - for (auto& obj: elements) {  
115 - if (!obj->getObjGen().isIndirect()) {  
116 - obj->disconnect();  
117 - } 86 + if (!sp_elements) {
  87 + sp_elements = std::make_unique<std::vector<QPDFObjectHandle>>(getAsVector());
118 } 88 }
  89 + return sp_elements->cbegin();
119 } 90 }
  91 + return {};
120 } 92 }
121 93
122 -std::string  
123 -QPDF_Array::unparse() 94 +Array::const_iterator
  95 +Array::cend()
124 { 96 {
125 - std::string result = "[ ";  
126 - if (sp) {  
127 - int next = 0;  
128 - for (auto& item: sp->elements) {  
129 - int key = item.first;  
130 - for (int j = next; j < key; ++j) {  
131 - result += "null ";  
132 - }  
133 - auto og = item.second->resolved_object()->getObjGen();  
134 - result += og.isIndirect() ? og.unparse(' ') + " R " : item.second->unparse() + " ";  
135 - next = ++key; 97 + if (auto a = as<QPDF_Array>()) {
  98 + if (!a->sp) {
  99 + return a->elements.cend();
136 } 100 }
137 - for (int j = next; j < sp->size; ++j) {  
138 - result += "null ";  
139 - }  
140 - } else {  
141 - for (auto const& item: elements) {  
142 - auto og = item->resolved_object()->getObjGen();  
143 - result += og.isIndirect() ? og.unparse(' ') + " R " : item->unparse() + " "; 101 + if (!sp_elements) {
  102 + sp_elements = std::make_unique<std::vector<QPDFObjectHandle>>(getAsVector());
144 } 103 }
  104 + return sp_elements->cend();
145 } 105 }
146 - result += "]";  
147 - return result; 106 + return {};
148 } 107 }
149 108
150 -void  
151 -QPDF_Array::writeJSON(int json_version, JSON::Writer& p)  
152 -{  
153 - p.writeStart('[');  
154 - if (sp) {  
155 - int next = 0;  
156 - for (auto& item: sp->elements) {  
157 - int key = item.first;  
158 - for (int j = next; j < key; ++j) {  
159 - p.writeNext() << "null";  
160 - }  
161 - p.writeNext();  
162 - auto og = item.second->getObjGen();  
163 - if (og.isIndirect()) {  
164 - p << "\"" << og.unparse(' ') << " R\"";  
165 - } else {  
166 - item.second->writeJSON(json_version, p);  
167 - }  
168 - next = ++key; 109 +Array::const_reverse_iterator
  110 +Array::crbegin()
  111 +{
  112 + if (auto a = as<QPDF_Array>()) {
  113 + if (!a->sp) {
  114 + return a->elements.crbegin();
169 } 115 }
170 - for (int j = next; j < sp->size; ++j) {  
171 - p.writeNext() << "null"; 116 + if (!sp_elements) {
  117 + sp_elements = std::make_unique<std::vector<QPDFObjectHandle>>(getAsVector());
172 } 118 }
173 - } else {  
174 - for (auto const& item: elements) {  
175 - p.writeNext();  
176 - auto og = item->getObjGen();  
177 - if (og.isIndirect()) {  
178 - p << "\"" << og.unparse(' ') << " R\"";  
179 - } else {  
180 - item->writeJSON(json_version, p);  
181 - } 119 + return sp_elements->crbegin();
  120 + }
  121 + return {};
  122 +}
  123 +
  124 +Array::const_reverse_iterator
  125 +Array::crend()
  126 +{
  127 + if (auto a = as<QPDF_Array>()) {
  128 + if (!a->sp) {
  129 + return a->elements.crend();
182 } 130 }
  131 + if (!sp_elements) {
  132 + sp_elements = std::make_unique<std::vector<QPDFObjectHandle>>(getAsVector());
  133 + }
  134 + return sp_elements->crend();
183 } 135 }
184 - p.writeEnd(']'); 136 + return {};
  137 +}
  138 +
  139 +QPDFObjectHandle
  140 +Array::null() const
  141 +{
  142 + return null_oh;
  143 +}
  144 +
  145 +int
  146 +Array::size() const
  147 +{
  148 + auto a = array();
  149 + return a->sp ? a->sp->size : int(a->elements.size());
185 } 150 }
186 151
187 std::pair<bool, QPDFObjectHandle> 152 std::pair<bool, QPDFObjectHandle>
188 -QPDF_Array::at(int n) const noexcept 153 +Array::at(int n) const
189 { 154 {
  155 + auto a = array();
190 if (n < 0 || n >= size()) { 156 if (n < 0 || n >= size()) {
191 return {false, {}}; 157 return {false, {}};
192 - } else if (sp) {  
193 - auto const& iter = sp->elements.find(n);  
194 - return {true, iter == sp->elements.end() ? null_oh : (*iter).second};  
195 - } else {  
196 - return {true, elements[size_t(n)]};  
197 } 158 }
  159 + if (!a->sp) {
  160 + return {true, a->elements[size_t(n)]};
  161 + }
  162 + auto const& iter = a->sp->elements.find(n);
  163 + return {true, iter == a->sp->elements.end() ? null() : iter->second};
198 } 164 }
199 165
200 std::vector<QPDFObjectHandle> 166 std::vector<QPDFObjectHandle>
201 -QPDF_Array::getAsVector() const 167 +Array::getAsVector() const
202 { 168 {
203 - if (sp) { 169 + auto a = array();
  170 + if (a->sp) {
204 std::vector<QPDFObjectHandle> v; 171 std::vector<QPDFObjectHandle> v;
205 v.reserve(size_t(size())); 172 v.reserve(size_t(size()));
206 - for (auto const& item: sp->elements) { 173 + for (auto const& item: a->sp->elements) {
207 v.resize(size_t(item.first), null_oh); 174 v.resize(size_t(item.first), null_oh);
208 v.emplace_back(item.second); 175 v.emplace_back(item.second);
209 } 176 }
210 v.resize(size_t(size()), null_oh); 177 v.resize(size_t(size()), null_oh);
211 return v; 178 return v;
212 } else { 179 } else {
213 - return {elements.cbegin(), elements.cend()}; 180 + return a->elements;
214 } 181 }
215 } 182 }
216 183
217 bool 184 bool
218 -QPDF_Array::setAt(int at, QPDFObjectHandle const& oh) 185 +Array::setAt(int at, QPDFObjectHandle const& oh)
219 { 186 {
220 if (at < 0 || at >= size()) { 187 if (at < 0 || at >= size()) {
221 return false; 188 return false;
222 } 189 }
  190 + auto a = array();
223 checkOwnership(oh); 191 checkOwnership(oh);
224 - if (sp) {  
225 - sp->elements[at] = oh.getObj(); 192 + if (a->sp) {
  193 + a->sp->elements[at] = oh;
226 } else { 194 } else {
227 - elements[size_t(at)] = oh.getObj(); 195 + a->elements[size_t(at)] = oh;
228 } 196 }
229 return true; 197 return true;
230 } 198 }
231 199
232 void 200 void
233 -QPDF_Array::setFromVector(std::vector<QPDFObjectHandle> const& v) 201 +Array::setFromVector(std::vector<QPDFObjectHandle> const& v)
234 { 202 {
235 - elements.resize(0);  
236 - elements.reserve(v.size()); 203 + auto a = array();
  204 + a->elements.resize(0);
  205 + a->elements.reserve(v.size());
237 for (auto const& item: v) { 206 for (auto const& item: v) {
238 checkOwnership(item); 207 checkOwnership(item);
239 - elements.push_back(item.getObj()); 208 + a->elements.emplace_back(item);
240 } 209 }
241 } 210 }
242 211
243 bool 212 bool
244 -QPDF_Array::insert(int at, QPDFObjectHandle const& item) 213 +Array::insert(int at, QPDFObjectHandle const& item)
245 { 214 {
  215 + auto a = array();
246 int sz = size(); 216 int sz = size();
247 if (at < 0 || at > sz) { 217 if (at < 0 || at > sz) {
248 // As special case, also allow insert beyond the end 218 // As special case, also allow insert beyond the end
@@ -251,61 +221,257 @@ QPDF_Array::insert(int at, QPDFObjectHandle const&amp; item) @@ -251,61 +221,257 @@ QPDF_Array::insert(int at, QPDFObjectHandle const&amp; item)
251 push_back(item); 221 push_back(item);
252 } else { 222 } else {
253 checkOwnership(item); 223 checkOwnership(item);
254 - if (sp) {  
255 - auto iter = sp->elements.crbegin();  
256 - while (iter != sp->elements.crend()) { 224 + if (a->sp) {
  225 + auto iter = a->sp->elements.crbegin();
  226 + while (iter != a->sp->elements.crend()) {
257 auto key = (iter++)->first; 227 auto key = (iter++)->first;
258 if (key >= at) { 228 if (key >= at) {
259 - auto nh = sp->elements.extract(key); 229 + auto nh = a->sp->elements.extract(key);
260 ++nh.key(); 230 ++nh.key();
261 - sp->elements.insert(std::move(nh)); 231 + a->sp->elements.insert(std::move(nh));
262 } else { 232 } else {
263 break; 233 break;
264 } 234 }
265 } 235 }
266 - sp->elements[at] = item.getObj();  
267 - ++sp->size; 236 + a->sp->elements[at] = item.getObj();
  237 + ++a->sp->size;
268 } else { 238 } else {
269 - elements.insert(elements.cbegin() + at, item.getObj()); 239 + a->elements.insert(a->elements.cbegin() + at, item.getObj());
270 } 240 }
271 } 241 }
272 return true; 242 return true;
273 } 243 }
274 244
275 void 245 void
276 -QPDF_Array::push_back(QPDFObjectHandle const& item) 246 +Array::push_back(QPDFObjectHandle const& item)
277 { 247 {
  248 + auto a = array();
278 checkOwnership(item); 249 checkOwnership(item);
279 - if (sp) {  
280 - sp->elements[(sp->size)++] = item.getObj(); 250 + if (a->sp) {
  251 + a->sp->elements[(a->sp->size)++] = item;
281 } else { 252 } else {
282 - elements.push_back(item.getObj()); 253 + a->elements.emplace_back(item);
283 } 254 }
284 } 255 }
285 256
286 bool 257 bool
287 -QPDF_Array::erase(int at) 258 +Array::erase(int at)
288 { 259 {
  260 + auto a = array();
289 if (at < 0 || at >= size()) { 261 if (at < 0 || at >= size()) {
290 return false; 262 return false;
291 } 263 }
292 - if (sp) {  
293 - auto end = sp->elements.end();  
294 - if (auto iter = sp->elements.lower_bound(at); iter != end) { 264 + if (a->sp) {
  265 + auto end = a->sp->elements.end();
  266 + if (auto iter = a->sp->elements.lower_bound(at); iter != end) {
295 if (iter->first == at) { 267 if (iter->first == at) {
296 iter++; 268 iter++;
297 - sp->elements.erase(at); 269 + a->sp->elements.erase(at);
298 } 270 }
299 271
300 while (iter != end) { 272 while (iter != end) {
301 - auto nh = sp->elements.extract(iter++); 273 + auto nh = a->sp->elements.extract(iter++);
302 --nh.key(); 274 --nh.key();
303 - sp->elements.insert(std::move(nh)); 275 + a->sp->elements.insert(std::move(nh));
304 } 276 }
305 } 277 }
306 - --(sp->size); 278 + --(a->sp->size);
307 } else { 279 } else {
308 - elements.erase(elements.cbegin() + at); 280 + a->elements.erase(a->elements.cbegin() + at);
309 } 281 }
310 return true; 282 return true;
311 } 283 }
  284 +
  285 +int
  286 +QPDFObjectHandle::getArrayNItems() const
  287 +{
  288 + if (auto array = as_array(strict)) {
  289 + return array.size();
  290 + }
  291 + typeWarning("array", "treating as empty");
  292 + QTC::TC("qpdf", "QPDFObjectHandle array treating as empty");
  293 + return 0;
  294 +}
  295 +
  296 +QPDFObjectHandle
  297 +QPDFObjectHandle::getArrayItem(int n) const
  298 +{
  299 + if (auto array = as_array(strict)) {
  300 + if (auto const [success, oh] = array.at(n); success) {
  301 + return oh;
  302 + } else {
  303 + objectWarning("returning null for out of bounds array access");
  304 + QTC::TC("qpdf", "QPDFObjectHandle array bounds");
  305 + }
  306 + } else {
  307 + typeWarning("array", "returning null");
  308 + QTC::TC("qpdf", "QPDFObjectHandle array null for non-array");
  309 + }
  310 + static auto constexpr msg = " -> null returned from invalid array access"sv;
  311 + return QPDF_Null::create(obj, msg, "");
  312 +}
  313 +
  314 +bool
  315 +QPDFObjectHandle::isRectangle() const
  316 +{
  317 + if (auto array = as_array(strict)) {
  318 + for (int i = 0; i < 4; ++i) {
  319 + if (auto item = array.at(i).second; !item.isNumber()) {
  320 + return false;
  321 + }
  322 + }
  323 + return array.size() == 4;
  324 + }
  325 + return false;
  326 +}
  327 +
  328 +bool
  329 +QPDFObjectHandle::isMatrix() const
  330 +{
  331 + if (auto array = as_array(strict)) {
  332 + for (int i = 0; i < 6; ++i) {
  333 + if (auto item = array.at(i).second; !item.isNumber()) {
  334 + return false;
  335 + }
  336 + }
  337 + return array.size() == 6;
  338 + }
  339 + return false;
  340 +}
  341 +
  342 +QPDFObjectHandle::Rectangle
  343 +QPDFObjectHandle::getArrayAsRectangle() const
  344 +{
  345 + if (auto array = as_array(strict)) {
  346 + if (array.size() != 4) {
  347 + return {};
  348 + }
  349 + double items[4];
  350 + for (int i = 0; i < 4; ++i) {
  351 + if (auto item = array.at(i).second; !item.getValueAsNumber(items[i])) {
  352 + return {};
  353 + }
  354 + }
  355 + return {
  356 + std::min(items[0], items[2]),
  357 + std::min(items[1], items[3]),
  358 + std::max(items[0], items[2]),
  359 + std::max(items[1], items[3])};
  360 + }
  361 + return {};
  362 +}
  363 +
  364 +QPDFObjectHandle::Matrix
  365 +QPDFObjectHandle::getArrayAsMatrix() const
  366 +{
  367 + if (auto array = as_array(strict)) {
  368 + if (array.size() != 6) {
  369 + return {};
  370 + }
  371 + double items[6];
  372 + for (int i = 0; i < 6; ++i) {
  373 + if (auto item = array.at(i).second; !item.getValueAsNumber(items[i])) {
  374 + return {};
  375 + }
  376 + }
  377 + return {items[0], items[1], items[2], items[3], items[4], items[5]};
  378 + }
  379 + return {};
  380 +}
  381 +
  382 +std::vector<QPDFObjectHandle>
  383 +QPDFObjectHandle::getArrayAsVector() const
  384 +{
  385 + if (auto array = as_array(strict)) {
  386 + return array.getAsVector();
  387 + }
  388 + typeWarning("array", "treating as empty");
  389 + QTC::TC("qpdf", "QPDFObjectHandle array treating as empty vector");
  390 + return {};
  391 +}
  392 +
  393 +void
  394 +QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const& item)
  395 +{
  396 + if (auto array = as_array(strict)) {
  397 + if (!array.setAt(n, item)) {
  398 + objectWarning("ignoring attempt to set out of bounds array item");
  399 + QTC::TC("qpdf", "QPDFObjectHandle set array bounds");
  400 + }
  401 + } else {
  402 + typeWarning("array", "ignoring attempt to set item");
  403 + QTC::TC("qpdf", "QPDFObjectHandle array ignoring set item");
  404 + }
  405 +}
  406 +void
  407 +QPDFObjectHandle::setArrayFromVector(std::vector<QPDFObjectHandle> const& items)
  408 +{
  409 + if (auto array = as_array(strict)) {
  410 + array.setFromVector(items);
  411 + } else {
  412 + typeWarning("array", "ignoring attempt to replace items");
  413 + QTC::TC("qpdf", "QPDFObjectHandle array ignoring replace items");
  414 + }
  415 +}
  416 +
  417 +void
  418 +QPDFObjectHandle::insertItem(int at, QPDFObjectHandle const& item)
  419 +{
  420 + if (auto array = as_array(strict)) {
  421 + if (!array.insert(at, item)) {
  422 + objectWarning("ignoring attempt to insert out of bounds array item");
  423 + QTC::TC("qpdf", "QPDFObjectHandle insert array bounds");
  424 + }
  425 + } else {
  426 + typeWarning("array", "ignoring attempt to insert item");
  427 + QTC::TC("qpdf", "QPDFObjectHandle array ignoring insert item");
  428 + }
  429 +}
  430 +
  431 +QPDFObjectHandle
  432 +QPDFObjectHandle::insertItemAndGetNew(int at, QPDFObjectHandle const& item)
  433 +{
  434 + insertItem(at, item);
  435 + return item;
  436 +}
  437 +
  438 +void
  439 +QPDFObjectHandle::appendItem(QPDFObjectHandle const& item)
  440 +{
  441 + if (auto array = as_array(strict)) {
  442 + array.push_back(item);
  443 + } else {
  444 + typeWarning("array", "ignoring attempt to append item");
  445 + QTC::TC("qpdf", "QPDFObjectHandle array ignoring append item");
  446 + }
  447 +}
  448 +
  449 +QPDFObjectHandle
  450 +QPDFObjectHandle::appendItemAndGetNew(QPDFObjectHandle const& item)
  451 +{
  452 + appendItem(item);
  453 + return item;
  454 +}
  455 +
  456 +void
  457 +QPDFObjectHandle::eraseItem(int at)
  458 +{
  459 + if (auto array = as_array(strict)) {
  460 + if (!array.erase(at)) {
  461 + objectWarning("ignoring attempt to erase out of bounds array item");
  462 + QTC::TC("qpdf", "QPDFObjectHandle erase array bounds");
  463 + }
  464 + } else {
  465 + typeWarning("array", "ignoring attempt to erase item");
  466 + QTC::TC("qpdf", "QPDFObjectHandle array ignoring erase item");
  467 + }
  468 +}
  469 +
  470 +QPDFObjectHandle
  471 +QPDFObjectHandle::eraseItemAndGetOld(int at)
  472 +{
  473 + auto array = as_array(strict);
  474 + auto result = (array && at < array.size() && at >= 0) ? array.at(at).second : newNull();
  475 + eraseItem(at);
  476 + return result;
  477 +}
libqpdf/QPDF_Bool.cc deleted
1 -#include <qpdf/QPDF_Bool.hh>  
2 -  
3 -#include <qpdf/JSON_writer.hh>  
4 -  
5 -QPDF_Bool::QPDF_Bool(bool val) :  
6 - QPDFValue(::ot_boolean),  
7 - val(val)  
8 -{  
9 -}  
10 -  
11 -std::shared_ptr<QPDFObject>  
12 -QPDF_Bool::create(bool value)  
13 -{  
14 - return do_create(new QPDF_Bool(value));  
15 -}  
16 -  
17 -std::shared_ptr<QPDFObject>  
18 -QPDF_Bool::copy(bool shallow)  
19 -{  
20 - return create(val);  
21 -}  
22 -  
23 -std::string  
24 -QPDF_Bool::unparse()  
25 -{  
26 - return (val ? "true" : "false");  
27 -}  
28 -  
29 -void  
30 -QPDF_Bool::writeJSON(int json_version, JSON::Writer& p)  
31 -{  
32 - p << val;  
33 -}  
34 -  
35 -bool  
36 -QPDF_Bool::getVal() const  
37 -{  
38 - return this->val;  
39 -}  
libqpdf/QPDF_Destroyed.cc deleted
1 -#include <qpdf/QPDF_Destroyed.hh>  
2 -  
3 -#include <stdexcept>  
4 -  
5 -QPDF_Destroyed::QPDF_Destroyed() :  
6 - QPDFValue(::ot_destroyed)  
7 -{  
8 -}  
9 -  
10 -std::shared_ptr<QPDFValue>  
11 -QPDF_Destroyed::getInstance()  
12 -{  
13 - static std::shared_ptr<QPDFValue> instance(new QPDF_Destroyed());  
14 - return instance;  
15 -}  
16 -  
17 -std::shared_ptr<QPDFObject>  
18 -QPDF_Destroyed::copy(bool shallow)  
19 -{  
20 - throw std::logic_error("attempted to shallow copy QPDFObjectHandle from destroyed QPDF");  
21 - return nullptr;  
22 -}  
23 -  
24 -std::string  
25 -QPDF_Destroyed::unparse()  
26 -{  
27 - throw std::logic_error("attempted to unparse a QPDFObjectHandle from a destroyed QPDF");  
28 - return "";  
29 -}  
30 -  
31 -void  
32 -QPDF_Destroyed::writeJSON(int json_version, JSON::Writer& p)  
33 -{  
34 - throw std::logic_error("attempted to get JSON from a QPDFObjectHandle from a destroyed QPDF");  
35 -}  
36 \ No newline at end of file 0 \ No newline at end of file
libqpdf/QPDF_Dictionary.cc
1 -#include <qpdf/QPDF_Dictionary.hh> 1 +#include <qpdf/QPDFObjectHandle_private.hh>
2 2
3 -#include <qpdf/JSON_writer.hh>  
4 #include <qpdf/QPDFObject_private.hh> 3 #include <qpdf/QPDFObject_private.hh>
5 -#include <qpdf/QPDF_Name.hh>  
6 -#include <qpdf/QPDF_Null.hh>  
7 -#include <qpdf/QUtil.hh> 4 +#include <qpdf/QTC.hh>
8 5
9 using namespace std::literals; 6 using namespace std::literals;
  7 +using namespace qpdf;
10 8
11 -QPDF_Dictionary::QPDF_Dictionary(std::map<std::string, QPDFObjectHandle> const& items) :  
12 - QPDFValue(::ot_dictionary),  
13 - items(items) 9 +QPDF_Dictionary*
  10 +BaseDictionary::dict() const
14 { 11 {
  12 + if (auto d = as<QPDF_Dictionary>()) {
  13 + return d;
  14 + }
  15 + throw std::runtime_error("Expected a dictionary but found a non-dictionary object");
  16 + return nullptr; // unreachable
15 } 17 }
16 18
17 -QPDF_Dictionary::QPDF_Dictionary(std::map<std::string, QPDFObjectHandle>&& items) :  
18 - QPDFValue(::ot_dictionary),  
19 - items(items) 19 +bool
  20 +BaseDictionary::hasKey(std::string const& key) const
20 { 21 {
  22 + auto d = dict();
  23 + return d->items.count(key) > 0 && !d->items[key].isNull();
21 } 24 }
22 25
23 -std::shared_ptr<QPDFObject>  
24 -QPDF_Dictionary::create(std::map<std::string, QPDFObjectHandle> const& items) 26 +QPDFObjectHandle
  27 +BaseDictionary::getKey(std::string const& key) const
25 { 28 {
26 - return do_create(new QPDF_Dictionary(items));  
27 -} 29 + auto d = dict();
28 30
29 -std::shared_ptr<QPDFObject>  
30 -QPDF_Dictionary::create(std::map<std::string, QPDFObjectHandle>&& items)  
31 -{  
32 - return do_create(new QPDF_Dictionary(items)); 31 + // PDF spec says fetching a non-existent key from a dictionary returns the null object.
  32 + auto item = d->items.find(key);
  33 + if (item != d->items.end()) {
  34 + // May be a null object
  35 + return item->second;
  36 + }
  37 + static auto constexpr msg = " -> dictionary key $VD"sv;
  38 + return QPDF_Null::create(obj, msg, key);
33 } 39 }
34 40
35 -std::shared_ptr<QPDFObject>  
36 -QPDF_Dictionary::copy(bool shallow) 41 +std::set<std::string>
  42 +BaseDictionary::getKeys()
37 { 43 {
38 - if (shallow) {  
39 - return create(items);  
40 - } else {  
41 - std::map<std::string, QPDFObjectHandle> new_items;  
42 - for (auto const& item: this->items) {  
43 - auto value = item.second;  
44 - new_items[item.first] = value.isIndirect() ? value : value.shallowCopy(); 44 + std::set<std::string> result;
  45 + for (auto& iter: dict()->items) {
  46 + if (!iter.second.isNull()) {
  47 + result.insert(iter.first);
45 } 48 }
46 - return create(new_items);  
47 } 49 }
  50 + return result;
  51 +}
  52 +
  53 +std::map<std::string, QPDFObjectHandle> const&
  54 +BaseDictionary::getAsMap() const
  55 +{
  56 + return dict()->items;
48 } 57 }
49 58
50 void 59 void
51 -QPDF_Dictionary::disconnect() 60 +BaseDictionary::removeKey(std::string const& key)
52 { 61 {
53 - for (auto& iter: this->items) {  
54 - QPDFObjectHandle::DisconnectAccess::disconnect(iter.second);  
55 - } 62 + // no-op if key does not exist
  63 + dict()->items.erase(key);
56 } 64 }
57 65
58 -std::string  
59 -QPDF_Dictionary::unparse() 66 +void
  67 +BaseDictionary::replaceKey(std::string const& key, QPDFObjectHandle value)
60 { 68 {
61 - std::string result = "<< ";  
62 - for (auto& iter: this->items) {  
63 - if (!iter.second.isNull()) {  
64 - result += QPDF_Name::normalizeName(iter.first) + " " + iter.second.unparse() + " ";  
65 - } 69 + auto d = dict();
  70 + if (value.isNull() && !value.isIndirect()) {
  71 + // The PDF spec doesn't distinguish between keys with null values and missing keys.
  72 + // Allow indirect nulls which are equivalent to a dangling reference, which is
  73 + // permitted by the spec.
  74 + d->items.erase(key);
  75 + } else {
  76 + // add or replace value
  77 + d->items[key] = value;
66 } 78 }
67 - result += ">>";  
68 - return result;  
69 } 79 }
70 80
71 void 81 void
72 -QPDF_Dictionary::writeJSON(int json_version, JSON::Writer& p) 82 +QPDFObjectHandle::checkOwnership(QPDFObjectHandle const& item) const
73 { 83 {
74 - p.writeStart('{');  
75 - for (auto& iter: this->items) {  
76 - if (!iter.second.isNull()) {  
77 - p.writeNext();  
78 - if (json_version == 1) {  
79 - p << "\"" << JSON::Writer::encode_string(QPDF_Name::normalizeName(iter.first))  
80 - << "\": ";  
81 - } else if (auto res = QPDF_Name::analyzeJSONEncoding(iter.first); res.first) {  
82 - if (res.second) {  
83 - p << "\"" << iter.first << "\": ";  
84 - } else {  
85 - p << "\"" << JSON::Writer::encode_string(iter.first) << "\": ";  
86 - }  
87 - } else {  
88 - p << "\"n:" << JSON::Writer::encode_string(QPDF_Name::normalizeName(iter.first))  
89 - << "\": ";  
90 - }  
91 - iter.second.writeJSON(json_version, p);  
92 - } 84 + auto qpdf = getOwningQPDF();
  85 + auto item_qpdf = item.getOwningQPDF();
  86 + if (qpdf && item_qpdf && qpdf != item_qpdf) {
  87 + QTC::TC("qpdf", "QPDFObjectHandle check ownership");
  88 + throw std::logic_error(
  89 + "Attempting to add an object from a different QPDF. Use "
  90 + "QPDF::copyForeignObject to add objects from another file.");
93 } 91 }
94 - p.writeEnd('}');  
95 } 92 }
96 93
97 bool 94 bool
98 -QPDF_Dictionary::hasKey(std::string const& key) 95 +QPDFObjectHandle::hasKey(std::string const& key) const
99 { 96 {
100 - return ((this->items.count(key) > 0) && (!this->items[key].isNull())); 97 + auto dict = as_dictionary(strict);
  98 + if (dict) {
  99 + return dict.hasKey(key);
  100 + } else {
  101 + typeWarning("dictionary", "returning false for a key containment request");
  102 + QTC::TC("qpdf", "QPDFObjectHandle dictionary false for hasKey");
  103 + return false;
  104 + }
101 } 105 }
102 106
103 QPDFObjectHandle 107 QPDFObjectHandle
104 -QPDF_Dictionary::getKey(std::string const& key) 108 +QPDFObjectHandle::getKey(std::string const& key) const
105 { 109 {
106 - // PDF spec says fetching a non-existent key from a dictionary returns the null object.  
107 - auto item = this->items.find(key);  
108 - if (item != this->items.end()) {  
109 - // May be a null object  
110 - return item->second;  
111 - } else {  
112 - static auto constexpr msg = " -> dictionary key $VD"sv;  
113 - return QPDF_Null::create(shared_from_this(), msg, key); 110 + if (auto dict = as_dictionary(strict)) {
  111 + return dict.getKey(key);
114 } 112 }
  113 + typeWarning("dictionary", "returning null for attempted key retrieval");
  114 + QTC::TC("qpdf", "QPDFObjectHandle dictionary null for getKey");
  115 + static auto constexpr msg = " -> null returned from getting key $VD from non-Dictionary"sv;
  116 + return QPDF_Null::create(obj, msg, "");
  117 +}
  118 +
  119 +QPDFObjectHandle
  120 +QPDFObjectHandle::getKeyIfDict(std::string const& key) const
  121 +{
  122 + return isNull() ? newNull() : getKey(key);
115 } 123 }
116 124
117 std::set<std::string> 125 std::set<std::string>
118 -QPDF_Dictionary::getKeys() 126 +QPDFObjectHandle::getKeys() const
119 { 127 {
120 - std::set<std::string> result;  
121 - for (auto& iter: this->items) {  
122 - if (!iter.second.isNull()) {  
123 - result.insert(iter.first);  
124 - } 128 + if (auto dict = as_dictionary(strict)) {
  129 + return dict.getKeys();
125 } 130 }
126 - return result; 131 + typeWarning("dictionary", "treating as empty");
  132 + QTC::TC("qpdf", "QPDFObjectHandle dictionary empty set for getKeys");
  133 + return {};
127 } 134 }
128 135
129 -std::map<std::string, QPDFObjectHandle> const&  
130 -QPDF_Dictionary::getAsMap() const 136 +std::map<std::string, QPDFObjectHandle>
  137 +QPDFObjectHandle::getDictAsMap() const
131 { 138 {
132 - return this->items; 139 + if (auto dict = as_dictionary(strict)) {
  140 + return dict.getAsMap();
  141 + }
  142 + typeWarning("dictionary", "treating as empty");
  143 + QTC::TC("qpdf", "QPDFObjectHandle dictionary empty map for asMap");
  144 + return {};
133 } 145 }
134 146
135 void 147 void
136 -QPDF_Dictionary::replaceKey(std::string const& key, QPDFObjectHandle value) 148 +QPDFObjectHandle::replaceKey(std::string const& key, QPDFObjectHandle const& value)
137 { 149 {
138 - if (value.isNull() && !value.isIndirect()) {  
139 - // The PDF spec doesn't distinguish between keys with null values and missing keys. Allow  
140 - // indirect nulls which are equivalent to a dangling reference, which is permitted by the  
141 - // spec.  
142 - removeKey(key);  
143 - } else {  
144 - // add or replace value  
145 - this->items[key] = value; 150 + if (auto dict = as_dictionary(strict)) {
  151 + checkOwnership(value);
  152 + dict.replaceKey(key, value);
  153 + return;
146 } 154 }
  155 + typeWarning("dictionary", "ignoring key replacement request");
  156 + QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring replaceKey");
  157 +}
  158 +
  159 +QPDFObjectHandle
  160 +QPDFObjectHandle::replaceKeyAndGetNew(std::string const& key, QPDFObjectHandle const& value)
  161 +{
  162 + replaceKey(key, value);
  163 + return value;
  164 +}
  165 +
  166 +QPDFObjectHandle
  167 +QPDFObjectHandle::replaceKeyAndGetOld(std::string const& key, QPDFObjectHandle const& value)
  168 +{
  169 + QPDFObjectHandle old = removeKeyAndGetOld(key);
  170 + replaceKey(key, value);
  171 + return old;
147 } 172 }
148 173
149 void 174 void
150 -QPDF_Dictionary::removeKey(std::string const& key) 175 +QPDFObjectHandle::removeKey(std::string const& key)
151 { 176 {
152 - // no-op if key does not exist  
153 - this->items.erase(key); 177 + if (auto dict = as_dictionary(strict)) {
  178 + dict.removeKey(key);
  179 + return;
  180 + }
  181 + typeWarning("dictionary", "ignoring key removal request");
  182 + QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring removeKey");
  183 +}
  184 +
  185 +QPDFObjectHandle
  186 +QPDFObjectHandle::removeKeyAndGetOld(std::string const& key)
  187 +{
  188 + auto result = QPDFObjectHandle::newNull();
  189 + if (auto dict = as_dictionary(strict)) {
  190 + result = dict.getKey(key);
  191 + }
  192 + removeKey(key);
  193 + return result;
154 } 194 }
libqpdf/QPDF_InlineImage.cc deleted
1 -#include <qpdf/QPDF_InlineImage.hh>  
2 -  
3 -#include <qpdf/JSON_writer.hh>  
4 -  
5 -QPDF_InlineImage::QPDF_InlineImage(std::string const& val) :  
6 - QPDFValue(::ot_inlineimage),  
7 - val(val)  
8 -{  
9 -}  
10 -  
11 -std::shared_ptr<QPDFObject>  
12 -QPDF_InlineImage::create(std::string const& val)  
13 -{  
14 - return do_create(new QPDF_InlineImage(val));  
15 -}  
16 -  
17 -std::shared_ptr<QPDFObject>  
18 -QPDF_InlineImage::copy(bool shallow)  
19 -{  
20 - return create(val);  
21 -}  
22 -  
23 -std::string  
24 -QPDF_InlineImage::unparse()  
25 -{  
26 - return this->val;  
27 -}  
28 -  
29 -void  
30 -QPDF_InlineImage::writeJSON(int json_version, JSON::Writer& p)  
31 -{  
32 - p << "null";  
33 -}  
libqpdf/QPDF_Integer.cc deleted
1 -#include <qpdf/QPDF_Integer.hh>  
2 -  
3 -#include <qpdf/JSON_writer.hh>  
4 -#include <qpdf/QUtil.hh>  
5 -  
6 -QPDF_Integer::QPDF_Integer(long long val) :  
7 - QPDFValue(::ot_integer),  
8 - val(val)  
9 -{  
10 -}  
11 -  
12 -std::shared_ptr<QPDFObject>  
13 -QPDF_Integer::create(long long value)  
14 -{  
15 - return do_create(new QPDF_Integer(value));  
16 -}  
17 -  
18 -std::shared_ptr<QPDFObject>  
19 -QPDF_Integer::copy(bool shallow)  
20 -{  
21 - return create(val);  
22 -}  
23 -  
24 -std::string  
25 -QPDF_Integer::unparse()  
26 -{  
27 - return std::to_string(this->val);  
28 -}  
29 -  
30 -void  
31 -QPDF_Integer::writeJSON(int json_version, JSON::Writer& p)  
32 -{  
33 - p << std::to_string(this->val);  
34 -}  
35 -  
36 -long long  
37 -QPDF_Integer::getVal() const  
38 -{  
39 - return this->val;  
40 -}  
libqpdf/QPDF_Name.cc
1 -#include <qpdf/QPDF_Name.hh>  
2 -  
3 -#include <qpdf/JSON_writer.hh>  
4 -#include <qpdf/QUtil.hh>  
5 -  
6 -QPDF_Name::QPDF_Name(std::string const& name) :  
7 - QPDFValue(::ot_name),  
8 - name(name)  
9 -{  
10 -}  
11 -  
12 -std::shared_ptr<QPDFObject>  
13 -QPDF_Name::create(std::string const& name)  
14 -{  
15 - return do_create(new QPDF_Name(name));  
16 -}  
17 -  
18 -std::shared_ptr<QPDFObject>  
19 -QPDF_Name::copy(bool shallow)  
20 -{  
21 - return create(name);  
22 -}  
23 -  
24 -std::string  
25 -QPDF_Name::normalizeName(std::string const& name)  
26 -{  
27 - if (name.empty()) {  
28 - return name;  
29 - }  
30 - std::string result;  
31 - result += name.at(0);  
32 - for (size_t i = 1; i < name.length(); ++i) {  
33 - char ch = name.at(i);  
34 - // Don't use locale/ctype here; follow PDF spec guidelines.  
35 - if (ch == '\0') {  
36 - // QPDFTokenizer embeds a null character to encode an invalid #.  
37 - result += "#";  
38 - } else if (  
39 - ch < 33 || ch == '#' || ch == '/' || ch == '(' || ch == ')' || ch == '{' || ch == '}' ||  
40 - ch == '<' || ch == '>' || ch == '[' || ch == ']' || ch == '%' || ch > 126) {  
41 - result += QUtil::hex_encode_char(ch);  
42 - } else {  
43 - result += ch;  
44 - }  
45 - }  
46 - return result;  
47 -}  
48 -  
49 -std::string  
50 -QPDF_Name::unparse()  
51 -{  
52 - return normalizeName(this->name);  
53 -}  
54 -  
55 -std::pair<bool, bool>  
56 -QPDF_Name::analyzeJSONEncoding(const std::string& name)  
57 -{  
58 - int tail = 0; // Number of continuation characters expected.  
59 - bool tail2 = false; // Potential overlong 3 octet utf-8.  
60 - bool tail3 = false; // potential overlong 4 octet  
61 - bool needs_escaping = false;  
62 - for (auto const& it: name) {  
63 - auto c = static_cast<unsigned char>(it);  
64 - if (tail) {  
65 - if ((c & 0xc0) != 0x80) {  
66 - return {false, false};  
67 - }  
68 - if (tail2) {  
69 - if ((c & 0xe0) == 0x80) {  
70 - return {false, false};  
71 - }  
72 - tail2 = false;  
73 - } else if (tail3) {  
74 - if ((c & 0xf0) == 0x80) {  
75 - return {false, false};  
76 - }  
77 - tail3 = false;  
78 - }  
79 - tail--;  
80 - } else if (c < 0x80) {  
81 - if (!needs_escaping) {  
82 - needs_escaping = !((c > 34 && c != '\\') || c == ' ' || c == 33);  
83 - }  
84 - } else if ((c & 0xe0) == 0xc0) {  
85 - if ((c & 0xfe) == 0xc0) {  
86 - return {false, false};  
87 - }  
88 - tail = 1;  
89 - } else if ((c & 0xf0) == 0xe0) {  
90 - tail2 = (c == 0xe0);  
91 - tail = 2;  
92 - } else if ((c & 0xf8) == 0xf0) {  
93 - tail3 = (c == 0xf0);  
94 - tail = 3;  
95 - } else {  
96 - return {false, false};  
97 - }  
98 - }  
99 - return {tail == 0, !needs_escaping};  
100 -}  
101 -  
102 -void  
103 -QPDF_Name::writeJSON(int json_version, JSON::Writer& p)  
104 -{  
105 - // For performance reasons this code is duplicated in QPDF_Dictionary::writeJSON. When updating  
106 - // this method make sure QPDF_Dictionary is also update.  
107 - if (json_version == 1) {  
108 - p << "\"" << JSON::Writer::encode_string(normalizeName(name)) << "\"";  
109 - } else {  
110 - if (auto res = analyzeJSONEncoding(name); res.first) {  
111 - if (res.second) {  
112 - p << "\"" << name << "\"";  
113 - } else {  
114 - p << "\"" << JSON::Writer::encode_string(name) << "\"";  
115 - }  
116 - } else {  
117 - p << "\"n:" << JSON::Writer::encode_string(normalizeName(name)) << "\"";  
118 - }  
119 - }  
120 -}  
libqpdf/QPDF_Null.cc deleted
1 -#include <qpdf/QPDF_Null.hh>  
2 -  
3 -#include <qpdf/JSON_writer.hh>  
4 -#include <qpdf/QPDFObject_private.hh>  
5 -  
6 -QPDF_Null::QPDF_Null(QPDF* qpdf, QPDFObjGen og) :  
7 - QPDFValue(::ot_null, qpdf, og)  
8 -{  
9 -}  
10 -  
11 -std::shared_ptr<QPDFObject>  
12 -QPDF_Null::create(QPDF* qpdf, QPDFObjGen og)  
13 -{  
14 - return do_create(new QPDF_Null(qpdf, og));  
15 -}  
16 -  
17 -std::shared_ptr<QPDFObject>  
18 -QPDF_Null::create(  
19 - std::shared_ptr<QPDFObject> parent, std::string_view const& static_descr, std::string var_descr)  
20 -{  
21 - auto n = do_create(new QPDF_Null());  
22 - n->setChildDescription(parent, static_descr, var_descr);  
23 - return n;  
24 -}  
25 -  
26 -std::shared_ptr<QPDFObject>  
27 -QPDF_Null::create(  
28 - std::shared_ptr<QPDFValue> parent, std::string_view const& static_descr, std::string var_descr)  
29 -{  
30 - auto n = do_create(new QPDF_Null());  
31 - n->setChildDescription(parent, static_descr, var_descr);  
32 - return n;  
33 -}  
34 -  
35 -std::shared_ptr<QPDFObject>  
36 -QPDF_Null::copy(bool shallow)  
37 -{  
38 - return create();  
39 -}  
40 -  
41 -std::string  
42 -QPDF_Null::unparse()  
43 -{  
44 - return "null";  
45 -}  
46 -  
47 -void  
48 -QPDF_Null::writeJSON(int json_version, JSON::Writer& p)  
49 -{  
50 - p << "null";  
51 -}  
libqpdf/QPDF_Operator.cc deleted
1 -#include <qpdf/QPDF_Operator.hh>  
2 -  
3 -#include <qpdf/JSON_writer.hh>  
4 -  
5 -QPDF_Operator::QPDF_Operator(std::string const& val) :  
6 - QPDFValue(::ot_operator),  
7 - val(val)  
8 -{  
9 -}  
10 -  
11 -std::shared_ptr<QPDFObject>  
12 -QPDF_Operator::create(std::string const& val)  
13 -{  
14 - return do_create(new QPDF_Operator(val));  
15 -}  
16 -  
17 -std::shared_ptr<QPDFObject>  
18 -QPDF_Operator::copy(bool shallow)  
19 -{  
20 - return create(val);  
21 -}  
22 -  
23 -std::string  
24 -QPDF_Operator::unparse()  
25 -{  
26 - return val;  
27 -}  
28 -  
29 -void  
30 -QPDF_Operator::writeJSON(int json_version, JSON::Writer& p)  
31 -{  
32 - p << "null";  
33 -}  
libqpdf/QPDF_Real.cc deleted
1 -#include <qpdf/QPDF_Real.hh>  
2 -  
3 -#include <qpdf/JSON_writer.hh>  
4 -#include <qpdf/QUtil.hh>  
5 -  
6 -QPDF_Real::QPDF_Real(std::string const& val) :  
7 - QPDFValue(::ot_real),  
8 - val(val)  
9 -{  
10 -}  
11 -  
12 -QPDF_Real::QPDF_Real(double value, int decimal_places, bool trim_trailing_zeroes) :  
13 - QPDFValue(::ot_real),  
14 - val(QUtil::double_to_string(value, decimal_places, trim_trailing_zeroes))  
15 -{  
16 -}  
17 -  
18 -std::shared_ptr<QPDFObject>  
19 -QPDF_Real::create(std::string const& val)  
20 -{  
21 - return do_create(new QPDF_Real(val));  
22 -}  
23 -  
24 -std::shared_ptr<QPDFObject>  
25 -QPDF_Real::create(double value, int decimal_places, bool trim_trailing_zeroes)  
26 -{  
27 - return do_create(new QPDF_Real(value, decimal_places, trim_trailing_zeroes));  
28 -}  
29 -  
30 -std::shared_ptr<QPDFObject>  
31 -QPDF_Real::copy(bool shallow)  
32 -{  
33 - return create(val);  
34 -}  
35 -  
36 -std::string  
37 -QPDF_Real::unparse()  
38 -{  
39 - return this->val;  
40 -}  
41 -  
42 -void  
43 -QPDF_Real::writeJSON(int json_version, JSON::Writer& p)  
44 -{  
45 - if (this->val.length() == 0) {  
46 - // Can't really happen...  
47 - p << "0";  
48 - } else if (this->val.at(0) == '.') {  
49 - p << "0" << this->val;  
50 - } else if (this->val.length() >= 2 && this->val.at(0) == '-' && this->val.at(1) == '.') {  
51 - p << "-0." << this->val.substr(2);  
52 - } else {  
53 - p << this->val;  
54 - }  
55 - if (val.back() == '.') {  
56 - p << "0";  
57 - }  
58 -}  
libqpdf/QPDF_Reserved.cc deleted
1 -#include <qpdf/QPDF_Reserved.hh>  
2 -  
3 -#include <stdexcept>  
4 -  
5 -QPDF_Reserved::QPDF_Reserved() :  
6 - QPDFValue(::ot_reserved)  
7 -{  
8 -}  
9 -  
10 -std::shared_ptr<QPDFObject>  
11 -QPDF_Reserved::create()  
12 -{  
13 - return do_create(new QPDF_Reserved());  
14 -}  
15 -  
16 -std::shared_ptr<QPDFObject>  
17 -QPDF_Reserved::copy(bool shallow)  
18 -{  
19 - return create();  
20 -}  
21 -  
22 -std::string  
23 -QPDF_Reserved::unparse()  
24 -{  
25 - throw std::logic_error("QPDFObjectHandle: attempting to unparse a reserved object");  
26 - return "";  
27 -}  
28 -  
29 -void  
30 -QPDF_Reserved::writeJSON(int json_version, JSON::Writer& p)  
31 -{  
32 - throw std::logic_error("QPDFObjectHandle: attempting to get JSON from a reserved object");  
33 -}  
libqpdf/QPDF_Stream.cc
1 -#include <qpdf/QPDF_Stream.hh> 1 +#include <qpdf/QPDFObjectHandle_private.hh>
2 2
3 #include <qpdf/ContentNormalizer.hh> 3 #include <qpdf/ContentNormalizer.hh>
4 #include <qpdf/JSON_writer.hh> 4 #include <qpdf/JSON_writer.hh>
@@ -22,6 +22,9 @@ @@ -22,6 +22,9 @@
22 22
23 #include <stdexcept> 23 #include <stdexcept>
24 24
  25 +using namespace std::literals;
  26 +using namespace qpdf;
  27 +
25 namespace 28 namespace
26 { 29 {
27 class SF_Crypt: public QPDFStreamFilter 30 class SF_Crypt: public QPDFStreamFilter
@@ -60,16 +63,24 @@ namespace @@ -60,16 +63,24 @@ namespace
60 class StreamBlobProvider 63 class StreamBlobProvider
61 { 64 {
62 public: 65 public:
63 - StreamBlobProvider(QPDF_Stream* stream, qpdf_stream_decode_level_e decode_level);  
64 - void operator()(Pipeline*); 66 + StreamBlobProvider(Stream stream, qpdf_stream_decode_level_e decode_level) :
  67 + stream(stream),
  68 + decode_level(decode_level)
  69 + {
  70 + }
  71 + void
  72 + operator()(Pipeline* p)
  73 + {
  74 + stream.pipeStreamData(p, nullptr, 0, decode_level, false, false);
  75 + }
65 76
66 private: 77 private:
67 - QPDF_Stream* stream; 78 + Stream stream;
68 qpdf_stream_decode_level_e decode_level; 79 qpdf_stream_decode_level_e decode_level;
69 }; 80 };
70 } // namespace 81 } // namespace
71 82
72 -std::map<std::string, std::string> QPDF_Stream::filter_abbreviations = { 83 +std::map<std::string, std::string> Stream::filter_abbreviations = {
73 // The PDF specification provides these filter abbreviations for use in inline images, but 84 // The PDF specification provides these filter abbreviations for use in inline images, but
74 // according to table H.1 in the pre-ISO versions of the PDF specification, Adobe Reader also 85 // according to table H.1 in the pre-ISO versions of the PDF specification, Adobe Reader also
75 // accepts them for stream filters. 86 // accepts them for stream filters.
@@ -82,8 +93,8 @@ std::map&lt;std::string, std::string&gt; QPDF_Stream::filter_abbreviations = { @@ -82,8 +93,8 @@ std::map&lt;std::string, std::string&gt; QPDF_Stream::filter_abbreviations = {
82 {"/DCT", "/DCTDecode"}, 93 {"/DCT", "/DCTDecode"},
83 }; 94 };
84 95
85 -std::map<std::string, std::function<std::shared_ptr<QPDFStreamFilter>()>>  
86 - QPDF_Stream::filter_factories = { 96 +std::map<std::string, std::function<std::shared_ptr<QPDFStreamFilter>()>> Stream::filter_factories =
  97 + {
87 {"/Crypt", []() { return std::make_shared<SF_Crypt>(); }}, 98 {"/Crypt", []() { return std::make_shared<SF_Crypt>(); }},
88 {"/FlateDecode", SF_FlateLzwDecode::flate_factory}, 99 {"/FlateDecode", SF_FlateLzwDecode::flate_factory},
89 {"/LZWDecode", SF_FlateLzwDecode::lzw_factory}, 100 {"/LZWDecode", SF_FlateLzwDecode::lzw_factory},
@@ -93,90 +104,25 @@ std::map&lt;std::string, std::function&lt;std::shared_ptr&lt;QPDFStreamFilter&gt;()&gt;&gt; @@ -93,90 +104,25 @@ std::map&lt;std::string, std::function&lt;std::shared_ptr&lt;QPDFStreamFilter&gt;()&gt;&gt;
93 {"/ASCIIHexDecode", SF_ASCIIHexDecode::factory}, 104 {"/ASCIIHexDecode", SF_ASCIIHexDecode::factory},
94 }; 105 };
95 106
96 -StreamBlobProvider::StreamBlobProvider(  
97 - QPDF_Stream* stream, qpdf_stream_decode_level_e decode_level) :  
98 - stream(stream),  
99 - decode_level(decode_level) 107 +Stream::Stream(
  108 + QPDF& qpdf, QPDFObjGen og, QPDFObjectHandle stream_dict, qpdf_offset_t offset, size_t length) :
  109 + BaseHandle(QPDFObject::create<QPDF_Stream>(&qpdf, og, std::move(stream_dict), length))
100 { 110 {
  111 + auto descr = std::make_shared<QPDFObject::Description>(
  112 + qpdf.getFilename() + ", stream object " + og.unparse(' '));
  113 + obj->setDescription(&qpdf, descr, offset);
  114 + setDictDescription();
101 } 115 }
102 116
103 void 117 void
104 -StreamBlobProvider::operator()(Pipeline* p)  
105 -{  
106 - this->stream->pipeStreamData(p, nullptr, 0, decode_level, false, false);  
107 -}  
108 -  
109 -QPDF_Stream::QPDF_Stream(  
110 - QPDF* qpdf, QPDFObjGen og, QPDFObjectHandle stream_dict, qpdf_offset_t offset, size_t length) :  
111 - QPDFValue(::ot_stream, qpdf, og),  
112 - filter_on_write(true),  
113 - stream_dict(stream_dict),  
114 - length(length)  
115 -{  
116 - if (!stream_dict.isDictionary()) {  
117 - throw std::logic_error(  
118 - "stream object instantiated with non-dictionary object for dictionary");  
119 - }  
120 - auto descr = std::make_shared<QPDFValue::Description>(  
121 - qpdf->getFilename() + ", stream object " + og.unparse(' '));  
122 - setDescription(qpdf, descr, offset);  
123 -}  
124 -  
125 -std::shared_ptr<QPDFObject>  
126 -QPDF_Stream::create(  
127 - QPDF* qpdf, QPDFObjGen og, QPDFObjectHandle stream_dict, qpdf_offset_t offset, size_t length)  
128 -{  
129 - return do_create(new QPDF_Stream(qpdf, og, stream_dict, offset, length));  
130 -}  
131 -  
132 -std::shared_ptr<QPDFObject>  
133 -QPDF_Stream::copy(bool shallow)  
134 -{  
135 - QTC::TC("qpdf", "QPDF_Stream ERR shallow copy stream");  
136 - throw std::runtime_error("stream objects cannot be cloned");  
137 -}  
138 -  
139 -void  
140 -QPDF_Stream::registerStreamFilter( 118 +Stream::registerStreamFilter(
141 std::string const& filter_name, std::function<std::shared_ptr<QPDFStreamFilter>()> factory) 119 std::string const& filter_name, std::function<std::shared_ptr<QPDFStreamFilter>()> factory)
142 { 120 {
143 filter_factories[filter_name] = factory; 121 filter_factories[filter_name] = factory;
144 } 122 }
145 123
146 -void  
147 -QPDF_Stream::setFilterOnWrite(bool val)  
148 -{  
149 - this->filter_on_write = val;  
150 -}  
151 -  
152 -bool  
153 -QPDF_Stream::getFilterOnWrite() const  
154 -{  
155 - return this->filter_on_write;  
156 -}  
157 -  
158 -void  
159 -QPDF_Stream::disconnect()  
160 -{  
161 - this->stream_provider = nullptr;  
162 - QPDFObjectHandle::DisconnectAccess::disconnect(this->stream_dict);  
163 -}  
164 -  
165 -std::string  
166 -QPDF_Stream::unparse()  
167 -{  
168 - // Unparse stream objects as indirect references  
169 - return og.unparse(' ') + " R";  
170 -}  
171 -  
172 -void  
173 -QPDF_Stream::writeJSON(int json_version, JSON::Writer& jw)  
174 -{  
175 - stream_dict.writeJSON(json_version, jw);  
176 -}  
177 -  
178 JSON 124 JSON
179 -QPDF_Stream::getStreamJSON( 125 +Stream::getStreamJSON(
180 int json_version, 126 int json_version,
181 qpdf_json_stream_data_e json_data, 127 qpdf_json_stream_data_e json_data,
182 qpdf_stream_decode_level_e decode_level, 128 qpdf_stream_decode_level_e decode_level,
@@ -190,13 +136,13 @@ QPDF_Stream::getStreamJSON( @@ -190,13 +136,13 @@ QPDF_Stream::getStreamJSON(
190 pb.finish(); 136 pb.finish();
191 auto result = JSON::parse(pb.getString()); 137 auto result = JSON::parse(pb.getString());
192 if (json_data == qpdf_sj_inline) { 138 if (json_data == qpdf_sj_inline) {
193 - result.addDictionaryMember("data", JSON::makeBlob(StreamBlobProvider(this, decode_level))); 139 + result.addDictionaryMember("data", JSON::makeBlob(StreamBlobProvider(*this, decode_level)));
194 } 140 }
195 return result; 141 return result;
196 } 142 }
197 143
198 qpdf_stream_decode_level_e 144 qpdf_stream_decode_level_e
199 -QPDF_Stream::writeStreamJSON( 145 +Stream::writeStreamJSON(
200 int json_version, 146 int json_version,
201 JSON::Writer& jw, 147 JSON::Writer& jw,
202 qpdf_json_stream_data_e json_data, 148 qpdf_json_stream_data_e json_data,
@@ -205,6 +151,7 @@ QPDF_Stream::writeStreamJSON( @@ -205,6 +151,7 @@ QPDF_Stream::writeStreamJSON(
205 std::string const& data_filename, 151 std::string const& data_filename,
206 bool no_data_key) 152 bool no_data_key)
207 { 153 {
  154 + auto s = stream();
208 switch (json_data) { 155 switch (json_data) {
209 case qpdf_sj_none: 156 case qpdf_sj_none:
210 case qpdf_sj_inline: 157 case qpdf_sj_inline:
@@ -232,7 +179,7 @@ QPDF_Stream::writeStreamJSON( @@ -232,7 +179,7 @@ QPDF_Stream::writeStreamJSON(
232 if (json_data == qpdf_sj_none) { 179 if (json_data == qpdf_sj_none) {
233 jw.writeNext(); 180 jw.writeNext();
234 jw << R"("dict": )"; 181 jw << R"("dict": )";
235 - stream_dict.writeJSON(json_version, jw); 182 + s->stream_dict.writeJSON(json_version, jw);
236 jw.writeEnd('}'); 183 jw.writeEnd('}');
237 return decode_level; 184 return decode_level;
238 } 185 }
@@ -264,7 +211,7 @@ QPDF_Stream::writeStreamJSON( @@ -264,7 +211,7 @@ QPDF_Stream::writeStreamJSON(
264 throw std::logic_error("QPDF_Stream: failed to get stream data"); 211 throw std::logic_error("QPDF_Stream: failed to get stream data");
265 } 212 }
266 // We can use unsafeShallowCopy because we are only touching top-level keys. 213 // We can use unsafeShallowCopy because we are only touching top-level keys.
267 - auto dict = stream_dict.unsafeShallowCopy(); 214 + auto dict = s->stream_dict.unsafeShallowCopy();
268 dict.removeKey("/Length"); 215 dict.removeKey("/Length");
269 if (filter && filtered) { 216 if (filter && filtered) {
270 dict.removeKey("/Filter"); 217 dict.removeKey("/Filter");
@@ -290,53 +237,17 @@ QPDF_Stream::writeStreamJSON( @@ -290,53 +237,17 @@ QPDF_Stream::writeStreamJSON(
290 } 237 }
291 238
292 void 239 void
293 -QPDF_Stream::setDescription(  
294 - QPDF* qpdf, std::shared_ptr<QPDFValue::Description>& description, qpdf_offset_t offset)  
295 -{  
296 - this->QPDFValue::setDescription(qpdf, description, offset);  
297 - setDictDescription();  
298 -}  
299 -  
300 -void  
301 -QPDF_Stream::setDictDescription() 240 +qpdf::Stream::setDictDescription()
302 { 241 {
303 - if (!this->stream_dict.hasObjectDescription()) {  
304 - this->stream_dict.setObjectDescription(qpdf, getDescription() + " -> stream dictionary"); 242 + auto s = stream();
  243 + if (!s->stream_dict.hasObjectDescription()) {
  244 + s->stream_dict.setObjectDescription(
  245 + obj->getQPDF(), obj->getDescription() + " -> stream dictionary");
305 } 246 }
306 } 247 }
307 248
308 -QPDFObjectHandle  
309 -QPDF_Stream::getDict() const  
310 -{  
311 - return this->stream_dict;  
312 -}  
313 -  
314 -bool  
315 -QPDF_Stream::isDataModified() const  
316 -{  
317 - return (!this->token_filters.empty());  
318 -}  
319 -  
320 -size_t  
321 -QPDF_Stream::getLength() const  
322 -{  
323 - return this->length;  
324 -}  
325 -  
326 std::shared_ptr<Buffer> 249 std::shared_ptr<Buffer>
327 -QPDF_Stream::getStreamDataBuffer() const  
328 -{  
329 - return this->stream_data;  
330 -}  
331 -  
332 -std::shared_ptr<QPDFObjectHandle::StreamDataProvider>  
333 -QPDF_Stream::getStreamDataProvider() const  
334 -{  
335 - return this->stream_provider;  
336 -}  
337 -  
338 -std::shared_ptr<Buffer>  
339 -QPDF_Stream::getStreamData(qpdf_stream_decode_level_e decode_level) 250 +Stream::getStreamData(qpdf_stream_decode_level_e decode_level)
340 { 251 {
341 Pl_Buffer buf("stream data buffer"); 252 Pl_Buffer buf("stream data buffer");
342 bool filtered; 253 bool filtered;
@@ -344,9 +255,9 @@ QPDF_Stream::getStreamData(qpdf_stream_decode_level_e decode_level) @@ -344,9 +255,9 @@ QPDF_Stream::getStreamData(qpdf_stream_decode_level_e decode_level)
344 if (!filtered) { 255 if (!filtered) {
345 throw QPDFExc( 256 throw QPDFExc(
346 qpdf_e_unsupported, 257 qpdf_e_unsupported,
347 - qpdf->getFilename(), 258 + obj->getQPDF()->getFilename(),
348 "", 259 "",
349 - this->parsed_offset, 260 + obj->getParsedOffset(),
350 "getStreamData called on unfilterable stream"); 261 "getStreamData called on unfilterable stream");
351 } 262 }
352 QTC::TC("qpdf", "QPDF_Stream getStreamData"); 263 QTC::TC("qpdf", "QPDF_Stream getStreamData");
@@ -354,15 +265,15 @@ QPDF_Stream::getStreamData(qpdf_stream_decode_level_e decode_level) @@ -354,15 +265,15 @@ QPDF_Stream::getStreamData(qpdf_stream_decode_level_e decode_level)
354 } 265 }
355 266
356 std::shared_ptr<Buffer> 267 std::shared_ptr<Buffer>
357 -QPDF_Stream::getRawStreamData() 268 +Stream::getRawStreamData()
358 { 269 {
359 Pl_Buffer buf("stream data buffer"); 270 Pl_Buffer buf("stream data buffer");
360 if (!pipeStreamData(&buf, nullptr, 0, qpdf_dl_none, false, false)) { 271 if (!pipeStreamData(&buf, nullptr, 0, qpdf_dl_none, false, false)) {
361 throw QPDFExc( 272 throw QPDFExc(
362 qpdf_e_unsupported, 273 qpdf_e_unsupported,
363 - qpdf->getFilename(), 274 + obj->getQPDF()->getFilename(),
364 "", 275 "",
365 - this->parsed_offset, 276 + obj->getParsedOffset(),
366 "error getting raw stream data"); 277 "error getting raw stream data");
367 } 278 }
368 QTC::TC("qpdf", "QPDF_Stream getRawStreamData"); 279 QTC::TC("qpdf", "QPDF_Stream getRawStreamData");
@@ -370,14 +281,15 @@ QPDF_Stream::getRawStreamData() @@ -370,14 +281,15 @@ QPDF_Stream::getRawStreamData()
370 } 281 }
371 282
372 bool 283 bool
373 -QPDF_Stream::filterable( 284 +Stream::filterable(
374 std::vector<std::shared_ptr<QPDFStreamFilter>>& filters, 285 std::vector<std::shared_ptr<QPDFStreamFilter>>& filters,
375 bool& specialized_compression, 286 bool& specialized_compression,
376 bool& lossy_compression) 287 bool& lossy_compression)
377 { 288 {
  289 + auto s = stream();
378 // Check filters 290 // Check filters
379 291
380 - QPDFObjectHandle filter_obj = this->stream_dict.getKey("/Filter"); 292 + QPDFObjectHandle filter_obj = s->stream_dict.getKey("/Filter");
381 bool filters_okay = true; 293 bool filters_okay = true;
382 294
383 std::vector<std::string> filter_names; 295 std::vector<std::string> filter_names;
@@ -432,7 +344,7 @@ QPDF_Stream::filterable( @@ -432,7 +344,7 @@ QPDF_Stream::filterable(
432 344
433 // See if we can support any decode parameters that are specified. 345 // See if we can support any decode parameters that are specified.
434 346
435 - QPDFObjectHandle decode_obj = this->stream_dict.getKey("/DecodeParms"); 347 + QPDFObjectHandle decode_obj = s->stream_dict.getKey("/DecodeParms");
436 std::vector<QPDFObjectHandle> decode_parms; 348 std::vector<QPDFObjectHandle> decode_parms;
437 if (decode_obj.isArray() && (decode_obj.getArrayNItems() == 0)) { 349 if (decode_obj.isArray() && (decode_obj.getArrayNItems() == 0)) {
438 decode_obj = QPDFObjectHandle::newNull(); 350 decode_obj = QPDFObjectHandle::newNull();
@@ -479,7 +391,7 @@ QPDF_Stream::filterable( @@ -479,7 +391,7 @@ QPDF_Stream::filterable(
479 } 391 }
480 392
481 bool 393 bool
482 -QPDF_Stream::pipeStreamData( 394 +Stream::pipeStreamData(
483 Pipeline* pipeline, 395 Pipeline* pipeline,
484 bool* filterp, 396 bool* filterp,
485 int encode_flags, 397 int encode_flags,
@@ -487,6 +399,7 @@ QPDF_Stream::pipeStreamData( @@ -487,6 +399,7 @@ QPDF_Stream::pipeStreamData(
487 bool suppress_warnings, 399 bool suppress_warnings,
488 bool will_retry) 400 bool will_retry)
489 { 401 {
  402 + auto s = stream();
490 std::vector<std::shared_ptr<QPDFStreamFilter>> filters; 403 std::vector<std::shared_ptr<QPDFStreamFilter>> filters;
491 bool specialized_compression = false; 404 bool specialized_compression = false;
492 bool lossy_compression = false; 405 bool lossy_compression = false;
@@ -543,7 +456,7 @@ QPDF_Stream::pipeStreamData( @@ -543,7 +456,7 @@ QPDF_Stream::pipeStreamData(
543 pipeline = new_pipeline.get(); 456 pipeline = new_pipeline.get();
544 } 457 }
545 458
546 - for (auto iter = this->token_filters.rbegin(); iter != this->token_filters.rend(); ++iter) { 459 + for (auto iter = s->token_filters.rbegin(); iter != s->token_filters.rend(); ++iter) {
547 new_pipeline = 460 new_pipeline =
548 std::make_shared<Pl_QPDFTokenizer>("token filter", (*iter).get(), pipeline); 461 std::make_shared<Pl_QPDFTokenizer>("token filter", (*iter).get(), pipeline);
549 to_delete.push_back(new_pipeline); 462 to_delete.push_back(new_pipeline);
@@ -562,25 +475,25 @@ QPDF_Stream::pipeStreamData( @@ -562,25 +475,25 @@ QPDF_Stream::pipeStreamData(
562 } 475 }
563 } 476 }
564 477
565 - if (this->stream_data.get()) { 478 + if (s->stream_data.get()) {
566 QTC::TC("qpdf", "QPDF_Stream pipe replaced stream data"); 479 QTC::TC("qpdf", "QPDF_Stream pipe replaced stream data");
567 - pipeline->write(this->stream_data->getBuffer(), this->stream_data->getSize()); 480 + pipeline->write(s->stream_data->getBuffer(), s->stream_data->getSize());
568 pipeline->finish(); 481 pipeline->finish();
569 - } else if (this->stream_provider.get()) { 482 + } else if (s->stream_provider.get()) {
570 Pl_Count count("stream provider count", pipeline); 483 Pl_Count count("stream provider count", pipeline);
571 - if (this->stream_provider->supportsRetry()) {  
572 - if (!this->stream_provider->provideStreamData(  
573 - og, &count, suppress_warnings, will_retry)) { 484 + if (s->stream_provider->supportsRetry()) {
  485 + if (!s->stream_provider->provideStreamData(
  486 + obj->getObjGen(), &count, suppress_warnings, will_retry)) {
574 filter = false; 487 filter = false;
575 success = false; 488 success = false;
576 } 489 }
577 } else { 490 } else {
578 - this->stream_provider->provideStreamData(og, &count); 491 + s->stream_provider->provideStreamData(obj->getObjGen(), &count);
579 } 492 }
580 qpdf_offset_t actual_length = count.getCount(); 493 qpdf_offset_t actual_length = count.getCount();
581 qpdf_offset_t desired_length = 0; 494 qpdf_offset_t desired_length = 0;
582 - if (success && this->stream_dict.hasKey("/Length")) {  
583 - desired_length = this->stream_dict.getKey("/Length").getIntValue(); 495 + if (success && s->stream_dict.hasKey("/Length")) {
  496 + desired_length = s->stream_dict.getKey("/Length").getIntValue();
584 if (actual_length == desired_length) { 497 if (actual_length == desired_length) {
585 QTC::TC("qpdf", "QPDF_Stream pipe use stream provider"); 498 QTC::TC("qpdf", "QPDF_Stream pipe use stream provider");
586 } else { 499 } else {
@@ -588,25 +501,25 @@ QPDF_Stream::pipeStreamData( @@ -588,25 +501,25 @@ QPDF_Stream::pipeStreamData(
588 // This would be caused by programmer error on the part of a library user, not by 501 // This would be caused by programmer error on the part of a library user, not by
589 // invalid input data. 502 // invalid input data.
590 throw std::runtime_error( 503 throw std::runtime_error(
591 - "stream data provider for " + og.unparse(' ') + " provided " + 504 + "stream data provider for " + obj->getObjGen().unparse(' ') + " provided " +
592 std::to_string(actual_length) + " bytes instead of expected " + 505 std::to_string(actual_length) + " bytes instead of expected " +
593 std::to_string(desired_length) + " bytes"); 506 std::to_string(desired_length) + " bytes");
594 } 507 }
595 } else if (success) { 508 } else if (success) {
596 QTC::TC("qpdf", "QPDF_Stream provider length not provided"); 509 QTC::TC("qpdf", "QPDF_Stream provider length not provided");
597 - this->stream_dict.replaceKey("/Length", QPDFObjectHandle::newInteger(actual_length)); 510 + s->stream_dict.replaceKey("/Length", QPDFObjectHandle::newInteger(actual_length));
598 } 511 }
599 - } else if (this->parsed_offset == 0) { 512 + } else if (obj->getParsedOffset() == 0) {
600 QTC::TC("qpdf", "QPDF_Stream pipe no stream data"); 513 QTC::TC("qpdf", "QPDF_Stream pipe no stream data");
601 throw std::logic_error("pipeStreamData called for stream with no data"); 514 throw std::logic_error("pipeStreamData called for stream with no data");
602 } else { 515 } else {
603 QTC::TC("qpdf", "QPDF_Stream pipe original stream data"); 516 QTC::TC("qpdf", "QPDF_Stream pipe original stream data");
604 if (!QPDF::Pipe::pipeStreamData( 517 if (!QPDF::Pipe::pipeStreamData(
605 - this->qpdf,  
606 - og,  
607 - this->parsed_offset,  
608 - this->length,  
609 - this->stream_dict, 518 + obj->getQPDF(),
  519 + obj->getObjGen(),
  520 + obj->getParsedOffset(),
  521 + s->length,
  522 + s->stream_dict,
610 pipeline, 523 pipeline,
611 suppress_warnings, 524 suppress_warnings,
612 will_retry)) { 525 will_retry)) {
@@ -634,60 +547,235 @@ QPDF_Stream::pipeStreamData( @@ -634,60 +547,235 @@ QPDF_Stream::pipeStreamData(
634 } 547 }
635 548
636 void 549 void
637 -QPDF_Stream::replaceStreamData( 550 +Stream::replaceStreamData(
638 std::shared_ptr<Buffer> data, 551 std::shared_ptr<Buffer> data,
639 QPDFObjectHandle const& filter, 552 QPDFObjectHandle const& filter,
640 QPDFObjectHandle const& decode_parms) 553 QPDFObjectHandle const& decode_parms)
641 { 554 {
642 - this->stream_data = data;  
643 - this->stream_provider = nullptr; 555 + auto s = stream();
  556 + s->stream_data = data;
  557 + s->stream_provider = nullptr;
644 replaceFilterData(filter, decode_parms, data->getSize()); 558 replaceFilterData(filter, decode_parms, data->getSize());
645 } 559 }
646 560
647 void 561 void
648 -QPDF_Stream::replaceStreamData( 562 +Stream::replaceStreamData(
649 std::shared_ptr<QPDFObjectHandle::StreamDataProvider> provider, 563 std::shared_ptr<QPDFObjectHandle::StreamDataProvider> provider,
650 QPDFObjectHandle const& filter, 564 QPDFObjectHandle const& filter,
651 QPDFObjectHandle const& decode_parms) 565 QPDFObjectHandle const& decode_parms)
652 { 566 {
653 - this->stream_provider = provider;  
654 - this->stream_data = nullptr; 567 + auto s = stream();
  568 + s->stream_provider = provider;
  569 + s->stream_data = nullptr;
655 replaceFilterData(filter, decode_parms, 0); 570 replaceFilterData(filter, decode_parms, 0);
656 } 571 }
657 572
658 void 573 void
659 -QPDF_Stream::addTokenFilter(std::shared_ptr<QPDFObjectHandle::TokenFilter> token_filter)  
660 -{  
661 - this->token_filters.push_back(token_filter);  
662 -}  
663 -  
664 -void  
665 -QPDF_Stream::replaceFilterData( 574 +Stream::replaceFilterData(
666 QPDFObjectHandle const& filter, QPDFObjectHandle const& decode_parms, size_t length) 575 QPDFObjectHandle const& filter, QPDFObjectHandle const& decode_parms, size_t length)
667 { 576 {
  577 + auto s = stream();
668 if (filter) { 578 if (filter) {
669 - stream_dict.replaceKey("/Filter", filter); 579 + s->stream_dict.replaceKey("/Filter", filter);
670 } 580 }
671 if (decode_parms) { 581 if (decode_parms) {
672 - stream_dict.replaceKey("/DecodeParms", decode_parms); 582 + s->stream_dict.replaceKey("/DecodeParms", decode_parms);
673 } 583 }
674 if (length == 0) { 584 if (length == 0) {
675 QTC::TC("qpdf", "QPDF_Stream unknown stream length"); 585 QTC::TC("qpdf", "QPDF_Stream unknown stream length");
676 - stream_dict.removeKey("/Length"); 586 + s->stream_dict.removeKey("/Length");
677 } else { 587 } else {
678 - stream_dict.replaceKey("/Length", QPDFObjectHandle::newInteger(QIntC::to_longlong(length))); 588 + s->stream_dict.replaceKey(
  589 + "/Length", QPDFObjectHandle::newInteger(QIntC::to_longlong(length)));
679 } 590 }
680 } 591 }
681 592
682 void 593 void
683 -QPDF_Stream::replaceDict(QPDFObjectHandle const& new_dict) 594 +Stream::warn(std::string const& message)
684 { 595 {
685 - this->stream_dict = new_dict;  
686 - setDictDescription(); 596 + obj->getQPDF()->warn(qpdf_e_damaged_pdf, "", obj->getParsedOffset(), message);
  597 +}
  598 +
  599 +QPDFObjectHandle
  600 +QPDFObjectHandle::getDict() const
  601 +{
  602 + return as_stream(error).getDict();
  603 +}
  604 +
  605 +void
  606 +QPDFObjectHandle::setFilterOnWrite(bool val)
  607 +{
  608 + as_stream(error).setFilterOnWrite(val);
  609 +}
  610 +
  611 +bool
  612 +QPDFObjectHandle::getFilterOnWrite()
  613 +{
  614 + return as_stream(error).getFilterOnWrite();
  615 +}
  616 +
  617 +bool
  618 +QPDFObjectHandle::isDataModified()
  619 +{
  620 + return as_stream(error).isDataModified();
687 } 621 }
688 622
689 void 623 void
690 -QPDF_Stream::warn(std::string const& message) 624 +QPDFObjectHandle::replaceDict(QPDFObjectHandle const& new_dict)
  625 +{
  626 + as_stream(error).replaceDict(new_dict);
  627 +}
  628 +
  629 +std::shared_ptr<Buffer>
  630 +QPDFObjectHandle::getStreamData(qpdf_stream_decode_level_e level)
  631 +{
  632 + return as_stream(error).getStreamData(level);
  633 +}
  634 +
  635 +std::shared_ptr<Buffer>
  636 +QPDFObjectHandle::getRawStreamData()
  637 +{
  638 + return as_stream(error).getRawStreamData();
  639 +}
  640 +
  641 +bool
  642 +QPDFObjectHandle::pipeStreamData(
  643 + Pipeline* p,
  644 + bool* filtering_attempted,
  645 + int encode_flags,
  646 + qpdf_stream_decode_level_e decode_level,
  647 + bool suppress_warnings,
  648 + bool will_retry)
  649 +{
  650 + return as_stream(error).pipeStreamData(
  651 + p, filtering_attempted, encode_flags, decode_level, suppress_warnings, will_retry);
  652 +}
  653 +
  654 +bool
  655 +QPDFObjectHandle::pipeStreamData(
  656 + Pipeline* p,
  657 + int encode_flags,
  658 + qpdf_stream_decode_level_e decode_level,
  659 + bool suppress_warnings,
  660 + bool will_retry)
  661 +{
  662 + bool filtering_attempted;
  663 + as_stream(error).pipeStreamData(
  664 + p, &filtering_attempted, encode_flags, decode_level, suppress_warnings, will_retry);
  665 + return filtering_attempted;
  666 +}
  667 +
  668 +bool
  669 +QPDFObjectHandle::pipeStreamData(Pipeline* p, bool filter, bool normalize, bool compress)
  670 +{
  671 + int encode_flags = 0;
  672 + qpdf_stream_decode_level_e decode_level = qpdf_dl_none;
  673 + if (filter) {
  674 + decode_level = qpdf_dl_generalized;
  675 + if (normalize) {
  676 + encode_flags |= qpdf_ef_normalize;
  677 + }
  678 + if (compress) {
  679 + encode_flags |= qpdf_ef_compress;
  680 + }
  681 + }
  682 + return pipeStreamData(p, encode_flags, decode_level, false);
  683 +}
  684 +
  685 +void
  686 +QPDFObjectHandle::replaceStreamData(
  687 + std::shared_ptr<Buffer> data,
  688 + QPDFObjectHandle const& filter,
  689 + QPDFObjectHandle const& decode_parms)
  690 +{
  691 + as_stream(error).replaceStreamData(data, filter, decode_parms);
  692 +}
  693 +
  694 +void
  695 +QPDFObjectHandle::replaceStreamData(
  696 + std::string const& data, QPDFObjectHandle const& filter, QPDFObjectHandle const& decode_parms)
  697 +{
  698 + auto b = std::make_shared<Buffer>(data.length());
  699 + unsigned char* bp = b->getBuffer();
  700 + if (bp) {
  701 + memcpy(bp, data.c_str(), data.length());
  702 + }
  703 + as_stream(error).replaceStreamData(b, filter, decode_parms);
  704 +}
  705 +
  706 +void
  707 +QPDFObjectHandle::replaceStreamData(
  708 + std::shared_ptr<StreamDataProvider> provider,
  709 + QPDFObjectHandle const& filter,
  710 + QPDFObjectHandle const& decode_parms)
  711 +{
  712 + as_stream(error).replaceStreamData(provider, filter, decode_parms);
  713 +}
  714 +
  715 +namespace
  716 +{
  717 + class FunctionProvider: public QPDFObjectHandle::StreamDataProvider
  718 + {
  719 + public:
  720 + FunctionProvider(std::function<void(Pipeline*)> provider) :
  721 + StreamDataProvider(false),
  722 + p1(provider),
  723 + p2(nullptr)
  724 + {
  725 + }
  726 + FunctionProvider(std::function<bool(Pipeline*, bool, bool)> provider) :
  727 + StreamDataProvider(true),
  728 + p1(nullptr),
  729 + p2(provider)
  730 + {
  731 + }
  732 +
  733 + void
  734 + provideStreamData(QPDFObjGen const&, Pipeline* pipeline) override
  735 + {
  736 + p1(pipeline);
  737 + }
  738 +
  739 + bool
  740 + provideStreamData(
  741 + QPDFObjGen const&, Pipeline* pipeline, bool suppress_warnings, bool will_retry) override
  742 + {
  743 + return p2(pipeline, suppress_warnings, will_retry);
  744 + }
  745 +
  746 + private:
  747 + std::function<void(Pipeline*)> p1;
  748 + std::function<bool(Pipeline*, bool, bool)> p2;
  749 + };
  750 +} // namespace
  751 +
  752 +void
  753 +QPDFObjectHandle::replaceStreamData(
  754 + std::function<void(Pipeline*)> provider,
  755 + QPDFObjectHandle const& filter,
  756 + QPDFObjectHandle const& decode_parms)
  757 +{
  758 + auto sdp = std::shared_ptr<StreamDataProvider>(new FunctionProvider(provider));
  759 + as_stream(error).replaceStreamData(sdp, filter, decode_parms);
  760 +}
  761 +
  762 +void
  763 +QPDFObjectHandle::replaceStreamData(
  764 + std::function<bool(Pipeline*, bool, bool)> provider,
  765 + QPDFObjectHandle const& filter,
  766 + QPDFObjectHandle const& decode_parms)
  767 +{
  768 + auto sdp = std::shared_ptr<StreamDataProvider>(new FunctionProvider(provider));
  769 + as_stream(error).replaceStreamData(sdp, filter, decode_parms);
  770 +}
  771 +
  772 +JSON
  773 +QPDFObjectHandle::getStreamJSON(
  774 + int json_version,
  775 + qpdf_json_stream_data_e json_data,
  776 + qpdf_stream_decode_level_e decode_level,
  777 + Pipeline* p,
  778 + std::string const& data_filename)
691 { 779 {
692 - this->qpdf->warn(qpdf_e_damaged_pdf, "", this->parsed_offset, message); 780 + return as_stream(error).getStreamJSON(json_version, json_data, decode_level, p, data_filename);
693 } 781 }
libqpdf/QPDF_String.cc
1 -#include <qpdf/QPDF_String.hh> 1 +#include <qpdf/QPDFObject_private.hh>
2 2
3 -#include <qpdf/JSON_writer.hh> 3 +#include <qpdf/QPDFObjectHandle_private.hh>
4 #include <qpdf/QUtil.hh> 4 #include <qpdf/QUtil.hh>
5 5
6 // DO NOT USE ctype -- it is locale dependent for some things, and it's not worth the risk of 6 // DO NOT USE ctype -- it is locale dependent for some things, and it's not worth the risk of
@@ -12,18 +12,6 @@ is_iso_latin1_printable(char ch) @@ -12,18 +12,6 @@ is_iso_latin1_printable(char ch)
12 return (((ch >= 32) && (ch <= 126)) || (static_cast<unsigned char>(ch) >= 160)); 12 return (((ch >= 32) && (ch <= 126)) || (static_cast<unsigned char>(ch) >= 160));
13 } 13 }
14 14
15 -QPDF_String::QPDF_String(std::string const& val) :  
16 - QPDFValue(::ot_string),  
17 - val(val)  
18 -{  
19 -}  
20 -  
21 -std::shared_ptr<QPDFObject>  
22 -QPDF_String::create(std::string const& val)  
23 -{  
24 - return do_create(new QPDF_String(val));  
25 -}  
26 -  
27 std::shared_ptr<QPDFObject> 15 std::shared_ptr<QPDFObject>
28 QPDF_String::create_utf16(std::string const& utf8_val) 16 QPDF_String::create_utf16(std::string const& utf8_val)
29 { 17 {
@@ -31,19 +19,7 @@ QPDF_String::create_utf16(std::string const&amp; utf8_val) @@ -31,19 +19,7 @@ QPDF_String::create_utf16(std::string const&amp; utf8_val)
31 if (!QUtil::utf8_to_pdf_doc(utf8_val, result, '?')) { 19 if (!QUtil::utf8_to_pdf_doc(utf8_val, result, '?')) {
32 result = QUtil::utf8_to_utf16(utf8_val); 20 result = QUtil::utf8_to_utf16(utf8_val);
33 } 21 }
34 - return do_create(new QPDF_String(result));  
35 -}  
36 -  
37 -std::shared_ptr<QPDFObject>  
38 -QPDF_String::copy(bool shallow)  
39 -{  
40 - return create(val);  
41 -}  
42 -  
43 -std::string  
44 -QPDF_String::unparse()  
45 -{  
46 - return unparse(false); 22 + return QPDFObject::create<QPDF_String>(result);
47 } 23 }
48 24
49 void 25 void
libqpdf/QPDF_Unresolved.cc deleted
1 -#include <qpdf/QPDF_Unresolved.hh>  
2 -  
3 -#include <qpdf/QPDF.hh>  
4 -#include <qpdf/QPDFObject_private.hh>  
5 -  
6 -QPDF_Unresolved::QPDF_Unresolved(QPDF* qpdf, QPDFObjGen og) :  
7 - QPDFValue(::ot_unresolved, qpdf, og)  
8 -{  
9 -}  
10 -  
11 -std::shared_ptr<QPDFObject>  
12 -QPDF_Unresolved::create(QPDF* qpdf, QPDFObjGen og)  
13 -{  
14 - return do_create(new QPDF_Unresolved(qpdf, og));  
15 -}  
16 -  
17 -std::shared_ptr<QPDFObject>  
18 -QPDF_Unresolved::copy(bool shallow)  
19 -{  
20 - return QPDF::Resolver::resolved(qpdf, og)->copy(shallow);  
21 -}  
22 -  
23 -std::string  
24 -QPDF_Unresolved::unparse()  
25 -{  
26 - return QPDF::Resolver::resolved(qpdf, og)->unparse();  
27 -}  
28 -  
29 -void  
30 -QPDF_Unresolved::writeJSON(int json_version, JSON::Writer& p)  
31 -{  
32 - QPDF::Resolver::resolved(qpdf, og)->writeJSON(json_version, p);  
33 -}  
34 -  
35 -std::string  
36 -QPDF_Unresolved::getStringValue() const  
37 -{  
38 - return QPDF::Resolver::resolved(qpdf, og)->getStringValue();  
39 -}  
libqpdf/QPDF_json.cc
@@ -5,10 +5,8 @@ @@ -5,10 +5,8 @@
5 #include <qpdf/Pl_Base64.hh> 5 #include <qpdf/Pl_Base64.hh>
6 #include <qpdf/Pl_StdioFile.hh> 6 #include <qpdf/Pl_StdioFile.hh>
7 #include <qpdf/QIntC.hh> 7 #include <qpdf/QIntC.hh>
  8 +#include <qpdf/QPDFObjectHandle_private.hh>
8 #include <qpdf/QPDFObject_private.hh> 9 #include <qpdf/QPDFObject_private.hh>
9 -#include <qpdf/QPDFValue.hh>  
10 -#include <qpdf/QPDF_Null.hh>  
11 -#include <qpdf/QPDF_Stream.hh>  
12 #include <qpdf/QTC.hh> 10 #include <qpdf/QTC.hh>
13 #include <qpdf/QUtil.hh> 11 #include <qpdf/QUtil.hh>
14 #include <algorithm> 12 #include <algorithm>
@@ -238,8 +236,8 @@ class QPDF::JSONReactor: public JSON::Reactor @@ -238,8 +236,8 @@ class QPDF::JSONReactor: public JSON::Reactor
238 is(is), 236 is(is),
239 must_be_complete(must_be_complete), 237 must_be_complete(must_be_complete),
240 descr( 238 descr(
241 - std::make_shared<QPDFValue::Description>(  
242 - QPDFValue::JSON_Descr(std::make_shared<std::string>(is->getName()), ""))) 239 + std::make_shared<QPDFObject::Description>(
  240 + QPDFObject::JSON_Descr(std::make_shared<std::string>(is->getName()), "")))
243 { 241 {
244 } 242 }
245 ~JSONReactor() override = default; 243 ~JSONReactor() override = default;
@@ -286,7 +284,7 @@ class QPDF::JSONReactor: public JSON::Reactor @@ -286,7 +284,7 @@ class QPDF::JSONReactor: public JSON::Reactor
286 QPDF& pdf; 284 QPDF& pdf;
287 std::shared_ptr<InputSource> is; 285 std::shared_ptr<InputSource> is;
288 bool must_be_complete{true}; 286 bool must_be_complete{true};
289 - std::shared_ptr<QPDFValue::Description> descr; 287 + std::shared_ptr<QPDFObject::Description> descr;
290 bool errors{false}; 288 bool errors{false};
291 bool saw_qpdf{false}; 289 bool saw_qpdf{false};
292 bool saw_qpdf_meta{false}; 290 bool saw_qpdf_meta{false};
@@ -576,8 +574,8 @@ QPDF::JSONReactor::dictionaryItem(std::string const&amp; key, JSON const&amp; value) @@ -576,8 +574,8 @@ QPDF::JSONReactor::dictionaryItem(std::string const&amp; key, JSON const&amp; value)
576 } else { 574 } else {
577 this_stream_needs_data = true; 575 this_stream_needs_data = true;
578 replaceObject( 576 replaceObject(
579 - QPDF_Stream::create(  
580 - &pdf, tos.object.getObjGen(), QPDFObjectHandle::newDictionary(), 0, 0), 577 + qpdf::Stream(
  578 + pdf, tos.object.getObjGen(), QPDFObjectHandle::newDictionary(), 0, 0),
581 value); 579 value);
582 } 580 }
583 next_obj = tos.object; 581 next_obj = tos.object;
@@ -706,10 +704,10 @@ QPDF::JSONReactor::arrayItem(JSON const&amp; value) @@ -706,10 +704,10 @@ QPDF::JSONReactor::arrayItem(JSON const&amp; value)
706 void 704 void
707 QPDF::JSONReactor::setObjectDescription(QPDFObjectHandle& oh, JSON const& value) 705 QPDF::JSONReactor::setObjectDescription(QPDFObjectHandle& oh, JSON const& value)
708 { 706 {
709 - auto j_descr = std::get<QPDFValue::JSON_Descr>(*descr); 707 + auto j_descr = std::get<QPDFObject::JSON_Descr>(*descr);
710 if (j_descr.object != cur_object) { 708 if (j_descr.object != cur_object) {
711 - descr = std::make_shared<QPDFValue::Description>(  
712 - QPDFValue::JSON_Descr(j_descr.input, cur_object)); 709 + descr = std::make_shared<QPDFObject::Description>(
  710 + QPDFObject::JSON_Descr(j_descr.input, cur_object));
713 } 711 }
714 712
715 oh.getObjectPtr()->setDescription(&pdf, descr, value.getStart()); 713 oh.getObjectPtr()->setDescription(&pdf, descr, value.getStart());
@@ -821,7 +819,7 @@ void @@ -821,7 +819,7 @@ void
821 writeJSONStreamFile( 819 writeJSONStreamFile(
822 int version, 820 int version,
823 JSON::Writer& jw, 821 JSON::Writer& jw,
824 - QPDF_Stream& stream, 822 + qpdf::Stream& stream,
825 int id, 823 int id,
826 qpdf_stream_decode_level_e decode_level, 824 qpdf_stream_decode_level_e decode_level,
827 std::string const& file_prefix) 825 std::string const& file_prefix)
@@ -894,13 +892,13 @@ QPDF::writeJSON( @@ -894,13 +892,13 @@ QPDF::writeJSON(
894 } else { 892 } else {
895 jw << "\n },\n \"" << key; 893 jw << "\n },\n \"" << key;
896 } 894 }
897 - if (auto* stream = obj.getObjectPtr()->as<QPDF_Stream>()) { 895 + if (auto stream = obj.as_stream()) {
898 jw << "\": {\n \"stream\": "; 896 jw << "\": {\n \"stream\": ";
899 if (json_stream_data == qpdf_sj_file) { 897 if (json_stream_data == qpdf_sj_file) {
900 writeJSONStreamFile( 898 writeJSONStreamFile(
901 - version, jw, *stream, og.getObj(), decode_level, file_prefix); 899 + version, jw, stream, og.getObj(), decode_level, file_prefix);
902 } else { 900 } else {
903 - stream->writeStreamJSON( 901 + stream.writeStreamJSON(
904 version, jw, json_stream_data, decode_level, nullptr, ""); 902 version, jw, json_stream_data, decode_level, nullptr, "");
905 } 903 }
906 } else { 904 } else {
libqpdf/QPDF_optimization.cc
@@ -5,9 +5,8 @@ @@ -5,9 +5,8 @@
5 #include <qpdf/QPDF.hh> 5 #include <qpdf/QPDF.hh>
6 6
7 #include <qpdf/QPDFExc.hh> 7 #include <qpdf/QPDFExc.hh>
  8 +#include <qpdf/QPDFObjectHandle_private.hh>
8 #include <qpdf/QPDFWriter_private.hh> 9 #include <qpdf/QPDFWriter_private.hh>
9 -#include <qpdf/QPDF_Array.hh>  
10 -#include <qpdf/QPDF_Dictionary.hh>  
11 #include <qpdf/QTC.hh> 10 #include <qpdf/QTC.hh>
12 11
13 QPDF::ObjUser::ObjUser() : 12 QPDF::ObjUser::ObjUser() :
@@ -115,24 +114,25 @@ QPDF::optimize_internal( @@ -115,24 +114,25 @@ QPDF::optimize_internal(
115 } 114 }
116 115
117 // Traverse document-level items 116 // Traverse document-level items
118 - for (auto const& key: m->trailer.getKeys()) { 117 + for (auto const& [key, value]: m->trailer.as_dictionary()) {
119 if (key == "/Root") { 118 if (key == "/Root") {
120 // handled separately 119 // handled separately
121 } else { 120 } else {
122 - updateObjectMaps(  
123 - ObjUser(ObjUser::ou_trailer_key, key),  
124 - m->trailer.getKey(key),  
125 - skip_stream_parameters); 121 + if (!value.null()) {
  122 + updateObjectMaps(
  123 + ObjUser(ObjUser::ou_trailer_key, key), value, skip_stream_parameters);
  124 + }
126 } 125 }
127 } 126 }
128 127
129 - for (auto const& key: root.getKeys()) { 128 + for (auto const& [key, value]: root.as_dictionary()) {
130 // Technically, /I keys from /Thread dictionaries are supposed to be handled separately, but 129 // Technically, /I keys from /Thread dictionaries are supposed to be handled separately, but
131 // we are going to disregard that specification for now. There is loads of evidence that 130 // we are going to disregard that specification for now. There is loads of evidence that
132 // pdlin and Acrobat both disregard things like this from time to time, so this is almost 131 // pdlin and Acrobat both disregard things like this from time to time, so this is almost
133 // certain not to cause any problems. 132 // certain not to cause any problems.
134 - updateObjectMaps(  
135 - ObjUser(ObjUser::ou_root_key, key), root.getKey(key), skip_stream_parameters); 133 + if (!value.null()) {
  134 + updateObjectMaps(ObjUser(ObjUser::ou_root_key, key), value, skip_stream_parameters);
  135 + }
136 } 136 }
137 137
138 ObjUser root_ou = ObjUser(ObjUser::ou_root); 138 ObjUser root_ou = ObjUser(ObjUser::ou_root);
@@ -319,9 +319,8 @@ QPDF::updateObjectMaps( @@ -319,9 +319,8 @@ QPDF::updateObjectMaps(
319 } 319 }
320 320
321 if (cur.oh.isArray()) { 321 if (cur.oh.isArray()) {
322 - int n = cur.oh.getArrayNItems();  
323 - for (int i = 0; i < n; ++i) {  
324 - pending.emplace_back(cur.ou, cur.oh.getArrayItem(i), false); 322 + for (auto const& item: cur.oh.as_array()) {
  323 + pending.emplace_back(cur.ou, item, false);
325 } 324 }
326 } else if (cur.oh.isDictionary() || cur.oh.isStream()) { 325 } else if (cur.oh.isDictionary() || cur.oh.isStream()) {
327 QPDFObjectHandle dict = cur.oh; 326 QPDFObjectHandle dict = cur.oh;
@@ -334,7 +333,11 @@ QPDF::updateObjectMaps( @@ -334,7 +333,11 @@ QPDF::updateObjectMaps(
334 } 333 }
335 } 334 }
336 335
337 - for (auto const& key: dict.getKeys()) { 336 + for (auto& [key, value]: dict.as_dictionary()) {
  337 + if (value.null()) {
  338 + continue;
  339 + }
  340 +
338 if (is_page_node && (key == "/Thumb")) { 341 if (is_page_node && (key == "/Thumb")) {
339 // Traverse page thumbnail dictionaries as a special case. There can only ever 342 // Traverse page thumbnail dictionaries as a special case. There can only ever
340 // be one /Thumb key on a page, and we see at most one page node per call. 343 // be one /Thumb key on a page, and we see at most one page node per call.
@@ -347,7 +350,7 @@ QPDF::updateObjectMaps( @@ -347,7 +350,7 @@ QPDF::updateObjectMaps(
347 ((ssp >= 2) && ((key == "/Filter") || (key == "/DecodeParms")))) { 350 ((ssp >= 2) && ((key == "/Filter") || (key == "/DecodeParms")))) {
348 // Don't traverse into stream parameters that we are not going to write. 351 // Don't traverse into stream parameters that we are not going to write.
349 } else { 352 } else {
350 - pending.emplace_back(cur.ou, dict.getKey(key), false); 353 + pending.emplace_back(cur.ou, value, false);
351 } 354 }
352 } 355 }
353 } 356 }
libqpdf/QPDF_pages.cc
1 #include <qpdf/QPDF.hh> 1 #include <qpdf/QPDF.hh>
2 2
3 #include <qpdf/QPDFExc.hh> 3 #include <qpdf/QPDFExc.hh>
  4 +#include <qpdf/QPDFObjectHandle_private.hh>
4 #include <qpdf/QTC.hh> 5 #include <qpdf/QTC.hh>
5 #include <qpdf/QUtil.hh> 6 #include <qpdf/QUtil.hh>
6 7
@@ -108,9 +109,9 @@ QPDF::getAllPagesInternal( @@ -108,9 +109,9 @@ QPDF::getAllPagesInternal(
108 QTC::TC("qpdf", "QPDF inherit mediabox", media_box ? 0 : 1); 109 QTC::TC("qpdf", "QPDF inherit mediabox", media_box ? 0 : 1);
109 } 110 }
110 auto kids = cur_node.getKey("/Kids"); 111 auto kids = cur_node.getKey("/Kids");
111 - int n = kids.getArrayNItems();  
112 - for (int i = 0; i < n; ++i) {  
113 - auto kid = kids.getArrayItem(i); 112 + int i = -1;
  113 + for (auto& kid: kids.as_array()) {
  114 + ++i;
114 if (!kid.isDictionary()) { 115 if (!kid.isDictionary()) {
115 kid.warnIfPossible("Pages tree includes non-dictionary object; ignoring"); 116 kid.warnIfPossible("Pages tree includes non-dictionary object; ignoring");
116 m->invalid_page_found = true; 117 m->invalid_page_found = true;
@@ -133,7 +134,6 @@ QPDF::getAllPagesInternal( @@ -133,7 +134,6 @@ QPDF::getAllPagesInternal(
133 cur_node.warnIfPossible( 134 cur_node.warnIfPossible(
134 "kid " + std::to_string(i) + " (from 0) is direct; converting to indirect"); 135 "kid " + std::to_string(i) + " (from 0) is direct; converting to indirect");
135 kid = makeIndirectObject(kid); 136 kid = makeIndirectObject(kid);
136 - kids.setArrayItem(i, kid);  
137 } else if (!seen.add(kid)) { 137 } else if (!seen.add(kid)) {
138 // Make a copy of the page. This does the same as shallowCopyPage in 138 // Make a copy of the page. This does the same as shallowCopyPage in
139 // QPDFPageObjectHelper. 139 // QPDFPageObjectHelper.
@@ -144,7 +144,6 @@ QPDF::getAllPagesInternal( @@ -144,7 +144,6 @@ QPDF::getAllPagesInternal(
144 " creating a new page object as a copy"); 144 " creating a new page object as a copy");
145 kid = makeIndirectObject(QPDFObjectHandle(kid).shallowCopy()); 145 kid = makeIndirectObject(QPDFObjectHandle(kid).shallowCopy());
146 seen.add(kid); 146 seen.add(kid);
147 - kids.setArrayItem(i, kid);  
148 } 147 }
149 if (!kid.isDictionaryOfType("/Page")) { 148 if (!kid.isDictionaryOfType("/Page")) {
150 kid.warnIfPossible("/Type key should be /Page but is not; overriding"); 149 kid.warnIfPossible("/Type key should be /Page but is not; overriding");
libqpdf/qpdf/QPDFObjectHandle_private.hh 0 → 100644
  1 +#ifndef OBJECTHANDLE_PRIVATE_HH
  2 +#define OBJECTHANDLE_PRIVATE_HH
  3 +
  4 +#include <qpdf/QPDFObjectHandle.hh>
  5 +
  6 +#include <qpdf/QPDFObject_private.hh>
  7 +#include <qpdf/QUtil.hh>
  8 +
  9 +namespace qpdf
  10 +{
  11 + class Array final: public BaseHandle
  12 + {
  13 + public:
  14 + explicit Array(std::shared_ptr<QPDFObject> const& obj) :
  15 + BaseHandle(obj)
  16 + {
  17 + }
  18 +
  19 + explicit Array(std::shared_ptr<QPDFObject>&& obj) :
  20 + BaseHandle(std::move(obj))
  21 + {
  22 + }
  23 +
  24 + using iterator = std::vector<QPDFObjectHandle>::iterator;
  25 + using const_iterator = std::vector<QPDFObjectHandle>::const_iterator;
  26 + using const_reverse_iterator = std::vector<QPDFObjectHandle>::const_reverse_iterator;
  27 +
  28 + iterator begin();
  29 +
  30 + iterator end();
  31 +
  32 + const_iterator cbegin();
  33 +
  34 + const_iterator cend();
  35 +
  36 + const_reverse_iterator crbegin();
  37 +
  38 + const_reverse_iterator crend();
  39 +
  40 + int size() const;
  41 + std::pair<bool, QPDFObjectHandle> at(int n) const;
  42 + bool setAt(int at, QPDFObjectHandle const& oh);
  43 + bool insert(int at, QPDFObjectHandle const& item);
  44 + void push_back(QPDFObjectHandle const& item);
  45 + bool erase(int at);
  46 +
  47 + std::vector<QPDFObjectHandle> getAsVector() const;
  48 + void setFromVector(std::vector<QPDFObjectHandle> const& items);
  49 +
  50 + private:
  51 + QPDF_Array* array() const;
  52 + void checkOwnership(QPDFObjectHandle const& item) const;
  53 + QPDFObjectHandle null() const;
  54 +
  55 + std::unique_ptr<std::vector<QPDFObjectHandle>> sp_elements{};
  56 + };
  57 +
  58 + // BaseDictionary is only used as a base class. It does not contain any methods exposed in the
  59 + // public API.
  60 + class BaseDictionary: public BaseHandle
  61 + {
  62 + public:
  63 + using iterator = std::map<std::string, QPDFObjectHandle>::iterator;
  64 + using const_iterator = std::map<std::string, QPDFObjectHandle>::const_iterator;
  65 + using reverse_iterator = std::map<std::string, QPDFObjectHandle>::reverse_iterator;
  66 + using const_reverse_iterator =
  67 + std::map<std::string, QPDFObjectHandle>::const_reverse_iterator;
  68 +
  69 + iterator
  70 + begin()
  71 + {
  72 + if (auto d = as<QPDF_Dictionary>()) {
  73 + return d->items.begin();
  74 + }
  75 + return {};
  76 + }
  77 +
  78 + iterator
  79 + end()
  80 + {
  81 + if (auto d = as<QPDF_Dictionary>()) {
  82 + return d->items.end();
  83 + }
  84 + return {};
  85 + }
  86 +
  87 + const_iterator
  88 + cbegin()
  89 + {
  90 + if (auto d = as<QPDF_Dictionary>()) {
  91 + return d->items.cbegin();
  92 + }
  93 + return {};
  94 + }
  95 +
  96 + const_iterator
  97 + cend()
  98 + {
  99 + if (auto d = as<QPDF_Dictionary>()) {
  100 + return d->items.cend();
  101 + }
  102 + return {};
  103 + }
  104 +
  105 + reverse_iterator
  106 + rbegin()
  107 + {
  108 + if (auto d = as<QPDF_Dictionary>()) {
  109 + return d->items.rbegin();
  110 + }
  111 + return {};
  112 + }
  113 +
  114 + reverse_iterator
  115 + rend()
  116 + {
  117 + if (auto d = as<QPDF_Dictionary>()) {
  118 + return d->items.rend();
  119 + }
  120 + return {};
  121 + }
  122 +
  123 + const_reverse_iterator
  124 + crbegin()
  125 + {
  126 + if (auto d = as<QPDF_Dictionary>()) {
  127 + return d->items.crbegin();
  128 + }
  129 + return {};
  130 + }
  131 +
  132 + const_reverse_iterator
  133 + crend()
  134 + {
  135 + if (auto d = as<QPDF_Dictionary>()) {
  136 + return d->items.crend();
  137 + }
  138 + return {};
  139 + }
  140 +
  141 + // The following methods are not part of the public API.
  142 + bool hasKey(std::string const& key) const;
  143 + QPDFObjectHandle getKey(std::string const& key) const;
  144 + std::set<std::string> getKeys();
  145 + std::map<std::string, QPDFObjectHandle> const& getAsMap() const;
  146 + void removeKey(std::string const& key);
  147 + void replaceKey(std::string const& key, QPDFObjectHandle value);
  148 +
  149 + protected:
  150 + BaseDictionary() = default;
  151 + BaseDictionary(std::shared_ptr<QPDFObject> const& obj) :
  152 + BaseHandle(obj) {};
  153 + BaseDictionary(std::shared_ptr<QPDFObject>&& obj) :
  154 + BaseHandle(std::move(obj)) {};
  155 + BaseDictionary(BaseDictionary const&) = default;
  156 + BaseDictionary& operator=(BaseDictionary const&) = default;
  157 + BaseDictionary(BaseDictionary&&) = default;
  158 + BaseDictionary& operator=(BaseDictionary&&) = default;
  159 + ~BaseDictionary() = default;
  160 +
  161 + QPDF_Dictionary* dict() const;
  162 + };
  163 +
  164 + class Dictionary final: public BaseDictionary
  165 + {
  166 + public:
  167 + explicit Dictionary(std::shared_ptr<QPDFObject> const& obj) :
  168 + BaseDictionary(obj)
  169 + {
  170 + }
  171 +
  172 + explicit Dictionary(std::shared_ptr<QPDFObject>&& obj) :
  173 + BaseDictionary(std::move(obj))
  174 + {
  175 + }
  176 + };
  177 +
  178 + class Name final: public BaseHandle
  179 + {
  180 + public:
  181 + // Put # into strings with characters unsuitable for name token
  182 + static std::string normalize(std::string const& name);
  183 +
  184 + // Check whether name is valid utf-8 and whether it contains characters that require
  185 + // escaping. Return {false, false} if the name is not valid utf-8, otherwise return {true,
  186 + // true} if no characters require or {true, false} if escaping is required.
  187 + static std::pair<bool, bool> analyzeJSONEncoding(std::string const& name);
  188 + };
  189 +
  190 + class Stream final: public BaseHandle
  191 + {
  192 + public:
  193 + explicit Stream(std::shared_ptr<QPDFObject> const& obj) :
  194 + BaseHandle(obj)
  195 + {
  196 + }
  197 +
  198 + explicit Stream(std::shared_ptr<QPDFObject>&& obj) :
  199 + BaseHandle(std::move(obj))
  200 + {
  201 + }
  202 +
  203 + Stream(
  204 + QPDF& qpdf,
  205 + QPDFObjGen og,
  206 + QPDFObjectHandle stream_dict,
  207 + qpdf_offset_t offset,
  208 + size_t length);
  209 +
  210 + QPDFObjectHandle
  211 + getDict() const
  212 + {
  213 + return stream()->stream_dict;
  214 + }
  215 + bool
  216 + isDataModified() const
  217 + {
  218 + return !stream()->token_filters.empty();
  219 + }
  220 + void
  221 + setFilterOnWrite(bool val)
  222 + {
  223 + stream()->filter_on_write = val;
  224 + }
  225 + bool
  226 + getFilterOnWrite() const
  227 + {
  228 + return stream()->filter_on_write;
  229 + }
  230 +
  231 + // Methods to help QPDF copy foreign streams
  232 + size_t
  233 + getLength() const
  234 + {
  235 + return stream()->length;
  236 + }
  237 + std::shared_ptr<Buffer>
  238 + getStreamDataBuffer() const
  239 + {
  240 + return stream()->stream_data;
  241 + }
  242 + std::shared_ptr<QPDFObjectHandle::StreamDataProvider>
  243 + getStreamDataProvider() const
  244 + {
  245 + return stream()->stream_provider;
  246 + }
  247 +
  248 + // See comments in QPDFObjectHandle.hh for these methods.
  249 + bool pipeStreamData(
  250 + Pipeline* p,
  251 + bool* tried_filtering,
  252 + int encode_flags,
  253 + qpdf_stream_decode_level_e decode_level,
  254 + bool suppress_warnings,
  255 + bool will_retry);
  256 + std::shared_ptr<Buffer> getStreamData(qpdf_stream_decode_level_e level);
  257 + std::shared_ptr<Buffer> getRawStreamData();
  258 + void replaceStreamData(
  259 + std::shared_ptr<Buffer> data,
  260 + QPDFObjectHandle const& filter,
  261 + QPDFObjectHandle const& decode_parms);
  262 + void replaceStreamData(
  263 + std::shared_ptr<QPDFObjectHandle::StreamDataProvider> provider,
  264 + QPDFObjectHandle const& filter,
  265 + QPDFObjectHandle const& decode_parms);
  266 + void
  267 + addTokenFilter(std::shared_ptr<QPDFObjectHandle::TokenFilter> token_filter)
  268 + {
  269 + stream()->token_filters.emplace_back(token_filter);
  270 + }
  271 + JSON getStreamJSON(
  272 + int json_version,
  273 + qpdf_json_stream_data_e json_data,
  274 + qpdf_stream_decode_level_e decode_level,
  275 + Pipeline* p,
  276 + std::string const& data_filename);
  277 + qpdf_stream_decode_level_e writeStreamJSON(
  278 + int json_version,
  279 + JSON::Writer& jw,
  280 + qpdf_json_stream_data_e json_data,
  281 + qpdf_stream_decode_level_e decode_level,
  282 + Pipeline* p,
  283 + std::string const& data_filename,
  284 + bool no_data_key = false);
  285 + void
  286 + replaceDict(QPDFObjectHandle const& new_dict)
  287 + {
  288 + auto s = stream();
  289 + s->stream_dict = new_dict;
  290 + setDictDescription();
  291 + }
  292 +
  293 + void setDictDescription();
  294 +
  295 + static void registerStreamFilter(
  296 + std::string const& filter_name,
  297 + std::function<std::shared_ptr<QPDFStreamFilter>()> factory);
  298 +
  299 + private:
  300 + QPDF_Stream::Members*
  301 + stream() const
  302 + {
  303 + if (auto s = as<QPDF_Stream>()) {
  304 + if (auto ptr = s->m.get()) {
  305 + return ptr;
  306 + }
  307 + throw std::logic_error("QPDF_Stream: unexpected nullptr");
  308 + }
  309 + throw std::runtime_error("operation for stream attempted on non-stream object");
  310 + return nullptr; // unreachable
  311 + }
  312 + bool filterable(
  313 + std::vector<std::shared_ptr<QPDFStreamFilter>>& filters,
  314 + bool& specialized_compression,
  315 + bool& lossy_compression);
  316 + void replaceFilterData(
  317 + QPDFObjectHandle const& filter, QPDFObjectHandle const& decode_parms, size_t length);
  318 +
  319 + void warn(std::string const& message);
  320 +
  321 + static std::map<std::string, std::string> filter_abbreviations;
  322 + static std::map<std::string, std::function<std::shared_ptr<QPDFStreamFilter>()>>
  323 + filter_factories;
  324 + };
  325 +
  326 + template <typename T>
  327 + T*
  328 + BaseHandle::as() const
  329 + {
  330 + if (!obj) {
  331 + return nullptr;
  332 + }
  333 + if (std::holds_alternative<T>(obj->value)) {
  334 + return &std::get<T>(obj->value);
  335 + }
  336 + if (std::holds_alternative<QPDF_Unresolved>(obj->value)) {
  337 + return BaseHandle(QPDF::Resolver::resolved(obj->qpdf, obj->og)).as<T>();
  338 + }
  339 + if (std::holds_alternative<QPDF_Reference>(obj->value)) {
  340 + // see comment in QPDF_Reference.
  341 + return BaseHandle(std::get<QPDF_Reference>(obj->value).obj).as<T>();
  342 + }
  343 + return nullptr;
  344 + }
  345 +
  346 + inline QPDFObjGen
  347 + BaseHandle::id_gen() const
  348 + {
  349 + return obj ? obj->og : QPDFObjGen();
  350 + }
  351 +
  352 + inline bool
  353 + BaseHandle::indirect() const
  354 + {
  355 + return obj ? obj->og.isIndirect() : false;
  356 + }
  357 +
  358 + inline bool
  359 + BaseHandle::null() const
  360 + {
  361 + return !obj || obj->getResolvedTypeCode() == ::ot_null;
  362 + }
  363 +
  364 + inline QPDF*
  365 + BaseHandle::qpdf() const
  366 + {
  367 + return obj ? obj->qpdf : nullptr;
  368 + }
  369 +
  370 + inline qpdf_object_type_e
  371 + BaseHandle::raw_type_code() const
  372 + {
  373 + return obj ? static_cast<qpdf_object_type_e>(obj->value.index()) : ::ot_uninitialized;
  374 + }
  375 +
  376 + inline qpdf_object_type_e
  377 + BaseHandle::type_code() const
  378 + {
  379 + if (!obj) {
  380 + return ::ot_uninitialized;
  381 + }
  382 + if (raw_type_code() == ::ot_unresolved) {
  383 + return QPDF::Resolver::resolved(obj->qpdf, obj->og)->getTypeCode();
  384 + }
  385 + if (raw_type_code() == ::ot_reference) {
  386 + return std::get<QPDF_Reference>(obj->value).obj->getResolvedTypeCode();
  387 + }
  388 + return raw_type_code();
  389 + }
  390 +
  391 +} // namespace qpdf
  392 +
  393 +inline QPDF_Dictionary::QPDF_Dictionary(std::map<std::string, QPDFObjectHandle>&& items) :
  394 + items(std::move(items))
  395 +{
  396 +}
  397 +
  398 +inline std::shared_ptr<QPDFObject>
  399 +QPDF_Null::create(
  400 + std::shared_ptr<QPDFObject> parent, std::string_view const& static_descr, std::string var_descr)
  401 +{
  402 + auto n = QPDFObject::create<QPDF_Null>();
  403 + n->setChildDescription(parent->getQPDF(), parent, static_descr, var_descr);
  404 + return n;
  405 +}
  406 +
  407 +inline QPDF_Real::QPDF_Real(double value, int decimal_places, bool trim_trailing_zeroes) :
  408 + val(QUtil::double_to_string(value, decimal_places, trim_trailing_zeroes))
  409 +{
  410 +}
  411 +
  412 +template <typename T, typename... Args>
  413 +inline std::shared_ptr<QPDFObject>
  414 +QPDFObject::create(Args&&... args)
  415 +{
  416 + return std::make_shared<QPDFObject>(std::forward<T>(T(std::forward<Args>(args)...)));
  417 +}
  418 +
  419 +inline qpdf::Array
  420 +QPDFObjectHandle::as_array(qpdf::typed options) const
  421 +{
  422 + if (options & qpdf::error) {
  423 + assertType("array", false);
  424 + }
  425 + if (options & qpdf::any_flag || type_code() == ::ot_array ||
  426 + (options & qpdf::optional && type_code() == ::ot_null)) {
  427 + return qpdf::Array(obj);
  428 + }
  429 + return qpdf::Array(std::shared_ptr<QPDFObject>());
  430 +}
  431 +
  432 +inline qpdf::Dictionary
  433 +QPDFObjectHandle::as_dictionary(qpdf::typed options) const
  434 +{
  435 + if (options & qpdf::any_flag || type_code() == ::ot_dictionary ||
  436 + (options & qpdf::optional && type_code() == ::ot_null)) {
  437 + return qpdf::Dictionary(obj);
  438 + }
  439 + if (options & qpdf::error) {
  440 + assertType("dictionary", false);
  441 + }
  442 + return qpdf::Dictionary(std::shared_ptr<QPDFObject>());
  443 +}
  444 +
  445 +inline qpdf::Stream
  446 +QPDFObjectHandle::as_stream(qpdf::typed options) const
  447 +{
  448 + if (options & qpdf::any_flag || type_code() == ::ot_stream ||
  449 + (options & qpdf::optional && type_code() == ::ot_null)) {
  450 + return qpdf::Stream(obj);
  451 + }
  452 + if (options & qpdf::error) {
  453 + assertType("stream", false);
  454 + }
  455 + return qpdf::Stream(std::shared_ptr<QPDFObject>());
  456 +}
  457 +
  458 +#endif // OBJECTHANDLE_PRIVATE_HH
libqpdf/qpdf/QPDFObject_private.hh
@@ -6,182 +6,486 @@ @@ -6,182 +6,486 @@
6 6
7 #include <qpdf/Constants.h> 7 #include <qpdf/Constants.h>
8 #include <qpdf/JSON.hh> 8 #include <qpdf/JSON.hh>
  9 +#include <qpdf/JSON_writer.hh>
9 #include <qpdf/QPDF.hh> 10 #include <qpdf/QPDF.hh>
10 -#include <qpdf/QPDFValue.hh> 11 +#include <qpdf/QPDFObjGen.hh>
11 #include <qpdf/Types.h> 12 #include <qpdf/Types.h>
12 13
  14 +#include <map>
  15 +#include <memory>
13 #include <string> 16 #include <string>
14 #include <string_view> 17 #include <string_view>
  18 +#include <variant>
  19 +#include <vector>
15 20
16 -class QPDF; 21 +class QPDFObject;
17 class QPDFObjectHandle; 22 class QPDFObjectHandle;
18 23
19 -class QPDFObject 24 +namespace qpdf
20 { 25 {
21 - friend class QPDFValue; 26 + class Array;
  27 + class BaseDictionary;
  28 + class Dictionary;
  29 + class Stream;
  30 +} // namespace qpdf
22 31
23 - public:  
24 - QPDFObject() = default; 32 +class QPDF_Array final
  33 +{
  34 + private:
  35 + struct Sparse
  36 + {
  37 + int size{0};
  38 + std::map<int, QPDFObjectHandle> elements;
  39 + };
25 40
26 - std::shared_ptr<QPDFObject>  
27 - copy(bool shallow = false) 41 + public:
  42 + QPDF_Array() = default;
  43 + QPDF_Array(QPDF_Array const& other) :
  44 + sp(other.sp ? std::make_unique<Sparse>(*other.sp) : nullptr)
28 { 45 {
29 - return value->copy(shallow);  
30 } 46 }
31 - std::string  
32 - unparse() 47 +
  48 + QPDF_Array(QPDF_Array&&) = default;
  49 + QPDF_Array& operator=(QPDF_Array&&) = default;
  50 +
  51 + private:
  52 + friend class QPDFObject;
  53 + friend class qpdf::Array;
  54 + QPDF_Array(std::vector<QPDFObjectHandle> const& items) :
  55 + elements(items)
33 { 56 {
34 - return value->unparse();  
35 } 57 }
36 - void  
37 - writeJSON(int json_version, JSON::Writer& p) 58 + QPDF_Array(std::vector<QPDFObjectHandle>&& items, bool sparse);
  59 +
  60 + QPDF_Array(std::vector<QPDFObjectHandle>&& items) :
  61 + elements(std::move(items))
38 { 62 {
39 - return value->writeJSON(json_version, p);  
40 } 63 }
41 - std::string  
42 - getStringValue() const 64 +
  65 + int
  66 + size() const
43 { 67 {
44 - return value->getStringValue(); 68 + return sp ? sp->size : int(elements.size());
45 } 69 }
46 - // Return a unique type code for the resolved object  
47 - qpdf_object_type_e  
48 - getResolvedTypeCode() const 70 +
  71 + std::unique_ptr<Sparse> sp;
  72 + std::vector<QPDFObjectHandle> elements;
  73 +};
  74 +
  75 +class QPDF_Bool final
  76 +{
  77 + friend class QPDFObject;
  78 + friend class QPDFObjectHandle;
  79 +
  80 + explicit QPDF_Bool(bool val) :
  81 + val(val)
49 { 82 {
50 - auto tc = value->type_code;  
51 - return tc == ::ot_unresolved  
52 - ? QPDF::Resolver::resolved(value->qpdf, value->og)->value->type_code  
53 - : tc;  
54 } 83 }
55 - // Return a unique type code for the object  
56 - qpdf_object_type_e  
57 - getTypeCode() const noexcept 84 + bool val;
  85 +};
  86 +
  87 +class QPDF_Destroyed final
  88 +{
  89 +};
  90 +
  91 +class QPDF_Dictionary final
  92 +{
  93 + friend class QPDFObject;
  94 + friend class qpdf::BaseDictionary;
  95 +
  96 + QPDF_Dictionary(std::map<std::string, QPDFObjectHandle> const& items) :
  97 + items(items)
58 { 98 {
59 - return value->type_code;  
60 } 99 }
  100 + inline QPDF_Dictionary(std::map<std::string, QPDFObjectHandle>&& items);
61 101
62 - QPDF*  
63 - getQPDF() const 102 + std::map<std::string, QPDFObjectHandle> items;
  103 +};
  104 +
  105 +class QPDF_InlineImage final
  106 +{
  107 + friend class QPDFObject;
  108 +
  109 + explicit QPDF_InlineImage(std::string val) :
  110 + val(std::move(val))
64 { 111 {
65 - return value->qpdf;  
66 } 112 }
67 - QPDFObjGen  
68 - getObjGen() const 113 + std::string val;
  114 +};
  115 +
  116 +class QPDF_Integer final
  117 +{
  118 + friend class QPDFObject;
  119 + friend class QPDFObjectHandle;
  120 +
  121 + QPDF_Integer(long long val) :
  122 + val(val)
69 { 123 {
70 - return value->og;  
71 } 124 }
72 - void  
73 - setDescription(  
74 - QPDF* qpdf, std::shared_ptr<QPDFValue::Description>& description, qpdf_offset_t offset = -1) 125 + long long val;
  126 +};
  127 +
  128 +class QPDF_Name final
  129 +{
  130 + friend class QPDFObject;
  131 +
  132 + explicit QPDF_Name(std::string name) :
  133 + name(std::move(name))
75 { 134 {
76 - return value->setDescription(qpdf, description, offset);  
77 } 135 }
78 - void  
79 - setChildDescription( 136 + std::string name;
  137 +};
  138 +
  139 +class QPDF_Null final
  140 +{
  141 + friend class QPDFObject;
  142 +
  143 + public:
  144 + static inline std::shared_ptr<QPDFObject> create(
80 std::shared_ptr<QPDFObject> parent, 145 std::shared_ptr<QPDFObject> parent,
81 std::string_view const& static_descr, 146 std::string_view const& static_descr,
82 - std::string var_descr) 147 + std::string var_descr);
  148 +};
  149 +
  150 +class QPDF_Operator final
  151 +{
  152 + friend class QPDFObject;
  153 +
  154 + QPDF_Operator(std::string val) :
  155 + val(std::move(val))
83 { 156 {
84 - auto qpdf = parent ? parent->value->qpdf : nullptr;  
85 - value->setChildDescription(qpdf, parent->value, static_descr, var_descr);  
86 } 157 }
87 - void  
88 - setChildDescription(  
89 - std::shared_ptr<QPDFValue> parent,  
90 - std::string_view const& static_descr,  
91 - std::string var_descr) 158 +
  159 + std::string val;
  160 +};
  161 +
  162 +class QPDF_Real final
  163 +{
  164 + friend class QPDFObject;
  165 +
  166 + QPDF_Real(std::string val) :
  167 + val(std::move(val))
92 { 168 {
93 - auto qpdf = parent ? parent->qpdf : nullptr;  
94 - value->setChildDescription(qpdf, parent, static_descr, var_descr);  
95 } 169 }
96 - bool  
97 - getDescription(QPDF*& qpdf, std::string& description) 170 + inline QPDF_Real(double value, int decimal_places, bool trim_trailing_zeroes);
  171 + // Store reals as strings to avoid roundoff errors.
  172 + std::string val;
  173 +};
  174 +
  175 +class QPDF_Reference
  176 +{
  177 + // This is a minimal implementation to support QPDF::replaceObject. Once we support parsing of
  178 + // objects that are an indirect reference we will need to support multiple levels of
  179 + // indirection, including the possibility of circular references.
  180 + friend class QPDFObject;
  181 + friend class qpdf::BaseHandle;
  182 +
  183 + QPDF_Reference(std::shared_ptr<QPDFObject> obj) :
  184 + obj(std::move(obj))
98 { 185 {
99 - qpdf = value->qpdf;  
100 - description = value->getDescription();  
101 - return qpdf != nullptr;  
102 } 186 }
103 - bool  
104 - hasDescription() 187 +
  188 + std::shared_ptr<QPDFObject> obj;
  189 +};
  190 +
  191 +class QPDF_Reserved final
  192 +{
  193 +};
  194 +
  195 +class QPDF_Stream final
  196 +{
  197 + class Members
105 { 198 {
106 - return value->hasDescription(); 199 + friend class QPDF_Stream;
  200 + friend class QPDFObject;
  201 + friend class qpdf::Stream;
  202 +
  203 + public:
  204 + Members(QPDFObjectHandle stream_dict, size_t length) :
  205 + stream_dict(std::move(stream_dict)),
  206 + length(length)
  207 + {
  208 + }
  209 +
  210 + private:
  211 + bool filter_on_write{true};
  212 + QPDFObjectHandle stream_dict;
  213 + size_t length{0};
  214 + std::shared_ptr<Buffer> stream_data;
  215 + std::shared_ptr<QPDFObjectHandle::StreamDataProvider> stream_provider;
  216 + std::vector<std::shared_ptr<QPDFObjectHandle::TokenFilter>> token_filters;
  217 + };
  218 +
  219 + friend class QPDFObject;
  220 + friend class qpdf::Stream;
  221 +
  222 + QPDF_Stream(QPDFObjectHandle stream_dict, size_t length) :
  223 + m(std::make_unique<Members>(stream_dict, length))
  224 + {
  225 + if (!stream_dict.isDictionary()) {
  226 + throw std::logic_error(
  227 + "stream object instantiated with non-dictionary object for dictionary");
  228 + }
107 } 229 }
108 - void  
109 - setParsedOffset(qpdf_offset_t offset) 230 +
  231 + std::unique_ptr<Members> m;
  232 +};
  233 +
  234 +// QPDF_Strings may included embedded null characters.
  235 +class QPDF_String final
  236 +{
  237 + friend class QPDFObject;
  238 + friend class QPDFWriter;
  239 +
  240 + public:
  241 + static std::shared_ptr<QPDFObject> create_utf16(std::string const& utf8_val);
  242 + std::string unparse(bool force_binary = false);
  243 + void writeJSON(int json_version, JSON::Writer& p);
  244 + std::string getUTF8Val() const;
  245 +
  246 + private:
  247 + QPDF_String(std::string val) :
  248 + val(std::move(val))
110 { 249 {
111 - value->setParsedOffset(offset);  
112 } 250 }
113 - qpdf_offset_t  
114 - getParsedOffset() 251 + bool useHexString() const;
  252 + std::string val;
  253 +};
  254 +
  255 +class QPDF_Unresolved final
  256 +{
  257 +};
  258 +
  259 +class QPDFObject
  260 +{
  261 + public:
  262 + template <typename T>
  263 + QPDFObject(T&& value) :
  264 + value(std::forward<T>(value))
115 { 265 {
116 - return value->getParsedOffset();  
117 } 266 }
118 - void  
119 - assign(std::shared_ptr<QPDFObject> o) 267 +
  268 + template <typename T>
  269 + QPDFObject(QPDF* qpdf, QPDFObjGen og, T&& value) :
  270 + value(std::forward<T>(value)),
  271 + qpdf(qpdf),
  272 + og(og)
120 { 273 {
121 - value = o->value;  
122 } 274 }
123 - void  
124 - swapWith(std::shared_ptr<QPDFObject> o) 275 +
  276 + template <typename T, typename... Args>
  277 + inline static std::shared_ptr<QPDFObject> create(Args&&... args);
  278 +
  279 + template <typename T, typename... Args>
  280 + inline static std::shared_ptr<QPDFObject>
  281 + create(QPDF* qpdf, QPDFObjGen og, Args&&... args)
125 { 282 {
126 - auto v = value;  
127 - value = o->value;  
128 - o->value = v;  
129 - auto og = value->og;  
130 - value->og = o->value->og;  
131 - o->value->og = og; 283 + return std::make_shared<QPDFObject>(
  284 + qpdf, og, std::forward<T>(T(std::forward<Args>(args)...)));
132 } 285 }
133 286
  287 + std::shared_ptr<QPDFObject> copy(bool shallow = false);
  288 + std::string unparse();
  289 + void write_json(int json_version, JSON::Writer& p);
  290 + void disconnect();
  291 + std::string getStringValue() const;
  292 +
  293 + // Return a unique type code for the resolved object
  294 + qpdf_object_type_e
  295 + getResolvedTypeCode() const
  296 + {
  297 + if (getTypeCode() == ::ot_unresolved) {
  298 + return QPDF::Resolver::resolved(qpdf, og)->getTypeCode();
  299 + }
  300 + if (getTypeCode() == ::ot_reference) {
  301 + return std::get<QPDF_Reference>(value).obj->getResolvedTypeCode();
  302 + }
  303 + return getTypeCode();
  304 + }
  305 + // Return a unique type code for the object
  306 + qpdf_object_type_e
  307 + getTypeCode() const
  308 + {
  309 + return static_cast<qpdf_object_type_e>(value.index());
  310 + }
134 void 311 void
135 - setDefaultDescription(QPDF* qpdf, QPDFObjGen og) 312 + assign_null()
136 { 313 {
137 - // Intended for use by the QPDF class  
138 - value->setDefaultDescription(qpdf, og); 314 + value = QPDF_Null();
  315 + qpdf = nullptr;
  316 + og = QPDFObjGen();
  317 + object_description = nullptr;
  318 + parsed_offset = -1;
139 } 319 }
140 void 320 void
141 - setObjGen(QPDF* qpdf, QPDFObjGen og) 321 + move_to(std::shared_ptr<QPDFObject>& o, bool destroy)
142 { 322 {
143 - value->qpdf = qpdf;  
144 - value->og = og; 323 + o->value = std::move(value);
  324 + o->qpdf = qpdf;
  325 + o->og = og;
  326 + o->object_description = object_description;
  327 + o->parsed_offset = parsed_offset;
  328 + if (!destroy) {
  329 + value = QPDF_Reference(o);
  330 + }
145 } 331 }
146 void 332 void
147 - disconnect() 333 + swapWith(std::shared_ptr<QPDFObject> o)
  334 + {
  335 + std::swap(value, o->value);
  336 + std::swap(qpdf, o->qpdf);
  337 + std::swap(object_description, o->object_description);
  338 + std::swap(parsed_offset, o->parsed_offset);
  339 + }
  340 +
  341 + void
  342 + setObjGen(QPDF* a_qpdf, QPDFObjGen a_og)
148 { 343 {
149 - // Disconnect an object from its owning QPDF. This is called by QPDF's destructor.  
150 - value->disconnect();  
151 - value->qpdf = nullptr;  
152 - value->og = QPDFObjGen(); 344 + qpdf = a_qpdf;
  345 + og = a_og;
153 } 346 }
154 // Mark an object as destroyed. Used by QPDF's destructor for its indirect objects. 347 // Mark an object as destroyed. Used by QPDF's destructor for its indirect objects.
155 - void destroy(); 348 + void
  349 + destroy()
  350 + {
  351 + value = QPDF_Destroyed();
  352 + }
156 353
157 bool 354 bool
158 isUnresolved() const 355 isUnresolved() const
159 { 356 {
160 - return value->type_code == ::ot_unresolved; 357 + return getTypeCode() == ::ot_unresolved;
161 } 358 }
162 const QPDFObject* 359 const QPDFObject*
163 resolved_object() const 360 resolved_object() const
164 { 361 {
165 - return isUnresolved() ? QPDF::Resolver::resolved(value->qpdf, value->og) : this; 362 + return isUnresolved() ? QPDF::Resolver::resolved(qpdf, og).get() : this;
166 } 363 }
167 364
168 - template <typename T>  
169 - T*  
170 - as() const  
171 - {  
172 - if (auto result = dynamic_cast<T*>(value.get())) {  
173 - return result;  
174 - } else {  
175 - return isUnresolved()  
176 - ? dynamic_cast<T*>(QPDF::Resolver::resolved(value->qpdf, value->og)->value.get())  
177 - : nullptr; 365 + struct JSON_Descr
  366 + {
  367 + JSON_Descr(std::shared_ptr<std::string> input, std::string const& object) :
  368 + input(input),
  369 + object(object)
  370 + {
178 } 371 }
  372 +
  373 + std::shared_ptr<std::string> input;
  374 + std::string object;
  375 + };
  376 +
  377 + struct ChildDescr
  378 + {
  379 + ChildDescr(
  380 + std::shared_ptr<QPDFObject> parent,
  381 + std::string_view const& static_descr,
  382 + std::string var_descr) :
  383 + parent(parent),
  384 + static_descr(static_descr),
  385 + var_descr(var_descr)
  386 + {
  387 + }
  388 +
  389 + std::weak_ptr<QPDFObject> parent;
  390 + std::string_view const& static_descr;
  391 + std::string var_descr;
  392 + };
  393 +
  394 + using Description = std::variant<std::string, JSON_Descr, ChildDescr>;
  395 +
  396 + void
  397 + setDescription(
  398 + QPDF* qpdf_p, std::shared_ptr<Description>& description, qpdf_offset_t offset = -1)
  399 + {
  400 + qpdf = qpdf_p;
  401 + object_description = description;
  402 + setParsedOffset(offset);
  403 + }
  404 + void
  405 + setDefaultDescription(QPDF* a_qpdf, QPDFObjGen const& a_og)
  406 + {
  407 + qpdf = a_qpdf;
  408 + og = a_og;
  409 + }
  410 + void
  411 + setChildDescription(
  412 + QPDF* a_qpdf,
  413 + std::shared_ptr<QPDFObject> parent,
  414 + std::string_view const& static_descr,
  415 + std::string var_descr)
  416 + {
  417 + object_description =
  418 + std::make_shared<Description>(ChildDescr(parent, static_descr, var_descr));
  419 + qpdf = a_qpdf;
  420 + }
  421 + std::string getDescription();
  422 + bool
  423 + hasDescription()
  424 + {
  425 + return object_description || og.isIndirect();
  426 + }
  427 + void
  428 + setParsedOffset(qpdf_offset_t offset)
  429 + {
  430 + if (parsed_offset < 0) {
  431 + parsed_offset = offset;
  432 + }
  433 + }
  434 + bool
  435 + getDescription(QPDF*& a_qpdf, std::string& description)
  436 + {
  437 + a_qpdf = qpdf;
  438 + description = getDescription();
  439 + return qpdf != nullptr;
  440 + }
  441 + qpdf_offset_t
  442 + getParsedOffset()
  443 + {
  444 + return parsed_offset;
  445 + }
  446 + QPDF*
  447 + getQPDF()
  448 + {
  449 + return qpdf;
  450 + }
  451 + QPDFObjGen
  452 + getObjGen()
  453 + {
  454 + return og;
179 } 455 }
180 456
181 private: 457 private:
  458 + friend class QPDF_Stream;
  459 + friend class qpdf::BaseHandle;
  460 +
  461 + typedef std::variant<
  462 + std::monostate,
  463 + QPDF_Reserved,
  464 + QPDF_Null,
  465 + QPDF_Bool,
  466 + QPDF_Integer,
  467 + QPDF_Real,
  468 + QPDF_String,
  469 + QPDF_Name,
  470 + QPDF_Array,
  471 + QPDF_Dictionary,
  472 + QPDF_Stream,
  473 + QPDF_Operator,
  474 + QPDF_InlineImage,
  475 + QPDF_Unresolved,
  476 + QPDF_Destroyed,
  477 + QPDF_Reference>
  478 + Value;
  479 + Value value;
  480 +
182 QPDFObject(QPDFObject const&) = delete; 481 QPDFObject(QPDFObject const&) = delete;
183 QPDFObject& operator=(QPDFObject const&) = delete; 482 QPDFObject& operator=(QPDFObject const&) = delete;
184 - std::shared_ptr<QPDFValue> value; 483 +
  484 + std::shared_ptr<Description> object_description;
  485 +
  486 + QPDF* qpdf{nullptr};
  487 + QPDFObjGen og{};
  488 + qpdf_offset_t parsed_offset{-1};
185 }; 489 };
186 490
187 #endif // QPDFOBJECT_HH 491 #endif // QPDFOBJECT_HH
libqpdf/qpdf/QPDFParser.hh
1 #ifndef QPDFPARSER_HH 1 #ifndef QPDFPARSER_HH
2 #define QPDFPARSER_HH 2 #define QPDFPARSER_HH
3 3
4 -#include <qpdf/QPDFObjectHandle.hh>  
5 -#include <qpdf/QPDFValue.hh> 4 +#include <qpdf/QPDFObjectHandle_private.hh>
  5 +#include <qpdf/QPDFObject_private.hh>
6 6
7 #include <memory> 7 #include <memory>
8 #include <string> 8 #include <string>
@@ -24,7 +24,7 @@ class QPDFParser @@ -24,7 +24,7 @@ class QPDFParser
24 decrypter(decrypter), 24 decrypter(decrypter),
25 context(context), 25 context(context),
26 description( 26 description(
27 - std::make_shared<QPDFValue::Description>( 27 + std::make_shared<QPDFObject::Description>(
28 std::string(input.getName() + ", " + object_description + " at offset $PO"))), 28 std::string(input.getName() + ", " + object_description + " at offset $PO"))),
29 parse_pdf(parse_pdf) 29 parse_pdf(parse_pdf)
30 { 30 {
@@ -46,7 +46,7 @@ class QPDFParser @@ -46,7 +46,7 @@ class QPDFParser
46 { 46 {
47 } 47 }
48 48
49 - std::vector<std::shared_ptr<QPDFObject>> olist; 49 + std::vector<QPDFObjectHandle> olist;
50 std::map<std::string, QPDFObjectHandle> dict; 50 std::map<std::string, QPDFObjectHandle> dict;
51 parser_state_e state; 51 parser_state_e state;
52 std::string key; 52 std::string key;
@@ -78,7 +78,7 @@ class QPDFParser @@ -78,7 +78,7 @@ class QPDFParser
78 QPDFTokenizer& tokenizer; 78 QPDFTokenizer& tokenizer;
79 QPDFObjectHandle::StringDecrypter* decrypter; 79 QPDFObjectHandle::StringDecrypter* decrypter;
80 QPDF* context; 80 QPDF* context;
81 - std::shared_ptr<QPDFValue::Description> description; 81 + std::shared_ptr<QPDFObject::Description> description;
82 bool parse_pdf; 82 bool parse_pdf;
83 83
84 std::vector<StackFrame> stack; 84 std::vector<StackFrame> stack;
libqpdf/qpdf/QPDFValue.hh deleted
1 -#ifndef QPDFVALUE_HH  
2 -#define QPDFVALUE_HH  
3 -  
4 -#include <qpdf/Constants.h>  
5 -#include <qpdf/DLL.h>  
6 -#include <qpdf/JSON.hh>  
7 -#include <qpdf/QPDFObjGen.hh>  
8 -#include <qpdf/Types.h>  
9 -  
10 -#include <string>  
11 -#include <string_view>  
12 -#include <variant>  
13 -  
14 -class QPDF;  
15 -class QPDFObjectHandle;  
16 -class QPDFObject;  
17 -  
18 -class QPDFValue: public std::enable_shared_from_this<QPDFValue>  
19 -{  
20 - friend class QPDFObject;  
21 -  
22 - public:  
23 - virtual ~QPDFValue() = default;  
24 -  
25 - virtual std::shared_ptr<QPDFObject> copy(bool shallow = false) = 0;  
26 - virtual std::string unparse() = 0;  
27 - virtual void writeJSON(int json_version, JSON::Writer& p) = 0;  
28 -  
29 - struct JSON_Descr  
30 - {  
31 - JSON_Descr(std::shared_ptr<std::string> input, std::string const& object) :  
32 - input(input),  
33 - object(object)  
34 - {  
35 - }  
36 -  
37 - std::shared_ptr<std::string> input;  
38 - std::string object;  
39 - };  
40 -  
41 - struct ChildDescr  
42 - {  
43 - ChildDescr(  
44 - std::shared_ptr<QPDFValue> parent,  
45 - std::string_view const& static_descr,  
46 - std::string var_descr) :  
47 - parent(parent),  
48 - static_descr(static_descr),  
49 - var_descr(var_descr)  
50 - {  
51 - }  
52 -  
53 - std::weak_ptr<QPDFValue> parent;  
54 - std::string_view const& static_descr;  
55 - std::string var_descr;  
56 - };  
57 -  
58 - using Description = std::variant<std::string, JSON_Descr, ChildDescr>;  
59 -  
60 - virtual void  
61 - setDescription(QPDF* qpdf_p, std::shared_ptr<Description>& description, qpdf_offset_t offset)  
62 - {  
63 - qpdf = qpdf_p;  
64 - object_description = description;  
65 - setParsedOffset(offset);  
66 - }  
67 - void  
68 - setDefaultDescription(QPDF* a_qpdf, QPDFObjGen a_og)  
69 - {  
70 - qpdf = a_qpdf;  
71 - og = a_og;  
72 - }  
73 - void  
74 - setChildDescription(  
75 - QPDF* a_qpdf,  
76 - std::shared_ptr<QPDFValue> parent,  
77 - std::string_view const& static_descr,  
78 - std::string var_descr)  
79 - {  
80 - object_description =  
81 - std::make_shared<Description>(ChildDescr(parent, static_descr, var_descr));  
82 - qpdf = a_qpdf;  
83 - }  
84 - std::string getDescription();  
85 - bool  
86 - hasDescription()  
87 - {  
88 - return object_description || og.isIndirect();  
89 - }  
90 - void  
91 - setParsedOffset(qpdf_offset_t offset)  
92 - {  
93 - if (parsed_offset < 0) {  
94 - parsed_offset = offset;  
95 - }  
96 - }  
97 - qpdf_offset_t  
98 - getParsedOffset()  
99 - {  
100 - return parsed_offset;  
101 - }  
102 - QPDF*  
103 - getQPDF()  
104 - {  
105 - return qpdf;  
106 - }  
107 - QPDFObjGen  
108 - getObjGen()  
109 - {  
110 - return og;  
111 - }  
112 - virtual void  
113 - disconnect()  
114 - {  
115 - }  
116 - virtual std::string  
117 - getStringValue() const  
118 - {  
119 - return "";  
120 - }  
121 -  
122 - protected:  
123 - QPDFValue() = default;  
124 -  
125 - QPDFValue(qpdf_object_type_e type_code) :  
126 - type_code(type_code)  
127 - {  
128 - }  
129 - QPDFValue(qpdf_object_type_e type_code, QPDF* qpdf, QPDFObjGen og) :  
130 - type_code(type_code),  
131 - qpdf(qpdf),  
132 - og(og)  
133 - {  
134 - }  
135 -  
136 - static std::shared_ptr<QPDFObject> do_create(QPDFValue*);  
137 -  
138 - private:  
139 - QPDFValue(QPDFValue const&) = delete;  
140 - QPDFValue& operator=(QPDFValue const&) = delete;  
141 - std::shared_ptr<Description> object_description;  
142 -  
143 - const qpdf_object_type_e type_code{::ot_uninitialized};  
144 -  
145 - protected:  
146 - QPDF* qpdf{nullptr};  
147 - QPDFObjGen og{};  
148 - qpdf_offset_t parsed_offset{-1};  
149 -};  
150 -  
151 -#endif // QPDFVALUE_HH  
libqpdf/qpdf/QPDF_Array.hh deleted
1 -#ifndef QPDF_ARRAY_HH  
2 -#define QPDF_ARRAY_HH  
3 -  
4 -#include <qpdf/QPDFValue.hh>  
5 -  
6 -#include <map>  
7 -#include <vector>  
8 -  
9 -class QPDF_Array: public QPDFValue  
10 -{  
11 - private:  
12 - struct Sparse  
13 - {  
14 - int size{0};  
15 - std::map<int, std::shared_ptr<QPDFObject>> elements;  
16 - };  
17 -  
18 - public:  
19 - ~QPDF_Array() override = default;  
20 - static std::shared_ptr<QPDFObject> create(std::vector<QPDFObjectHandle> const& items);  
21 - static std::shared_ptr<QPDFObject>  
22 - create(std::vector<std::shared_ptr<QPDFObject>>&& items, bool sparse);  
23 - std::shared_ptr<QPDFObject> copy(bool shallow = false) override;  
24 - std::string unparse() override;  
25 - void writeJSON(int json_version, JSON::Writer& p) override;  
26 - void disconnect() override;  
27 -  
28 - int  
29 - size() const noexcept  
30 - {  
31 - return sp ? sp->size : int(elements.size());  
32 - }  
33 - std::pair<bool, QPDFObjectHandle> at(int n) const noexcept;  
34 - bool setAt(int n, QPDFObjectHandle const& oh);  
35 - std::vector<QPDFObjectHandle> getAsVector() const;  
36 - void setFromVector(std::vector<QPDFObjectHandle> const& items);  
37 - bool insert(int at, QPDFObjectHandle const& item);  
38 - void push_back(QPDFObjectHandle const& item);  
39 - bool erase(int at);  
40 -  
41 - private:  
42 - QPDF_Array();  
43 - QPDF_Array(QPDF_Array const&);  
44 - QPDF_Array(std::vector<QPDFObjectHandle> const& items);  
45 - QPDF_Array(std::vector<std::shared_ptr<QPDFObject>>&& items, bool sparse);  
46 -  
47 - void checkOwnership(QPDFObjectHandle const& item) const;  
48 -  
49 - std::unique_ptr<Sparse> sp;  
50 - std::vector<std::shared_ptr<QPDFObject>> elements;  
51 -};  
52 -  
53 -#endif // QPDF_ARRAY_HH  
libqpdf/qpdf/QPDF_Bool.hh deleted
1 -#ifndef QPDF_BOOL_HH  
2 -#define QPDF_BOOL_HH  
3 -  
4 -#include <qpdf/QPDFValue.hh>  
5 -  
6 -class QPDF_Bool: public QPDFValue  
7 -{  
8 - public:  
9 - ~QPDF_Bool() override = default;  
10 - static std::shared_ptr<QPDFObject> create(bool val);  
11 - std::shared_ptr<QPDFObject> copy(bool shallow = false) override;  
12 - std::string unparse() override;  
13 - void writeJSON(int json_version, JSON::Writer& p) override;  
14 -  
15 - bool getVal() const;  
16 -  
17 - private:  
18 - QPDF_Bool(bool val);  
19 - bool val;  
20 -};  
21 -  
22 -#endif // QPDF_BOOL_HH  
libqpdf/qpdf/QPDF_Destroyed.hh deleted
1 -#ifndef QPDF_DESTROYED_HH  
2 -#define QPDF_DESTROYED_HH  
3 -  
4 -#include <qpdf/QPDFValue.hh>  
5 -  
6 -class QPDF_Destroyed: public QPDFValue  
7 -{  
8 - public:  
9 - ~QPDF_Destroyed() override = default;  
10 - std::shared_ptr<QPDFObject> copy(bool shallow = false) override;  
11 - std::string unparse() override;  
12 - void writeJSON(int json_version, JSON::Writer& p) override;  
13 - static std::shared_ptr<QPDFValue> getInstance();  
14 -  
15 - private:  
16 - QPDF_Destroyed();  
17 -};  
18 -  
19 -#endif // QPDF_DESTROYED_HH  
libqpdf/qpdf/QPDF_Dictionary.hh deleted
1 -#ifndef QPDF_DICTIONARY_HH  
2 -#define QPDF_DICTIONARY_HH  
3 -  
4 -#include <qpdf/QPDFValue.hh>  
5 -  
6 -#include <map>  
7 -#include <set>  
8 -  
9 -#include <qpdf/QPDFObjectHandle.hh>  
10 -  
11 -class QPDF_Dictionary: public QPDFValue  
12 -{  
13 - public:  
14 - ~QPDF_Dictionary() override = default;  
15 - static std::shared_ptr<QPDFObject> create(std::map<std::string, QPDFObjectHandle> const& items);  
16 - static std::shared_ptr<QPDFObject> create(std::map<std::string, QPDFObjectHandle>&& items);  
17 - std::shared_ptr<QPDFObject> copy(bool shallow = false) override;  
18 - std::string unparse() override;  
19 - void writeJSON(int json_version, JSON::Writer& p) override;  
20 - void disconnect() override;  
21 -  
22 - // hasKey() and getKeys() treat keys with null values as if they aren't there. getKey() returns  
23 - // null for the value of a non-existent key. This is as per the PDF spec.  
24 - bool hasKey(std::string const&);  
25 - QPDFObjectHandle getKey(std::string const&);  
26 - std::set<std::string> getKeys();  
27 - std::map<std::string, QPDFObjectHandle> const& getAsMap() const;  
28 -  
29 - // If value is null, remove key; otherwise, replace the value of key, adding it if it does not  
30 - // exist.  
31 - void replaceKey(std::string const& key, QPDFObjectHandle value);  
32 - // Remove key, doing nothing if key does not exist  
33 - void removeKey(std::string const& key);  
34 -  
35 - private:  
36 - QPDF_Dictionary(std::map<std::string, QPDFObjectHandle> const& items);  
37 - QPDF_Dictionary(std::map<std::string, QPDFObjectHandle>&& items);  
38 - std::map<std::string, QPDFObjectHandle> items;  
39 -};  
40 -  
41 -#endif // QPDF_DICTIONARY_HH  
libqpdf/qpdf/QPDF_InlineImage.hh deleted
1 -#ifndef QPDF_INLINEIMAGE_HH  
2 -#define QPDF_INLINEIMAGE_HH  
3 -  
4 -#include <qpdf/QPDFValue.hh>  
5 -  
6 -class QPDF_InlineImage: public QPDFValue  
7 -{  
8 - public:  
9 - ~QPDF_InlineImage() override = default;  
10 - static std::shared_ptr<QPDFObject> create(std::string const& val);  
11 - std::shared_ptr<QPDFObject> copy(bool shallow = false) override;  
12 - std::string unparse() override;  
13 - void writeJSON(int json_version, JSON::Writer& p) override;  
14 - std::string  
15 - getStringValue() const override  
16 - {  
17 - return val;  
18 - }  
19 -  
20 - private:  
21 - QPDF_InlineImage(std::string const& val);  
22 - std::string val;  
23 -};  
24 -  
25 -#endif // QPDF_INLINEIMAGE_HH  
libqpdf/qpdf/QPDF_Integer.hh deleted
1 -#ifndef QPDF_INTEGER_HH  
2 -#define QPDF_INTEGER_HH  
3 -  
4 -#include <qpdf/QPDFValue.hh>  
5 -  
6 -class QPDF_Integer: public QPDFValue  
7 -{  
8 - public:  
9 - ~QPDF_Integer() override = default;  
10 - static std::shared_ptr<QPDFObject> create(long long value);  
11 - std::shared_ptr<QPDFObject> copy(bool shallow = false) override;  
12 - std::string unparse() override;  
13 - void writeJSON(int json_version, JSON::Writer& p) override;  
14 - long long getVal() const;  
15 -  
16 - private:  
17 - QPDF_Integer(long long val);  
18 - long long val;  
19 -};  
20 -  
21 -#endif // QPDF_INTEGER_HH  
libqpdf/qpdf/QPDF_Name.hh
1 -#ifndef QPDF_NAME_HH  
2 -#define QPDF_NAME_HH  
3 -  
4 -#include <qpdf/QPDFValue.hh>  
5 -  
6 -class QPDF_Name: public QPDFValue  
7 -{  
8 - public:  
9 - ~QPDF_Name() override = default;  
10 - static std::shared_ptr<QPDFObject> create(std::string const& name);  
11 - std::shared_ptr<QPDFObject> copy(bool shallow = false) override;  
12 - std::string unparse() override;  
13 - void writeJSON(int json_version, JSON::Writer& p) override;  
14 -  
15 - // Put # into strings with characters unsuitable for name token  
16 - static std::string normalizeName(std::string const& name);  
17 -  
18 - // Check whether name is valid utf-8 and whether it contains characters that require escaping.  
19 - // Return {false, false} if the name is not valid utf-8, otherwise return {true, true} if no  
20 - // characters require or {true, false} if escaping is required.  
21 - static std::pair<bool, bool> analyzeJSONEncoding(std::string const& name);  
22 - std::string  
23 - getStringValue() const override  
24 - {  
25 - return name;  
26 - }  
27 -  
28 - private:  
29 - QPDF_Name(std::string const& name);  
30 - std::string name;  
31 -};  
32 -  
33 -#endif // QPDF_NAME_HH  
libqpdf/qpdf/QPDF_Null.hh deleted
1 -#ifndef QPDF_NULL_HH  
2 -#define QPDF_NULL_HH  
3 -  
4 -#include <qpdf/QPDFValue.hh>  
5 -  
6 -class QPDF_Null: public QPDFValue  
7 -{  
8 - public:  
9 - ~QPDF_Null() override = default;  
10 - static std::shared_ptr<QPDFObject> create(QPDF* qpdf = nullptr, QPDFObjGen og = QPDFObjGen());  
11 - static std::shared_ptr<QPDFObject> create(  
12 - std::shared_ptr<QPDFObject> parent,  
13 - std::string_view const& static_descr,  
14 - std::string var_descr);  
15 - static std::shared_ptr<QPDFObject> create(  
16 - std::shared_ptr<QPDFValue> parent,  
17 - std::string_view const& static_descr,  
18 - std::string var_descr);  
19 - std::shared_ptr<QPDFObject> copy(bool shallow = false) override;  
20 - std::string unparse() override;  
21 - void writeJSON(int json_version, JSON::Writer& p) override;  
22 -  
23 - private:  
24 - QPDF_Null(QPDF* qpdf = nullptr, QPDFObjGen og = QPDFObjGen());  
25 -};  
26 -  
27 -#endif // QPDF_NULL_HH  
libqpdf/qpdf/QPDF_Operator.hh deleted
1 -#ifndef QPDF_OPERATOR_HH  
2 -#define QPDF_OPERATOR_HH  
3 -  
4 -#include <qpdf/QPDFValue.hh>  
5 -  
6 -class QPDF_Operator: public QPDFValue  
7 -{  
8 - public:  
9 - ~QPDF_Operator() override = default;  
10 - static std::shared_ptr<QPDFObject> create(std::string const& val);  
11 - std::shared_ptr<QPDFObject> copy(bool shallow = false) override;  
12 - std::string unparse() override;  
13 - void writeJSON(int json_version, JSON::Writer& p) override;  
14 - std::string  
15 - getStringValue() const override  
16 - {  
17 - return val;  
18 - }  
19 -  
20 - private:  
21 - QPDF_Operator(std::string const& val);  
22 - std::string val;  
23 -};  
24 -  
25 -#endif // QPDF_OPERATOR_HH  
libqpdf/qpdf/QPDF_Real.hh deleted
1 -#ifndef QPDF_REAL_HH  
2 -#define QPDF_REAL_HH  
3 -  
4 -#include <qpdf/QPDFValue.hh>  
5 -  
6 -class QPDF_Real: public QPDFValue  
7 -{  
8 - public:  
9 - ~QPDF_Real() override = default;  
10 - static std::shared_ptr<QPDFObject> create(std::string const& val);  
11 - static std::shared_ptr<QPDFObject>  
12 - create(double value, int decimal_places, bool trim_trailing_zeroes);  
13 - std::shared_ptr<QPDFObject> copy(bool shallow = false) override;  
14 - std::string unparse() override;  
15 - void writeJSON(int json_version, JSON::Writer& p) override;  
16 - std::string  
17 - getStringValue() const override  
18 - {  
19 - return val;  
20 - }  
21 -  
22 - private:  
23 - QPDF_Real(std::string const& val);  
24 - QPDF_Real(double value, int decimal_places, bool trim_trailing_zeroes);  
25 - // Store reals as strings to avoid roundoff errors.  
26 - std::string val;  
27 -};  
28 -  
29 -#endif // QPDF_REAL_HH  
libqpdf/qpdf/QPDF_Reserved.hh deleted
1 -#ifndef QPDF_RESERVED_HH  
2 -#define QPDF_RESERVED_HH  
3 -  
4 -#include <qpdf/QPDFValue.hh>  
5 -  
6 -class QPDF_Reserved: public QPDFValue  
7 -{  
8 - public:  
9 - ~QPDF_Reserved() override = default;  
10 - static std::shared_ptr<QPDFObject> create();  
11 - std::shared_ptr<QPDFObject> copy(bool shallow = false) override;  
12 - std::string unparse() override;  
13 - void writeJSON(int json_version, JSON::Writer& p) override;  
14 -  
15 - private:  
16 - QPDF_Reserved();  
17 -};  
18 -  
19 -#endif // QPDF_RESERVED_HH  
libqpdf/qpdf/QPDF_Stream.hh deleted
1 -#ifndef QPDF_STREAM_HH  
2 -#define QPDF_STREAM_HH  
3 -  
4 -#include <qpdf/Types.h>  
5 -  
6 -#include <qpdf/QPDFObjectHandle.hh>  
7 -#include <qpdf/QPDFStreamFilter.hh>  
8 -#include <qpdf/QPDFValue.hh>  
9 -  
10 -#include <functional>  
11 -#include <memory>  
12 -  
13 -class Pipeline;  
14 -class QPDF;  
15 -  
16 -class QPDF_Stream final: public QPDFValue  
17 -{  
18 - public:  
19 - ~QPDF_Stream() final = default;  
20 - static std::shared_ptr<QPDFObject>  
21 - create(QPDF*, QPDFObjGen og, QPDFObjectHandle stream_dict, qpdf_offset_t offset, size_t length);  
22 - std::shared_ptr<QPDFObject> copy(bool shallow = false) final;  
23 - std::string unparse() final;  
24 - void writeJSON(int json_version, JSON::Writer& p) final;  
25 - void setDescription(  
26 - QPDF*, std::shared_ptr<QPDFValue::Description>& description, qpdf_offset_t offset) final;  
27 - void disconnect() final;  
28 - QPDFObjectHandle getDict() const;  
29 - bool isDataModified() const;  
30 - void setFilterOnWrite(bool);  
31 - bool getFilterOnWrite() const;  
32 -  
33 - // Methods to help QPDF copy foreign streams  
34 - size_t getLength() const;  
35 - std::shared_ptr<Buffer> getStreamDataBuffer() const;  
36 - std::shared_ptr<QPDFObjectHandle::StreamDataProvider> getStreamDataProvider() const;  
37 -  
38 - // See comments in QPDFObjectHandle.hh for these methods.  
39 - bool pipeStreamData(  
40 - Pipeline*,  
41 - bool* tried_filtering,  
42 - int encode_flags,  
43 - qpdf_stream_decode_level_e decode_level,  
44 - bool suppress_warnings,  
45 - bool will_retry);  
46 - std::shared_ptr<Buffer> getStreamData(qpdf_stream_decode_level_e);  
47 - std::shared_ptr<Buffer> getRawStreamData();  
48 - void replaceStreamData(  
49 - std::shared_ptr<Buffer> data,  
50 - QPDFObjectHandle const& filter,  
51 - QPDFObjectHandle const& decode_parms);  
52 - void replaceStreamData(  
53 - std::shared_ptr<QPDFObjectHandle::StreamDataProvider> provider,  
54 - QPDFObjectHandle const& filter,  
55 - QPDFObjectHandle const& decode_parms);  
56 - void addTokenFilter(std::shared_ptr<QPDFObjectHandle::TokenFilter> token_filter);  
57 - JSON getStreamJSON(  
58 - int json_version,  
59 - qpdf_json_stream_data_e json_data,  
60 - qpdf_stream_decode_level_e decode_level,  
61 - Pipeline* p,  
62 - std::string const& data_filename);  
63 - qpdf_stream_decode_level_e writeStreamJSON(  
64 - int json_version,  
65 - JSON::Writer& jw,  
66 - qpdf_json_stream_data_e json_data,  
67 - qpdf_stream_decode_level_e decode_level,  
68 - Pipeline* p,  
69 - std::string const& data_filename,  
70 - bool no_data_key = false);  
71 -  
72 - void replaceDict(QPDFObjectHandle const& new_dict);  
73 -  
74 - static void registerStreamFilter(  
75 - std::string const& filter_name, std::function<std::shared_ptr<QPDFStreamFilter>()> factory);  
76 -  
77 - private:  
78 - QPDF_Stream(  
79 - QPDF*, QPDFObjGen og, QPDFObjectHandle stream_dict, qpdf_offset_t offset, size_t length);  
80 - static std::map<std::string, std::string> filter_abbreviations;  
81 - static std::map<std::string, std::function<std::shared_ptr<QPDFStreamFilter>()>>  
82 - filter_factories;  
83 -  
84 - void replaceFilterData(  
85 - QPDFObjectHandle const& filter, QPDFObjectHandle const& decode_parms, size_t length);  
86 - bool filterable(  
87 - std::vector<std::shared_ptr<QPDFStreamFilter>>& filters,  
88 - bool& specialized_compression,  
89 - bool& lossy_compression);  
90 - void warn(std::string const& message);  
91 - void setDictDescription();  
92 -  
93 - bool filter_on_write;  
94 - QPDFObjectHandle stream_dict;  
95 - size_t length;  
96 - std::shared_ptr<Buffer> stream_data;  
97 - std::shared_ptr<QPDFObjectHandle::StreamDataProvider> stream_provider;  
98 - std::vector<std::shared_ptr<QPDFObjectHandle::TokenFilter>> token_filters;  
99 -};  
100 -  
101 -#endif // QPDF_STREAM_HH  
libqpdf/qpdf/QPDF_String.hh deleted
1 -#ifndef QPDF_STRING_HH  
2 -#define QPDF_STRING_HH  
3 -  
4 -#include <qpdf/QPDFValue.hh>  
5 -  
6 -// QPDF_Strings may included embedded null characters.  
7 -  
8 -class QPDF_String: public QPDFValue  
9 -{  
10 - friend class QPDFWriter;  
11 -  
12 - public:  
13 - ~QPDF_String() override = default;  
14 - static std::shared_ptr<QPDFObject> create(std::string const& val);  
15 - static std::shared_ptr<QPDFObject> create_utf16(std::string const& utf8_val);  
16 - std::shared_ptr<QPDFObject> copy(bool shallow = false) override;  
17 - std::string unparse() override;  
18 - std::string unparse(bool force_binary);  
19 - void writeJSON(int json_version, JSON::Writer& p) override;  
20 - std::string getUTF8Val() const;  
21 - std::string  
22 - getStringValue() const override  
23 - {  
24 - return val;  
25 - }  
26 -  
27 - private:  
28 - QPDF_String(std::string const& val);  
29 - bool useHexString() const;  
30 - std::string val;  
31 -};  
32 -  
33 -#endif // QPDF_STRING_HH  
libqpdf/qpdf/QPDF_Unresolved.hh deleted
1 -#ifndef QPDF_UNRESOLVED_HH  
2 -#define QPDF_UNRESOLVED_HH  
3 -  
4 -#include <qpdf/QPDFValue.hh>  
5 -  
6 -class QPDF_Unresolved: public QPDFValue  
7 -{  
8 - public:  
9 - ~QPDF_Unresolved() override = default;  
10 - static std::shared_ptr<QPDFObject> create(QPDF* qpdf, QPDFObjGen og);  
11 - std::shared_ptr<QPDFObject> copy(bool shallow = false) override;  
12 - std::string unparse() override;  
13 - void writeJSON(int json_version, JSON::Writer& p) override;  
14 - std::string getStringValue() const override;  
15 -  
16 - private:  
17 - QPDF_Unresolved(QPDF* qpdf, QPDFObjGen og);  
18 -};  
19 -  
20 -#endif // QPDF_UNRESOLVED_HH  
libtests/sparse_array.cc
1 #include <qpdf/assert_test.h> 1 #include <qpdf/assert_test.h>
2 2
3 #include <qpdf/QPDF.hh> 3 #include <qpdf/QPDF.hh>
4 -#include <qpdf/QPDFObjectHandle.hh> 4 +#include <qpdf/QPDFObjectHandle_private.hh>
5 #include <qpdf/QPDFObject_private.hh> 5 #include <qpdf/QPDFObject_private.hh>
6 -#include <qpdf/QPDF_Array.hh>  
7 6
8 #include <iostream> 7 #include <iostream>
9 8
10 int 9 int
11 main() 10 main()
12 { 11 {
13 - auto obj = QPDF_Array::create({}, true);  
14 - QPDF_Array& a = *obj->as<QPDF_Array>(); 12 + auto obj = QPDFObject::create<QPDF_Array>(std::vector<QPDFObjectHandle>(), true);
  13 + auto a = qpdf::Array(obj);
15 14
16 assert(a.size() == 0); 15 assert(a.size() == 0);
17 16
@@ -88,16 +87,17 @@ main() @@ -88,16 +87,17 @@ main()
88 QPDF pdf; 87 QPDF pdf;
89 pdf.emptyPDF(); 88 pdf.emptyPDF();
90 89
91 - obj = QPDF_Array::create({10, "null"_qpdf.getObj()}, true);  
92 - QPDF_Array& b = *obj->as<QPDF_Array>(); 90 + obj = QPDFObject::create<QPDF_Array>(
  91 + std::vector<QPDFObjectHandle>{10, "null"_qpdf.getObj()}, true);
  92 + auto b = qpdf::Array(obj);
93 b.setAt(5, pdf.newIndirectNull()); 93 b.setAt(5, pdf.newIndirectNull());
94 b.setAt(7, "[0 1 2 3]"_qpdf); 94 b.setAt(7, "[0 1 2 3]"_qpdf);
95 assert(b.at(3).second.isNull()); 95 assert(b.at(3).second.isNull());
96 assert(b.at(8).second.isNull()); 96 assert(b.at(8).second.isNull());
97 assert(b.at(5).second.isIndirect()); 97 assert(b.at(5).second.isIndirect());
98 - assert(b.unparse() == "[ null null null null null 3 0 R null [ 0 1 2 3 ] null null ]");  
99 - auto c = b.copy(true);  
100 - auto d = b.copy(false); 98 + assert(obj->unparse() == "[ null null null null null 3 0 R null [ 0 1 2 3 ] null null ]");
  99 + auto c = obj->copy(true);
  100 + auto d = obj->copy(false);
101 b.at(7).second.setArrayItem(2, "42"_qpdf); 101 b.at(7).second.setArrayItem(2, "42"_qpdf);
102 assert(c->unparse() == "[ null null null null null 3 0 R null [ 0 1 42 3 ] null null ]"); 102 assert(c->unparse() == "[ null null null null null 3 0 R null [ 0 1 42 3 ] null null ]");
103 assert(d->unparse() == "[ null null null null null 3 0 R null [ 0 1 2 3 ] null null ]"); 103 assert(d->unparse() == "[ null null null null null 3 0 R null [ 0 1 2 3 ] null null ]");
qpdf/sizes.cc
@@ -109,7 +109,6 @@ main() @@ -109,7 +109,6 @@ main()
109 print_size(QPDFNumberTreeObjectHelper); 109 print_size(QPDFNumberTreeObjectHelper);
110 print_size(QPDFNumberTreeObjectHelper::iterator); 110 print_size(QPDFNumberTreeObjectHelper::iterator);
111 print_size(QPDFObjGen); 111 print_size(QPDFObjGen);
112 - print_size(QPDFObjGen::set);  
113 print_size(QPDFObjectHandle); 112 print_size(QPDFObjectHandle);
114 print_size(QPDFObjectHandle::ParserCallbacks); 113 print_size(QPDFObjectHandle::ParserCallbacks);
115 print_size(QPDFObjectHandle::QPDFArrayItems); 114 print_size(QPDFObjectHandle::QPDFArrayItems);
@@ -118,6 +117,7 @@ main() @@ -118,6 +117,7 @@ main()
118 print_size(QPDFObjectHandle::QPDFDictItems::iterator); 117 print_size(QPDFObjectHandle::QPDFDictItems::iterator);
119 print_size(QPDFObjectHandle::StreamDataProvider); 118 print_size(QPDFObjectHandle::StreamDataProvider);
120 print_size(QPDFObjectHandle::TokenFilter); 119 print_size(QPDFObjectHandle::TokenFilter);
  120 + print_size(QPDFObjectHelper);
121 print_size(QPDFOutlineDocumentHelper); 121 print_size(QPDFOutlineDocumentHelper);
122 print_size(QPDFOutlineObjectHelper); 122 print_size(QPDFOutlineObjectHelper);
123 print_size(QPDFPageDocumentHelper); 123 print_size(QPDFPageDocumentHelper);