Commit f8fd7d60e301b9b1bf4d705ce747e281c320487e

Authored by Jay Berkenbilt
Committed by GitHub
2 parents a078202c 805c1ad4

Merge pull request #726 from m-holger/tidy3

Split QPDFObject into QPDFObject and QPDFValue
include/qpdf/Constants.h
... ... @@ -82,6 +82,8 @@ enum qpdf_object_type_e {
82 82 /* Additional object types that can occur in content streams */
83 83 ot_operator,
84 84 ot_inlineimage,
  85 + /* Object types internal to qpdf */
  86 + ot_unresolved,
85 87 /* NOTE: if adding to this list, update QPDFObject.hh */
86 88 };
87 89  
... ...
include/qpdf/QPDF.hh
... ... @@ -843,19 +843,13 @@ class QPDF
843 843 // it can resolve indirect references.
844 844 class Resolver
845 845 {
846   - friend class QPDFObjectHandle;
  846 + friend class QPDFObject;
847 847  
848 848 private:
849   - static std::shared_ptr<QPDFObject>
  849 + static void
850 850 resolve(QPDF* qpdf, QPDFObjGen const& og)
851 851 {
852   - return qpdf->resolve(og);
853   - }
854   - static bool
855   - objectChanged(
856   - QPDF* qpdf, QPDFObjGen const& og, std::shared_ptr<QPDFObject>& oph)
857   - {
858   - return qpdf->objectChanged(og, oph);
  852 + qpdf->resolve(og);
859 853 }
860 854 };
861 855 friend class Resolver;
... ... @@ -1174,12 +1168,20 @@ class QPDF
1174 1168 std::string const& description,
1175 1169 QPDFObjGen const& exp_og,
1176 1170 QPDFObjGen& og);
1177   - bool objectChanged(QPDFObjGen const& og, std::shared_ptr<QPDFObject>& oph);
1178   - std::shared_ptr<QPDFObject> resolve(QPDFObjGen const& og);
  1171 + void resolve(QPDFObjGen const& og);
1179 1172 void resolveObjectsInStream(int obj_stream_number);
1180 1173 void stopOnError(std::string const& message);
1181 1174 QPDFObjectHandle reserveObjectIfNotExists(QPDFObjGen const& og);
1182 1175 QPDFObjectHandle reserveStream(QPDFObjGen const& og);
  1176 + QPDFObjectHandle
  1177 + newIndirect(QPDFObjGen const&, std::shared_ptr<QPDFObject> const&);
  1178 + bool isCached(QPDFObjGen const& og);
  1179 + bool isUnresolved(QPDFObjGen const& og);
  1180 + void updateCache(
  1181 + QPDFObjGen const& og,
  1182 + std::shared_ptr<QPDFObject> const& object,
  1183 + qpdf_offset_t end_before_space,
  1184 + qpdf_offset_t end_after_space);
1183 1185  
1184 1186 // Calls finish() on the pipeline when done but does not delete it
1185 1187 bool pipeStreamData(
... ... @@ -1727,7 +1729,6 @@ class QPDF
1727 1729 bool in_parse;
1728 1730 bool parsed;
1729 1731 std::set<int> resolved_object_streams;
1730   - bool ever_replaced_objects;
1731 1732  
1732 1733 // Linearization data
1733 1734 qpdf_offset_t first_xref_item_offset; // actual value from file
... ...
include/qpdf/QPDFObject.hh
... ... @@ -25,6 +25,7 @@
25 25 #include <qpdf/Constants.h>
26 26 #include <qpdf/DLL.h>
27 27 #include <qpdf/JSON.hh>
  28 +#include <qpdf/QPDFValue.hh>
28 29 #include <qpdf/Types.h>
29 30  
30 31 #include <string>
... ... @@ -34,9 +35,9 @@ class QPDFObjectHandle;
34 35  
35 36 class QPDFObject
36 37 {
37   - public:
38   - QPDFObject();
  38 + friend class QPDFValue;
39 39  
  40 + public:
40 41 // Objects derived from QPDFObject are accessible through
41 42 // QPDFObjectHandle. Each object returns a unique type code that
42 43 // has one of the valid qpdf_object_type_e values. As new object
... ... @@ -61,18 +62,128 @@ class QPDFObject
61 62 static constexpr object_type_e ot_stream = ::ot_stream;
62 63 static constexpr object_type_e ot_operator = ::ot_operator;
63 64 static constexpr object_type_e ot_inlineimage = ::ot_inlineimage;
  65 + static constexpr object_type_e ot_unresolved = ::ot_unresolved;
64 66  
  67 + QPDFObject() = default;
65 68 virtual ~QPDFObject() = default;
66   - virtual std::shared_ptr<QPDFObject> shallowCopy() = 0;
67   - virtual std::string unparse() = 0;
68   - virtual JSON getJSON(int json_version) = 0;
  69 +
  70 + std::shared_ptr<QPDFObject>
  71 + shallowCopy()
  72 + {
  73 + return value->shallowCopy();
  74 + }
  75 + std::string
  76 + unparse()
  77 + {
  78 + return value->unparse();
  79 + }
  80 + JSON
  81 + getJSON(int json_version)
  82 + {
  83 + return value->getJSON(json_version);
  84 + }
69 85  
70 86 // Return a unique type code for the object
71   - virtual object_type_e getTypeCode() const = 0;
  87 + object_type_e
  88 + getTypeCode() const
  89 + {
  90 + return value->type_code;
  91 + }
72 92  
73 93 // Return a string literal that describes the type, useful for
74 94 // debugging and testing
75   - virtual char const* getTypeName() const = 0;
  95 + char const*
  96 + getTypeName() const
  97 + {
  98 + return value->type_name;
  99 + }
  100 + // Returns nullptr for direct objects
  101 + QPDF*
  102 + getQPDF() const
  103 + {
  104 + return value->qpdf;
  105 + }
  106 + QPDFObjGen
  107 + getObjGen() const
  108 + {
  109 + return value->og;
  110 + }
  111 +
  112 + void
  113 + setDescription(QPDF* qpdf, std::string const& description)
  114 + {
  115 + return value->setDescription(qpdf, description);
  116 + }
  117 + bool
  118 + getDescription(QPDF*& qpdf, std::string& description)
  119 + {
  120 + return value->getDescription(qpdf, description);
  121 + }
  122 + bool
  123 + hasDescription()
  124 + {
  125 + return value->hasDescription();
  126 + }
  127 + void
  128 + setParsedOffset(qpdf_offset_t offset)
  129 + {
  130 + value->setParsedOffset(offset);
  131 + }
  132 + qpdf_offset_t
  133 + getParsedOffset()
  134 + {
  135 + return value->getParsedOffset();
  136 + }
  137 + void
  138 + assign(std::shared_ptr<QPDFObject> o)
  139 + {
  140 + value = o->value;
  141 + }
  142 + void
  143 + swapWith(std::shared_ptr<QPDFObject> o)
  144 + {
  145 + auto v = value;
  146 + value = o->value;
  147 + o->value = v;
  148 + auto og = value->og;
  149 + value->og = o->value->og;
  150 + o->value->og = og;
  151 + }
  152 +
  153 + // The following two methods are for use by class QPDF only
  154 + void
  155 + setObjGen(QPDF* qpdf, QPDFObjGen const& og)
  156 + {
  157 + value->qpdf = qpdf;
  158 + value->og = og;
  159 + }
  160 + void
  161 + resetObjGen()
  162 + {
  163 + value->qpdf = nullptr;
  164 + value->og = QPDFObjGen();
  165 + }
  166 +
  167 + bool
  168 + isUnresolved() const
  169 + {
  170 + return value->type_code == ::ot_unresolved;
  171 + }
  172 + void
  173 + resolve()
  174 + {
  175 + if (isUnresolved()) {
  176 + doResolve();
  177 + }
  178 + }
  179 + void doResolve();
  180 +
  181 + template <typename T>
  182 + T*
  183 + as()
  184 + {
  185 + return dynamic_cast<T*>(value.get());
  186 + }
76 187  
77 188 // Accessor to give specific access to non-public methods
78 189 class ObjAccessor
... ... @@ -89,29 +200,20 @@ class QPDFObject
89 200 }
90 201 }
91 202 };
92   - friend class ObjAccessor;
93 203  
94   - virtual void setDescription(QPDF*, std::string const&);
95   - bool getDescription(QPDF*&, std::string&);
96   - bool hasDescription();
97   -
98   - void setParsedOffset(qpdf_offset_t offset);
99   - qpdf_offset_t getParsedOffset();
  204 + friend class ObjAccessor;
100 205  
101 206 protected:
102 207 virtual void
103 208 releaseResolved()
104 209 {
  210 + value->releaseResolved();
105 211 }
106   - static std::shared_ptr<QPDFObject> do_create(QPDFObject*);
107 212  
108 213 private:
109 214 QPDFObject(QPDFObject const&) = delete;
110 215 QPDFObject& operator=(QPDFObject const&) = delete;
111   -
112   - QPDF* owning_qpdf;
113   - std::string object_description;
114   - qpdf_offset_t parsed_offset;
  216 + std::shared_ptr<QPDFValue> value;
115 217 };
116 218  
117 219 #endif // QPDFOBJECT_HH
... ...
include/qpdf/QPDFObjectHandle.hh
... ... @@ -43,8 +43,18 @@
43 43  
44 44 class Pipeline;
45 45 class QPDF;
46   -class QPDF_Dictionary;
47 46 class QPDF_Array;
  47 +class QPDF_Bool;
  48 +class QPDF_Dictionary;
  49 +class QPDF_InlineImage;
  50 +class QPDF_Integer;
  51 +class QPDF_Name;
  52 +class QPDF_Null;
  53 +class QPDF_Operator;
  54 +class QPDF_Real;
  55 +class QPDF_Reserved;
  56 +class QPDF_Stream;
  57 +class QPDF_String;
48 58 class QPDFTokenizer;
49 59 class QPDFExc;
50 60 class Pl_QPDFTokenizer;
... ... @@ -316,7 +326,7 @@ class QPDFObjectHandle
316 326 };
317 327  
318 328 QPDF_DLL
319   - QPDFObjectHandle();
  329 + QPDFObjectHandle() = default;
320 330 QPDF_DLL
321 331 QPDFObjectHandle(QPDFObjectHandle const&) = default;
322 332 QPDF_DLL
... ... @@ -963,8 +973,8 @@ class QPDFObjectHandle
963 973 // null for a direct object if allow_nullptr is set to true or
964 974 // throws a runtime error otherwise.
965 975 QPDF_DLL
966   - inline QPDF*
967   - getOwningQPDF(bool allow_nullptr = true, std::string const& error_msg = "");
  976 + inline QPDF* getOwningQPDF(
  977 + bool allow_nullptr = true, std::string const& error_msg = "") const;
968 978  
969 979 // Create a shallow copy of an object as a direct object, but do not
970 980 // traverse across indirect object boundaries. That means that,
... ... @@ -1443,9 +1453,9 @@ class QPDFObjectHandle
1443 1453  
1444 1454 private:
1445 1455 static QPDFObjectHandle
1446   - newIndirect(QPDF* qpdf, QPDFObjGen const& og)
  1456 + newIndirect(std::shared_ptr<QPDFObject> const& obj)
1447 1457 {
1448   - return QPDFObjectHandle::newIndirect(qpdf, og);
  1458 + return QPDFObjectHandle(obj);
1449 1459 }
1450 1460 static QPDFObjectHandle
1451 1461 newStream(
... ... @@ -1458,12 +1468,6 @@ class QPDFObjectHandle
1458 1468 return QPDFObjectHandle::newStream(
1459 1469 qpdf, og, stream_dict, offset, length);
1460 1470 }
1461   - // Reserve an object with a specific ID
1462   - static QPDFObjectHandle
1463   - makeReserved()
1464   - {
1465   - return QPDFObjectHandle::makeReserved();
1466   - }
1467 1471 };
1468 1472 friend class Factory;
1469 1473  
... ... @@ -1483,6 +1487,16 @@ class QPDFObjectHandle
1483 1487 };
1484 1488 return o.obj;
1485 1489 }
  1490 + static QPDF_Array*
  1491 + asArray(QPDFObjectHandle& oh)
  1492 + {
  1493 + return oh.asArray();
  1494 + }
  1495 + static QPDF_Stream*
  1496 + asStream(QPDFObjectHandle& oh)
  1497 + {
  1498 + return oh.asStream();
  1499 + }
1486 1500 };
1487 1501 friend class ObjAccessor;
1488 1502  
... ... @@ -1563,18 +1577,32 @@ class QPDFObjectHandle
1563 1577 bool isImage(bool exclude_imagemask = true);
1564 1578  
1565 1579 private:
1566   - QPDFObjectHandle(QPDF*, QPDFObjGen const& og);
1567   - QPDFObjectHandle(std::shared_ptr<QPDFObject> const&);
  1580 + QPDFObjectHandle(std::shared_ptr<QPDFObject> const& obj) :
  1581 + obj(obj)
  1582 + {
  1583 + }
1568 1584  
1569 1585 // Private object factory methods
1570   - static QPDFObjectHandle newIndirect(QPDF*, QPDFObjGen const& og);
1571 1586 static QPDFObjectHandle newStream(
1572 1587 QPDF* qpdf,
1573 1588 QPDFObjGen const& og,
1574 1589 QPDFObjectHandle stream_dict,
1575 1590 qpdf_offset_t offset,
1576 1591 size_t length);
1577   - static QPDFObjectHandle makeReserved();
  1592 +
  1593 + QPDF_Array* asArray();
  1594 + QPDF_Bool* asBool();
  1595 + QPDF_Dictionary* asDictionary();
  1596 + QPDF_InlineImage* asInlineImage();
  1597 + QPDF_Integer* asInteger();
  1598 + QPDF_Name* asName();
  1599 + QPDF_Null* asNull();
  1600 + QPDF_Operator* asOperator();
  1601 + QPDF_Real* asReal();
  1602 + QPDF_Reserved* asReserved();
  1603 + QPDF_Stream* asStream();
  1604 + QPDF_Stream* asStreamWithAssert();
  1605 + QPDF_String* asString();
1578 1606  
1579 1607 void typeWarning(char const* expected_type, std::string const& warning);
1580 1608 void objectWarning(std::string const& warning);
... ... @@ -1601,15 +1629,10 @@ class QPDFObjectHandle
1601 1629 static void warn(QPDF*, QPDFExc const&);
1602 1630 void checkOwnership(QPDFObjectHandle const&) const;
1603 1631  
1604   - bool initialized;
1605   -
1606 1632 // Moving members of QPDFObjectHandle into a smart pointer incurs
1607 1633 // a substantial performance penalty since QPDFObjectHandle
1608 1634 // objects are copied around so frequently.
1609   - QPDF* qpdf;
1610   - QPDFObjGen og;
1611 1635 std::shared_ptr<QPDFObject> obj;
1612   - bool reserved;
1613 1636 };
1614 1637  
1615 1638 #ifndef QPDF_NO_QPDF_STRING
... ... @@ -1832,44 +1855,45 @@ class QPDFObjectHandle::QPDFArrayItems
1832 1855 inline QPDFObjGen
1833 1856 QPDFObjectHandle::getObjGen() const
1834 1857 {
1835   - return og;
  1858 + return isInitialized() ? obj->getObjGen() : QPDFObjGen();
1836 1859 }
1837 1860  
1838 1861 inline int
1839 1862 QPDFObjectHandle::getObjectID() const
1840 1863 {
1841   - return og.getObj();
  1864 + return getObjGen().getObj();
1842 1865 }
1843 1866  
1844 1867 inline int
1845 1868 QPDFObjectHandle::getGeneration() const
1846 1869 {
1847   - return og.getGen();
  1870 + return getObjGen().getGen();
1848 1871 }
1849 1872  
1850 1873 inline bool
1851 1874 QPDFObjectHandle::isIndirect() const
1852 1875 {
1853   - return initialized && (getObjectID() != 0);
  1876 + return (obj != nullptr) && (getObjectID() != 0);
1854 1877 }
1855 1878  
1856 1879 inline bool
1857 1880 QPDFObjectHandle::isInitialized() const
1858 1881 {
1859   - return initialized;
  1882 + return obj != nullptr;
1860 1883 }
1861 1884  
1862 1885 // Indirect object accessors
1863 1886 inline QPDF*
1864 1887 QPDFObjectHandle::getOwningQPDF(
1865   - bool allow_nullptr, std::string const& error_msg)
  1888 + bool allow_nullptr, std::string const& error_msg) const
1866 1889 {
1867 1890 // Will be null for direct objects
1868   - if (!allow_nullptr && (this->qpdf == nullptr)) {
  1891 + auto result = isInitialized() ? this->obj->getQPDF() : nullptr;
  1892 + if (!allow_nullptr && (result == nullptr)) {
1869 1893 throw std::runtime_error(
1870 1894 error_msg == "" ? "attempt to use a null qpdf object" : error_msg);
1871 1895 }
1872   - return this->qpdf;
  1896 + return result;
1873 1897 }
1874 1898  
1875 1899 inline void
... ... @@ -1877,7 +1901,7 @@ QPDFObjectHandle::setParsedOffset(qpdf_offset_t offset)
1877 1901 {
1878 1902 // This is called during parsing on newly created direct objects,
1879 1903 // so we can't call dereference() here.
1880   - if (this->obj.get()) {
  1904 + if (isInitialized()) {
1881 1905 this->obj->setParsedOffset(offset);
1882 1906 }
1883 1907 }
... ...
include/qpdf/QPDFValue.hh 0 → 100644
  1 +// Copyright (c) 2005-2022 Jay Berkenbilt
  2 +//
  3 +// This file is part of qpdf.
  4 +//
  5 +// Licensed under the Apache License, Version 2.0 (the "License");
  6 +// you may not use this file except in compliance with the License.
  7 +// 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
  12 +// distributed under the License is distributed on an "AS IS" BASIS,
  13 +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 +// See the License for the specific language governing permissions and
  15 +// limitations under the License.
  16 +//
  17 +// Versions of qpdf prior to version 7 were released under the terms
  18 +// of version 2.0 of the Artistic License. At your option, you may
  19 +// continue to consider qpdf to be licensed under those terms. Please
  20 +// see the manual for additional information.
  21 +
  22 +#ifndef QPDFVALUE_HH
  23 +#define QPDFVALUE_HH
  24 +
  25 +#include <qpdf/Constants.h>
  26 +#include <qpdf/DLL.h>
  27 +#include <qpdf/JSON.hh>
  28 +#include <qpdf/QPDFObjGen.hh>
  29 +#include <qpdf/Types.h>
  30 +
  31 +#include <string>
  32 +
  33 +class QPDF;
  34 +class QPDFObjectHandle;
  35 +class QPDFObject;
  36 +
  37 +class QPDFValue
  38 +{
  39 + friend class QPDFObject;
  40 +
  41 + public:
  42 + virtual ~QPDFValue() = default;
  43 +
  44 + virtual std::shared_ptr<QPDFObject> shallowCopy() = 0;
  45 + virtual std::string unparse() = 0;
  46 + virtual JSON getJSON(int json_version) = 0;
  47 + virtual void
  48 + setDescription(QPDF* qpdf, std::string const& description)
  49 + {
  50 + owning_qpdf = qpdf;
  51 + object_description = description;
  52 + }
  53 + bool
  54 + getDescription(QPDF*& qpdf, std::string& description)
  55 + {
  56 + qpdf = owning_qpdf;
  57 + description = object_description;
  58 + return owning_qpdf != nullptr;
  59 + }
  60 + bool
  61 + hasDescription()
  62 + {
  63 + return owning_qpdf != nullptr;
  64 + }
  65 + void
  66 + setParsedOffset(qpdf_offset_t offset)
  67 + {
  68 + if (parsed_offset < 0) {
  69 + parsed_offset = offset;
  70 + }
  71 + }
  72 + qpdf_offset_t
  73 + getParsedOffset()
  74 + {
  75 + return parsed_offset;
  76 + }
  77 + QPDF*
  78 + getQPDF()
  79 + {
  80 + return qpdf;
  81 + }
  82 + QPDFObjGen
  83 + getObjGen()
  84 + {
  85 + return og;
  86 + }
  87 +
  88 + protected:
  89 + QPDFValue() :
  90 + type_code(::ot_uninitialized),
  91 + type_name("uninitialized")
  92 + {
  93 + }
  94 + QPDFValue(qpdf_object_type_e type_code, char const* type_name) :
  95 + type_code(type_code),
  96 + type_name(type_name)
  97 + {
  98 + }
  99 + QPDFValue(
  100 + qpdf_object_type_e type_code,
  101 + char const* type_name,
  102 + QPDF* qpdf,
  103 + QPDFObjGen const& og) :
  104 + type_code(type_code),
  105 + type_name(type_name),
  106 + qpdf(qpdf),
  107 + og(og)
  108 + {
  109 + }
  110 + virtual void
  111 + releaseResolved()
  112 + {
  113 + }
  114 + static std::shared_ptr<QPDFObject> do_create(QPDFValue*);
  115 +
  116 + private:
  117 + QPDFValue(QPDFValue const&) = delete;
  118 + QPDFValue& operator=(QPDFValue const&) = delete;
  119 + QPDF* owning_qpdf{nullptr};
  120 + std::string object_description;
  121 + qpdf_offset_t parsed_offset{-1};
  122 + const qpdf_object_type_e type_code;
  123 + char const* type_name;
  124 +
  125 + protected:
  126 + QPDF* qpdf{nullptr};
  127 + QPDFObjGen og;
  128 +};
  129 +
  130 +#endif // QPDFVALUE_HH
... ...
libqpdf/CMakeLists.txt
... ... @@ -85,6 +85,7 @@ set(libqpdf_SOURCES
85 85 QPDFSystemError.cc
86 86 QPDFTokenizer.cc
87 87 QPDFUsage.cc
  88 + QPDFValue.cc
88 89 QPDFWriter.cc
89 90 QPDFXRefEntry.cc
90 91 QPDF_Array.cc
... ... @@ -99,6 +100,7 @@ set(libqpdf_SOURCES
99 100 QPDF_Reserved.cc
100 101 QPDF_Stream.cc
101 102 QPDF_String.cc
  103 + QPDF_Unresolved.cc
102 104 QPDF_encryption.cc
103 105 QPDF_json.cc
104 106 QPDF_linearization.cc
... ...
libqpdf/QPDF.cc
... ... @@ -24,7 +24,9 @@
24 24 #include <qpdf/QPDF_Array.hh>
25 25 #include <qpdf/QPDF_Dictionary.hh>
26 26 #include <qpdf/QPDF_Null.hh>
  27 +#include <qpdf/QPDF_Reserved.hh>
27 28 #include <qpdf/QPDF_Stream.hh>
  29 +#include <qpdf/QPDF_Unresolved.hh>
28 30 #include <qpdf/QTC.hh>
29 31 #include <qpdf/QUtil.hh>
30 32  
... ... @@ -222,7 +224,6 @@ QPDF::Members::Members() :
222 224 immediate_copy_from(false),
223 225 in_parse(false),
224 226 parsed(false),
225   - ever_replaced_objects(false),
226 227 first_xref_item_offset(0),
227 228 uncompressed_after_compressed(false)
228 229 {
... ... @@ -258,6 +259,7 @@ QPDF::~QPDF()
258 259 this->m->xref_table.clear();
259 260 for (auto const& iter: this->m->obj_cache) {
260 261 QPDFObject::ObjAccessor::releaseResolved(iter.second.object.get());
  262 + iter.second.object->resetObjGen();
261 263 }
262 264 }
263 265  
... ... @@ -1397,7 +1399,7 @@ QPDF::fixDanglingReferences(bool force)
1397 1399 std::list<QPDFObjectHandle> queue;
1398 1400 queue.push_back(this->m->trailer);
1399 1401 for (auto const& og: to_process) {
1400   - QPDFObjectHandle obj = QPDFObjectHandle::Factory::newIndirect(this, og);
  1402 + auto obj = getObject(og);
1401 1403 if (obj.isDictionary() || obj.isArray()) {
1402 1404 queue.push_back(obj);
1403 1405 } else if (obj.isStream()) {
... ... @@ -1419,18 +1421,15 @@ QPDF::fixDanglingReferences(bool force)
1419 1421 to_check.push_back(iter.second);
1420 1422 }
1421 1423 } else if (obj.isArray()) {
1422   - QPDF_Array* arr = dynamic_cast<QPDF_Array*>(
1423   - QPDFObjectHandle::ObjAccessor::getObject(obj).get());
  1424 + auto arr = QPDFObjectHandle::ObjAccessor::asArray(obj);
1424 1425 arr->addExplicitElementsToList(to_check);
1425 1426 }
1426 1427 for (auto sub: to_check) {
1427 1428 if (sub.isIndirect()) {
1428   - if (sub.getOwningQPDF() == this) {
1429   - QPDFObjGen og(sub.getObjGen());
1430   - if (this->m->obj_cache.count(og) == 0) {
1431   - QTC::TC("qpdf", "QPDF detected dangling ref");
1432   - queue.push_back(sub);
1433   - }
  1429 + if ((sub.getOwningQPDF() == this) &&
  1430 + isUnresolved(sub.getObjGen())) {
  1431 + QTC::TC("qpdf", "QPDF detected dangling ref");
  1432 + queue.push_back(sub);
1434 1433 }
1435 1434 } else {
1436 1435 queue.push_back(sub);
... ... @@ -1462,8 +1461,7 @@ QPDF::getAllObjects()
1462 1461 fixDanglingReferences(true);
1463 1462 std::vector<QPDFObjectHandle> result;
1464 1463 for (auto const& iter: this->m->obj_cache) {
1465   - QPDFObjGen const& og = iter.first;
1466   - result.push_back(QPDFObjectHandle::Factory::newIndirect(this, og));
  1464 + result.push_back(newIndirect(iter.first, iter.second.object));
1467 1465 }
1468 1466 return result;
1469 1467 }
... ... @@ -1888,7 +1886,7 @@ QPDF::readObjectAtOffset(
1888 1886 "expected endobj");
1889 1887 }
1890 1888  
1891   - if (!this->m->obj_cache.count(og)) {
  1889 + if (isUnresolved(og)) {
1892 1890 // Store the object in the cache here so it gets cached
1893 1891 // whether we first know the offset or whether we first know
1894 1892 // the object ID and generation (in which we case we would get
... ... @@ -1919,8 +1917,8 @@ QPDF::readObjectAtOffset(
1919 1917 }
1920 1918 }
1921 1919 qpdf_offset_t end_after_space = this->m->file->tell();
1922   -
1923   - this->m->obj_cache[og] = ObjCache(
  1920 + updateCache(
  1921 + og,
1924 1922 QPDFObjectHandle::ObjAccessor::getObject(oh),
1925 1923 end_before_space,
1926 1924 end_after_space);
... ... @@ -1929,31 +1927,14 @@ QPDF::readObjectAtOffset(
1929 1927 return oh;
1930 1928 }
1931 1929  
1932   -bool
1933   -QPDF::objectChanged(QPDFObjGen const& og, std::shared_ptr<QPDFObject>& oph)
1934   -{
1935   - // See if the object cached at og, if any, is the one passed in.
1936   - // QPDFObjectHandle uses this to detect outdated handles to
1937   - // replaced or swapped objects. This is a somewhat expensive check
1938   - // because it happens with every dereference of a
1939   - // QPDFObjectHandle. To reduce the hit somewhat, short-circuit the
1940   - // check if we never called a function that replaces an object
1941   - // already in cache. It is important for functions that do this to
1942   - // set ever_replaced_objects = true.
1943   -
1944   - if (!this->m->ever_replaced_objects) {
1945   - return false;
1946   - }
1947   - auto c = this->m->obj_cache.find(og);
1948   - if (c == this->m->obj_cache.end()) {
1949   - return true;
1950   - }
1951   - return (c->second.object.get() != oph.get());
1952   -}
1953   -
1954   -std::shared_ptr<QPDFObject>
  1930 +void
1955 1931 QPDF::resolve(QPDFObjGen const& og)
1956 1932 {
  1933 + if (isCached(og) && !isUnresolved(og)) {
  1934 + // We only need to resolve unresolved objects
  1935 + return;
  1936 + }
  1937 +
1957 1938 // Check object cache before checking xref table. This allows us
1958 1939 // to insert things into the object cache that don't actually
1959 1940 // exist in the file.
... ... @@ -1967,11 +1948,12 @@ QPDF::resolve(QPDFObjGen const&amp; og)
1967 1948 "",
1968 1949 this->m->file->getLastOffset(),
1969 1950 ("loop detected resolving object " + og.unparse(' ')));
1970   - return QPDF_Null::create();
  1951 + updateCache(og, QPDF_Null::create(), -1, -1);
  1952 + return;
1971 1953 }
1972 1954 ResolveRecorder rr(this, og);
1973 1955  
1974   - if ((!this->m->obj_cache.count(og)) && this->m->xref_table.count(og)) {
  1956 + if (m->xref_table.count(og) != 0) {
1975 1957 QPDFXRefEntry const& entry = this->m->xref_table[og];
1976 1958 try {
1977 1959 switch (entry.getType()) {
... ... @@ -2009,19 +1991,17 @@ QPDF::resolve(QPDFObjGen const&amp; og)
2009 1991 ": error reading object: " + e.what()));
2010 1992 }
2011 1993 }
2012   - if (this->m->obj_cache.count(og) == 0) {
  1994 +
  1995 + if (isUnresolved(og)) {
2013 1996 // PDF spec says unknown objects resolve to the null object.
2014 1997 QTC::TC("qpdf", "QPDF resolve failure to null");
2015   - QPDFObjectHandle oh = QPDFObjectHandle::newNull();
2016   - this->m->obj_cache[og] =
2017   - ObjCache(QPDFObjectHandle::ObjAccessor::getObject(oh), -1, -1);
  1998 + updateCache(og, QPDF_Null::create(), -1, -1);
2018 1999 }
2019 2000  
2020   - std::shared_ptr<QPDFObject> result(this->m->obj_cache[og].object);
  2001 + auto result(this->m->obj_cache[og].object);
2021 2002 if (!result->hasDescription()) {
2022 2003 result->setDescription(this, ("object " + og.unparse(' ')));
2023 2004 }
2024   - return result;
2025 2005 }
2026 2006  
2027 2007 void
... ... @@ -2109,15 +2089,15 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
2109 2089 // objects appended to the file, so it is necessary to recheck the
2110 2090 // xref table and only cache what would actually be resolved here.
2111 2091 for (auto const& iter: offsets) {
2112   - int obj = iter.first;
2113   - QPDFObjGen og(obj, 0);
  2092 + QPDFObjGen og(iter.first, 0);
2114 2093 QPDFXRefEntry const& entry = this->m->xref_table[og];
2115 2094 if ((entry.getType() == 2) &&
2116 2095 (entry.getObjStreamNumber() == obj_stream_number)) {
2117 2096 int offset = iter.second;
2118 2097 input->seek(offset, SEEK_SET);
2119 2098 QPDFObjectHandle oh = readObject(input, "", og, true);
2120   - this->m->obj_cache[og] = ObjCache(
  2099 + updateCache(
  2100 + og,
2121 2101 QPDFObjectHandle::ObjAccessor::getObject(oh),
2122 2102 end_before_space,
2123 2103 end_after_space);
... ... @@ -2128,6 +2108,47 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
2128 2108 }
2129 2109  
2130 2110 QPDFObjectHandle
  2111 +QPDF::newIndirect(QPDFObjGen const& og, std::shared_ptr<QPDFObject> const& obj)
  2112 +{
  2113 + obj->setObjGen(this, og);
  2114 + if (!obj->hasDescription()) {
  2115 + obj->setDescription(this, "object " + og.unparse(' '));
  2116 + }
  2117 + return QPDFObjectHandle::Factory::newIndirect(obj);
  2118 +}
  2119 +
  2120 +void
  2121 +QPDF::updateCache(
  2122 + QPDFObjGen const& og,
  2123 + std::shared_ptr<QPDFObject> const& object,
  2124 + qpdf_offset_t end_before_space,
  2125 + qpdf_offset_t end_after_space)
  2126 +{
  2127 + object->setObjGen(this, og);
  2128 + if (isCached(og)) {
  2129 + auto& cache = m->obj_cache[og];
  2130 + cache.object->resetObjGen();
  2131 + cache.object->assign(object);
  2132 + cache.end_before_space = end_before_space;
  2133 + cache.end_after_space = end_after_space;
  2134 + } else {
  2135 + m->obj_cache[og] = ObjCache(object, end_before_space, end_after_space);
  2136 + }
  2137 +}
  2138 +
  2139 +bool
  2140 +QPDF::isCached(QPDFObjGen const& og)
  2141 +{
  2142 + return m->obj_cache.count(og) != 0;
  2143 +}
  2144 +
  2145 +bool
  2146 +QPDF::isUnresolved(QPDFObjGen const& og)
  2147 +{
  2148 + return !isCached(og) || m->obj_cache[og].object->isUnresolved();
  2149 +}
  2150 +
  2151 +QPDFObjectHandle
2131 2152 QPDF::makeIndirectObject(QPDFObjectHandle oh)
2132 2153 {
2133 2154 int max_objid = toI(getObjectCount());
... ... @@ -2136,19 +2157,21 @@ QPDF::makeIndirectObject(QPDFObjectHandle oh)
2136 2157 "max object id is too high to create new objects");
2137 2158 }
2138 2159 QPDFObjGen next(max_objid + 1, 0);
2139   - this->m->obj_cache[next] =
  2160 + m->obj_cache[next] =
2140 2161 ObjCache(QPDFObjectHandle::ObjAccessor::getObject(oh), -1, -1);
2141   - return QPDFObjectHandle::Factory::newIndirect(this, next);
  2162 + return newIndirect(next, m->obj_cache[next].object);
2142 2163 }
2143 2164  
2144 2165 QPDFObjectHandle
2145 2166 QPDF::reserveObjectIfNotExists(QPDFObjGen const& og)
2146 2167 {
2147   - if ((!this->m->obj_cache.count(og)) && (!this->m->xref_table.count(og))) {
  2168 + if (!isCached(og) && !m->xref_table.count(og)) {
2148 2169 resolve(og);
2149   - replaceObject(og, QPDFObjectHandle::Factory::makeReserved());
  2170 + m->obj_cache[og].object = QPDF_Reserved::create();
  2171 + return newIndirect(og, m->obj_cache[og].object);
  2172 + } else {
  2173 + return getObject(og);
2150 2174 }
2151   - return getObject(og);
2152 2175 }
2153 2176  
2154 2177 QPDFObjectHandle
... ... @@ -2161,7 +2184,13 @@ QPDF::reserveStream(QPDFObjGen const&amp; og)
2161 2184 QPDFObjectHandle
2162 2185 QPDF::getObject(QPDFObjGen const& og)
2163 2186 {
2164   - return QPDFObjectHandle::Factory::newIndirect(this, og);
  2187 + if (!og.isIndirect()) {
  2188 + return QPDFObjectHandle::newNull();
  2189 + }
  2190 + if (!isCached(og)) {
  2191 + m->obj_cache[og] = ObjCache(QPDF_Unresolved::create(this, og), -1, -1);
  2192 + }
  2193 + return newIndirect(og, m->obj_cache[og].object);
2165 2194 }
2166 2195  
2167 2196 QPDFObjectHandle
... ... @@ -2196,14 +2225,11 @@ QPDF::replaceObject(QPDFObjGen const&amp; og, QPDFObjectHandle oh)
2196 2225 throw std::logic_error(
2197 2226 "QPDF::replaceObject called with indirect object handle");
2198 2227 }
2199   -
2200 2228 // Force new object to appear in the cache
2201 2229 resolve(og);
2202 2230  
2203 2231 // Replace the object in the object cache
2204   - this->m->ever_replaced_objects = true;
2205   - this->m->obj_cache[og] =
2206   - ObjCache(QPDFObjectHandle::ObjAccessor::getObject(oh), -1, -1);
  2232 + updateCache(og, QPDFObjectHandle::ObjAccessor::getObject(oh), -1, -1);
2207 2233 }
2208 2234  
2209 2235 void
... ... @@ -2456,12 +2482,12 @@ QPDF::copyStreamData(QPDFObjectHandle result, QPDFObjectHandle foreign)
2456 2482 QPDFObjGen local_og(result.getObjGen());
2457 2483 // Copy information from the foreign stream so we can pipe its
2458 2484 // data later without keeping the original QPDF object around.
  2485 +
2459 2486 QPDF* foreign_stream_qpdf = foreign.getOwningQPDF(
2460 2487 false, "unable to retrieve owning qpdf from foreign stream");
2461 2488  
2462   - QPDF_Stream* stream = dynamic_cast<QPDF_Stream*>(
2463   - QPDFObjectHandle::ObjAccessor::getObject(foreign).get());
2464   - if (!stream) {
  2489 + auto stream = QPDFObjectHandle::ObjAccessor::asStream(foreign);
  2490 + if (stream == nullptr) {
2465 2491 throw std::logic_error("unable to retrieve underlying"
2466 2492 " stream object from foreign stream");
2467 2493 }
... ... @@ -2525,10 +2551,7 @@ QPDF::swapObjects(QPDFObjGen const&amp; og1, QPDFObjGen const&amp; og2)
2525 2551 // cache.
2526 2552 resolve(og1);
2527 2553 resolve(og2);
2528   - ObjCache t = this->m->obj_cache[og1];
2529   - this->m->ever_replaced_objects = true;
2530   - this->m->obj_cache[og1] = this->m->obj_cache[og2];
2531   - this->m->obj_cache[og2] = t;
  2554 + m->obj_cache[og1].object->swapWith(m->obj_cache[og2].object);
2532 2555 }
2533 2556  
2534 2557 unsigned long long
... ...
libqpdf/QPDFObject.cc
1 1 #include <qpdf/QPDFObject.hh>
2 2  
3   -QPDFObject::QPDFObject() :
4   - owning_qpdf(nullptr),
5   - parsed_offset(-1)
6   -{
7   -}
8   -
9   -std::shared_ptr<QPDFObject>
10   -QPDFObject::do_create(QPDFObject* object)
11   -{
12   - std::shared_ptr<QPDFObject> obj(object);
13   - return obj;
14   -}
15   -
16   -void
17   -QPDFObject::setDescription(QPDF* qpdf, std::string const& description)
18   -{
19   - this->owning_qpdf = qpdf;
20   - this->object_description = description;
21   -}
22   -
23   -bool
24   -QPDFObject::getDescription(QPDF*& qpdf, std::string& description)
25   -{
26   - qpdf = this->owning_qpdf;
27   - description = this->object_description;
28   - return this->owning_qpdf != nullptr;
29   -}
30   -
31   -bool
32   -QPDFObject::hasDescription()
33   -{
34   - return this->owning_qpdf != nullptr;
35   -}
  3 +#include <qpdf/QPDF.hh>
36 4  
37 5 void
38   -QPDFObject::setParsedOffset(qpdf_offset_t offset)
39   -{
40   - this->parsed_offset = offset;
41   -}
42   -
43   -qpdf_offset_t
44   -QPDFObject::getParsedOffset()
  6 +QPDFObject::doResolve()
45 7 {
46   - return this->parsed_offset;
  8 + auto og = value->og;
  9 + QPDF::Resolver::resolve(value->qpdf, og);
47 10 }
... ...
libqpdf/QPDFObjectHandle.cc
... ... @@ -21,6 +21,7 @@
21 21 #include <qpdf/QPDF_Reserved.hh>
22 22 #include <qpdf/QPDF_Stream.hh>
23 23 #include <qpdf/QPDF_String.hh>
  24 +#include <qpdf/QPDF_Unresolved.hh>
24 25 #include <qpdf/SparseOHArray.hh>
25 26  
26 27 #include <qpdf/QIntC.hh>
... ... @@ -234,29 +235,6 @@ LastChar::getLastChar()
234 235 return this->last_char;
235 236 }
236 237  
237   -QPDFObjectHandle::QPDFObjectHandle() :
238   - initialized(false),
239   - qpdf(nullptr),
240   - reserved(false)
241   -{
242   -}
243   -
244   -QPDFObjectHandle::QPDFObjectHandle(QPDF* qpdf, QPDFObjGen const& og) :
245   - initialized(true),
246   - qpdf(qpdf),
247   - og(og),
248   - reserved(false)
249   -{
250   -}
251   -
252   -QPDFObjectHandle::QPDFObjectHandle(std::shared_ptr<QPDFObject> const& data) :
253   - initialized(true),
254   - qpdf(nullptr),
255   - obj(data),
256   - reserved(false)
257   -{
258   -}
259   -
260 238 void
261 239 QPDFObjectHandle::releaseResolved()
262 240 {
... ... @@ -286,24 +264,90 @@ QPDFObjectHandle::getTypeName()
286 264 return dereference() ? this->obj->getTypeName() : "uninitialized";
287 265 }
288 266  
289   -namespace
  267 +QPDF_Array*
  268 +QPDFObjectHandle::asArray()
290 269 {
291   - template <class T>
292   - class QPDFObjectTypeAccessor
293   - {
294   - public:
295   - static bool
296   - check(std::shared_ptr<QPDFObject> const& o)
297   - {
298   - return (o && dynamic_cast<T const*>(o.get()));
299   - }
300   - };
301   -} // namespace
  270 + return dereference() ? obj->as<QPDF_Array>() : nullptr;
  271 +}
  272 +
  273 +QPDF_Bool*
  274 +QPDFObjectHandle::asBool()
  275 +{
  276 + return dereference() ? obj->as<QPDF_Bool>() : nullptr;
  277 +}
  278 +
  279 +QPDF_Dictionary*
  280 +QPDFObjectHandle::asDictionary()
  281 +{
  282 + return dereference() ? obj->as<QPDF_Dictionary>() : nullptr;
  283 +}
  284 +
  285 +QPDF_InlineImage*
  286 +QPDFObjectHandle::asInlineImage()
  287 +{
  288 + return dereference() ? obj->as<QPDF_InlineImage>() : nullptr;
  289 +}
  290 +
  291 +QPDF_Integer*
  292 +QPDFObjectHandle::asInteger()
  293 +{
  294 + return dereference() ? obj->as<QPDF_Integer>() : nullptr;
  295 +}
  296 +
  297 +QPDF_Name*
  298 +QPDFObjectHandle::asName()
  299 +{
  300 + return dereference() ? obj->as<QPDF_Name>() : nullptr;
  301 +}
  302 +
  303 +QPDF_Null*
  304 +QPDFObjectHandle::asNull()
  305 +{
  306 + return dereference() ? obj->as<QPDF_Null>() : nullptr;
  307 +}
  308 +
  309 +QPDF_Operator*
  310 +QPDFObjectHandle::asOperator()
  311 +{
  312 + return dereference() ? obj->as<QPDF_Operator>() : nullptr;
  313 +}
  314 +
  315 +QPDF_Real*
  316 +QPDFObjectHandle::asReal()
  317 +{
  318 + return dereference() ? obj->as<QPDF_Real>() : nullptr;
  319 +}
  320 +
  321 +QPDF_Reserved*
  322 +QPDFObjectHandle::asReserved()
  323 +{
  324 + return dereference() ? obj->as<QPDF_Reserved>() : nullptr;
  325 +}
  326 +
  327 +QPDF_Stream*
  328 +QPDFObjectHandle::asStream()
  329 +{
  330 + return dereference() ? obj->as<QPDF_Stream>() : nullptr;
  331 +}
  332 +
  333 +QPDF_Stream*
  334 +QPDFObjectHandle::asStreamWithAssert()
  335 +{
  336 + auto stream = asStream();
  337 + assertType("stream", stream);
  338 + return stream;
  339 +}
  340 +
  341 +QPDF_String*
  342 +QPDFObjectHandle::asString()
  343 +{
  344 + return dereference() ? obj->as<QPDF_String>() : nullptr;
  345 +}
302 346  
303 347 bool
304 348 QPDFObjectHandle::isBool()
305 349 {
306   - return dereference() && QPDFObjectTypeAccessor<QPDF_Bool>::check(obj);
  350 + return dereference() && (obj->getTypeCode() == QPDFObject::ot_boolean);
307 351 }
308 352  
309 353 bool
... ... @@ -312,26 +356,26 @@ QPDFObjectHandle::isDirectNull() const
312 356 // Don't call dereference() -- this is a const method, and we know
313 357 // objid == 0, so there's nothing to resolve.
314 358 return (
315   - this->initialized && (getObjectID() == 0) &&
316   - QPDFObjectTypeAccessor<QPDF_Null>::check(obj));
  359 + isInitialized() && (getObjectID() == 0) &&
  360 + (obj->getTypeCode() == QPDFObject::ot_null));
317 361 }
318 362  
319 363 bool
320 364 QPDFObjectHandle::isNull()
321 365 {
322   - return dereference() && QPDFObjectTypeAccessor<QPDF_Null>::check(obj);
  366 + return dereference() && (obj->getTypeCode() == QPDFObject::ot_null);
323 367 }
324 368  
325 369 bool
326 370 QPDFObjectHandle::isInteger()
327 371 {
328   - return dereference() && QPDFObjectTypeAccessor<QPDF_Integer>::check(obj);
  372 + return dereference() && (obj->getTypeCode() == QPDFObject::ot_integer);
329 373 }
330 374  
331 375 bool
332 376 QPDFObjectHandle::isReal()
333 377 {
334   - return dereference() && QPDFObjectTypeAccessor<QPDF_Real>::check(obj);
  378 + return dereference() && (obj->getTypeCode() == QPDFObject::ot_real);
335 379 }
336 380  
337 381 bool
... ... @@ -368,51 +412,49 @@ QPDFObjectHandle::getValueAsNumber(double&amp; value)
368 412 bool
369 413 QPDFObjectHandle::isName()
370 414 {
371   - return dereference() && QPDFObjectTypeAccessor<QPDF_Name>::check(obj);
  415 + return dereference() && (obj->getTypeCode() == QPDFObject::ot_name);
372 416 }
373 417  
374 418 bool
375 419 QPDFObjectHandle::isString()
376 420 {
377   - return dereference() && QPDFObjectTypeAccessor<QPDF_String>::check(obj);
  421 + return dereference() && (obj->getTypeCode() == QPDFObject::ot_string);
378 422 }
379 423  
380 424 bool
381 425 QPDFObjectHandle::isOperator()
382 426 {
383   - return dereference() && QPDFObjectTypeAccessor<QPDF_Operator>::check(obj);
  427 + return dereference() && (obj->getTypeCode() == QPDFObject::ot_operator);
384 428 }
385 429  
386 430 bool
387 431 QPDFObjectHandle::isInlineImage()
388 432 {
389   - return dereference() &&
390   - QPDFObjectTypeAccessor<QPDF_InlineImage>::check(obj);
  433 + return dereference() && (obj->getTypeCode() == QPDFObject::ot_inlineimage);
391 434 }
392 435  
393 436 bool
394 437 QPDFObjectHandle::isArray()
395 438 {
396   - return dereference() && QPDFObjectTypeAccessor<QPDF_Array>::check(obj);
  439 + return dereference() && (obj->getTypeCode() == QPDFObject::ot_array);
397 440 }
398 441  
399 442 bool
400 443 QPDFObjectHandle::isDictionary()
401 444 {
402   - return dereference() && QPDFObjectTypeAccessor<QPDF_Dictionary>::check(obj);
  445 + return dereference() && (obj->getTypeCode() == QPDFObject::ot_dictionary);
403 446 }
404 447  
405 448 bool
406 449 QPDFObjectHandle::isStream()
407 450 {
408   - return dereference() && QPDFObjectTypeAccessor<QPDF_Stream>::check(obj);
  451 + return dereference() && (obj->getTypeCode() == QPDFObject::ot_stream);
409 452 }
410 453  
411 454 bool
412 455 QPDFObjectHandle::isReserved()
413 456 {
414   - // dereference will clear reserved if this has been replaced
415   - return dereference() && this->reserved;
  457 + return dereference() && (obj->getTypeCode() == QPDFObject::ot_reserved);
416 458 }
417 459  
418 460 bool
... ... @@ -450,8 +492,9 @@ QPDFObjectHandle::isStreamOfType(
450 492 bool
451 493 QPDFObjectHandle::getBoolValue()
452 494 {
453   - if (isBool()) {
454   - return dynamic_cast<QPDF_Bool*>(obj.get())->getVal();
  495 + auto boolean = asBool();
  496 + if (boolean) {
  497 + return boolean->getVal();
455 498 } else {
456 499 typeWarning("boolean", "returning false");
457 500 QTC::TC("qpdf", "QPDFObjectHandle boolean returning false");
... ... @@ -462,10 +505,11 @@ QPDFObjectHandle::getBoolValue()
462 505 bool
463 506 QPDFObjectHandle::getValueAsBool(bool& value)
464 507 {
465   - if (!isBool()) {
  508 + auto boolean = asBool();
  509 + if (boolean == nullptr) {
466 510 return false;
467 511 }
468   - value = dynamic_cast<QPDF_Bool*>(obj.get())->getVal();
  512 + value = boolean->getVal();
469 513 return true;
470 514 }
471 515  
... ... @@ -474,8 +518,9 @@ QPDFObjectHandle::getValueAsBool(bool&amp; value)
474 518 long long
475 519 QPDFObjectHandle::getIntValue()
476 520 {
477   - if (isInteger()) {
478   - return dynamic_cast<QPDF_Integer*>(obj.get())->getVal();
  521 + auto integer = asInteger();
  522 + if (integer) {
  523 + return integer->getVal();
479 524 } else {
480 525 typeWarning("integer", "returning 0");
481 526 QTC::TC("qpdf", "QPDFObjectHandle integer returning 0");
... ... @@ -486,10 +531,11 @@ QPDFObjectHandle::getIntValue()
486 531 bool
487 532 QPDFObjectHandle::getValueAsInt(long long& value)
488 533 {
489   - if (!isInteger()) {
  534 + auto integer = asInteger();
  535 + if (integer == nullptr) {
490 536 return false;
491 537 }
492   - value = dynamic_cast<QPDF_Integer*>(obj.get())->getVal();
  538 + value = integer->getVal();
493 539 return true;
494 540 }
495 541  
... ... @@ -585,8 +631,9 @@ QPDFObjectHandle::getValueAsUInt(unsigned int&amp; value)
585 631 std::string
586 632 QPDFObjectHandle::getRealValue()
587 633 {
588   - if (isReal()) {
589   - return dynamic_cast<QPDF_Real*>(obj.get())->getVal();
  634 + auto real = asReal();
  635 + if (real) {
  636 + return real->getVal();
590 637 } else {
591 638 typeWarning("real", "returning 0.0");
592 639 QTC::TC("qpdf", "QPDFObjectHandle real returning 0.0");
... ... @@ -597,10 +644,11 @@ QPDFObjectHandle::getRealValue()
597 644 bool
598 645 QPDFObjectHandle::getValueAsReal(std::string& value)
599 646 {
600   - if (!isReal()) {
  647 + auto real = asReal();
  648 + if (real == nullptr) {
601 649 return false;
602 650 }
603   - value = dynamic_cast<QPDF_Real*>(obj.get())->getVal();
  651 + value = real->getVal();
604 652 return true;
605 653 }
606 654  
... ... @@ -609,8 +657,9 @@ QPDFObjectHandle::getValueAsReal(std::string&amp; value)
609 657 std::string
610 658 QPDFObjectHandle::getName()
611 659 {
612   - if (isName()) {
613   - return dynamic_cast<QPDF_Name*>(obj.get())->getName();
  660 + auto name = asName();
  661 + if (name) {
  662 + return name->getName();
614 663 } else {
615 664 typeWarning("name", "returning dummy name");
616 665 QTC::TC("qpdf", "QPDFObjectHandle name returning dummy name");
... ... @@ -621,10 +670,11 @@ QPDFObjectHandle::getName()
621 670 bool
622 671 QPDFObjectHandle::getValueAsName(std::string& value)
623 672 {
624   - if (!isName()) {
  673 + auto name = asName();
  674 + if (name == nullptr) {
625 675 return false;
626 676 }
627   - value = dynamic_cast<QPDF_Name*>(obj.get())->getName();
  677 + value = name->getName();
628 678 return true;
629 679 }
630 680  
... ... @@ -633,8 +683,9 @@ QPDFObjectHandle::getValueAsName(std::string&amp; value)
633 683 std::string
634 684 QPDFObjectHandle::getStringValue()
635 685 {
636   - if (isString()) {
637   - return dynamic_cast<QPDF_String*>(obj.get())->getVal();
  686 + auto str = asString();
  687 + if (str) {
  688 + return str->getVal();
638 689 } else {
639 690 typeWarning("string", "returning empty string");
640 691 QTC::TC("qpdf", "QPDFObjectHandle string returning empty string");
... ... @@ -645,18 +696,20 @@ QPDFObjectHandle::getStringValue()
645 696 bool
646 697 QPDFObjectHandle::getValueAsString(std::string& value)
647 698 {
648   - if (!isString()) {
  699 + auto str = asString();
  700 + if (str == nullptr) {
649 701 return false;
650 702 }
651   - value = dynamic_cast<QPDF_String*>(obj.get())->getVal();
  703 + value = str->getVal();
652 704 return true;
653 705 }
654 706  
655 707 std::string
656 708 QPDFObjectHandle::getUTF8Value()
657 709 {
658   - if (isString()) {
659   - return dynamic_cast<QPDF_String*>(obj.get())->getUTF8Val();
  710 + auto str = asString();
  711 + if (str) {
  712 + return str->getUTF8Val();
660 713 } else {
661 714 typeWarning("string", "returning empty string");
662 715 QTC::TC("qpdf", "QPDFObjectHandle string returning empty utf8");
... ... @@ -667,10 +720,11 @@ QPDFObjectHandle::getUTF8Value()
667 720 bool
668 721 QPDFObjectHandle::getValueAsUTF8(std::string& value)
669 722 {
670   - if (!isString()) {
  723 + auto str = asString();
  724 + if (str == nullptr) {
671 725 return false;
672 726 }
673   - value = dynamic_cast<QPDF_String*>(obj.get())->getUTF8Val();
  727 + value = str->getUTF8Val();
674 728 return true;
675 729 }
676 730  
... ... @@ -679,8 +733,9 @@ QPDFObjectHandle::getValueAsUTF8(std::string&amp; value)
679 733 std::string
680 734 QPDFObjectHandle::getOperatorValue()
681 735 {
682   - if (isOperator()) {
683   - return dynamic_cast<QPDF_Operator*>(obj.get())->getVal();
  736 + auto op = asOperator();
  737 + if (op) {
  738 + return op->getVal();
684 739 } else {
685 740 typeWarning("operator", "returning fake value");
686 741 QTC::TC("qpdf", "QPDFObjectHandle operator returning fake value");
... ... @@ -691,18 +746,20 @@ QPDFObjectHandle::getOperatorValue()
691 746 bool
692 747 QPDFObjectHandle::getValueAsOperator(std::string& value)
693 748 {
694   - if (!isOperator()) {
  749 + auto op = asOperator();
  750 + if (op == nullptr) {
695 751 return false;
696 752 }
697   - value = dynamic_cast<QPDF_Operator*>(obj.get())->getVal();
  753 + value = op->getVal();
698 754 return true;
699 755 }
700 756  
701 757 std::string
702 758 QPDFObjectHandle::getInlineImageValue()
703 759 {
704   - if (isInlineImage()) {
705   - return dynamic_cast<QPDF_InlineImage*>(obj.get())->getVal();
  760 + auto image = asInlineImage();
  761 + if (image) {
  762 + return image->getVal();
706 763 } else {
707 764 typeWarning("inlineimage", "returning empty data");
708 765 QTC::TC("qpdf", "QPDFObjectHandle inlineimage returning empty data");
... ... @@ -713,10 +770,11 @@ QPDFObjectHandle::getInlineImageValue()
713 770 bool
714 771 QPDFObjectHandle::getValueAsInlineImage(std::string& value)
715 772 {
716   - if (!isInlineImage()) {
  773 + auto image = asInlineImage();
  774 + if (image == nullptr) {
717 775 return false;
718 776 }
719   - value = dynamic_cast<QPDF_InlineImage*>(obj.get())->getVal();
  777 + value = image->getVal();
720 778 return true;
721 779 }
722 780  
... ... @@ -731,8 +789,9 @@ QPDFObjectHandle::aitems()
731 789 int
732 790 QPDFObjectHandle::getArrayNItems()
733 791 {
734   - if (isArray()) {
735   - return dynamic_cast<QPDF_Array*>(obj.get())->getNItems();
  792 + auto array = asArray();
  793 + if (array) {
  794 + return array->getNItems();
736 795 } else {
737 796 typeWarning("array", "treating as empty");
738 797 QTC::TC("qpdf", "QPDFObjectHandle array treating as empty");
... ... @@ -744,11 +803,12 @@ QPDFObjectHandle
744 803 QPDFObjectHandle::getArrayItem(int n)
745 804 {
746 805 QPDFObjectHandle result;
747   - if (isArray() && (n < getArrayNItems()) && (n >= 0)) {
748   - result = dynamic_cast<QPDF_Array*>(obj.get())->getItem(n);
  806 + auto array = asArray();
  807 + if (array && (n < array->getNItems()) && (n >= 0)) {
  808 + result = array->getItem(n);
749 809 } else {
750 810 result = newNull();
751   - if (isArray()) {
  811 + if (array) {
752 812 objectWarning("returning null for out of bounds array access");
753 813 QTC::TC("qpdf", "QPDFObjectHandle array bounds");
754 814 } else {
... ... @@ -757,7 +817,7 @@ QPDFObjectHandle::getArrayItem(int n)
757 817 }
758 818 QPDF* context = nullptr;
759 819 std::string description;
760   - if (this->obj->getDescription(context, description)) {
  820 + if (obj->getDescription(context, description)) {
761 821 result.setObjectDescription(
762 822 context,
763 823 description + " -> null returned from invalid array access");
... ... @@ -769,14 +829,12 @@ QPDFObjectHandle::getArrayItem(int n)
769 829 bool
770 830 QPDFObjectHandle::isRectangle()
771 831 {
772   - if (!isArray()) {
773   - return false;
774   - }
775   - if (getArrayNItems() != 4) {
  832 + auto array = asArray();
  833 + if ((array == nullptr) || (array->getNItems() != 4)) {
776 834 return false;
777 835 }
778 836 for (int i = 0; i < 4; ++i) {
779   - if (!getArrayItem(i).isNumber()) {
  837 + if (!array->getItem(i).isNumber()) {
780 838 return false;
781 839 }
782 840 }
... ... @@ -786,14 +844,12 @@ QPDFObjectHandle::isRectangle()
786 844 bool
787 845 QPDFObjectHandle::isMatrix()
788 846 {
789   - if (!isArray()) {
790   - return false;
791   - }
792   - if (getArrayNItems() != 6) {
  847 + auto array = asArray();
  848 + if ((array == nullptr) || (array->getNItems() != 6)) {
793 849 return false;
794 850 }
795 851 for (int i = 0; i < 6; ++i) {
796   - if (!getArrayItem(i).isNumber()) {
  852 + if (!array->getItem(i).isNumber()) {
797 853 return false;
798 854 }
799 855 }
... ... @@ -805,13 +861,14 @@ QPDFObjectHandle::getArrayAsRectangle()
805 861 {
806 862 Rectangle result;
807 863 if (isRectangle()) {
  864 + auto array = asArray();
808 865 // Rectangle coordinates are always supposed to be llx, lly,
809 866 // urx, ury, but files have been found in the wild where
810 867 // llx > urx or lly > ury.
811   - double i0 = getArrayItem(0).getNumericValue();
812   - double i1 = getArrayItem(1).getNumericValue();
813   - double i2 = getArrayItem(2).getNumericValue();
814   - double i3 = getArrayItem(3).getNumericValue();
  868 + double i0 = array->getItem(0).getNumericValue();
  869 + double i1 = array->getItem(1).getNumericValue();
  870 + double i2 = array->getItem(2).getNumericValue();
  871 + double i3 = array->getItem(3).getNumericValue();
815 872 result = Rectangle(
816 873 std::min(i0, i2),
817 874 std::min(i1, i3),
... ... @@ -826,13 +883,14 @@ QPDFObjectHandle::getArrayAsMatrix()
826 883 {
827 884 Matrix result;
828 885 if (isMatrix()) {
  886 + auto array = asArray();
829 887 result = Matrix(
830   - getArrayItem(0).getNumericValue(),
831   - getArrayItem(1).getNumericValue(),
832   - getArrayItem(2).getNumericValue(),
833   - getArrayItem(3).getNumericValue(),
834   - getArrayItem(4).getNumericValue(),
835   - getArrayItem(5).getNumericValue());
  888 + array->getItem(0).getNumericValue(),
  889 + array->getItem(1).getNumericValue(),
  890 + array->getItem(2).getNumericValue(),
  891 + array->getItem(3).getNumericValue(),
  892 + array->getItem(4).getNumericValue(),
  893 + array->getItem(5).getNumericValue());
836 894 }
837 895 return result;
838 896 }
... ... @@ -841,8 +899,9 @@ std::vector&lt;QPDFObjectHandle&gt;
841 899 QPDFObjectHandle::getArrayAsVector()
842 900 {
843 901 std::vector<QPDFObjectHandle> result;
844   - if (isArray()) {
845   - dynamic_cast<QPDF_Array*>(obj.get())->getAsVector(result);
  902 + auto array = asArray();
  903 + if (array) {
  904 + array->getAsVector(result);
846 905 } else {
847 906 typeWarning("array", "treating as empty");
848 907 QTC::TC("qpdf", "QPDFObjectHandle array treating as empty vector");
... ... @@ -855,9 +914,10 @@ QPDFObjectHandle::getArrayAsVector()
855 914 void
856 915 QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const& item)
857 916 {
858   - if (isArray()) {
  917 + auto array = asArray();
  918 + if (array) {
859 919 checkOwnership(item);
860   - dynamic_cast<QPDF_Array*>(obj.get())->setItem(n, item);
  920 + array->setItem(n, item);
861 921 } else {
862 922 typeWarning("array", "ignoring attempt to set item");
863 923 QTC::TC("qpdf", "QPDFObjectHandle array ignoring set item");
... ... @@ -867,11 +927,12 @@ QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const&amp; item)
867 927 void
868 928 QPDFObjectHandle::setArrayFromVector(std::vector<QPDFObjectHandle> const& items)
869 929 {
870   - if (isArray()) {
  930 + auto array = asArray();
  931 + if (array) {
871 932 for (auto const& item: items) {
872 933 checkOwnership(item);
873 934 }
874   - dynamic_cast<QPDF_Array*>(obj.get())->setFromVector(items);
  935 + array->setFromVector(items);
875 936 } else {
876 937 typeWarning("array", "ignoring attempt to replace items");
877 938 QTC::TC("qpdf", "QPDFObjectHandle array ignoring replace items");
... ... @@ -881,8 +942,9 @@ QPDFObjectHandle::setArrayFromVector(std::vector&lt;QPDFObjectHandle&gt; const&amp; items)
881 942 void
882 943 QPDFObjectHandle::insertItem(int at, QPDFObjectHandle const& item)
883 944 {
884   - if (isArray()) {
885   - dynamic_cast<QPDF_Array*>(obj.get())->insertItem(at, item);
  945 + auto array = asArray();
  946 + if (array) {
  947 + array->insertItem(at, item);
886 948 } else {
887 949 typeWarning("array", "ignoring attempt to insert item");
888 950 QTC::TC("qpdf", "QPDFObjectHandle array ignoring insert item");
... ... @@ -899,9 +961,10 @@ QPDFObjectHandle::insertItemAndGetNew(int at, QPDFObjectHandle const&amp; item)
899 961 void
900 962 QPDFObjectHandle::appendItem(QPDFObjectHandle const& item)
901 963 {
902   - if (isArray()) {
  964 + auto array = asArray();
  965 + if (array) {
903 966 checkOwnership(item);
904   - dynamic_cast<QPDF_Array*>(obj.get())->appendItem(item);
  967 + array->appendItem(item);
905 968 } else {
906 969 typeWarning("array", "ignoring attempt to append item");
907 970 QTC::TC("qpdf", "QPDFObjectHandle array ignoring append item");
... ... @@ -918,10 +981,11 @@ QPDFObjectHandle::appendItemAndGetNew(QPDFObjectHandle const&amp; item)
918 981 void
919 982 QPDFObjectHandle::eraseItem(int at)
920 983 {
921   - if (isArray() && (at < getArrayNItems()) && (at >= 0)) {
922   - dynamic_cast<QPDF_Array*>(obj.get())->eraseItem(at);
  984 + auto array = asArray();
  985 + if (array && (at < array->getNItems()) && (at >= 0)) {
  986 + array->eraseItem(at);
923 987 } else {
924   - if (isArray()) {
  988 + if (array) {
925 989 objectWarning("ignoring attempt to erase out of bounds array item");
926 990 QTC::TC("qpdf", "QPDFObjectHandle erase array bounds");
927 991 } else {
... ... @@ -935,8 +999,9 @@ QPDFObjectHandle
935 999 QPDFObjectHandle::eraseItemAndGetOld(int at)
936 1000 {
937 1001 auto result = QPDFObjectHandle::newNull();
938   - if (isArray() && (at < getArrayNItems()) && (at >= 0)) {
939   - result = getArrayItem(at);
  1002 + auto array = asArray();
  1003 + if (array && (at < array->getNItems()) && (at >= 0)) {
  1004 + result = array->getItem(at);
940 1005 }
941 1006 eraseItem(at);
942 1007 return result;
... ... @@ -953,8 +1018,9 @@ QPDFObjectHandle::ditems()
953 1018 bool
954 1019 QPDFObjectHandle::hasKey(std::string const& key)
955 1020 {
956   - if (isDictionary()) {
957   - return dynamic_cast<QPDF_Dictionary*>(obj.get())->hasKey(key);
  1021 + auto dict = asDictionary();
  1022 + if (dict) {
  1023 + return dict->hasKey(key);
958 1024 } else {
959 1025 typeWarning(
960 1026 "dictionary", "returning false for a key containment request");
... ... @@ -967,15 +1033,16 @@ QPDFObjectHandle
967 1033 QPDFObjectHandle::getKey(std::string const& key)
968 1034 {
969 1035 QPDFObjectHandle result;
970   - if (isDictionary()) {
971   - result = dynamic_cast<QPDF_Dictionary*>(obj.get())->getKey(key);
  1036 + auto dict = asDictionary();
  1037 + if (dict) {
  1038 + result = dict->getKey(key);
972 1039 } else {
973 1040 typeWarning("dictionary", "returning null for attempted key retrieval");
974 1041 QTC::TC("qpdf", "QPDFObjectHandle dictionary null for getKey");
975 1042 result = newNull();
976 1043 QPDF* qpdf = nullptr;
977 1044 std::string description;
978   - if (this->obj->getDescription(qpdf, description)) {
  1045 + if (obj->getDescription(qpdf, description)) {
979 1046 result.setObjectDescription(
980 1047 qpdf,
981 1048 (description + " -> null returned from getting key " + key +
... ... @@ -995,8 +1062,9 @@ std::set&lt;std::string&gt;
995 1062 QPDFObjectHandle::getKeys()
996 1063 {
997 1064 std::set<std::string> result;
998   - if (isDictionary()) {
999   - result = dynamic_cast<QPDF_Dictionary*>(obj.get())->getKeys();
  1065 + auto dict = asDictionary();
  1066 + if (dict) {
  1067 + result = dict->getKeys();
1000 1068 } else {
1001 1069 typeWarning("dictionary", "treating as empty");
1002 1070 QTC::TC("qpdf", "QPDFObjectHandle dictionary empty set for getKeys");
... ... @@ -1008,8 +1076,9 @@ std::map&lt;std::string, QPDFObjectHandle&gt;
1008 1076 QPDFObjectHandle::getDictAsMap()
1009 1077 {
1010 1078 std::map<std::string, QPDFObjectHandle> result;
1011   - if (isDictionary()) {
1012   - result = dynamic_cast<QPDF_Dictionary*>(obj.get())->getAsMap();
  1079 + auto dict = asDictionary();
  1080 + if (dict) {
  1081 + result = dict->getAsMap();
1013 1082 } else {
1014 1083 typeWarning("dictionary", "treating as empty");
1015 1084 QTC::TC("qpdf", "QPDFObjectHandle dictionary empty map for asMap");
... ... @@ -1200,9 +1269,10 @@ void
1200 1269 QPDFObjectHandle::replaceKey(
1201 1270 std::string const& key, QPDFObjectHandle const& value)
1202 1271 {
1203   - if (isDictionary()) {
  1272 + auto dict = asDictionary();
  1273 + if (dict) {
1204 1274 checkOwnership(value);
1205   - dynamic_cast<QPDF_Dictionary*>(obj.get())->replaceKey(key, value);
  1275 + dict->replaceKey(key, value);
1206 1276 } else {
1207 1277 typeWarning("dictionary", "ignoring key replacement request");
1208 1278 QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring replaceKey");
... ... @@ -1229,8 +1299,9 @@ QPDFObjectHandle::replaceKeyAndGetOld(
1229 1299 void
1230 1300 QPDFObjectHandle::removeKey(std::string const& key)
1231 1301 {
1232   - if (isDictionary()) {
1233   - dynamic_cast<QPDF_Dictionary*>(obj.get())->removeKey(key);
  1302 + auto dict = asDictionary();
  1303 + if (dict) {
  1304 + dict->removeKey(key);
1234 1305 } else {
1235 1306 typeWarning("dictionary", "ignoring key removal request");
1236 1307 QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring removeKey");
... ... @@ -1241,8 +1312,9 @@ QPDFObjectHandle
1241 1312 QPDFObjectHandle::removeKeyAndGetOld(std::string const& key)
1242 1313 {
1243 1314 auto result = QPDFObjectHandle::newNull();
1244   - if (isDictionary()) {
1245   - result = getKey(key);
  1315 + auto dict = asDictionary();
  1316 + if (dict) {
  1317 + result = dict->getKey(key);
1246 1318 }
1247 1319 removeKey(key);
1248 1320 return result;
... ... @@ -1259,50 +1331,43 @@ QPDFObjectHandle::replaceOrRemoveKey(
1259 1331 QPDFObjectHandle
1260 1332 QPDFObjectHandle::getDict()
1261 1333 {
1262   - assertStream();
1263   - return dynamic_cast<QPDF_Stream*>(obj.get())->getDict();
  1334 + return asStreamWithAssert()->getDict();
1264 1335 }
1265 1336  
1266 1337 void
1267 1338 QPDFObjectHandle::setFilterOnWrite(bool val)
1268 1339 {
1269   - assertStream();
1270   - dynamic_cast<QPDF_Stream*>(obj.get())->setFilterOnWrite(val);
  1340 + asStreamWithAssert()->setFilterOnWrite(val);
1271 1341 }
1272 1342  
1273 1343 bool
1274 1344 QPDFObjectHandle::getFilterOnWrite()
1275 1345 {
1276   - assertStream();
1277   - return dynamic_cast<QPDF_Stream*>(obj.get())->getFilterOnWrite();
  1346 + return asStreamWithAssert()->getFilterOnWrite();
1278 1347 }
1279 1348  
1280 1349 bool
1281 1350 QPDFObjectHandle::isDataModified()
1282 1351 {
1283   - assertStream();
1284   - return dynamic_cast<QPDF_Stream*>(obj.get())->isDataModified();
  1352 + return asStreamWithAssert()->isDataModified();
1285 1353 }
1286 1354  
1287 1355 void
1288 1356 QPDFObjectHandle::replaceDict(QPDFObjectHandle const& new_dict)
1289 1357 {
1290   - assertStream();
1291   - dynamic_cast<QPDF_Stream*>(obj.get())->replaceDict(new_dict);
  1358 + asStreamWithAssert()->replaceDict(new_dict);
1292 1359 }
1293 1360  
1294 1361 std::shared_ptr<Buffer>
1295 1362 QPDFObjectHandle::getStreamData(qpdf_stream_decode_level_e level)
1296 1363 {
1297   - assertStream();
1298   - return dynamic_cast<QPDF_Stream*>(obj.get())->getStreamData(level);
  1364 + return asStreamWithAssert()->getStreamData(level);
1299 1365 }
1300 1366  
1301 1367 std::shared_ptr<Buffer>
1302 1368 QPDFObjectHandle::getRawStreamData()
1303 1369 {
1304   - assertStream();
1305   - return dynamic_cast<QPDF_Stream*>(obj.get())->getRawStreamData();
  1370 + return asStreamWithAssert()->getRawStreamData();
1306 1371 }
1307 1372  
1308 1373 bool
... ... @@ -1314,8 +1379,7 @@ QPDFObjectHandle::pipeStreamData(
1314 1379 bool suppress_warnings,
1315 1380 bool will_retry)
1316 1381 {
1317   - assertStream();
1318   - return dynamic_cast<QPDF_Stream*>(obj.get())->pipeStreamData(
  1382 + return asStreamWithAssert()->pipeStreamData(
1319 1383 p,
1320 1384 filtering_attempted,
1321 1385 encode_flags,
... ... @@ -1332,9 +1396,8 @@ QPDFObjectHandle::pipeStreamData(
1332 1396 bool suppress_warnings,
1333 1397 bool will_retry)
1334 1398 {
1335   - assertStream();
1336 1399 bool filtering_attempted;
1337   - dynamic_cast<QPDF_Stream*>(obj.get())->pipeStreamData(
  1400 + asStreamWithAssert()->pipeStreamData(
1338 1401 p,
1339 1402 &filtering_attempted,
1340 1403 encode_flags,
... ... @@ -1368,9 +1431,7 @@ QPDFObjectHandle::replaceStreamData(
1368 1431 QPDFObjectHandle const& filter,
1369 1432 QPDFObjectHandle const& decode_parms)
1370 1433 {
1371   - assertStream();
1372   - dynamic_cast<QPDF_Stream*>(obj.get())->replaceStreamData(
1373   - data, filter, decode_parms);
  1434 + asStreamWithAssert()->replaceStreamData(data, filter, decode_parms);
1374 1435 }
1375 1436  
1376 1437 void
... ... @@ -1379,14 +1440,12 @@ QPDFObjectHandle::replaceStreamData(
1379 1440 QPDFObjectHandle const& filter,
1380 1441 QPDFObjectHandle const& decode_parms)
1381 1442 {
1382   - assertStream();
1383 1443 auto b = std::make_shared<Buffer>(data.length());
1384 1444 unsigned char* bp = b->getBuffer();
1385 1445 if (bp) {
1386 1446 memcpy(bp, data.c_str(), data.length());
1387 1447 }
1388   - dynamic_cast<QPDF_Stream*>(obj.get())->replaceStreamData(
1389   - b, filter, decode_parms);
  1448 + asStreamWithAssert()->replaceStreamData(b, filter, decode_parms);
1390 1449 }
1391 1450  
1392 1451 void
... ... @@ -1395,9 +1454,7 @@ QPDFObjectHandle::replaceStreamData(
1395 1454 QPDFObjectHandle const& filter,
1396 1455 QPDFObjectHandle const& decode_parms)
1397 1456 {
1398   - assertStream();
1399   - dynamic_cast<QPDF_Stream*>(obj.get())->replaceStreamData(
1400   - provider, filter, decode_parms);
  1457 + asStreamWithAssert()->replaceStreamData(provider, filter, decode_parms);
1401 1458 }
1402 1459  
1403 1460 namespace
... ... @@ -1446,11 +1503,9 @@ QPDFObjectHandle::replaceStreamData(
1446 1503 QPDFObjectHandle const& filter,
1447 1504 QPDFObjectHandle const& decode_parms)
1448 1505 {
1449   - assertStream();
1450 1506 auto sdp =
1451 1507 std::shared_ptr<StreamDataProvider>(new FunctionProvider(provider));
1452   - dynamic_cast<QPDF_Stream*>(obj.get())->replaceStreamData(
1453   - sdp, filter, decode_parms);
  1508 + asStreamWithAssert()->replaceStreamData(sdp, filter, decode_parms);
1454 1509 }
1455 1510  
1456 1511 void
... ... @@ -1459,11 +1514,9 @@ QPDFObjectHandle::replaceStreamData(
1459 1514 QPDFObjectHandle const& filter,
1460 1515 QPDFObjectHandle const& decode_parms)
1461 1516 {
1462   - assertStream();
1463 1517 auto sdp =
1464 1518 std::shared_ptr<StreamDataProvider>(new FunctionProvider(provider));
1465   - dynamic_cast<QPDF_Stream*>(obj.get())->replaceStreamData(
1466   - sdp, filter, decode_parms);
  1519 + asStreamWithAssert()->replaceStreamData(sdp, filter, decode_parms);
1467 1520 }
1468 1521  
1469 1522 std::map<std::string, QPDFObjectHandle>
... ... @@ -1478,10 +1531,11 @@ QPDFObjectHandle::arrayOrStreamToStreamArray(
1478 1531 {
1479 1532 all_description = description;
1480 1533 std::vector<QPDFObjectHandle> result;
1481   - if (isArray()) {
1482   - int n_items = getArrayNItems();
  1534 + auto array = asArray();
  1535 + if (array) {
  1536 + int n_items = array->getNItems();
1483 1537 for (int i = 0; i < n_items; ++i) {
1484   - QPDFObjectHandle item = getArrayItem(i);
  1538 + QPDFObjectHandle item = array->getItem(i);
1485 1539 if (item.isStream()) {
1486 1540 result.push_back(item);
1487 1541 } else {
... ... @@ -1648,18 +1702,16 @@ QPDFObjectHandle::unparseResolved()
1648 1702 if (!dereference()) {
1649 1703 throw std::logic_error(
1650 1704 "attempted to dereference an uninitialized QPDFObjectHandle");
1651   - } else if (this->reserved) {
1652   - throw std::logic_error(
1653   - "QPDFObjectHandle: attempting to unparse a reserved object");
1654 1705 }
1655   - return this->obj->unparse();
  1706 + return obj->unparse();
1656 1707 }
1657 1708  
1658 1709 std::string
1659 1710 QPDFObjectHandle::unparseBinary()
1660 1711 {
1661   - if (this->isString()) {
1662   - return dynamic_cast<QPDF_String*>(this->obj.get())->unparse(true);
  1712 + auto str = asString();
  1713 + if (str) {
  1714 + return str->unparse(true);
1663 1715 } else {
1664 1716 return unparse();
1665 1717 }
... ... @@ -1675,16 +1727,13 @@ QPDFObjectHandle::getJSON(bool dereference_indirect)
1675 1727 JSON
1676 1728 QPDFObjectHandle::getJSON(int json_version, bool dereference_indirect)
1677 1729 {
1678   - if ((!dereference_indirect) && this->isIndirect()) {
  1730 + if ((!dereference_indirect) && isIndirect()) {
1679 1731 return JSON::makeString(unparse());
1680 1732 } else if (!dereference()) {
1681 1733 throw std::logic_error(
1682 1734 "attempted to dereference an uninitialized QPDFObjectHandle");
1683   - } else if (this->reserved) {
1684   - throw std::logic_error(
1685   - "QPDFObjectHandle: attempting to unparse a reserved object");
1686 1735 } else {
1687   - return this->obj->getJSON(json_version);
  1736 + return obj->getJSON(json_version);
1688 1737 }
1689 1738 }
1690 1739  
... ... @@ -1696,8 +1745,7 @@ QPDFObjectHandle::getStreamJSON(
1696 1745 Pipeline* p,
1697 1746 std::string const& data_filename)
1698 1747 {
1699   - assertStream();
1700   - return dynamic_cast<QPDF_Stream*>(obj.get())->getStreamJSON(
  1748 + return asStreamWithAssert()->getStreamJSON(
1701 1749 json_version, json_data, decode_level, p, data_filename);
1702 1750 }
1703 1751  
... ... @@ -1917,8 +1965,7 @@ QPDFObjectHandle::addContentTokenFilter(std::shared_ptr&lt;TokenFilter&gt; filter)
1917 1965 void
1918 1966 QPDFObjectHandle::addTokenFilter(std::shared_ptr<TokenFilter> filter)
1919 1967 {
1920   - assertStream();
1921   - return dynamic_cast<QPDF_Stream*>(obj.get())->addTokenFilter(filter);
  1968 + return asStreamWithAssert()->addTokenFilter(filter);
1922 1969 }
1923 1970  
1924 1971 QPDFObjectHandle
... ... @@ -1945,21 +1992,6 @@ QPDFObjectHandle::getParsedOffset()
1945 1992 }
1946 1993  
1947 1994 QPDFObjectHandle
1948   -QPDFObjectHandle::newIndirect(QPDF* qpdf, QPDFObjGen const& og)
1949   -{
1950   - if (!og.isIndirect()) {
1951   - // Special case: QPDF uses objid 0 as a sentinel for direct
1952   - // objects, and the PDF specification doesn't allow for object
1953   - // 0. Treat indirect references to object 0 as null so that we
1954   - // never create an indirect object with objid 0.
1955   - QTC::TC("qpdf", "QPDFObjectHandle indirect with 0 objid");
1956   - return newNull();
1957   - }
1958   -
1959   - return QPDFObjectHandle(qpdf, og);
1960   -}
1961   -
1962   -QPDFObjectHandle
1963 1995 QPDFObjectHandle::newBool(bool value)
1964 1996 {
1965 1997 return QPDFObjectHandle(QPDF_Bool::create(value));
... ... @@ -2128,8 +2160,7 @@ QPDFObjectHandle::newStream(QPDF* qpdf)
2128 2160 QPDFObjectHandle stream_dict = newDictionary();
2129 2161 QPDFObjectHandle result = qpdf->makeIndirectObject(QPDFObjectHandle(
2130 2162 QPDF_Stream::create(qpdf, QPDFObjGen(), stream_dict, 0, 0)));
2131   - result.dereference();
2132   - QPDF_Stream* stream = dynamic_cast<QPDF_Stream*>(result.obj.get());
  2163 + auto stream = result.asStream();
2133 2164 stream->setObjGen(result.getObjGen());
2134 2165 return result;
2135 2166 }
... ... @@ -2155,18 +2186,7 @@ QPDFObjectHandle::newStream(QPDF* qpdf, std::string const&amp; data)
2155 2186 QPDFObjectHandle
2156 2187 QPDFObjectHandle::newReserved(QPDF* qpdf)
2157 2188 {
2158   - // Reserve a spot for this object by assigning it an object
2159   - // number, but then return an unresolved handle to the object.
2160   - QPDFObjectHandle reserved = qpdf->makeIndirectObject(makeReserved());
2161   - QPDFObjectHandle result = newIndirect(qpdf, reserved.getObjGen());
2162   - result.reserved = true;
2163   - return result;
2164   -}
2165   -
2166   -QPDFObjectHandle
2167   -QPDFObjectHandle::makeReserved()
2168   -{
2169   - return QPDFObjectHandle(QPDF_Reserved::create());
  2189 + return qpdf->makeIndirectObject(QPDFObjectHandle(QPDF_Reserved::create()));
2170 2190 }
2171 2191  
2172 2192 void
... ... @@ -2212,12 +2232,7 @@ QPDFObjectHandle::shallowCopyInternal(
2212 2232 QTC::TC("qpdf", "QPDFObjectHandle ERR shallow copy stream");
2213 2233 throw std::runtime_error("attempt to make a shallow copy of a stream");
2214 2234 }
2215   -
2216   - if (isArray() || isDictionary()) {
2217   - new_obj = QPDFObjectHandle(obj->shallowCopy());
2218   - } else {
2219   - new_obj = *this;
2220   - }
  2235 + new_obj = QPDFObjectHandle(obj->shallowCopy());
2221 2236  
2222 2237 std::set<QPDFObjGen> visited;
2223 2238 new_obj.copyObject(visited, false, first_level_only, false);
... ... @@ -2258,9 +2273,6 @@ QPDFObjectHandle::copyObject(
2258 2273 " reserved object handle direct");
2259 2274 }
2260 2275  
2261   - qpdf = nullptr;
2262   - og = QPDFObjGen();
2263   -
2264 2276 std::shared_ptr<QPDFObject> new_obj;
2265 2277  
2266 2278 if (isBool() || isInteger() || isName() || isNull() || isReal() ||
... ... @@ -2268,9 +2280,10 @@ QPDFObjectHandle::copyObject(
2268 2280 new_obj = obj->shallowCopy();
2269 2281 } else if (isArray()) {
2270 2282 std::vector<QPDFObjectHandle> items;
2271   - int n = getArrayNItems();
  2283 + auto array = asArray();
  2284 + int n = array->getNItems();
2272 2285 for (int i = 0; i < n; ++i) {
2273   - items.push_back(getArrayItem(i));
  2286 + items.push_back(array->getItem(i));
2274 2287 if ((!first_level_only) &&
2275 2288 (cross_indirect || (!items.back().isIndirect()))) {
2276 2289 items.back().copyObject(
... ... @@ -2280,8 +2293,9 @@ QPDFObjectHandle::copyObject(
2280 2293 new_obj = QPDF_Array::create(items);
2281 2294 } else if (isDictionary()) {
2282 2295 std::map<std::string, QPDFObjectHandle> items;
  2296 + auto dict = asDictionary();
2283 2297 for (auto const& key: getKeys()) {
2284   - items[key] = getKey(key);
  2298 + items[key] = dict->getKey(key);
2285 2299 if ((!first_level_only) &&
2286 2300 (cross_indirect || (!items[key].isIndirect()))) {
2287 2301 items[key].copyObject(
... ... @@ -2329,7 +2343,7 @@ QPDFObjectHandle::makeDirect(bool allow_streams)
2329 2343 void
2330 2344 QPDFObjectHandle::assertInitialized() const
2331 2345 {
2332   - if (!this->initialized) {
  2346 + if (!isInitialized()) {
2333 2347 throw std::logic_error("operation attempted on uninitialized "
2334 2348 "QPDFObjectHandle");
2335 2349 }
... ... @@ -2544,8 +2558,9 @@ QPDFObjectHandle::isImage(bool exclude_imagemask)
2544 2558 void
2545 2559 QPDFObjectHandle::checkOwnership(QPDFObjectHandle const& item) const
2546 2560 {
2547   - if ((this->qpdf != nullptr) && (item.qpdf != nullptr) &&
2548   - (this->qpdf != item.qpdf)) {
  2561 + auto qpdf = getOwningQPDF();
  2562 + auto item_qpdf = item.getOwningQPDF();
  2563 + if ((qpdf != nullptr) && (item_qpdf != nullptr) && (qpdf != item_qpdf)) {
2549 2564 QTC::TC("qpdf", "QPDFObjectHandle check ownership");
2550 2565 throw std::logic_error(
2551 2566 "Attempting to add an object from a different QPDF."
... ... @@ -2564,28 +2579,10 @@ QPDFObjectHandle::assertPageObject()
2564 2579 bool
2565 2580 QPDFObjectHandle::dereference()
2566 2581 {
2567   - if (!this->initialized) {
  2582 + if (!isInitialized()) {
2568 2583 return false;
2569 2584 }
2570   - if (this->obj.get() && getObjectID() &&
2571   - QPDF::Resolver::objectChanged(this->qpdf, getObjGen(), this->obj)) {
2572   - this->obj = nullptr;
2573   - }
2574   - if (this->obj == nullptr) {
2575   - std::shared_ptr<QPDFObject> obj =
2576   - QPDF::Resolver::resolve(this->qpdf, getObjGen());
2577   - if (obj == nullptr) {
2578   - // QPDF::resolve never returns an uninitialized object, but
2579   - // check just in case.
2580   - this->obj = QPDF_Null::create();
2581   - } else if (dynamic_cast<QPDF_Reserved*>(obj.get())) {
2582   - // Do not resolve
2583   - this->reserved = true;
2584   - } else {
2585   - this->reserved = false;
2586   - this->obj = obj;
2587   - }
2588   - }
  2585 + this->obj->resolve();
2589 2586 return true;
2590 2587 }
2591 2588  
... ...
libqpdf/QPDFParser.cc
1 1 #include <qpdf/QPDFParser.hh>
2 2  
3 3 #include <qpdf/QPDF.hh>
  4 +#include <qpdf/QPDFObjGen.hh>
4 5 #include <qpdf/QPDFObjectHandle.hh>
5   -#include <qpdf/QPDF_Array.hh>
6 6 #include <qpdf/QTC.hh>
7 7 #include <qpdf/QUtil.hh>
8 8  
... ... @@ -55,6 +55,7 @@ QPDFParser::parse(bool&amp; empty, bool content_stream)
55 55  
56 56 while (!done) {
57 57 bool bad = false;
  58 + bool indirect_ref = false;
58 59 is_null = false;
59 60 auto& frame = stack.back();
60 61 auto& olist = frame.olist;
... ... @@ -185,12 +186,16 @@ QPDFParser::parse(bool&amp; empty, bool content_stream)
185 186 "QPDFObjectHandle::parse called without context"
186 187 " on an object with indirect references");
187 188 }
188   - // Try to resolve indirect objects
189   - object = QPDFObjectHandle::newIndirect(
190   - context,
191   - QPDFObjGen(
192   - olist.at(size - 2).getIntValueAsInt(),
193   - olist.back().getIntValueAsInt()));
  189 + auto ref_og = QPDFObjGen(
  190 + olist.at(size - 2).getIntValueAsInt(),
  191 + olist.back().getIntValueAsInt());
  192 + if (ref_og.isIndirect()) {
  193 + object = context->getObject(ref_og);
  194 + indirect_ref = true;
  195 + } else {
  196 + QTC::TC("qpdf", "QPDFParser indirect with 0 objid");
  197 + is_null = true;
  198 + }
194 199 olist.pop_back();
195 200 olist.pop_back();
196 201 } else if ((value == "endobj") && (state == st_top)) {
... ... @@ -274,8 +279,8 @@ QPDFParser::parse(bool&amp; empty, bool content_stream)
274 279  
275 280 case st_dictionary:
276 281 case st_array:
277   - if (!object.isDirectNull()) {
278   - // No need to set description for direct nulls- they will
  282 + if (!indirect_ref && !object.isDirectNull()) {
  283 + // No need to set description for direct nulls - they will
279 284 // become implicit.
280 285 setDescriptionFromInput(object, input->getLastOffset());
281 286 object.setParsedOffset(input->getLastOffset());
... ...
libqpdf/QPDFValue.cc 0 → 100644
  1 +#include <qpdf/QPDFValue.hh>
  2 +
  3 +#include <qpdf/QPDFObject.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 +}
... ...
libqpdf/QPDF_Array.cc
... ... @@ -4,12 +4,14 @@
4 4 #include <qpdf/QUtil.hh>
5 5 #include <stdexcept>
6 6  
7   -QPDF_Array::QPDF_Array(std::vector<QPDFObjectHandle> const& v)
  7 +QPDF_Array::QPDF_Array(std::vector<QPDFObjectHandle> const& v) :
  8 + QPDFValue(::ot_array, "array")
8 9 {
9 10 setFromVector(v);
10 11 }
11 12  
12 13 QPDF_Array::QPDF_Array(SparseOHArray const& items) :
  14 + QPDFValue(::ot_array, "array"),
13 15 elements(items)
14 16 {
15 17 }
... ... @@ -62,18 +64,6 @@ QPDF_Array::getJSON(int json_version)
62 64 return j;
63 65 }
64 66  
65   -QPDFObject::object_type_e
66   -QPDF_Array::getTypeCode() const
67   -{
68   - return QPDFObject::ot_array;
69   -}
70   -
71   -char const*
72   -QPDF_Array::getTypeName() const
73   -{
74   - return "array";
75   -}
76   -
77 67 int
78 68 QPDF_Array::getNItems() const
79 69 {
... ...
libqpdf/QPDF_Bool.cc
1 1 #include <qpdf/QPDF_Bool.hh>
2 2  
3 3 QPDF_Bool::QPDF_Bool(bool val) :
  4 + QPDFValue(::ot_boolean, "boolean"),
4 5 val(val)
5 6 {
6 7 }
... ... @@ -29,18 +30,6 @@ QPDF_Bool::getJSON(int json_version)
29 30 return JSON::makeBool(this->val);
30 31 }
31 32  
32   -QPDFObject::object_type_e
33   -QPDF_Bool::getTypeCode() const
34   -{
35   - return QPDFObject::ot_boolean;
36   -}
37   -
38   -char const*
39   -QPDF_Bool::getTypeName() const
40   -{
41   - return "boolean";
42   -}
43   -
44 33 bool
45 34 QPDF_Bool::getVal() const
46 35 {
... ...
libqpdf/QPDF_Dictionary.cc
1 1 #include <qpdf/QPDF_Dictionary.hh>
2 2  
3 3 #include <qpdf/QPDF_Name.hh>
4   -#include <qpdf/QPDF_Null.hh>
5 4  
6 5 QPDF_Dictionary::QPDF_Dictionary(
7 6 std::map<std::string, QPDFObjectHandle> const& items) :
  7 + QPDFValue(::ot_dictionary, "dictionary"),
8 8 items(items)
9 9 {
10 10 }
... ... @@ -58,18 +58,6 @@ QPDF_Dictionary::getJSON(int json_version)
58 58 return j;
59 59 }
60 60  
61   -QPDFObject::object_type_e
62   -QPDF_Dictionary::getTypeCode() const
63   -{
64   - return QPDFObject::ot_dictionary;
65   -}
66   -
67   -char const*
68   -QPDF_Dictionary::getTypeName() const
69   -{
70   - return "dictionary";
71   -}
72   -
73 61 bool
74 62 QPDF_Dictionary::hasKey(std::string const& key)
75 63 {
... ...
libqpdf/QPDF_InlineImage.cc
1 1 #include <qpdf/QPDF_InlineImage.hh>
2 2  
3 3 QPDF_InlineImage::QPDF_InlineImage(std::string const& val) :
  4 + QPDFValue(::ot_inlineimage, "inline-image"),
4 5 val(val)
5 6 {
6 7 }
... ... @@ -29,18 +30,6 @@ QPDF_InlineImage::getJSON(int json_version)
29 30 return JSON::makeNull();
30 31 }
31 32  
32   -QPDFObject::object_type_e
33   -QPDF_InlineImage::getTypeCode() const
34   -{
35   - return QPDFObject::ot_inlineimage;
36   -}
37   -
38   -char const*
39   -QPDF_InlineImage::getTypeName() const
40   -{
41   - return "inline-image";
42   -}
43   -
44 33 std::string
45 34 QPDF_InlineImage::getVal() const
46 35 {
... ...
libqpdf/QPDF_Integer.cc
... ... @@ -3,6 +3,7 @@
3 3 #include <qpdf/QUtil.hh>
4 4  
5 5 QPDF_Integer::QPDF_Integer(long long val) :
  6 + QPDFValue(::ot_integer, "integer"),
6 7 val(val)
7 8 {
8 9 }
... ... @@ -31,18 +32,6 @@ QPDF_Integer::getJSON(int json_version)
31 32 return JSON::makeInt(this->val);
32 33 }
33 34  
34   -QPDFObject::object_type_e
35   -QPDF_Integer::getTypeCode() const
36   -{
37   - return QPDFObject::ot_integer;
38   -}
39   -
40   -char const*
41   -QPDF_Integer::getTypeName() const
42   -{
43   - return "integer";
44   -}
45   -
46 35 long long
47 36 QPDF_Integer::getVal() const
48 37 {
... ...
libqpdf/QPDF_Name.cc
... ... @@ -5,6 +5,7 @@
5 5 #include <string.h>
6 6  
7 7 QPDF_Name::QPDF_Name(std::string const& name) :
  8 + QPDFValue(::ot_name, "name"),
8 9 name(name)
9 10 {
10 11 }
... ... @@ -61,18 +62,6 @@ QPDF_Name::getJSON(int json_version)
61 62 }
62 63 }
63 64  
64   -QPDFObject::object_type_e
65   -QPDF_Name::getTypeCode() const
66   -{
67   - return QPDFObject::ot_name;
68   -}
69   -
70   -char const*
71   -QPDF_Name::getTypeName() const
72   -{
73   - return "name";
74   -}
75   -
76 65 std::string
77 66 QPDF_Name::getName() const
78 67 {
... ...
libqpdf/QPDF_Null.cc
1 1 #include <qpdf/QPDF_Null.hh>
2 2  
  3 +QPDF_Null::QPDF_Null() :
  4 + QPDFValue(::ot_null, "null")
  5 +{
  6 +}
  7 +
3 8 std::shared_ptr<QPDFObject>
4 9 QPDF_Null::create()
5 10 {
... ... @@ -23,15 +28,3 @@ QPDF_Null::getJSON(int json_version)
23 28 {
24 29 return JSON::makeNull();
25 30 }
26   -
27   -QPDFObject::object_type_e
28   -QPDF_Null::getTypeCode() const
29   -{
30   - return QPDFObject::ot_null;
31   -}
32   -
33   -char const*
34   -QPDF_Null::getTypeName() const
35   -{
36   - return "null";
37   -}
... ...
libqpdf/QPDF_Operator.cc
1 1 #include <qpdf/QPDF_Operator.hh>
2 2  
3 3 QPDF_Operator::QPDF_Operator(std::string const& val) :
  4 + QPDFValue(::ot_operator, "operator"),
4 5 val(val)
5 6 {
6 7 }
... ... @@ -20,7 +21,7 @@ QPDF_Operator::shallowCopy()
20 21 std::string
21 22 QPDF_Operator::unparse()
22 23 {
23   - return this->val;
  24 + return val;
24 25 }
25 26  
26 27 JSON
... ... @@ -29,18 +30,6 @@ QPDF_Operator::getJSON(int json_version)
29 30 return JSON::makeNull();
30 31 }
31 32  
32   -QPDFObject::object_type_e
33   -QPDF_Operator::getTypeCode() const
34   -{
35   - return QPDFObject::ot_operator;
36   -}
37   -
38   -char const*
39   -QPDF_Operator::getTypeName() const
40   -{
41   - return "operator";
42   -}
43   -
44 33 std::string
45 34 QPDF_Operator::getVal() const
46 35 {
... ...
libqpdf/QPDF_Real.cc
... ... @@ -3,12 +3,14 @@
3 3 #include <qpdf/QUtil.hh>
4 4  
5 5 QPDF_Real::QPDF_Real(std::string const& val) :
  6 + QPDFValue(::ot_real, "real"),
6 7 val(val)
7 8 {
8 9 }
9 10  
10 11 QPDF_Real::QPDF_Real(
11 12 double value, int decimal_places, bool trim_trailing_zeroes) :
  13 + QPDFValue(::ot_real, "real"),
12 14 val(QUtil::double_to_string(value, decimal_places, trim_trailing_zeroes))
13 15 {
14 16 }
... ... @@ -60,18 +62,6 @@ QPDF_Real::getJSON(int json_version)
60 62 return JSON::makeNumber(result);
61 63 }
62 64  
63   -QPDFObject::object_type_e
64   -QPDF_Real::getTypeCode() const
65   -{
66   - return QPDFObject::ot_real;
67   -}
68   -
69   -char const*
70   -QPDF_Real::getTypeName() const
71   -{
72   - return "real";
73   -}
74   -
75 65 std::string
76 66 QPDF_Real::getVal()
77 67 {
... ...
libqpdf/QPDF_Reserved.cc
... ... @@ -2,6 +2,11 @@
2 2  
3 3 #include <stdexcept>
4 4  
  5 +QPDF_Reserved::QPDF_Reserved() :
  6 + QPDFValue(::ot_reserved, "reserved")
  7 +{
  8 +}
  9 +
5 10 std::shared_ptr<QPDFObject>
6 11 QPDF_Reserved::create()
7 12 {
... ... @@ -17,25 +22,15 @@ QPDF_Reserved::shallowCopy()
17 22 std::string
18 23 QPDF_Reserved::unparse()
19 24 {
20   - throw std::logic_error("attempt to unparse QPDF_Reserved");
  25 + throw std::logic_error(
  26 + "QPDFObjectHandle: attempting to unparse a reserved object");
21 27 return "";
22 28 }
23 29  
24 30 JSON
25 31 QPDF_Reserved::getJSON(int json_version)
26 32 {
27   - throw std::logic_error("attempt to generate JSON from QPDF_Reserved");
  33 + throw std::logic_error(
  34 + "QPDFObjectHandle: attempting to unparse a reserved object");
28 35 return JSON::makeNull();
29 36 }
30   -
31   -QPDFObject::object_type_e
32   -QPDF_Reserved::getTypeCode() const
33   -{
34   - return QPDFObject::ot_reserved;
35   -}
36   -
37   -char const*
38   -QPDF_Reserved::getTypeName() const
39   -{
40   - return "reserved";
41   -}
... ...
libqpdf/QPDF_Stream.cc
... ... @@ -114,6 +114,7 @@ QPDF_Stream::QPDF_Stream(
114 114 QPDFObjectHandle stream_dict,
115 115 qpdf_offset_t offset,
116 116 size_t length) :
  117 + QPDFValue(::ot_stream, "stream"),
117 118 qpdf(qpdf),
118 119 og(og),
119 120 filter_on_write(true),
... ... @@ -291,22 +292,10 @@ QPDF_Stream::getStreamJSON(
291 292 return result;
292 293 }
293 294  
294   -QPDFObject::object_type_e
295   -QPDF_Stream::getTypeCode() const
296   -{
297   - return QPDFObject::ot_stream;
298   -}
299   -
300   -char const*
301   -QPDF_Stream::getTypeName() const
302   -{
303   - return "stream";
304   -}
305   -
306 295 void
307 296 QPDF_Stream::setDescription(QPDF* qpdf, std::string const& description)
308 297 {
309   - this->QPDFObject::setDescription(qpdf, description);
  298 + this->QPDFValue::setDescription(qpdf, description);
310 299 setDictDescription();
311 300 }
312 301  
... ...
libqpdf/QPDF_String.cc
... ... @@ -21,6 +21,7 @@ is_iso_latin1_printable(char ch)
21 21 }
22 22  
23 23 QPDF_String::QPDF_String(std::string const& val) :
  24 + QPDFValue(::ot_string, "string"),
24 25 val(val)
25 26 {
26 27 }
... ... @@ -84,18 +85,6 @@ QPDF_String::getJSON(int json_version)
84 85 return JSON::makeString(result);
85 86 }
86 87  
87   -QPDFObject::object_type_e
88   -QPDF_String::getTypeCode() const
89   -{
90   - return QPDFObject::ot_string;
91   -}
92   -
93   -char const*
94   -QPDF_String::getTypeName() const
95   -{
96   - return "string";
97   -}
98   -
99 88 bool
100 89 QPDF_String::useHexString() const
101 90 {
... ...
libqpdf/QPDF_Unresolved.cc 0 → 100644
  1 +#include <qpdf/QPDF_Unresolved.hh>
  2 +
  3 +#include <stdexcept>
  4 +
  5 +QPDF_Unresolved::QPDF_Unresolved(QPDF* qpdf, QPDFObjGen const& og) :
  6 + QPDFValue(::ot_unresolved, "unresolved", qpdf, og)
  7 +{
  8 +}
  9 +
  10 +std::shared_ptr<QPDFObject>
  11 +QPDF_Unresolved::create(QPDF* qpdf, QPDFObjGen const& og)
  12 +{
  13 + return do_create(new QPDF_Unresolved(qpdf, og));
  14 +}
  15 +
  16 +std::shared_ptr<QPDFObject>
  17 +QPDF_Unresolved::shallowCopy()
  18 +{
  19 + throw std::logic_error(
  20 + "attempted to shallow copy unresolved QPDFObjectHandle");
  21 + return create(qpdf, og);
  22 +}
  23 +
  24 +std::string
  25 +QPDF_Unresolved::unparse()
  26 +{
  27 + throw std::logic_error(
  28 + "attempted to unparse an unresolved QPDFObjectHandle");
  29 + return "";
  30 +}
  31 +
  32 +JSON
  33 +QPDF_Unresolved::getJSON(int json_version)
  34 +{
  35 + return JSON::makeNull();
  36 +}
... ...
libqpdf/QPDF_linearization.cc
... ... @@ -137,8 +137,7 @@ QPDF::isLinearized()
137 137 return false;
138 138 }
139 139  
140   - QPDFObjectHandle candidate = QPDFObjectHandle::Factory::newIndirect(
141   - this, QPDFObjGen(lindict_obj, 0));
  140 + auto candidate = getObjectByID(lindict_obj, 0);
142 141 if (!candidate.isDictionary()) {
143 142 return false;
144 143 }
... ...
libqpdf/qpdf/QPDF_Array.hh
1 1 #ifndef QPDF_ARRAY_HH
2 2 #define QPDF_ARRAY_HH
3 3  
4   -#include <qpdf/QPDFObject.hh>
  4 +#include <qpdf/QPDFValue.hh>
5 5  
6 6 #include <qpdf/SparseOHArray.hh>
7 7 #include <list>
8 8 #include <vector>
9 9  
10   -class QPDF_Array: public QPDFObject
  10 +class QPDF_Array: public QPDFValue
11 11 {
12 12 public:
13 13 virtual ~QPDF_Array() = default;
... ... @@ -17,8 +17,6 @@ class QPDF_Array: public QPDFObject
17 17 virtual std::shared_ptr<QPDFObject> shallowCopy();
18 18 virtual std::string unparse();
19 19 virtual JSON getJSON(int json_version);
20   - virtual QPDFObject::object_type_e getTypeCode() const;
21   - virtual char const* getTypeName() const;
22 20  
23 21 int getNItems() const;
24 22 QPDFObjectHandle getItem(int n) const;
... ...
libqpdf/qpdf/QPDF_Bool.hh
1 1 #ifndef QPDF_BOOL_HH
2 2 #define QPDF_BOOL_HH
3 3  
4   -#include <qpdf/QPDFObject.hh>
  4 +#include <qpdf/QPDFValue.hh>
5 5  
6   -class QPDF_Bool: public QPDFObject
  6 +class QPDF_Bool: public QPDFValue
7 7 {
8 8 public:
9 9 virtual ~QPDF_Bool() = default;
... ... @@ -11,8 +11,6 @@ class QPDF_Bool: public QPDFObject
11 11 virtual std::shared_ptr<QPDFObject> shallowCopy();
12 12 virtual std::string unparse();
13 13 virtual JSON getJSON(int json_version);
14   - virtual QPDFObject::object_type_e getTypeCode() const;
15   - virtual char const* getTypeName() const;
16 14 bool getVal() const;
17 15  
18 16 private:
... ...
libqpdf/qpdf/QPDF_Dictionary.hh
1 1 #ifndef QPDF_DICTIONARY_HH
2 2 #define QPDF_DICTIONARY_HH
3 3  
4   -#include <qpdf/QPDFObject.hh>
  4 +#include <qpdf/QPDFValue.hh>
5 5  
6 6 #include <map>
7 7 #include <set>
8 8  
9 9 #include <qpdf/QPDFObjectHandle.hh>
10 10  
11   -class QPDF_Dictionary: public QPDFObject
  11 +class QPDF_Dictionary: public QPDFValue
12 12 {
13 13 public:
14 14 virtual ~QPDF_Dictionary() = default;
... ... @@ -17,8 +17,6 @@ class QPDF_Dictionary: public QPDFObject
17 17 virtual std::shared_ptr<QPDFObject> shallowCopy();
18 18 virtual std::string unparse();
19 19 virtual JSON getJSON(int json_version);
20   - virtual QPDFObject::object_type_e getTypeCode() const;
21   - virtual char const* getTypeName() const;
22 20  
23 21 // hasKey() and getKeys() treat keys with null values as if they
24 22 // aren't there. getKey() returns null for the value of a
... ...
libqpdf/qpdf/QPDF_InlineImage.hh
1 1 #ifndef QPDF_INLINEIMAGE_HH
2 2 #define QPDF_INLINEIMAGE_HH
3 3  
4   -#include <qpdf/QPDFObject.hh>
  4 +#include <qpdf/QPDFValue.hh>
5 5  
6   -class QPDF_InlineImage: public QPDFObject
  6 +class QPDF_InlineImage: public QPDFValue
7 7 {
8 8 public:
9 9 virtual ~QPDF_InlineImage() = default;
... ... @@ -11,8 +11,6 @@ class QPDF_InlineImage: public QPDFObject
11 11 virtual std::shared_ptr<QPDFObject> shallowCopy();
12 12 virtual std::string unparse();
13 13 virtual JSON getJSON(int json_version);
14   - virtual QPDFObject::object_type_e getTypeCode() const;
15   - virtual char const* getTypeName() const;
16 14 std::string getVal() const;
17 15  
18 16 private:
... ...
libqpdf/qpdf/QPDF_Integer.hh
1 1 #ifndef QPDF_INTEGER_HH
2 2 #define QPDF_INTEGER_HH
3 3  
4   -#include <qpdf/QPDFObject.hh>
  4 +#include <qpdf/QPDFValue.hh>
5 5  
6   -class QPDF_Integer: public QPDFObject
  6 +class QPDF_Integer: public QPDFValue
7 7 {
8 8 public:
9 9 virtual ~QPDF_Integer() = default;
... ... @@ -11,8 +11,6 @@ class QPDF_Integer: public QPDFObject
11 11 virtual std::shared_ptr<QPDFObject> shallowCopy();
12 12 virtual std::string unparse();
13 13 virtual JSON getJSON(int json_version);
14   - virtual QPDFObject::object_type_e getTypeCode() const;
15   - virtual char const* getTypeName() const;
16 14 long long getVal() const;
17 15  
18 16 private:
... ...
libqpdf/qpdf/QPDF_Name.hh
1 1 #ifndef QPDF_NAME_HH
2 2 #define QPDF_NAME_HH
3 3  
4   -#include <qpdf/QPDFObject.hh>
  4 +#include <qpdf/QPDFValue.hh>
5 5  
6   -class QPDF_Name: public QPDFObject
  6 +class QPDF_Name: public QPDFValue
7 7 {
8 8 public:
9 9 virtual ~QPDF_Name() = default;
... ... @@ -11,8 +11,6 @@ class QPDF_Name: public QPDFObject
11 11 virtual std::shared_ptr<QPDFObject> shallowCopy();
12 12 virtual std::string unparse();
13 13 virtual JSON getJSON(int json_version);
14   - virtual QPDFObject::object_type_e getTypeCode() const;
15   - virtual char const* getTypeName() const;
16 14 std::string getName() const;
17 15  
18 16 // Put # into strings with characters unsuitable for name token
... ...
libqpdf/qpdf/QPDF_Null.hh
1 1 #ifndef QPDF_NULL_HH
2 2 #define QPDF_NULL_HH
3 3  
4   -#include <qpdf/QPDFObject.hh>
  4 +#include <qpdf/QPDFValue.hh>
5 5  
6   -class QPDF_Null: public QPDFObject
  6 +class QPDF_Null: public QPDFValue
7 7 {
8 8 public:
9 9 virtual ~QPDF_Null() = default;
... ... @@ -11,11 +11,9 @@ class QPDF_Null: public QPDFObject
11 11 virtual std::shared_ptr<QPDFObject> shallowCopy();
12 12 virtual std::string unparse();
13 13 virtual JSON getJSON(int json_version);
14   - virtual QPDFObject::object_type_e getTypeCode() const;
15   - virtual char const* getTypeName() const;
16 14  
17 15 private:
18   - QPDF_Null() = default;
  16 + QPDF_Null();
19 17 };
20 18  
21 19 #endif // QPDF_NULL_HH
... ...
libqpdf/qpdf/QPDF_Operator.hh
1 1 #ifndef QPDF_OPERATOR_HH
2 2 #define QPDF_OPERATOR_HH
3 3  
4   -#include <qpdf/QPDFObject.hh>
  4 +#include <qpdf/QPDFValue.hh>
5 5  
6   -class QPDF_Operator: public QPDFObject
  6 +class QPDF_Operator: public QPDFValue
7 7 {
8 8 public:
9 9 virtual ~QPDF_Operator() = default;
... ... @@ -11,8 +11,6 @@ class QPDF_Operator: public QPDFObject
11 11 virtual std::shared_ptr<QPDFObject> shallowCopy();
12 12 virtual std::string unparse();
13 13 virtual JSON getJSON(int json_version);
14   - virtual QPDFObject::object_type_e getTypeCode() const;
15   - virtual char const* getTypeName() const;
16 14 std::string getVal() const;
17 15  
18 16 private:
... ...
libqpdf/qpdf/QPDF_Real.hh
1 1 #ifndef QPDF_REAL_HH
2 2 #define QPDF_REAL_HH
3 3  
4   -#include <qpdf/QPDFObject.hh>
  4 +#include <qpdf/QPDFValue.hh>
5 5  
6   -class QPDF_Real: public QPDFObject
  6 +class QPDF_Real: public QPDFValue
7 7 {
8 8 public:
9 9 virtual ~QPDF_Real() = default;
... ... @@ -13,8 +13,6 @@ class QPDF_Real: public QPDFObject
13 13 virtual std::shared_ptr<QPDFObject> shallowCopy();
14 14 virtual std::string unparse();
15 15 virtual JSON getJSON(int json_version);
16   - virtual QPDFObject::object_type_e getTypeCode() const;
17   - virtual char const* getTypeName() const;
18 16 std::string getVal();
19 17  
20 18 private:
... ...
libqpdf/qpdf/QPDF_Reserved.hh
1 1 #ifndef QPDF_RESERVED_HH
2 2 #define QPDF_RESERVED_HH
3 3  
4   -#include <qpdf/QPDFObject.hh>
  4 +#include <qpdf/QPDFValue.hh>
5 5  
6   -class QPDF_Reserved: public QPDFObject
  6 +class QPDF_Reserved: public QPDFValue
7 7 {
8 8 public:
9 9 virtual ~QPDF_Reserved() = default;
... ... @@ -11,11 +11,9 @@ class QPDF_Reserved: public QPDFObject
11 11 virtual std::shared_ptr<QPDFObject> shallowCopy();
12 12 virtual std::string unparse();
13 13 virtual JSON getJSON(int json_version);
14   - virtual QPDFObject::object_type_e getTypeCode() const;
15   - virtual char const* getTypeName() const;
16 14  
17 15 private:
18   - QPDF_Reserved() = default;
  16 + QPDF_Reserved();
19 17 };
20 18  
21 19 #endif // QPDF_RESERVED_HH
... ...
libqpdf/qpdf/QPDF_Stream.hh
... ... @@ -3,9 +3,9 @@
3 3  
4 4 #include <qpdf/Types.h>
5 5  
6   -#include <qpdf/QPDFObject.hh>
7 6 #include <qpdf/QPDFObjectHandle.hh>
8 7 #include <qpdf/QPDFStreamFilter.hh>
  8 +#include <qpdf/QPDFValue.hh>
9 9  
10 10 #include <functional>
11 11 #include <memory>
... ... @@ -13,7 +13,7 @@
13 13 class Pipeline;
14 14 class QPDF;
15 15  
16   -class QPDF_Stream: public QPDFObject
  16 +class QPDF_Stream: public QPDFValue
17 17 {
18 18 public:
19 19 virtual ~QPDF_Stream() = default;
... ... @@ -26,8 +26,6 @@ class QPDF_Stream: public QPDFObject
26 26 virtual std::shared_ptr<QPDFObject> shallowCopy();
27 27 virtual std::string unparse();
28 28 virtual JSON getJSON(int json_version);
29   - virtual QPDFObject::object_type_e getTypeCode() const;
30   - virtual char const* getTypeName() const;
31 29 virtual void setDescription(QPDF*, std::string const&);
32 30 QPDFObjectHandle getDict() const;
33 31 bool isDataModified() const;
... ...
libqpdf/qpdf/QPDF_String.hh
1 1 #ifndef QPDF_STRING_HH
2 2 #define QPDF_STRING_HH
3 3  
4   -#include <qpdf/QPDFObject.hh>
  4 +#include <qpdf/QPDFValue.hh>
5 5  
6 6 // QPDF_Strings may included embedded null characters.
7 7  
8   -class QPDF_String: public QPDFObject
  8 +class QPDF_String: public QPDFValue
9 9 {
10 10 friend class QPDFWriter;
11 11  
... ... @@ -16,8 +16,6 @@ class QPDF_String: public QPDFObject
16 16 create_utf16(std::string const& utf8_val);
17 17 virtual std::shared_ptr<QPDFObject> shallowCopy();
18 18 virtual std::string unparse();
19   - virtual QPDFObject::object_type_e getTypeCode() const;
20   - virtual char const* getTypeName() const;
21 19 std::string unparse(bool force_binary);
22 20 virtual JSON getJSON(int json_version);
23 21 std::string getVal() const;
... ...
libqpdf/qpdf/QPDF_Unresolved.hh 0 → 100644
  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 + virtual ~QPDF_Unresolved() = default;
  10 + static std::shared_ptr<QPDFObject> create(QPDF* qpdf, QPDFObjGen const& og);
  11 + virtual std::shared_ptr<QPDFObject> shallowCopy();
  12 + virtual std::string unparse();
  13 + virtual JSON getJSON(int json_version);
  14 +
  15 + private:
  16 + QPDF_Unresolved(QPDF* qpdf, QPDFObjGen const& og);
  17 +};
  18 +
  19 +#endif // QPDF_UNRESOLVED_HH
... ...
qpdf/qpdf.testcov
... ... @@ -254,7 +254,7 @@ QPDFWriter standard deterministic ID 1
254 254 QPDFWriter linearized deterministic ID 1
255 255 QPDFWriter deterministic with no data 0
256 256 qpdf-c called qpdf_set_deterministic_ID 0
257   -QPDFObjectHandle indirect with 0 objid 0
  257 +QPDFParser indirect with 0 objid 0
258 258 QPDF object id 0 0
259 259 QPDF recursion loop in resolve 0
260 260 QPDFParser treat word as string 0
... ...
qpdf/qtest/qpdf/issue-51.out
... ... @@ -9,7 +9,6 @@ WARNING: issue-51.pdf (object 2 0, offset 26): /Length key in stream dictionary
9 9 WARNING: issue-51.pdf (object 2 0, offset 71): attempting to recover stream length
10 10 WARNING: issue-51.pdf (object 2 0, offset 71): unable to recover stream data; treating stream as empty
11 11 WARNING: issue-51.pdf (object 2 0, offset 977): expected endobj
12   -WARNING: issue-51.pdf (object 2 0, offset 977): EOF after endobj
13 12 WARNING: issue-51.pdf (object 3 0): object has offset 0
14 13 WARNING: issue-51.pdf (object 4 0): object has offset 0
15 14 WARNING: issue-51.pdf (object 5 0): object has offset 0
... ...
qpdf/qtest/type-checks.test
... ... @@ -14,7 +14,7 @@ cleanup();
14 14  
15 15 my $td = new TestDriver('type-checks');
16 16  
17   -my $n_tests = 5;
  17 +my $n_tests = 6;
18 18  
19 19 # Whenever object-types.pdf is edited, object-types-os.pdf should be
20 20 # regenerated.
... ... @@ -43,6 +43,10 @@ $td-&gt;runtest(&quot;compound type checks&quot;,
43 43 {$td->COMMAND => "test_driver 82 object-types-os.pdf"},
44 44 {$td->STRING => "test 82 done\n", $td->EXIT_STATUS => 0},
45 45 $td->NORMALIZE_NEWLINES);
  46 +$td->runtest("indirect objects belonging to destroyed QPDF",
  47 + {$td->COMMAND => "test_driver 92 -"},
  48 + {$td->STRING => "test 92 done\n", $td->EXIT_STATUS => 0},
  49 + $td->NORMALIZE_NEWLINES);
46 50  
47 51 cleanup();
48 52 $td->report($n_tests);
... ...
qpdf/test_driver.cc
... ... @@ -3258,6 +3258,20 @@ test_91(QPDF&amp; pdf, char const* arg2)
3258 3258 2, &p, qpdf_dl_none, qpdf_sj_inline, "", std::set<std::string>());
3259 3259 }
3260 3260  
  3261 +static void
  3262 +test_92(QPDF& pdf, char const* arg2)
  3263 +{
  3264 + // Exercise indirect objects owned by destroyed QPDF object.
  3265 + QPDF* qpdf = new QPDF();
  3266 + qpdf->emptyPDF();
  3267 + auto root = qpdf->getRoot();
  3268 + assert(root.getOwningQPDF() != nullptr);
  3269 + assert(root.isIndirect());
  3270 + delete qpdf;
  3271 + assert(root.getOwningQPDF() == nullptr);
  3272 + assert(!root.isIndirect());
  3273 +}
  3274 +
3261 3275 void
3262 3276 runtest(int n, char const* filename1, char const* arg2)
3263 3277 {
... ... @@ -3265,7 +3279,7 @@ runtest(int n, char const* filename1, char const* arg2)
3265 3279 // the test suite to see how the test is invoked to find the file
3266 3280 // that the test is supposed to operate on.
3267 3281  
3268   - std::set<int> ignore_filename = {61, 81, 83, 84, 85, 86, 87};
  3282 + std::set<int> ignore_filename = {61, 81, 83, 84, 85, 86, 87, 92};
3269 3283  
3270 3284 if (n == 0) {
3271 3285 // Throw in some random test cases that don't fit anywhere
... ... @@ -3362,7 +3376,8 @@ runtest(int n, char const* filename1, char const* arg2)
3362 3376 {76, test_76}, {77, test_77}, {78, test_78}, {79, test_79},
3363 3377 {80, test_80}, {81, test_81}, {82, test_82}, {83, test_83},
3364 3378 {84, test_84}, {85, test_85}, {86, test_86}, {87, test_87},
3365   - {88, test_88}, {89, test_89}, {90, test_90}, {91, test_91}};
  3379 + {88, test_88}, {89, test_89}, {90, test_90}, {91, test_91},
  3380 + {92, test_92}};
3366 3381  
3367 3382 auto fn = test_functions.find(n);
3368 3383 if (fn == test_functions.end()) {
... ...