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 126 /* Object types internal to qpdf */
127 127 ot_unresolved,
128 128 ot_destroyed,
  129 + ot_reference,
129 130 };
130 131  
131 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 798 {
799 799 friend class QPDFObject;
800 800 friend class QPDF_Unresolved;
  801 + friend class qpdf::BaseHandle;
801 802  
802 803 private:
803   - static QPDFObject*
  804 + static std::shared_ptr<QPDFObject> const&
804 805 resolved(QPDF* qpdf, QPDFObjGen og)
805 806 {
806 807 return qpdf->resolve(og);
... ... @@ -854,6 +855,7 @@ class QPDF
854 855 class Pipe
855 856 {
856 857 friend class QPDF_Stream;
  858 + friend class qpdf::Stream;
857 859  
858 860 private:
859 861 static bool
... ... @@ -1071,7 +1073,7 @@ class QPDF
1071 1073 QPDFObjGen exp_og,
1072 1074 QPDFObjGen& og,
1073 1075 bool skip_cache_if_in_xref);
1074   - QPDFObject* resolve(QPDFObjGen og);
  1076 + std::shared_ptr<QPDFObject> const& resolve(QPDFObjGen og);
1075 1077 void resolveObjectsInStream(int obj_stream_number);
1076 1078 void stopOnError(std::string const& message);
1077 1079 QPDFObjGen nextObjGen();
... ... @@ -1086,7 +1088,8 @@ class QPDF
1086 1088 QPDFObjGen og,
1087 1089 std::shared_ptr<QPDFObject> const& object,
1088 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 1093 static QPDFExc damagedPDF(
1091 1094 InputSource& input,
1092 1095 std::string const& object,
... ...
include/qpdf/QPDFObjGen.hh
... ... @@ -130,12 +130,6 @@ class QPDFObjGen
130 130 }
131 131  
132 132 QPDF_DLL
133   - bool add(QPDFObjectHandle const& oh);
134   -
135   - QPDF_DLL
136   - bool add(QPDFObjectHelper const& oh);
137   -
138   - QPDF_DLL
139 133 void
140 134 erase(QPDFObjGen og)
141 135 {
... ... @@ -143,12 +137,6 @@ class QPDFObjGen
143 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 142 private:
... ...
include/qpdf/QPDFObjectHandle.hh
... ... @@ -37,6 +37,7 @@
37 37 #include <qpdf/Buffer.hh>
38 38 #include <qpdf/InputSource.hh>
39 39 #include <qpdf/JSON.hh>
  40 +#include <qpdf/ObjectHandle.hh>
40 41 #include <qpdf/QPDFObjGen.hh>
41 42 #include <qpdf/QPDFTokenizer.hh>
42 43  
... ... @@ -55,13 +56,14 @@ class QPDF_Reserved;
55 56 class QPDF_Stream;
56 57 class QPDF_String;
57 58 class QPDFObject;
  59 +class QPDFObjectHandle;
58 60 class QPDFTokenizer;
59 61 class QPDFExc;
60 62 class Pl_QPDFTokenizer;
61 63 class QPDFMatrix;
62 64 class QPDFParser;
63 65  
64   -class QPDFObjectHandle
  66 +class QPDFObjectHandle final: public qpdf::BaseHandle
65 67 {
66 68 friend class QPDFParser;
67 69  
... ... @@ -290,25 +292,19 @@ class QPDFObjectHandle
290 292 QPDFObjectHandle(QPDFObjectHandle const&) = default;
291 293 QPDF_DLL
292 294 QPDFObjectHandle& operator=(QPDFObjectHandle const&) = default;
293   -
294 295 QPDF_DLL
295 296 QPDFObjectHandle(QPDFObjectHandle&&) = default;
296 297 QPDF_DLL
297 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 302 // This method returns true if the QPDFObjectHandle objects point to exactly the same underlying
307 303 // object, meaning that changes to one are reflected in the other, or "if you paint one, the
308 304 // other one changes color." This does not perform a structural comparison of the contents of
309 305 // the objects.
310 306 QPDF_DLL
311   - bool isSameObjectAs(QPDFObjectHandle const&) const noexcept;
  307 + bool isSameObjectAs(QPDFObjectHandle const&) const;
312 308  
313 309 // Return type code and type name of underlying object. These are useful for doing rapid type
314 310 // tests (like switch statements) or for testing and debugging.
... ... @@ -1258,8 +1254,7 @@ class QPDFObjectHandle
1258 1254 // Provide access to specific classes for recursive disconnected().
1259 1255 class DisconnectAccess
1260 1256 {
1261   - friend class QPDF_Dictionary;
1262   - friend class QPDF_Stream;
  1257 + friend class QPDFObject;
1263 1258  
1264 1259 private:
1265 1260 static void
... ... @@ -1329,7 +1324,11 @@ class QPDFObjectHandle
1329 1324 // The following methods do not form part of the public API and are for internal use only.
1330 1325  
1331 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 1334 std::shared_ptr<QPDFObject>
... ... @@ -1355,21 +1354,11 @@ class QPDFObjectHandle
1355 1354  
1356 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 1362 void typeWarning(char const* expected_type, std::string const& warning) const;
1374 1363 void objectWarning(std::string const& warning) const;
1375 1364 void assertType(char const* type_name, bool istype) const;
... ... @@ -1386,10 +1375,6 @@ class QPDFObjectHandle
1386 1375 arrayOrStreamToStreamArray(std::string const& description, std::string& all_description);
1387 1376 static void warn(QPDF*, QPDFExc const&);
1388 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 1380 #ifndef QPDF_NO_QPDF_STRING
... ... @@ -1606,6 +1591,22 @@ class QPDFObjectHandle::QPDFArrayItems
1606 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 1610 inline int
1610 1611 QPDFObjectHandle::getObjectID() const
1611 1612 {
... ... @@ -1625,15 +1626,9 @@ QPDFObjectHandle::isIndirect() const
1625 1626 }
1626 1627  
1627 1628 inline bool
1628   -QPDFObjectHandle::isInitialized() const noexcept
  1629 +QPDFObjectHandle::isInitialized() const
1629 1630 {
1630 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 1634 #endif // QPDFOBJECTHANDLE_HH
... ...
include/qpdf/QPDFObjectHandle_future.hh deleted
include/qpdf/QPDFObjectHelper.hh
... ... @@ -31,13 +31,12 @@
31 31 // underlying QPDF objects unless there is a specific comment in a specific helper method that says
32 32 // otherwise. The pattern of using helper objects was introduced to allow creation of higher level
33 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 36 public:
38 37 QPDF_DLL
39 38 QPDFObjectHelper(QPDFObjectHandle oh) :
40   - oh(oh)
  39 + qpdf::BaseHandle(oh.getObj())
41 40 {
42 41 }
43 42 QPDF_DLL
... ... @@ -46,17 +45,29 @@ class QPDF_DLL_CLASS QPDFObjectHelper
46 45 QPDFObjectHandle
47 46 getObjectHandle()
48 47 {
49   - return this->oh;
  48 + return {obj};
50 49 }
51 50 QPDF_DLL
52 51 QPDFObjectHandle const
53 52 getObjectHandle() const
54 53 {
55   - return this->oh;
  54 + return {obj};
56 55 }
57 56  
58 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 73 #endif // QPDFOBJECTHELPER_HH
... ...
libqpdf/CMakeLists.txt
... ... @@ -76,7 +76,6 @@ set(libqpdf_SOURCES
76 76 QPDFObject.cc
77 77 QPDFObjectHandle.cc
78 78 QPDFObjectHelper.cc
79   - QPDFObjGen.cc
80 79 QPDFOutlineDocumentHelper.cc
81 80 QPDFOutlineObjectHelper.cc
82 81 QPDFPageDocumentHelper.cc
... ... @@ -87,23 +86,12 @@ set(libqpdf_SOURCES
87 86 QPDFSystemError.cc
88 87 QPDFTokenizer.cc
89 88 QPDFUsage.cc
90   - QPDFValue.cc
91 89 QPDFWriter.cc
92 90 QPDFXRefEntry.cc
93 91 QPDF_Array.cc
94   - QPDF_Bool.cc
95   - QPDF_Destroyed.cc
96 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 93 QPDF_Stream.cc
105 94 QPDF_String.cc
106   - QPDF_Unresolved.cc
107 95 QPDF_encryption.cc
108 96 QPDF_json.cc
109 97 QPDF_linearization.cc
... ...
libqpdf/ContentNormalizer.cc
1 1 #include <qpdf/ContentNormalizer.hh>
2 2  
3   -#include <qpdf/QPDF_Name.hh>
  3 +#include <qpdf/QPDFObjectHandle_private.hh>
4 4 #include <qpdf/QUtil.hh>
5 5  
  6 +using namespace qpdf;
  7 +
6 8 ContentNormalizer::ContentNormalizer() :
7 9 any_bad_tokens(false),
8 10 last_token_was_bad(false)
... ... @@ -55,7 +57,7 @@ ContentNormalizer::handleToken(QPDFTokenizer::Token const&amp; token)
55 57 break;
56 58  
57 59 case QPDFTokenizer::tt_name:
58   - write(QPDF_Name::normalizeName(token.getValue()));
  60 + write(Name::normalize(token.getValue()));
59 61 break;
60 62  
61 63 default:
... ...
libqpdf/QPDF.cc
... ... @@ -17,14 +17,9 @@
17 17 #include <qpdf/Pipeline.hh>
18 18 #include <qpdf/QPDFExc.hh>
19 19 #include <qpdf/QPDFLogger.hh>
  20 +#include <qpdf/QPDFObjectHandle_private.hh>
20 21 #include <qpdf/QPDFObject_private.hh>
21 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 23 #include <qpdf/QTC.hh>
29 24 #include <qpdf/QUtil.hh>
30 25  
... ... @@ -298,7 +293,7 @@ void
298 293 QPDF::registerStreamFilter(
299 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 299 void
... ... @@ -1565,7 +1560,7 @@ QPDF::readStream(QPDFObjectHandle&amp; object, QPDFObjGen og, qpdf_offset_t offset)
1565 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 1566 void
... ... @@ -1872,11 +1867,11 @@ QPDF::readObjectAtOffset(
1872 1867 return oh;
1873 1868 }
1874 1869  
1875   -QPDFObject*
  1870 +std::shared_ptr<QPDFObject> const&
1876 1871 QPDF::resolve(QPDFObjGen og)
1877 1872 {
1878 1873 if (!isUnresolved(og)) {
1879   - return m->obj_cache[og].object.get();
  1874 + return m->obj_cache[og].object;
1880 1875 }
1881 1876  
1882 1877 if (m->resolving.count(og)) {
... ... @@ -1884,8 +1879,8 @@ QPDF::resolve(QPDFObjGen og)
1884 1879 // has to be resolved during object parsing, such as stream length.
1885 1880 QTC::TC("qpdf", "QPDF recursion loop in resolve");
1886 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 1885 ResolveRecorder rr(this, og);
1891 1886  
... ... @@ -1921,12 +1916,12 @@ QPDF::resolve(QPDFObjGen og)
1921 1916 if (isUnresolved(og)) {
1922 1917 // PDF spec says unknown objects resolve to the null object.
1923 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 1923 result->setDefaultDescription(this, og);
1929   - return result.get();
  1924 + return result;
1930 1925 }
1931 1926  
1932 1927 void
... ... @@ -2034,12 +2029,13 @@ QPDF::updateCache(
2034 2029 QPDFObjGen og,
2035 2030 std::shared_ptr<QPDFObject> const& object,
2036 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 2035 object->setObjGen(this, og);
2040 2036 if (isCached(og)) {
2041 2037 auto& cache = m->obj_cache[og];
2042   - cache.object->assign(object);
  2038 + object->move_to(cache.object, destroy);
2043 2039 cache.end_before_space = end_before_space;
2044 2040 cache.end_after_space = end_after_space;
2045 2041 } else {
... ... @@ -2089,20 +2085,20 @@ QPDF::makeIndirectObject(QPDFObjectHandle oh)
2089 2085 QPDFObjectHandle
2090 2086 QPDF::newReserved()
2091 2087 {
2092   - return makeIndirectFromQPDFObject(QPDF_Reserved::create());
  2088 + return makeIndirectFromQPDFObject(QPDFObject::create<QPDF_Reserved>());
2093 2089 }
2094 2090  
2095 2091 QPDFObjectHandle
2096 2092 QPDF::newIndirectNull()
2097 2093 {
2098   - return makeIndirectFromQPDFObject(QPDF_Null::create());
  2094 + return makeIndirectFromQPDFObject(QPDFObject::create<QPDF_Null>());
2099 2095 }
2100 2096  
2101 2097 QPDFObjectHandle
2102 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 2104 QPDFObjectHandle
... ... @@ -2130,12 +2126,13 @@ QPDF::getObjectForParser(int id, int gen, bool parse_pdf)
2130 2126 return iter->second.object;
2131 2127 }
2132 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 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 2138 std::shared_ptr<QPDFObject>
... ... @@ -2145,8 +2142,9 @@ QPDF::getObjectForJSON(int id, int gen)
2145 2142 auto [it, inserted] = m->obj_cache.try_emplace(og);
2146 2143 auto& obj = it->second.object;
2147 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 2149 return obj;
2152 2150 }
... ... @@ -2157,9 +2155,10 @@ QPDF::getObject(QPDFObjGen og)
2157 2155 if (auto it = m->obj_cache.find(og); it != m->obj_cache.end()) {
2158 2156 return {it->second.object};
2159 2157 } else if (m->parsed && !m->xref_table.count(og)) {
2160   - return QPDF_Null::create();
  2158 + return QPDFObject::create<QPDF_Null>();
2161 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 2162 return {result.first->second.object};
2164 2163 }
2165 2164 }
... ... @@ -2195,7 +2194,7 @@ QPDF::replaceObject(QPDFObjGen og, QPDFObjectHandle oh)
2195 2194 QTC::TC("qpdf", "QPDF replaceObject called with indirect object");
2196 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 2200 void
... ... @@ -2204,7 +2203,7 @@ QPDF::removeObject(QPDFObjGen og)
2204 2203 m->xref_table.erase(og);
2205 2204 if (auto cached = m->obj_cache.find(og); cached != m->obj_cache.end()) {
2206 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 2207 cached->second.object->setObjGen(nullptr, QPDFObjGen());
2209 2208 m->obj_cache.erase(cached);
2210 2209 }
... ... @@ -2347,14 +2346,15 @@ QPDF::reserveObjects(QPDFObjectHandle foreign, ObjCopier&amp; obj_copier, bool top)
2347 2346  
2348 2347 if (foreign_tc == ::ot_array) {
2349 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 2352 } else if (foreign_tc == ::ot_dictionary) {
2355 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 2359 } else if (foreign_tc == ::ot_stream) {
2360 2360 QTC::TC("qpdf", "QPDF reserve stream");
... ... @@ -2383,30 +2383,26 @@ QPDF::replaceForeignIndirectObjects(QPDFObjectHandle foreign, ObjCopier&amp; obj_cop
2383 2383 } else if (foreign_tc == ::ot_array) {
2384 2384 QTC::TC("qpdf", "QPDF replace array");
2385 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 2389 } else if (foreign_tc == ::ot_dictionary) {
2393 2390 QTC::TC("qpdf", "QPDF replace dictionary");
2394 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 2397 } else if (foreign_tc == ::ot_stream) {
2401 2398 QTC::TC("qpdf", "QPDF replace stream");
2402 2399 result = obj_copier.object_map[foreign.getObjGen()];
2403   - result.assertStream();
2404 2400 QPDFObjectHandle dict = result.getDict();
2405 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 2407 copyStreamData(result, foreign);
2412 2408 } else {
... ... @@ -2442,13 +2438,11 @@ QPDF::copyStreamData(QPDFObjectHandle result, QPDFObjectHandle foreign)
2442 2438 QPDF& foreign_stream_qpdf =
2443 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 2446 if ((foreign_stream_qpdf.m->immediate_copy_from) && (stream_buffer == nullptr)) {
2453 2447 // Pull the stream data into a buffer before attempting the copy operation. Do it on the
2454 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 2452 foreign.getRawStreamData(),
2459 2453 old_dict.getKey("/Filter"),
2460 2454 old_dict.getKey("/DecodeParms"));
2461   - stream_buffer = stream->getStreamDataBuffer();
  2455 + stream_buffer = stream.getStreamDataBuffer();
2462 2456 }
2463 2457 std::shared_ptr<QPDFObjectHandle::StreamDataProvider> stream_provider =
2464   - stream->getStreamDataProvider();
  2458 + stream.getStreamDataProvider();
2465 2459 if (stream_buffer.get()) {
2466 2460 QTC::TC("qpdf", "QPDF copy foreign stream with buffer");
2467 2461 result.replaceStreamData(
... ... @@ -2476,9 +2470,9 @@ QPDF::copyStreamData(QPDFObjectHandle result, QPDFObjectHandle foreign)
2476 2470 auto foreign_stream_data = std::make_shared<ForeignStreamData>(
2477 2471 foreign_stream_qpdf.m->encp,
2478 2472 foreign_stream_qpdf.m->file,
2479   - foreign.getObjGen(),
2480   - stream->getParsedOffset(),
2481   - stream->getLength(),
  2473 + foreign,
  2474 + foreign.getParsedOffset(),
  2475 + stream.getLength(),
2482 2476 dict);
2483 2477 m->copied_stream_data_provider->registerForeignStream(local_og, foreign_stream_data);
2484 2478 result.replaceStreamData(
... ... @@ -2692,30 +2686,32 @@ QPDF::getCompressibleObjGens()
2692 2686 }
2693 2687 }
2694 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 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 1 #include <qpdf/QPDFAcroFormDocumentHelper.hh>
2 2  
3 3 #include <qpdf/Pl_Buffer.hh>
  4 +#include <qpdf/QPDFObjectHandle_private.hh>
4 5 #include <qpdf/QPDFPageDocumentHelper.hh>
5 6 #include <qpdf/QTC.hh>
6 7 #include <qpdf/QUtil.hh>
7 8 #include <qpdf/ResourceFinder.hh>
8 9  
  10 +using namespace qpdf;
  11 +
9 12 QPDFAcroFormDocumentHelper::Members::Members() :
10 13 cache_valid(false)
11 14 {
... ... @@ -238,26 +241,25 @@ QPDFAcroFormDocumentHelper::analyze()
238 241 return;
239 242 }
240 243 m->cache_valid = true;
241   - QPDFObjectHandle acroform = this->qpdf.getRoot().getKey("/AcroForm");
  244 + QPDFObjectHandle acroform = qpdf.getRoot().getKey("/AcroForm");
242 245 if (!(acroform.isDictionary() && acroform.hasKey("/Fields"))) {
243 246 return;
244 247 }
245 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 258 QTC::TC("qpdf", "QPDFAcroFormDocumentHelper fields not array");
248 259 acroform.warnIfPossible("/Fields key of /AcroForm dictionary is not an array; ignoring");
249 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 263 // All Widget annotations should have been encountered by traversing /AcroForm, but in case any
262 264 // weren't, find them by walking through pages, and treat any widget annotation that is not
263 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 326  
325 327 bool is_annotation = false;
326 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 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 334 } else {
335 335 if (field.hasKey("/Parent")) {
... ... @@ -975,17 +975,14 @@ QPDFAcroFormDocumentHelper::transformAnnotations(
975 975 auto replace_stream = [](auto& dict, auto& key, auto& old) {
976 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 13 std::string
14 14 QPDFAnnotationObjectHelper::getSubtype()
15 15 {
16   - return this->oh.getKey("/Subtype").getName();
  16 + return oh().getKey("/Subtype").getName();
17 17 }
18 18  
19 19 QPDFObjectHandle::Rectangle
20 20 QPDFAnnotationObjectHelper::getRect()
21 21 {
22   - return this->oh.getKey("/Rect").getArrayAsRectangle();
  22 + return oh().getKey("/Rect").getArrayAsRectangle();
23 23 }
24 24  
25 25 QPDFObjectHandle
26 26 QPDFAnnotationObjectHelper::getAppearanceDictionary()
27 27 {
28   - return this->oh.getKey("/AP");
  28 + return oh().getKey("/AP");
29 29 }
30 30  
31 31 std::string
32 32 QPDFAnnotationObjectHelper::getAppearanceState()
33 33 {
34   - if (this->oh.getKey("/AS").isName()) {
  34 + if (oh().getKey("/AS").isName()) {
35 35 QTC::TC("qpdf", "QPDFAnnotationObjectHelper AS present");
36   - return this->oh.getKey("/AS").getName();
  36 + return oh().getKey("/AS").getName();
37 37 }
38 38 QTC::TC("qpdf", "QPDFAnnotationObjectHelper AS absent");
39 39 return "";
... ... @@ -42,7 +42,7 @@ QPDFAnnotationObjectHelper::getAppearanceState()
42 42 int
43 43 QPDFAnnotationObjectHelper::getFlags()
44 44 {
45   - QPDFObjectHandle flags_obj = this->oh.getKey("/F");
  45 + QPDFObjectHandle flags_obj = oh().getKey("/F");
46 46 return flags_obj.isInteger() ? flags_obj.getIntValueAsInt() : 0;
47 47 }
48 48  
... ... @@ -143,7 +143,7 @@ QPDFAnnotationObjectHelper::getPageContentForAppearance(
143 143  
144 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 147 QPDFObjectHandle as = getAppearanceStream("/N").getDict();
148 148 QPDFObjectHandle bbox_obj = as.getKey("/BBox");
149 149 QPDFObjectHandle matrix_obj = as.getKey("/Matrix");
... ...
libqpdf/QPDFEFStreamObjectHelper.cc
... ... @@ -16,7 +16,7 @@ QPDFEFStreamObjectHelper::QPDFEFStreamObjectHelper(QPDFObjectHandle oh) :
16 16 QPDFObjectHandle
17 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 20 if (params.isDictionary()) {
21 21 return params.getKey(pkey);
22 22 }
... ... @@ -26,10 +26,9 @@ QPDFEFStreamObjectHelper::getParam(std::string const&amp; pkey)
26 26 void
27 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 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 33 params.replaceKey(pkey, pval);
35 34 }
... ... @@ -67,7 +66,7 @@ QPDFEFStreamObjectHelper::getSize()
67 66 std::string
68 67 QPDFEFStreamObjectHelper::getSubtype()
69 68 {
70   - auto val = this->oh.getDict().getKey("/Subtype");
  69 + auto val = oh().getDict().getKey("/Subtype");
71 70 if (val.isName()) {
72 71 auto n = val.getName();
73 72 if (n.length() > 1) {
... ... @@ -124,7 +123,7 @@ QPDFEFStreamObjectHelper::setModDate(std::string const&amp; date)
124 123 QPDFEFStreamObjectHelper&
125 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 127 return *this;
129 128 }
130 129  
... ...
libqpdf/QPDFFileSpecObjectHelper.cc
... ... @@ -25,7 +25,7 @@ std::string
25 25 QPDFFileSpecObjectHelper::getDescription()
26 26 {
27 27 std::string result;
28   - auto desc = this->oh.getKey("/Desc");
  28 + auto desc = oh().getKey("/Desc");
29 29 if (desc.isString()) {
30 30 result = desc.getUTF8Value();
31 31 }
... ... @@ -36,7 +36,7 @@ std::string
36 36 QPDFFileSpecObjectHelper::getFilename()
37 37 {
38 38 for (auto const& i: name_keys) {
39   - auto k = this->oh.getKey(i);
  39 + auto k = oh().getKey(i);
40 40 if (k.isString()) {
41 41 return k.getUTF8Value();
42 42 }
... ... @@ -49,7 +49,7 @@ QPDFFileSpecObjectHelper::getFilenames()
49 49 {
50 50 std::map<std::string, std::string> result;
51 51 for (auto const& i: name_keys) {
52   - auto k = this->oh.getKey(i);
  52 + auto k = oh().getKey(i);
53 53 if (k.isString()) {
54 54 result[i] = k.getUTF8Value();
55 55 }
... ... @@ -60,7 +60,7 @@ QPDFFileSpecObjectHelper::getFilenames()
60 60 QPDFObjectHandle
61 61 QPDFFileSpecObjectHelper::getEmbeddedFileStream(std::string const& key)
62 62 {
63   - auto ef = this->oh.getKey("/EF");
  63 + auto ef = oh().getKey("/EF");
64 64 if (!ef.isDictionary()) {
65 65 return QPDFObjectHandle::newNull();
66 66 }
... ... @@ -79,7 +79,7 @@ QPDFFileSpecObjectHelper::getEmbeddedFileStream(std::string const&amp; key)
79 79 QPDFObjectHandle
80 80 QPDFFileSpecObjectHelper::getEmbeddedFileStreams()
81 81 {
82   - return this->oh.getKey("/EF");
  82 + return oh().getKey("/EF");
83 83 }
84 84  
85 85 QPDFFileSpecObjectHelper
... ... @@ -110,7 +110,7 @@ QPDFFileSpecObjectHelper::createFileSpec(
110 110 QPDFFileSpecObjectHelper&
111 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 114 return *this;
115 115 }
116 116  
... ... @@ -119,13 +119,13 @@ QPDFFileSpecObjectHelper::setFilename(
119 119 std::string const& unicode_name, std::string const& compat_name)
120 120 {
121 121 auto uf = QPDFObjectHandle::newUnicodeString(unicode_name);
122   - this->oh.replaceKey("/UF", uf);
  122 + oh().replaceKey("/UF", uf);
123 123 if (compat_name.empty()) {
124 124 QTC::TC("qpdf", "QPDFFileSpecObjectHelper empty compat_name");
125   - this->oh.replaceKey("/F", uf);
  125 + oh().replaceKey("/F", uf);
126 126 } else {
127 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 130 return *this;
131 131 }
... ...
libqpdf/QPDFFormFieldObjectHelper.cc
... ... @@ -4,6 +4,7 @@
4 4 #include <qpdf/QIntC.hh>
5 5 #include <qpdf/QPDFAcroFormDocumentHelper.hh>
6 6 #include <qpdf/QPDFAnnotationObjectHelper.hh>
  7 +#include <qpdf/QPDFObjectHandle_private.hh>
7 8 #include <qpdf/QTC.hh>
8 9 #include <qpdf/QUtil.hh>
9 10 #include <cstdlib>
... ... @@ -23,19 +24,19 @@ QPDFFormFieldObjectHelper::QPDFFormFieldObjectHelper() :
23 24 bool
24 25 QPDFFormFieldObjectHelper::isNull()
25 26 {
26   - return this->oh.isNull();
  27 + return oh().isNull();
27 28 }
28 29  
29 30 QPDFFormFieldObjectHelper
30 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 36 QPDFFormFieldObjectHelper
36 37 QPDFFormFieldObjectHelper::getTopLevelField(bool* is_different)
37 38 {
38   - auto top_field = this->oh;
  39 + auto top_field = oh();
39 40 QPDFObjGen::set seen;
40 41 while (seen.add(top_field) && !top_field.getKeyIfDict("/Parent").isNull()) {
41 42 top_field = top_field.getKey("/Parent");
... ... @@ -51,7 +52,7 @@ QPDFFormFieldObjectHelper::getFieldFromAcroForm(std::string const&amp; name)
51 52 {
52 53 QPDFObjectHandle result = QPDFObjectHandle::newNull();
53 54 // Fields are supposed to be indirect, so this should work.
54   - QPDF* q = this->oh.getOwningQPDF();
  55 + QPDF* q = oh().getOwningQPDF();
55 56 if (!q) {
56 57 return result;
57 58 }
... ... @@ -65,7 +66,7 @@ QPDFFormFieldObjectHelper::getFieldFromAcroForm(std::string const&amp; name)
65 66 QPDFObjectHandle
66 67 QPDFFormFieldObjectHelper::getInheritableFieldValue(std::string const& name)
67 68 {
68   - QPDFObjectHandle node = this->oh;
  69 + QPDFObjectHandle node = oh();
69 70 if (!node.isDictionary()) {
70 71 return QPDFObjectHandle::newNull();
71 72 }
... ... @@ -116,7 +117,7 @@ std::string
116 117 QPDFFormFieldObjectHelper::getFullyQualifiedName()
117 118 {
118 119 std::string result;
119   - QPDFObjectHandle node = this->oh;
  120 + QPDFObjectHandle node = oh();
120 121 QPDFObjGen::set seen;
121 122 while (!node.isNull() && seen.add(node)) {
122 123 if (node.getKey("/T").isString()) {
... ... @@ -135,8 +136,8 @@ std::string
135 136 QPDFFormFieldObjectHelper::getPartialName()
136 137 {
137 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 142 return result;
142 143 }
... ... @@ -144,9 +145,9 @@ QPDFFormFieldObjectHelper::getPartialName()
144 145 std::string
145 146 QPDFFormFieldObjectHelper::getAlternativeName()
146 147 {
147   - if (this->oh.getKey("/TU").isString()) {
  148 + if (oh().getKey("/TU").isString()) {
148 149 QTC::TC("qpdf", "QPDFFormFieldObjectHelper TU present");
149   - return this->oh.getKey("/TU").getUTF8Value();
  150 + return oh().getKey("/TU").getUTF8Value();
150 151 }
151 152 QTC::TC("qpdf", "QPDFFormFieldObjectHelper TU absent");
152 153 return getFullyQualifiedName();
... ... @@ -155,9 +156,9 @@ QPDFFormFieldObjectHelper::getAlternativeName()
155 156 std::string
156 157 QPDFFormFieldObjectHelper::getMappingName()
157 158 {
158   - if (this->oh.getKey("/TM").isString()) {
  159 + if (oh().getKey("/TM").isString()) {
159 160 QTC::TC("qpdf", "QPDFFormFieldObjectHelper TM present");
160   - return this->oh.getKey("/TM").getUTF8Value();
  161 + return oh().getKey("/TM").getUTF8Value();
161 162 }
162 163 QTC::TC("qpdf", "QPDFFormFieldObjectHelper TM absent");
163 164 return getAlternativeName();
... ... @@ -271,14 +272,9 @@ QPDFFormFieldObjectHelper::getChoices()
271 272 if (!isChoice()) {
272 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 280 return result;
... ... @@ -287,13 +283,13 @@ QPDFFormFieldObjectHelper::getChoices()
287 283 void
288 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 289 void
294 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 295 void
... ... @@ -310,18 +306,18 @@ QPDFFormFieldObjectHelper::setV(QPDFObjectHandle value, bool need_appearances)
310 306 setCheckBoxValue((name != "/Off"));
311 307 }
312 308 if (!okay) {
313   - this->oh.warnIfPossible(
  309 + oh().warnIfPossible(
314 310 "ignoring attempt to set a checkbox field to a value whose type is not name");
315 311 }
316 312 } else if (isRadioButton()) {
317 313 if (value.isName()) {
318 314 setRadioButtonValue(value);
319 315 } else {
320   - this->oh.warnIfPossible(
  316 + oh().warnIfPossible(
321 317 "ignoring attempt to set a radio button field to an object that is not a name");
322 318 }
323 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 322 return;
327 323 }
... ... @@ -331,7 +327,7 @@ QPDFFormFieldObjectHelper::setV(QPDFObjectHandle value, bool need_appearances)
331 327 setFieldAttribute("/V", value);
332 328 }
333 329 if (need_appearances) {
334   - QPDF& qpdf = this->oh.getQPDF(
  330 + QPDF& qpdf = oh().getQPDF(
335 331 "QPDFFormFieldObjectHelper::setV called with need_appearances = "
336 332 "true on an object that is not associated with an owning QPDF");
337 333 QPDFAcroFormDocumentHelper(qpdf).setNeedAppearances(true);
... ... @@ -355,7 +351,7 @@ QPDFFormFieldObjectHelper::setRadioButtonValue(QPDFObjectHandle name)
355 351 // its /AP (i.e. its normal appearance stream dictionary), set /AS to name; otherwise, if /Off
356 352 // is a member, set /AS to /Off.
357 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 355 if (parent.isDictionary() && parent.getKey("/Parent").isNull()) {
360 356 QPDFFormFieldObjectHelper ph(parent);
361 357 if (ph.isRadioButton()) {
... ... @@ -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 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 368 return;
375 369 }
376 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 372 QPDFObjectHandle AP = kid.getKey("/AP");
381 373 QPDFObjectHandle annot;
382   - if (AP.isNull()) {
  374 + if (AP.null()) {
383 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 384 } else {
... ... @@ -399,7 +386,7 @@ QPDFFormFieldObjectHelper::setRadioButtonValue(QPDFObjectHandle name)
399 386 }
400 387 if (!annot) {
401 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 390 continue;
404 391 }
405 392 if (AP.isDictionary() && AP.getKey("/N").isDictionary() &&
... ... @@ -416,39 +403,32 @@ QPDFFormFieldObjectHelper::setRadioButtonValue(QPDFObjectHandle name)
416 403 void
417 404 QPDFFormFieldObjectHelper::setCheckBoxValue(bool value)
418 405 {
419   - QPDFObjectHandle AP = this->oh.getKey("/AP");
  406 + QPDFObjectHandle AP = oh().getKey("/AP");
420 407 QPDFObjectHandle annot;
421   - if (AP.isNull()) {
  408 + if (AP.null()) {
422 409 // The widget may be below. If there is more than one, just
423 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 420 } else {
438   - annot = this->oh;
  421 + annot = oh();
439 422 }
440 423 std::string on_value;
441 424 if (value) {
442 425 // Set the "on" value to the first value in the appearance stream's normal state dictionary
443 426 // that isn't /Off. If not found, fall back to /Yes.
444 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 442 setFieldAttribute("/V", name);
463 443 if (!annot) {
464 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 446 return;
467 447 }
468 448 QTC::TC("qpdf", "QPDFFormFieldObjectHelper set checkbox AS");
... ... @@ -775,7 +755,7 @@ QPDFFormFieldObjectHelper::generateTextAppearance(QPDFAnnotationObjectHelper&amp; ao
775 755 "<< /Resources << /ProcSet [ /PDF /Text ] >>"
776 756 " /Type /XObject /Subtype /Form >>");
777 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 759 AS.replaceDict(dict);
780 760 QPDFObjectHandle AP = aoh.getAppearanceDictionary();
781 761 if (AP.isNull()) {
... ...
libqpdf/QPDFJob.cc
... ... @@ -19,6 +19,7 @@
19 19 #include <qpdf/QPDFEmbeddedFileDocumentHelper.hh>
20 20 #include <qpdf/QPDFExc.hh>
21 21 #include <qpdf/QPDFLogger.hh>
  22 +#include <qpdf/QPDFObjectHandle_private.hh>
22 23 #include <qpdf/QPDFOutlineDocumentHelper.hh>
23 24 #include <qpdf/QPDFPageDocumentHelper.hh>
24 25 #include <qpdf/QPDFPageLabelDocumentHelper.hh>
... ... @@ -916,10 +917,13 @@ QPDFJob::doListAttachments(QPDF&amp; pdf)
916 917 v << " " << i2.first << " -> " << i2.second << "\n";
917 918 }
918 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 927 v << " creation date: " << efs.getCreationDate() << "\n"
924 928 << " modification date: " << efs.getModDate() << "\n"
925 929 << " mime type: " << efs.getSubtype() << "\n"
... ... @@ -1338,9 +1342,12 @@ QPDFJob::doJSONAttachments(Pipeline* p, bool&amp; first, QPDF&amp; pdf)
1338 1342 j_names.addDictionaryMember(i2.first, JSON::makeString(i2.second));
1339 1343 }
1340 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 1351 j_stream.addDictionaryMember(
1345 1352 "creationdate", null_or_string(to_iso8601(efs.getCreationDate())));
1346 1353 j_stream.addDictionaryMember(
... ... @@ -2347,12 +2354,10 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF&amp; pdf)
2347 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 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 3 #include <qpdf/BufferInputSource.hh>
4 4 #include <qpdf/JSON_writer.hh>
... ... @@ -11,19 +11,6 @@
11 11 #include <qpdf/QPDFObject_private.hh>
12 12 #include <qpdf/QPDFPageObjectHelper.hh>
13 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 15 #include <qpdf/QIntC.hh>
29 16 #include <qpdf/QTC.hh>
... ... @@ -38,6 +25,13 @@
38 25 #include <stdexcept>
39 26  
40 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 36 namespace
43 37 {
... ... @@ -221,8 +215,441 @@ LastChar::getLastChar()
221 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 651 bool
225   -QPDFObjectHandle::isSameObjectAs(QPDFObjectHandle const& rhs) const noexcept
  652 +QPDFObjectHandle::isSameObjectAs(QPDFObjectHandle const& rhs) const
226 653 {
227 654 return this->obj == rhs.obj;
228 655 }
... ... @@ -246,7 +673,7 @@ QPDFObjectHandle::getTypeCode() const
246 673 char const*
247 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 677 "uninitialized",
251 678 "reserved",
252 679 "null",
... ... @@ -261,100 +688,21 @@ QPDFObjectHandle::getTypeName() const
261 688 "operator",
262 689 "inline-image",
263 690 "unresolved",
264   - "destroyed"};
  691 + "destroyed",
  692 + "reference"};
265 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 696 bool
349 697 QPDFObjectHandle::isDestroyed() const
350 698 {
351   - return obj && obj->getResolvedTypeCode() == ::ot_destroyed;
  699 + return type_code() == ::ot_destroyed;
352 700 }
353 701  
354 702 bool
355 703 QPDFObjectHandle::isBool() const
356 704 {
357   - return obj && obj->getResolvedTypeCode() == ::ot_boolean;
  705 + return type_code() == ::ot_boolean;
358 706 }
359 707  
360 708 bool
... ... @@ -362,25 +710,25 @@ QPDFObjectHandle::isDirectNull() const
362 710 {
363 711 // Don't call dereference() -- this is a const method, and we know
364 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 716 bool
369 717 QPDFObjectHandle::isNull() const
370 718 {
371   - return obj && obj->getResolvedTypeCode() == ::ot_null;
  719 + return type_code() == ::ot_null;
372 720 }
373 721  
374 722 bool
375 723 QPDFObjectHandle::isInteger() const
376 724 {
377   - return obj && obj->getResolvedTypeCode() == ::ot_integer;
  725 + return type_code() == ::ot_integer;
378 726 }
379 727  
380 728 bool
381 729 QPDFObjectHandle::isReal() const
382 730 {
383   - return obj && obj->getResolvedTypeCode() == ::ot_real;
  731 + return type_code() == ::ot_real;
384 732 }
385 733  
386 734 bool
... ... @@ -416,49 +764,49 @@ QPDFObjectHandle::getValueAsNumber(double&amp; value) const
416 764 bool
417 765 QPDFObjectHandle::isName() const
418 766 {
419   - return obj && obj->getResolvedTypeCode() == ::ot_name;
  767 + return type_code() == ::ot_name;
420 768 }
421 769  
422 770 bool
423 771 QPDFObjectHandle::isString() const
424 772 {
425   - return obj && obj->getResolvedTypeCode() == ::ot_string;
  773 + return type_code() == ::ot_string;
426 774 }
427 775  
428 776 bool
429 777 QPDFObjectHandle::isOperator() const
430 778 {
431   - return obj && obj->getResolvedTypeCode() == ::ot_operator;
  779 + return type_code() == ::ot_operator;
432 780 }
433 781  
434 782 bool
435 783 QPDFObjectHandle::isInlineImage() const
436 784 {
437   - return obj && obj->getResolvedTypeCode() == ::ot_inlineimage;
  785 + return type_code() == ::ot_inlineimage;
438 786 }
439 787  
440 788 bool
441 789 QPDFObjectHandle::isArray() const
442 790 {
443   - return obj && obj->getResolvedTypeCode() == ::ot_array;
  791 + return type_code() == ::ot_array;
444 792 }
445 793  
446 794 bool
447 795 QPDFObjectHandle::isDictionary() const
448 796 {
449   - return obj && obj->getResolvedTypeCode() == ::ot_dictionary;
  797 + return type_code() == ::ot_dictionary;
450 798 }
451 799  
452 800 bool
453 801 QPDFObjectHandle::isStream() const
454 802 {
455   - return obj && obj->getResolvedTypeCode() == ::ot_stream;
  803 + return type_code() == ::ot_stream;
456 804 }
457 805  
458 806 bool
459 807 QPDFObjectHandle::isReserved() const
460 808 {
461   - return obj && obj->getResolvedTypeCode() == ::ot_reserved;
  809 + return type_code() == ::ot_reserved;
462 810 }
463 811  
464 812 bool
... ... @@ -491,9 +839,8 @@ QPDFObjectHandle::isStreamOfType(std::string const&amp; type, std::string const&amp; sub
491 839 bool
492 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 844 } else {
498 845 typeWarning("boolean", "returning false");
499 846 QTC::TC("qpdf", "QPDFObjectHandle boolean returning false");
... ... @@ -504,12 +851,11 @@ QPDFObjectHandle::getBoolValue() const
504 851 bool
505 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 861 // Integer accessors
... ... @@ -517,9 +863,8 @@ QPDFObjectHandle::getValueAsBool(bool&amp; value) const
517 863 long long
518 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 868 } else {
524 869 typeWarning("integer", "returning 0");
525 870 QTC::TC("qpdf", "QPDFObjectHandle integer returning 0");
... ... @@ -530,12 +875,11 @@ QPDFObjectHandle::getIntValue() const
530 875 bool
531 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 885 int
... ... @@ -692,8 +1036,7 @@ QPDFObjectHandle::getValueAsString(std::string&amp; value) const
692 1036 std::string
693 1037 QPDFObjectHandle::getUTF8Value() const
694 1038 {
695   - auto str = asString();
696   - if (str) {
  1039 + if (auto str = as<QPDF_String>()) {
697 1040 return str->getUTF8Val();
698 1041 } else {
699 1042 typeWarning("string", "returning empty string");
... ... @@ -705,12 +1048,11 @@ QPDFObjectHandle::getUTF8Value() const
705 1048 bool
706 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 1058 // Operator and Inline Image accessors
... ... @@ -759,7 +1101,7 @@ QPDFObjectHandle::getValueAsInlineImage(std::string&amp; value) const
759 1101 return true;
760 1102 }
761 1103  
762   -// Array accessors
  1104 +// Array accessors and mutators are in QPDF_Array.cc
763 1105  
764 1106 QPDFObjectHandle::QPDFArrayItems
765 1107 QPDFObjectHandle::aitems()
... ... @@ -767,207 +1109,7 @@ QPDFObjectHandle::aitems()
767 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 1114 QPDFObjectHandle::QPDFDictItems
973 1115 QPDFObjectHandle::ditems()
... ... @@ -975,66 +1117,6 @@ QPDFObjectHandle::ditems()
975 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 1120 // Array and Name accessors
1039 1121  
1040 1122 bool
... ... @@ -1055,19 +1137,10 @@ QPDFObjectHandle::isOrHasName(std::string const&amp; value) const
1055 1137 void
1056 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 1157  
1085 1158 auto make_og_to_name = [](QPDFObjectHandle& dict,
1086 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 1167 // This algorithm is described in comments in QPDFObjectHandle.hh
1095 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 1171 if (hasKey(rtype)) {
1100 1172 QPDFObjectHandle this_val = getKey(rtype);
1101 1173 if (this_val.isDictionary() && other_val.isDictionary()) {
... ... @@ -1110,9 +1182,8 @@ QPDFObjectHandle::mergeResources(
1110 1182 std::set<std::string> rnames;
1111 1183 int min_suffix = 1;
1112 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 1187 if (!this_val.hasKey(key)) {
1117 1188 if (!rval.isIndirect()) {
1118 1189 QTC::TC("qpdf", "QPDFObjectHandle merge shallow copy");
... ... @@ -1171,14 +1242,10 @@ QPDFObjectHandle::getResourceNames() const
1171 1242 {
1172 1243 // Return second-level dictionary keys
1173 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 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 1282 std::map<std::string, QPDFObjectHandle>
1441 1283 QPDFObjectHandle::getPageImages()
... ... @@ -1449,12 +1291,12 @@ QPDFObjectHandle::arrayOrStreamToStreamArray(
1449 1291 {
1450 1292 all_description = description;
1451 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 1296 for (int i = 0; i < n_items; ++i) {
1455   - QPDFObjectHandle item = array->at(i).second;
  1297 + QPDFObjectHandle item = array.at(i).second;
1456 1298 if (item.isStream()) {
1457   - result.push_back(item);
  1299 + result.emplace_back(item);
1458 1300 } else {
1459 1301 QTC::TC("qpdf", "QPDFObjectHandle non-stream in stream array");
1460 1302 warn(
... ... @@ -1468,7 +1310,7 @@ QPDFObjectHandle::arrayOrStreamToStreamArray(
1468 1310 }
1469 1311 }
1470 1312 } else if (isStream()) {
1471   - result.push_back(*this);
  1313 + result.emplace_back(*this);
1472 1314 } else if (!isNull()) {
1473 1315 warn(
1474 1316 getOwningQPDF(),
... ... @@ -1602,7 +1444,7 @@ QPDFObjectHandle::unparseResolved() const
1602 1444 std::string
1603 1445 QPDFObjectHandle::unparseBinary() const
1604 1446 {
1605   - if (auto str = asString()) {
  1447 + if (auto str = as<QPDF_String>()) {
1606 1448 return str->unparse(true);
1607 1449 } else {
1608 1450 return unparse();
... ... @@ -1633,7 +1475,7 @@ QPDFObjectHandle::writeJSON(int json_version, JSON::Writer&amp; p, bool dereference_
1633 1475 } else if (!obj) {
1634 1476 throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle");
1635 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 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 1490 QPDFObjectHandle
1661 1491 QPDFObjectHandle::wrapInArray()
1662 1492 {
... ... @@ -1857,7 +1687,7 @@ QPDFObjectHandle::addContentTokenFilter(std::shared_ptr&lt;TokenFilter&gt; filter)
1857 1687 void
1858 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 1693 QPDFObjectHandle
... ... @@ -1882,43 +1712,43 @@ QPDFObjectHandle::getParsedOffset() const
1882 1712 QPDFObjectHandle
1883 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 1718 QPDFObjectHandle
1889 1719 QPDFObjectHandle::newNull()
1890 1720 {
1891   - return {QPDF_Null::create()};
  1721 + return {QPDFObject::create<QPDF_Null>()};
1892 1722 }
1893 1723  
1894 1724 QPDFObjectHandle
1895 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 1730 QPDFObjectHandle
1901 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 1736 QPDFObjectHandle
1907 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 1742 QPDFObjectHandle
1913 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 1748 QPDFObjectHandle
1919 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 1754 QPDFObjectHandle
... ... @@ -1930,13 +1760,13 @@ QPDFObjectHandle::newUnicodeString(std::string const&amp; utf8_str)
1930 1760 QPDFObjectHandle
1931 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 1766 QPDFObjectHandle
1937 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 1772 QPDFObjectHandle
... ... @@ -1948,7 +1778,7 @@ QPDFObjectHandle::newArray()
1948 1778 QPDFObjectHandle
1949 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 1784 QPDFObjectHandle
... ... @@ -2008,7 +1838,7 @@ QPDFObjectHandle::newDictionary()
2008 1838 QPDFObjectHandle
2009 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 1844 QPDFObjectHandle
... ... @@ -2054,7 +1884,7 @@ void
2054 1884 QPDFObjectHandle::setObjectDescription(QPDF* owning_qpdf, std::string const& object_description)
2055 1885 {
2056 1886 if (obj) {
2057   - auto descr = std::make_shared<QPDFValue::Description>(object_description);
  1887 + auto descr = std::make_shared<QPDFObject::Description>(object_description);
2058 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 1926  
2097 1927 if (isBool() || isInteger() || isName() || isNull() || isReal() || isString()) {
2098 1928 this->obj = obj->copy(true);
2099   - } else if (isArray()) {
  1929 + } else if (auto a = as_array(strict)) {
2100 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 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 1936 } else if (isDictionary()) {
2109 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 1945 } else if (isStream()) {
2117 1946 QTC::TC("qpdf", "QPDFObjectHandle copy stream", stop_at_streams ? 0 : 1);
2118 1947 if (!stop_at_streams) {
... ... @@ -2349,19 +2178,6 @@ QPDFObjectHandle::isImage(bool exclude_imagemask) const
2349 2178 }
2350 2179  
2351 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 2181 QPDFObjectHandle::assertPageObject() const
2366 2182 {
2367 2183 if (!isPageObject()) {
... ...
libqpdf/QPDFOutlineObjectHelper.cc
... ... @@ -9,8 +9,8 @@ QPDFOutlineObjectHelper::Members::Members(QPDFOutlineDocumentHelper&amp; dh) :
9 9 }
10 10  
11 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 14 m(new Members(dh))
15 15 {
16 16 if (depth > 50) {
... ... @@ -18,13 +18,13 @@ QPDFOutlineObjectHelper::QPDFOutlineObjectHelper(
18 18 // to 1.
19 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 22 QTC::TC("qpdf", "QPDFOutlineObjectHelper loop");
23 23 return;
24 24 }
25 25  
26 26 QPDFObjGen::set children;
27   - QPDFObjectHandle cur = oh.getKey("/First");
  27 + QPDFObjectHandle cur = a_oh.getKey("/First");
28 28 while (!cur.isNull() && cur.isIndirect() && children.add(cur)) {
29 29 QPDFOutlineObjectHelper new_ooh(cur, dh, 1 + depth);
30 30 new_ooh.m->parent = std::make_shared<QPDFOutlineObjectHelper>(*this);
... ... @@ -50,11 +50,11 @@ QPDFOutlineObjectHelper::getDest()
50 50 {
51 51 QPDFObjectHandle dest;
52 52 QPDFObjectHandle A;
53   - if (this->oh.hasKey("/Dest")) {
  53 + if (oh().hasKey("/Dest")) {
54 54 QTC::TC("qpdf", "QPDFOutlineObjectHelper direct dest");
55   - dest = this->oh.getKey("/Dest");
  55 + dest = oh().getKey("/Dest");
56 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 58 (A.getKey("/S").getName() == "/GoTo") && A.hasKey("/D")) {
59 59 QTC::TC("qpdf", "QPDFOutlineObjectHelper action dest");
60 60 dest = A.getKey("/D");
... ... @@ -85,8 +85,8 @@ int
85 85 QPDFOutlineObjectHelper::getCount()
86 86 {
87 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 91 return count;
92 92 }
... ... @@ -95,8 +95,8 @@ std::string
95 95 QPDFOutlineObjectHelper::getTitle()
96 96 {
97 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 101 return result;
102 102 }
... ...
libqpdf/QPDFPageObjectHelper.cc
... ... @@ -7,6 +7,7 @@
7 7 #include <qpdf/QPDFAcroFormDocumentHelper.hh>
8 8 #include <qpdf/QPDFExc.hh>
9 9 #include <qpdf/QPDFMatrix.hh>
  10 +#include <qpdf/QPDFObjectHandle_private.hh>
10 11 #include <qpdf/QTC.hh>
11 12 #include <qpdf/QUtil.hh>
12 13 #include <qpdf/ResourceFinder.hh>
... ... @@ -72,9 +73,12 @@ InlineImageTracker::convertIIDict(QPDFObjectHandle odict)
72 73 QPDFObjectHandle dict = QPDFObjectHandle::newDictionary();
73 74 dict.replaceKey("/Type", QPDFObjectHandle::newName("/XObject"));
74 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 82 if (key == "/BPC") {
79 83 key = "/BitsPerComponent";
80 84 } else if (key == "/CS") {
... ... @@ -227,9 +231,9 @@ QPDFPageObjectHelper::getAttribute(
227 231 std::function<QPDFObjectHandle()> get_fallback,
228 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 235 bool inherited = false;
232   - auto dict = is_form_xobject ? oh.getDict() : oh;
  236 + auto dict = is_form_xobject ? oh().getDict() : oh();
233 237 auto result = dict.getKey(name);
234 238  
235 239 if (!is_form_xobject && result.isNull() &&
... ... @@ -324,23 +328,24 @@ QPDFPageObjectHelper::forEachXObject(
324 328 QTC::TC(
325 329 "qpdf",
326 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 332 QPDFObjGen::set seen;
329 333 std::list<QPDFPageObjectHelper> queue;
330   - queue.push_back(*this);
  334 + queue.emplace_back(*this);
331 335 while (!queue.empty()) {
332 336 auto& ph = queue.front();
333 337 if (seen.add(ph)) {
334 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 407 // Calling mergeResources also ensures that /XObject becomes direct and is not shared with
403 408 // other pages.
404 409 resources.mergeResources("<< /XObject << >> >>"_qpdf);
405   - InlineImageTracker iit(this->oh.getOwningQPDF(), min_size, resources);
  410 + InlineImageTracker iit(oh().getOwningQPDF(), min_size, resources);
406 411 Pl_Buffer b("new page content");
407 412 bool filtered = false;
408 413 try {
409 414 filterContents(&iit, &b);
410 415 filtered = true;
411 416 } catch (std::exception& e) {
412   - this->oh.warnIfPossible(
  417 + oh().warnIfPossible(
413 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 421 if (filtered && iit.any_images) {
418   - if (this->oh.isFormXObject()) {
419   - this->oh.replaceStreamData(
  422 + if (oh().isFormXObject()) {
  423 + oh().replaceStreamData(
420 424 b.getBufferSharedPointer(),
421 425 QPDFObjectHandle::newNull(),
422 426 QPDFObjectHandle::newNull());
423 427 } else {
424   - this->oh.replaceKey(
  428 + oh().replaceKey(
425 429 "/Contents",
426   - QPDFObjectHandle::newStream(&this->oh.getQPDF(), b.getBufferSharedPointer()));
  430 + QPDFObjectHandle::newStream(&oh().getQPDF(), b.getBufferSharedPointer()));
427 431 }
428 432 }
429 433 } else {
... ... @@ -439,7 +443,7 @@ std::vector&lt;QPDFAnnotationObjectHelper&gt;
439 443 QPDFPageObjectHelper::getAnnotations(std::string const& only_subtype)
440 444 {
441 445 std::vector<QPDFAnnotationObjectHelper> result;
442   - QPDFObjectHandle annots = this->oh.getKey("/Annots");
  446 + QPDFObjectHandle annots = oh().getKey("/Annots");
443 447 if (annots.isArray()) {
444 448 int nannots = annots.getArrayNItems();
445 449 for (int i = 0; i < nannots; ++i) {
... ... @@ -455,25 +459,25 @@ QPDFPageObjectHelper::getAnnotations(std::string const&amp; only_subtype)
455 459 std::vector<QPDFObjectHandle>
456 460 QPDFPageObjectHelper::getPageContents()
457 461 {
458   - return this->oh.getPageContents();
  462 + return oh().getPageContents();
459 463 }
460 464  
461 465 void
462 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 471 void
468 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 477 void
474 478 QPDFPageObjectHelper::coalesceContentStreams()
475 479 {
476   - this->oh.coalesceContentStreams();
  480 + oh().coalesceContentStreams();
477 481 }
478 482  
479 483 void
... ... @@ -485,10 +489,10 @@ QPDFPageObjectHelper::parsePageContents(QPDFObjectHandle::ParserCallbacks* callb
485 489 void
486 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 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 505 void
502 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 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 521 void
518 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 526 } else {
523   - this->oh.pipePageContents(p);
  527 + oh().pipePageContents(p);
524 528 }
525 529 }
526 530  
... ... @@ -528,10 +532,10 @@ void
528 532 QPDFPageObjectHelper::addContentTokenFilter(
529 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 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 543 QPDFPageObjectHelper::removeUnreferencedResourcesHelper(
540 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 547 if (!is_page) {
544 548 QTC::TC("qpdf", "QPDFPageObjectHelper filter form xobject");
545 549 }
546 550  
547 551 ResourceFinder rf;
548 552 try {
549   - auto q = ph.oh.getOwningQPDF();
  553 + auto q = ph.oh().getOwningQPDF();
550 554 size_t before_nw = (q ? q->numWarnings() : 0);
551 555 ph.parseContents(&rf);
552 556 size_t after_nw = (q ? q->numWarnings() : 0);
553 557 if (after_nw > before_nw) {
554   - ph.oh.warnIfPossible(
  558 + ph.oh().warnIfPossible(
555 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 561 return false;
559 562 }
560 563 } catch (std::exception& e) {
561 564 QTC::TC("qpdf", "QPDFPageObjectHelper bad token finding names");
562   - ph.oh.warnIfPossible(
  565 + ph.oh().warnIfPossible(
563 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 568 return false;
567 569 }
568 570  
... ... @@ -646,7 +648,7 @@ QPDFPageObjectHelper::removeUnreferencedResources()
646 648 any_failures = true;
647 649 }
648 650 });
649   - if (this->oh.isFormXObject() || (!any_failures)) {
  651 + if (oh().isFormXObject() || (!any_failures)) {
650 652 removeUnreferencedResourcesHelper(*this, unresolved);
651 653 }
652 654 }
... ... @@ -654,9 +656,8 @@ QPDFPageObjectHelper::removeUnreferencedResources()
654 656 QPDFPageObjectHelper
655 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 661 return {qpdf.makeIndirectObject(new_page)};
661 662 }
662 663  
... ... @@ -707,7 +708,7 @@ QPDFObjectHandle
707 708 QPDFPageObjectHelper::getFormXObjectForPage(bool handle_transformations)
708 709 {
709 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 712 .newStream();
712 713 QPDFObjectHandle newdict = result.getDict();
713 714 newdict.replaceKey("/Type", QPDFObjectHandle::newName("/XObject"));
... ... @@ -716,13 +717,13 @@ QPDFPageObjectHelper::getFormXObjectForPage(bool handle_transformations)
716 717 newdict.replaceKey("/Group", getAttribute("/Group", false).shallowCopy());
717 718 QPDFObjectHandle bbox = getTrimBox(false).shallowCopy();
718 719 if (!bbox.isRectangle()) {
719   - this->oh.warnIfPossible(
  720 + oh().warnIfPossible(
720 721 "bounding box is invalid; form"
721 722 " XObject created from page will not work");
722 723 }
723 724 newdict.replaceKey("/BBox", bbox);
724 725 auto provider =
725   - std::shared_ptr<QPDFObjectHandle::StreamDataProvider>(new ContentProvider(this->oh));
  726 + std::shared_ptr<QPDFObjectHandle::StreamDataProvider>(new ContentProvider(oh()));
726 727 result.replaceStreamData(provider, QPDFObjectHandle::newNull(), QPDFObjectHandle::newNull());
727 728 QPDFObjectHandle rotate_obj = getAttribute("/Rotate", false);
728 729 QPDFObjectHandle scale_obj = getAttribute("/UserUnit", false);
... ... @@ -863,9 +864,8 @@ QPDFPageObjectHelper::placeFormXObject(
863 864 void
864 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 869 int rotate = 0;
870 870 if (rotate_oh.isInteger()) {
871 871 rotate = rotate_oh.getIntValueAsInt();
... ... @@ -873,7 +873,7 @@ QPDFPageObjectHelper::flattenRotation(QPDFAcroFormDocumentHelper* afdh)
873 873 if (!((rotate == 90) || (rotate == 180) || (rotate == 270))) {
874 874 return;
875 875 }
876   - auto mediabox = this->oh.getKey("/MediaBox");
  876 + auto mediabox = oh().getKey("/MediaBox");
877 877 if (!mediabox.isRectangle()) {
878 878 return;
879 879 }
... ... @@ -887,7 +887,7 @@ QPDFPageObjectHelper::flattenRotation(QPDFAcroFormDocumentHelper* afdh)
887 887 "/ArtBox",
888 888 };
889 889 for (auto const& boxkey: boxes) {
890   - auto box = this->oh.getKey(boxkey);
  890 + auto box = oh().getKey(boxkey);
891 891 if (!box.isRectangle()) {
892 892 continue;
893 893 }
... ... @@ -930,7 +930,7 @@ QPDFPageObjectHelper::flattenRotation(QPDFAcroFormDocumentHelper* afdh)
930 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 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 962 break;
963 963 }
964 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 968 QPDFObjectHandle rotate_obj = getAttribute("/Rotate", false);
969 969 if (!rotate_obj.isNull()) {
970 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 975 if (annots.isArray()) {
976 976 std::vector<QPDFObjectHandle> new_annots;
977 977 std::vector<QPDFObjectHandle> new_fields;
... ... @@ -986,7 +986,7 @@ QPDFPageObjectHelper::flattenRotation(QPDFAcroFormDocumentHelper* afdh)
986 986 for (auto const& f: new_fields) {
987 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 1005 QPDF& from_qpdf = from_page.getObjectHandle().getQPDF(
1006 1006 "QPDFPageObjectHelper::copyAnnotations: from page is a direct object");
1007 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 1010 std::vector<QPDFObjectHandle> new_annots;
1011 1011 std::vector<QPDFObjectHandle> new_fields;
... ... @@ -1032,9 +1032,9 @@ QPDFPageObjectHelper::copyAnnotations(
1032 1032 afdh->transformAnnotations(
1033 1033 old_annots, new_annots, new_fields, old_fields, cm, &from_qpdf, from_afdh);
1034 1034 afdh->addAndRenameFormFields(new_fields);
1035   - auto annots = this->oh.getKey("/Annots");
  1035 + auto annots = oh().getKey("/Annots");
1036 1036 if (!annots.isArray()) {
1037   - annots = this->oh.replaceKeyAndGetNew("/Annots", QPDFObjectHandle::newArray());
  1037 + annots = oh().replaceKeyAndGetNew("/Annots", QPDFObjectHandle::newArray());
1038 1038 }
1039 1039 for (auto const& annot: new_annots) {
1040 1040 annots.appendItem(annot);
... ...
libqpdf/QPDFParser.cc
... ... @@ -4,18 +4,6 @@
4 4 #include <qpdf/QPDFObjGen.hh>
5 5 #include <qpdf/QPDFObjectHandle.hh>
6 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 7 #include <qpdf/QTC.hh>
20 8 #include <qpdf/QUtil.hh>
21 9  
... ... @@ -47,27 +35,27 @@ QPDFParser::parse(bool&amp; empty, bool content_stream)
47 35 }
48 36 QTC::TC("qpdf", "QPDFParser eof in parse");
49 37 warn("unexpected EOF");
50   - return {QPDF_Null::create()};
  38 + return {QPDFObject::create<QPDF_Null>()};
51 39  
52 40 case QPDFTokenizer::tt_bad:
53 41 QTC::TC("qpdf", "QPDFParser bad token in parse");
54   - return {QPDF_Null::create()};
  42 + return {QPDFObject::create<QPDF_Null>()};
55 43  
56 44 case QPDFTokenizer::tt_brace_open:
57 45 case QPDFTokenizer::tt_brace_close:
58 46 QTC::TC("qpdf", "QPDFParser bad brace");
59 47 warn("treating unexpected brace token as null");
60   - return {QPDF_Null::create()};
  48 + return {QPDFObject::create<QPDF_Null>()};
61 49  
62 50 case QPDFTokenizer::tt_array_close:
63 51 QTC::TC("qpdf", "QPDFParser bad array close");
64 52 warn("treating unexpected array close token as null");
65   - return {QPDF_Null::create()};
  53 + return {QPDFObject::create<QPDF_Null>()};
66 54  
67 55 case QPDFTokenizer::tt_dict_close:
68 56 QTC::TC("qpdf", "QPDFParser bad dictionary close");
69 57 warn("unexpected dictionary close token");
70   - return {QPDF_Null::create()};
  58 + return {QPDFObject::create<QPDF_Null>()};
71 59  
72 60 case QPDFTokenizer::tt_array_open:
73 61 case QPDFTokenizer::tt_dict_open:
... ... @@ -82,7 +70,7 @@ QPDFParser::parse(bool&amp; empty, bool content_stream)
82 70 return withDescription<QPDF_Bool>(tokenizer.getValue() == "true");
83 71  
84 72 case QPDFTokenizer::tt_null:
85   - return {QPDF_Null::create()};
  73 + return {QPDFObject::create<QPDF_Null>()};
86 74  
87 75 case QPDFTokenizer::tt_integer:
88 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 91 // not move the input source's offset.
104 92 input.seek(input.getLastOffset(), SEEK_SET);
105 93 empty = true;
106   - return {QPDF_Null::create()};
  94 + return {QPDFObject::create<QPDF_Null>()};
107 95 } else {
108 96 QTC::TC("qpdf", "QPDFParser treat word as string");
109 97 warn("unknown token while reading object; treating as string");
... ... @@ -122,7 +110,7 @@ QPDFParser::parse(bool&amp; empty, bool content_stream)
122 110  
123 111 default:
124 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 182 }
195 183 QTC::TC("qpdf", "QPDFParser eof in parseRemainder");
196 184 warn("unexpected EOF");
197   - return {QPDF_Null::create()};
  185 + return {QPDFObject::create<QPDF_Null>()};
198 186  
199 187 case QPDFTokenizer::tt_bad:
200 188 QTC::TC("qpdf", "QPDFParser bad token in parseRemainder");
201 189 if (tooManyBadTokens()) {
202   - return {QPDF_Null::create()};
  190 + return {QPDFObject::create<QPDF_Null>()};
203 191 }
204 192 addNull();
205 193 continue;
... ... @@ -209,7 +197,7 @@ QPDFParser::parseRemainder(bool content_stream)
209 197 QTC::TC("qpdf", "QPDFParser bad brace in parseRemainder");
210 198 warn("treating unexpected brace token as null");
211 199 if (tooManyBadTokens()) {
212   - return {QPDF_Null::create()};
  200 + return {QPDFObject::create<QPDF_Null>()};
213 201 }
214 202 addNull();
215 203 continue;
... ... @@ -218,10 +206,12 @@ QPDFParser::parseRemainder(bool content_stream)
218 206 if (bad_count && !max_bad_count) {
219 207 // Trigger warning.
220 208 (void)tooManyBadTokens();
221   - return {QPDF_Null::create()};
  209 + return {QPDFObject::create<QPDF_Null>()};
222 210 }
223 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 215 setDescription(object, frame->offset - 1);
226 216 // The `offset` points to the next of "[". Set the rewind offset to point to the
227 217 // beginning of "[". This has been explicitly tested with whitespace surrounding the
... ... @@ -237,7 +227,7 @@ QPDFParser::parseRemainder(bool content_stream)
237 227 QTC::TC("qpdf", "QPDFParser bad array close in parseRemainder");
238 228 warn("treating unexpected array close token as null");
239 229 if (tooManyBadTokens()) {
240   - return {QPDF_Null::create()};
  230 + return {QPDFObject::create<QPDF_Null>()};
241 231 }
242 232 addNull();
243 233 }
... ... @@ -247,7 +237,7 @@ QPDFParser::parseRemainder(bool content_stream)
247 237 if (bad_count && !max_bad_count) {
248 238 // Trigger warning.
249 239 (void)tooManyBadTokens();
250   - return {QPDF_Null::create()};
  240 + return {QPDFObject::create<QPDF_Null>()};
251 241 }
252 242 if (frame->state <= st_dictionary_value) {
253 243 // Attempt to recover more or less gracefully from invalid dictionaries.
... ... @@ -258,7 +248,7 @@ QPDFParser::parseRemainder(bool content_stream)
258 248 warn(
259 249 frame->offset,
260 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 254 if (!frame->olist.empty()) {
... ... @@ -271,7 +261,7 @@ QPDFParser::parseRemainder(bool content_stream)
271 261 dict["/Contents"] = QPDFObjectHandle::newString(frame->contents_string);
272 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 265 setDescription(object, frame->offset - 2);
276 266 // The `offset` points to the next of "<<". Set the rewind offset to point to the
277 267 // beginning of "<<". This has been explicitly tested with whitespace surrounding
... ... @@ -287,7 +277,7 @@ QPDFParser::parseRemainder(bool content_stream)
287 277 QTC::TC("qpdf", "QPDFParser bad dictionary close in parseRemainder");
288 278 warn("unexpected dictionary close token");
289 279 if (tooManyBadTokens()) {
290   - return {QPDF_Null::create()};
  280 + return {QPDFObject::create<QPDF_Null>()};
291 281 }
292 282 addNull();
293 283 }
... ... @@ -298,7 +288,7 @@ QPDFParser::parseRemainder(bool content_stream)
298 288 if (stack.size() > 499) {
299 289 QTC::TC("qpdf", "QPDFParser too deep");
300 290 warn("ignoring excessively deeply nested data structure");
301   - return {QPDF_Null::create()};
  291 + return {QPDFObject::create<QPDF_Null>()};
302 292 } else {
303 293 b_contents = false;
304 294 stack.emplace_back(
... ... @@ -350,7 +340,7 @@ QPDFParser::parseRemainder(bool content_stream)
350 340 QTC::TC("qpdf", "QPDFParser treat word as string in parseRemainder");
351 341 warn("unknown token while reading object; treating as string");
352 342 if (tooManyBadTokens()) {
353   - return {QPDF_Null::create()};
  343 + return {QPDFObject::create<QPDF_Null>()};
354 344 }
355 345 addScalar<QPDF_String>(tokenizer.getValue());
356 346 }
... ... @@ -377,7 +367,7 @@ QPDFParser::parseRemainder(bool content_stream)
377 367 default:
378 368 warn("treating unknown token type as null while reading object");
379 369 if (tooManyBadTokens()) {
380   - return {QPDF_Null::create()};
  370 + return {QPDFObject::create<QPDF_Null>()};
381 371 }
382 372 addNull();
383 373 }
... ... @@ -402,7 +392,7 @@ QPDFParser::add(std::shared_ptr&lt;QPDFObject&gt;&amp;&amp; obj)
402 392 void
403 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 397 if (frame->state != st_dictionary_value) {
408 398 // If state is st_dictionary_key then there is a missing key. Push onto olist for
... ... @@ -420,7 +410,7 @@ QPDFParser::addNull()
420 410 void
421 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 414 obj->setDescription(context, description, last_offset_buffer[count % 2]);
425 415 add(std::move(obj));
426 416 }
... ... @@ -435,7 +425,7 @@ QPDFParser::addScalar(Args&amp;&amp;... args)
435 425 max_bad_count = 0;
436 426 return;
437 427 }
438   - auto obj = T::create(args...);
  428 + auto obj = QPDFObject::create<T>(std::forward<Args>(args)...);
439 429 obj->setDescription(context, description, input.getLastOffset());
440 430 add(std::move(obj));
441 431 }
... ... @@ -444,7 +434,7 @@ template &lt;typename T, typename... Args&gt;
444 434 QPDFObjectHandle
445 435 QPDFParser::withDescription(Args&&... args)
446 436 {
447   - auto obj = T::create(args...);
  437 + auto obj = QPDFObject::create<T>(std::forward<Args>(args)...);
448 438 obj->setDescription(context, description, start);
449 439 return {obj};
450 440 }
... ... @@ -462,8 +452,8 @@ QPDFParser::fixMissingKeys()
462 452 {
463 453 std::set<std::string> names;
464 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 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 15 #include <qpdf/Pl_StdioFile.hh>
16 16 #include <qpdf/QIntC.hh>
17 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 20 #include <qpdf/QTC.hh>
22 21 #include <qpdf/QUtil.hh>
23 22 #include <qpdf/RC4.hh>
... ... @@ -27,6 +26,7 @@
27 26 #include <stdexcept>
28 27  
29 28 using namespace std::literals;
  29 +using namespace qpdf;
30 30  
31 31 QPDFWriter::ProgressReporter::~ProgressReporter() // NOLINT (modernize-use-equals-default)
32 32 {
... ... @@ -1129,12 +1129,12 @@ QPDFWriter::enqueueObject(QPDFObjectHandle object)
1129 1129 return;
1130 1130 } else if (!m->linearized) {
1131 1131 if (object.isArray()) {
1132   - for (auto& item: object.getArrayAsVector()) {
  1132 + for (auto& item: object.as_array()) {
1133 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 1138 enqueueObject(item.second);
1139 1139 }
1140 1140 }
... ... @@ -1173,10 +1173,13 @@ QPDFWriter::writeTrailer(
1173 1173 writeString(" /Size ");
1174 1174 writeString(std::to_string(size));
1175 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 1180 writeStringQDF(" ");
1178 1181 writeStringNoQDF(" ");
1179   - writeString(QPDF_Name::normalizeName(key));
  1182 + writeString(Name::normalize(key));
1180 1183 writeString(" ");
1181 1184 if (key == "/Size") {
1182 1185 writeString(std::to_string(size));
... ... @@ -1187,7 +1190,7 @@ QPDFWriter::writeTrailer(
1187 1190 writePad(QIntC::to_size(pos - m->pipeline->getCount() + 21));
1188 1191 }
1189 1192 } else {
1190   - unparseChild(trailer.getKey(key), 1, 0);
  1193 + unparseChild(value, 1, 0);
1191 1194 }
1192 1195 writeStringQDF("\n");
1193 1196 }
... ... @@ -1347,7 +1350,7 @@ QPDFWriter::unparseObject(
1347 1350 // [ in the /H key of the linearization parameter dictionary. We'll do this unconditionally
1348 1351 // for all arrays because it looks nicer and doesn't make the files that much bigger.
1349 1352 writeString("[");
1350   - for (auto const& item: object.getArrayAsVector()) {
  1353 + for (auto const& item: object.as_array()) {
1351 1354 writeString(indent);
1352 1355 writeStringQDF(" ");
1353 1356 unparseChild(item, level + 1, child_flags);
... ... @@ -1498,20 +1501,18 @@ QPDFWriter::unparseObject(
1498 1501  
1499 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 1506 writeString(indent);
1505 1507 writeStringQDF(" ");
1506   - writeString(QPDF_Name::normalizeName(key));
  1508 + writeString(Name::normalize(key));
1507 1509 writeString(" ");
1508 1510 if (key == "/Contents" && object.isDictionaryOfType("/Sig") &&
1509 1511 object.hasKey("/ByteRange")) {
1510 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 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 1892 }
1892 1893 seed += " QPDF ";
1893 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 1897 seed += " ";
1899   - seed += obj.getStringValue();
  1898 + seed += item.second.getStringValue();
1900 1899 }
1901 1900 }
1902 1901 }
... ... @@ -1922,8 +1921,7 @@ QPDFWriter::generateID()
1922 1921 void
1923 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 1925 std::vector<QPDFObjectHandle> pages = m->pdf.getAllPages();
1928 1926 int num = 0;
1929 1927 for (auto& page: pages) {
... ... @@ -2939,8 +2937,10 @@ QPDFWriter::enqueueObjectsStandard()
2939 2937  
2940 2938 // Next place any other objects referenced from the trailer dictionary into the queue, handling
2941 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 2962  
2963 2963 // enqueue all the strips for each page
2964 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 3 #include <qpdf/QTC.hh>
7 4  
  5 +using namespace std::literals;
  6 +using namespace qpdf;
  7 +
8 8 static const QPDFObjectHandle null_oh = QPDFObjectHandle::newNull();
9 9  
10 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 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 25 if (sparse) {
49 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 29 sp->elements[sp->size] = std::move(item);
53 30 }
54 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 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 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 156 if (n < 0 || n >= size()) {
191 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 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 171 std::vector<QPDFObjectHandle> v;
205 172 v.reserve(size_t(size()));
206   - for (auto const& item: sp->elements) {
  173 + for (auto const& item: a->sp->elements) {
207 174 v.resize(size_t(item.first), null_oh);
208 175 v.emplace_back(item.second);
209 176 }
210 177 v.resize(size_t(size()), null_oh);
211 178 return v;
212 179 } else {
213   - return {elements.cbegin(), elements.cend()};
  180 + return a->elements;
214 181 }
215 182 }
216 183  
217 184 bool
218   -QPDF_Array::setAt(int at, QPDFObjectHandle const& oh)
  185 +Array::setAt(int at, QPDFObjectHandle const& oh)
219 186 {
220 187 if (at < 0 || at >= size()) {
221 188 return false;
222 189 }
  190 + auto a = array();
223 191 checkOwnership(oh);
224   - if (sp) {
225   - sp->elements[at] = oh.getObj();
  192 + if (a->sp) {
  193 + a->sp->elements[at] = oh;
226 194 } else {
227   - elements[size_t(at)] = oh.getObj();
  195 + a->elements[size_t(at)] = oh;
228 196 }
229 197 return true;
230 198 }
231 199  
232 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 206 for (auto const& item: v) {
238 207 checkOwnership(item);
239   - elements.push_back(item.getObj());
  208 + a->elements.emplace_back(item);
240 209 }
241 210 }
242 211  
243 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 216 int sz = size();
247 217 if (at < 0 || at > sz) {
248 218 // As special case, also allow insert beyond the end
... ... @@ -251,61 +221,257 @@ QPDF_Array::insert(int at, QPDFObjectHandle const&amp; item)
251 221 push_back(item);
252 222 } else {
253 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 227 auto key = (iter++)->first;
258 228 if (key >= at) {
259   - auto nh = sp->elements.extract(key);
  229 + auto nh = a->sp->elements.extract(key);
260 230 ++nh.key();
261   - sp->elements.insert(std::move(nh));
  231 + a->sp->elements.insert(std::move(nh));
262 232 } else {
263 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 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 242 return true;
273 243 }
274 244  
275 245 void
276   -QPDF_Array::push_back(QPDFObjectHandle const& item)
  246 +Array::push_back(QPDFObjectHandle const& item)
277 247 {
  248 + auto a = array();
278 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 252 } else {
282   - elements.push_back(item.getObj());
  253 + a->elements.emplace_back(item);
283 254 }
284 255 }
285 256  
286 257 bool
287   -QPDF_Array::erase(int at)
  258 +Array::erase(int at)
288 259 {
  260 + auto a = array();
289 261 if (at < 0 || at >= size()) {
290 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 267 if (iter->first == at) {
296 268 iter++;
297   - sp->elements.erase(at);
  269 + a->sp->elements.erase(at);
298 270 }
299 271  
300 272 while (iter != end) {
301   - auto nh = sp->elements.extract(iter++);
  273 + auto nh = a->sp->elements.extract(iter++);
302 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 279 } else {
308   - elements.erase(elements.cbegin() + at);
  280 + a->elements.erase(a->elements.cbegin() + at);
309 281 }
310 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 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 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 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 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 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 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 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 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 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 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 3 #include <qpdf/ContentNormalizer.hh>
4 4 #include <qpdf/JSON_writer.hh>
... ... @@ -22,6 +22,9 @@
22 22  
23 23 #include <stdexcept>
24 24  
  25 +using namespace std::literals;
  26 +using namespace qpdf;
  27 +
25 28 namespace
26 29 {
27 30 class SF_Crypt: public QPDFStreamFilter
... ... @@ -60,16 +63,24 @@ namespace
60 63 class StreamBlobProvider
61 64 {
62 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 77 private:
67   - QPDF_Stream* stream;
  78 + Stream stream;
68 79 qpdf_stream_decode_level_e decode_level;
69 80 };
70 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 84 // The PDF specification provides these filter abbreviations for use in inline images, but
74 85 // according to table H.1 in the pre-ISO versions of the PDF specification, Adobe Reader also
75 86 // accepts them for stream filters.
... ... @@ -82,8 +93,8 @@ std::map&lt;std::string, std::string&gt; QPDF_Stream::filter_abbreviations = {
82 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 98 {"/Crypt", []() { return std::make_shared<SF_Crypt>(); }},
88 99 {"/FlateDecode", SF_FlateLzwDecode::flate_factory},
89 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 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 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 119 std::string const& filter_name, std::function<std::shared_ptr<QPDFStreamFilter>()> factory)
142 120 {
143 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 124 JSON
179   -QPDF_Stream::getStreamJSON(
  125 +Stream::getStreamJSON(
180 126 int json_version,
181 127 qpdf_json_stream_data_e json_data,
182 128 qpdf_stream_decode_level_e decode_level,
... ... @@ -190,13 +136,13 @@ QPDF_Stream::getStreamJSON(
190 136 pb.finish();
191 137 auto result = JSON::parse(pb.getString());
192 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 141 return result;
196 142 }
197 143  
198 144 qpdf_stream_decode_level_e
199   -QPDF_Stream::writeStreamJSON(
  145 +Stream::writeStreamJSON(
200 146 int json_version,
201 147 JSON::Writer& jw,
202 148 qpdf_json_stream_data_e json_data,
... ... @@ -205,6 +151,7 @@ QPDF_Stream::writeStreamJSON(
205 151 std::string const& data_filename,
206 152 bool no_data_key)
207 153 {
  154 + auto s = stream();
208 155 switch (json_data) {
209 156 case qpdf_sj_none:
210 157 case qpdf_sj_inline:
... ... @@ -232,7 +179,7 @@ QPDF_Stream::writeStreamJSON(
232 179 if (json_data == qpdf_sj_none) {
233 180 jw.writeNext();
234 181 jw << R"("dict": )";
235   - stream_dict.writeJSON(json_version, jw);
  182 + s->stream_dict.writeJSON(json_version, jw);
236 183 jw.writeEnd('}');
237 184 return decode_level;
238 185 }
... ... @@ -264,7 +211,7 @@ QPDF_Stream::writeStreamJSON(
264 211 throw std::logic_error("QPDF_Stream: failed to get stream data");
265 212 }
266 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 215 dict.removeKey("/Length");
269 216 if (filter && filtered) {
270 217 dict.removeKey("/Filter");
... ... @@ -290,53 +237,17 @@ QPDF_Stream::writeStreamJSON(
290 237 }
291 238  
292 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 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 252 Pl_Buffer buf("stream data buffer");
342 253 bool filtered;
... ... @@ -344,9 +255,9 @@ QPDF_Stream::getStreamData(qpdf_stream_decode_level_e decode_level)
344 255 if (!filtered) {
345 256 throw QPDFExc(
346 257 qpdf_e_unsupported,
347   - qpdf->getFilename(),
  258 + obj->getQPDF()->getFilename(),
348 259 "",
349   - this->parsed_offset,
  260 + obj->getParsedOffset(),
350 261 "getStreamData called on unfilterable stream");
351 262 }
352 263 QTC::TC("qpdf", "QPDF_Stream getStreamData");
... ... @@ -354,15 +265,15 @@ QPDF_Stream::getStreamData(qpdf_stream_decode_level_e decode_level)
354 265 }
355 266  
356 267 std::shared_ptr<Buffer>
357   -QPDF_Stream::getRawStreamData()
  268 +Stream::getRawStreamData()
358 269 {
359 270 Pl_Buffer buf("stream data buffer");
360 271 if (!pipeStreamData(&buf, nullptr, 0, qpdf_dl_none, false, false)) {
361 272 throw QPDFExc(
362 273 qpdf_e_unsupported,
363   - qpdf->getFilename(),
  274 + obj->getQPDF()->getFilename(),
364 275 "",
365   - this->parsed_offset,
  276 + obj->getParsedOffset(),
366 277 "error getting raw stream data");
367 278 }
368 279 QTC::TC("qpdf", "QPDF_Stream getRawStreamData");
... ... @@ -370,14 +281,15 @@ QPDF_Stream::getRawStreamData()
370 281 }
371 282  
372 283 bool
373   -QPDF_Stream::filterable(
  284 +Stream::filterable(
374 285 std::vector<std::shared_ptr<QPDFStreamFilter>>& filters,
375 286 bool& specialized_compression,
376 287 bool& lossy_compression)
377 288 {
  289 + auto s = stream();
378 290 // Check filters
379 291  
380   - QPDFObjectHandle filter_obj = this->stream_dict.getKey("/Filter");
  292 + QPDFObjectHandle filter_obj = s->stream_dict.getKey("/Filter");
381 293 bool filters_okay = true;
382 294  
383 295 std::vector<std::string> filter_names;
... ... @@ -432,7 +344,7 @@ QPDF_Stream::filterable(
432 344  
433 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 348 std::vector<QPDFObjectHandle> decode_parms;
437 349 if (decode_obj.isArray() && (decode_obj.getArrayNItems() == 0)) {
438 350 decode_obj = QPDFObjectHandle::newNull();
... ... @@ -479,7 +391,7 @@ QPDF_Stream::filterable(
479 391 }
480 392  
481 393 bool
482   -QPDF_Stream::pipeStreamData(
  394 +Stream::pipeStreamData(
483 395 Pipeline* pipeline,
484 396 bool* filterp,
485 397 int encode_flags,
... ... @@ -487,6 +399,7 @@ QPDF_Stream::pipeStreamData(
487 399 bool suppress_warnings,
488 400 bool will_retry)
489 401 {
  402 + auto s = stream();
490 403 std::vector<std::shared_ptr<QPDFStreamFilter>> filters;
491 404 bool specialized_compression = false;
492 405 bool lossy_compression = false;
... ... @@ -543,7 +456,7 @@ QPDF_Stream::pipeStreamData(
543 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 460 new_pipeline =
548 461 std::make_shared<Pl_QPDFTokenizer>("token filter", (*iter).get(), pipeline);
549 462 to_delete.push_back(new_pipeline);
... ... @@ -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 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 481 pipeline->finish();
569   - } else if (this->stream_provider.get()) {
  482 + } else if (s->stream_provider.get()) {
570 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 487 filter = false;
575 488 success = false;
576 489 }
577 490 } else {
578   - this->stream_provider->provideStreamData(og, &count);
  491 + s->stream_provider->provideStreamData(obj->getObjGen(), &count);
579 492 }
580 493 qpdf_offset_t actual_length = count.getCount();
581 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 497 if (actual_length == desired_length) {
585 498 QTC::TC("qpdf", "QPDF_Stream pipe use stream provider");
586 499 } else {
... ... @@ -588,25 +501,25 @@ QPDF_Stream::pipeStreamData(
588 501 // This would be caused by programmer error on the part of a library user, not by
589 502 // invalid input data.
590 503 throw std::runtime_error(
591   - "stream data provider for " + og.unparse(' ') + " provided " +
  504 + "stream data provider for " + obj->getObjGen().unparse(' ') + " provided " +
592 505 std::to_string(actual_length) + " bytes instead of expected " +
593 506 std::to_string(desired_length) + " bytes");
594 507 }
595 508 } else if (success) {
596 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 513 QTC::TC("qpdf", "QPDF_Stream pipe no stream data");
601 514 throw std::logic_error("pipeStreamData called for stream with no data");
602 515 } else {
603 516 QTC::TC("qpdf", "QPDF_Stream pipe original stream data");
604 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 523 pipeline,
611 524 suppress_warnings,
612 525 will_retry)) {
... ... @@ -634,60 +547,235 @@ QPDF_Stream::pipeStreamData(
634 547 }
635 548  
636 549 void
637   -QPDF_Stream::replaceStreamData(
  550 +Stream::replaceStreamData(
638 551 std::shared_ptr<Buffer> data,
639 552 QPDFObjectHandle const& filter,
640 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 558 replaceFilterData(filter, decode_parms, data->getSize());
645 559 }
646 560  
647 561 void
648   -QPDF_Stream::replaceStreamData(
  562 +Stream::replaceStreamData(
649 563 std::shared_ptr<QPDFObjectHandle::StreamDataProvider> provider,
650 564 QPDFObjectHandle const& filter,
651 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 570 replaceFilterData(filter, decode_parms, 0);
656 571 }
657 572  
658 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 575 QPDFObjectHandle const& filter, QPDFObjectHandle const& decode_parms, size_t length)
667 576 {
  577 + auto s = stream();
668 578 if (filter) {
669   - stream_dict.replaceKey("/Filter", filter);
  579 + s->stream_dict.replaceKey("/Filter", filter);
670 580 }
671 581 if (decode_parms) {
672   - stream_dict.replaceKey("/DecodeParms", decode_parms);
  582 + s->stream_dict.replaceKey("/DecodeParms", decode_parms);
673 583 }
674 584 if (length == 0) {
675 585 QTC::TC("qpdf", "QPDF_Stream unknown stream length");
676   - stream_dict.removeKey("/Length");
  586 + s->stream_dict.removeKey("/Length");
677 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 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 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 4 #include <qpdf/QUtil.hh>
5 5  
6 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 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 15 std::shared_ptr<QPDFObject>
28 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 if (!QUtil::utf8_to_pdf_doc(utf8_val, result, '?')) {
32 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 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 5 #include <qpdf/Pl_Base64.hh>
6 6 #include <qpdf/Pl_StdioFile.hh>
7 7 #include <qpdf/QIntC.hh>
  8 +#include <qpdf/QPDFObjectHandle_private.hh>
8 9 #include <qpdf/QPDFObject_private.hh>
9   -#include <qpdf/QPDFValue.hh>
10   -#include <qpdf/QPDF_Null.hh>
11   -#include <qpdf/QPDF_Stream.hh>
12 10 #include <qpdf/QTC.hh>
13 11 #include <qpdf/QUtil.hh>
14 12 #include <algorithm>
... ... @@ -238,8 +236,8 @@ class QPDF::JSONReactor: public JSON::Reactor
238 236 is(is),
239 237 must_be_complete(must_be_complete),
240 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 243 ~JSONReactor() override = default;
... ... @@ -286,7 +284,7 @@ class QPDF::JSONReactor: public JSON::Reactor
286 284 QPDF& pdf;
287 285 std::shared_ptr<InputSource> is;
288 286 bool must_be_complete{true};
289   - std::shared_ptr<QPDFValue::Description> descr;
  287 + std::shared_ptr<QPDFObject::Description> descr;
290 288 bool errors{false};
291 289 bool saw_qpdf{false};
292 290 bool saw_qpdf_meta{false};
... ... @@ -576,8 +574,8 @@ QPDF::JSONReactor::dictionaryItem(std::string const&amp; key, JSON const&amp; value)
576 574 } else {
577 575 this_stream_needs_data = true;
578 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 579 value);
582 580 }
583 581 next_obj = tos.object;
... ... @@ -706,10 +704,10 @@ QPDF::JSONReactor::arrayItem(JSON const&amp; value)
706 704 void
707 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 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 713 oh.getObjectPtr()->setDescription(&pdf, descr, value.getStart());
... ... @@ -821,7 +819,7 @@ void
821 819 writeJSONStreamFile(
822 820 int version,
823 821 JSON::Writer& jw,
824   - QPDF_Stream& stream,
  822 + qpdf::Stream& stream,
825 823 int id,
826 824 qpdf_stream_decode_level_e decode_level,
827 825 std::string const& file_prefix)
... ... @@ -894,13 +892,13 @@ QPDF::writeJSON(
894 892 } else {
895 893 jw << "\n },\n \"" << key;
896 894 }
897   - if (auto* stream = obj.getObjectPtr()->as<QPDF_Stream>()) {
  895 + if (auto stream = obj.as_stream()) {
898 896 jw << "\": {\n \"stream\": ";
899 897 if (json_stream_data == qpdf_sj_file) {
900 898 writeJSONStreamFile(
901   - version, jw, *stream, og.getObj(), decode_level, file_prefix);
  899 + version, jw, stream, og.getObj(), decode_level, file_prefix);
902 900 } else {
903   - stream->writeStreamJSON(
  901 + stream.writeStreamJSON(
904 902 version, jw, json_stream_data, decode_level, nullptr, "");
905 903 }
906 904 } else {
... ...
libqpdf/QPDF_optimization.cc
... ... @@ -5,9 +5,8 @@
5 5 #include <qpdf/QPDF.hh>
6 6  
7 7 #include <qpdf/QPDFExc.hh>
  8 +#include <qpdf/QPDFObjectHandle_private.hh>
8 9 #include <qpdf/QPDFWriter_private.hh>
9   -#include <qpdf/QPDF_Array.hh>
10   -#include <qpdf/QPDF_Dictionary.hh>
11 10 #include <qpdf/QTC.hh>
12 11  
13 12 QPDF::ObjUser::ObjUser() :
... ... @@ -115,24 +114,25 @@ QPDF::optimize_internal(
115 114 }
116 115  
117 116 // Traverse document-level items
118   - for (auto const& key: m->trailer.getKeys()) {
  117 + for (auto const& [key, value]: m->trailer.as_dictionary()) {
119 118 if (key == "/Root") {
120 119 // handled separately
121 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 129 // Technically, /I keys from /Thread dictionaries are supposed to be handled separately, but
131 130 // we are going to disregard that specification for now. There is loads of evidence that
132 131 // pdlin and Acrobat both disregard things like this from time to time, so this is almost
133 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 138 ObjUser root_ou = ObjUser(ObjUser::ou_root);
... ... @@ -319,9 +319,8 @@ QPDF::updateObjectMaps(
319 319 }
320 320  
321 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 325 } else if (cur.oh.isDictionary() || cur.oh.isStream()) {
327 326 QPDFObjectHandle dict = cur.oh;
... ... @@ -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 341 if (is_page_node && (key == "/Thumb")) {
339 342 // Traverse page thumbnail dictionaries as a special case. There can only ever
340 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 350 ((ssp >= 2) && ((key == "/Filter") || (key == "/DecodeParms")))) {
348 351 // Don't traverse into stream parameters that we are not going to write.
349 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 1 #include <qpdf/QPDF.hh>
2 2  
3 3 #include <qpdf/QPDFExc.hh>
  4 +#include <qpdf/QPDFObjectHandle_private.hh>
4 5 #include <qpdf/QTC.hh>
5 6 #include <qpdf/QUtil.hh>
6 7  
... ... @@ -108,9 +109,9 @@ QPDF::getAllPagesInternal(
108 109 QTC::TC("qpdf", "QPDF inherit mediabox", media_box ? 0 : 1);
109 110 }
110 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 115 if (!kid.isDictionary()) {
115 116 kid.warnIfPossible("Pages tree includes non-dictionary object; ignoring");
116 117 m->invalid_page_found = true;
... ... @@ -133,7 +134,6 @@ QPDF::getAllPagesInternal(
133 134 cur_node.warnIfPossible(
134 135 "kid " + std::to_string(i) + " (from 0) is direct; converting to indirect");
135 136 kid = makeIndirectObject(kid);
136   - kids.setArrayItem(i, kid);
137 137 } else if (!seen.add(kid)) {
138 138 // Make a copy of the page. This does the same as shallowCopyPage in
139 139 // QPDFPageObjectHelper.
... ... @@ -144,7 +144,6 @@ QPDF::getAllPagesInternal(
144 144 " creating a new page object as a copy");
145 145 kid = makeIndirectObject(QPDFObjectHandle(kid).shallowCopy());
146 146 seen.add(kid);
147   - kids.setArrayItem(i, kid);
148 147 }
149 148 if (!kid.isDictionaryOfType("/Page")) {
150 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 6  
7 7 #include <qpdf/Constants.h>
8 8 #include <qpdf/JSON.hh>
  9 +#include <qpdf/JSON_writer.hh>
9 10 #include <qpdf/QPDF.hh>
10   -#include <qpdf/QPDFValue.hh>
  11 +#include <qpdf/QPDFObjGen.hh>
11 12 #include <qpdf/Types.h>
12 13  
  14 +#include <map>
  15 +#include <memory>
13 16 #include <string>
14 17 #include <string_view>
  18 +#include <variant>
  19 +#include <vector>
15 20  
16   -class QPDF;
  21 +class QPDFObject;
17 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 145 std::shared_ptr<QPDFObject> parent,
81 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 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 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 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 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 354 bool
158 355 isUnresolved() const
159 356 {
160   - return value->type_code == ::ot_unresolved;
  357 + return getTypeCode() == ::ot_unresolved;
161 358 }
162 359 const QPDFObject*
163 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 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 481 QPDFObject(QPDFObject const&) = delete;
183 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 491 #endif // QPDFOBJECT_HH
... ...
libqpdf/qpdf/QPDFParser.hh
1 1 #ifndef QPDFPARSER_HH
2 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 7 #include <memory>
8 8 #include <string>
... ... @@ -24,7 +24,7 @@ class QPDFParser
24 24 decrypter(decrypter),
25 25 context(context),
26 26 description(
27   - std::make_shared<QPDFValue::Description>(
  27 + std::make_shared<QPDFObject::Description>(
28 28 std::string(input.getName() + ", " + object_description + " at offset $PO"))),
29 29 parse_pdf(parse_pdf)
30 30 {
... ... @@ -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 50 std::map<std::string, QPDFObjectHandle> dict;
51 51 parser_state_e state;
52 52 std::string key;
... ... @@ -78,7 +78,7 @@ class QPDFParser
78 78 QPDFTokenizer& tokenizer;
79 79 QPDFObjectHandle::StringDecrypter* decrypter;
80 80 QPDF* context;
81   - std::shared_ptr<QPDFValue::Description> description;
  81 + std::shared_ptr<QPDFObject::Description> description;
82 82 bool parse_pdf;
83 83  
84 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 1 #include <qpdf/assert_test.h>
2 2  
3 3 #include <qpdf/QPDF.hh>
4   -#include <qpdf/QPDFObjectHandle.hh>
  4 +#include <qpdf/QPDFObjectHandle_private.hh>
5 5 #include <qpdf/QPDFObject_private.hh>
6   -#include <qpdf/QPDF_Array.hh>
7 6  
8 7 #include <iostream>
9 8  
10 9 int
11 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 15 assert(a.size() == 0);
17 16  
... ... @@ -88,16 +87,17 @@ main()
88 87 QPDF pdf;
89 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 93 b.setAt(5, pdf.newIndirectNull());
94 94 b.setAt(7, "[0 1 2 3]"_qpdf);
95 95 assert(b.at(3).second.isNull());
96 96 assert(b.at(8).second.isNull());
97 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 101 b.at(7).second.setArrayItem(2, "42"_qpdf);
102 102 assert(c->unparse() == "[ null null null null null 3 0 R null [ 0 1 42 3 ] null null ]");
103 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 109 print_size(QPDFNumberTreeObjectHelper);
110 110 print_size(QPDFNumberTreeObjectHelper::iterator);
111 111 print_size(QPDFObjGen);
112   - print_size(QPDFObjGen::set);
113 112 print_size(QPDFObjectHandle);
114 113 print_size(QPDFObjectHandle::ParserCallbacks);
115 114 print_size(QPDFObjectHandle::QPDFArrayItems);
... ... @@ -118,6 +117,7 @@ main()
118 117 print_size(QPDFObjectHandle::QPDFDictItems::iterator);
119 118 print_size(QPDFObjectHandle::StreamDataProvider);
120 119 print_size(QPDFObjectHandle::TokenFilter);
  120 + print_size(QPDFObjectHelper);
121 121 print_size(QPDFOutlineDocumentHelper);
122 122 print_size(QPDFOutlineObjectHelper);
123 123 print_size(QPDFPageDocumentHelper);
... ...