Commit 9641626cae44ed17ef4af4e72e89276065dd85ed

Authored by m-holger
1 parent ce5b864c

Refactor resolving of objects

include/qpdf/QPDF.hh
... ... @@ -792,12 +792,13 @@ class QPDF
792 792 class Resolver
793 793 {
794 794 friend class QPDFObject;
  795 + friend class QPDF_Unresolved;
795 796  
796 797 private:
797   - static void
798   - resolve(QPDF* qpdf, QPDFObjGen const& og)
  798 + static QPDFObject*
  799 + resolved(QPDF* qpdf, QPDFObjGen og)
799 800 {
800   - qpdf->resolve(og);
  801 + return qpdf->resolve(og);
801 802 }
802 803 };
803 804  
... ... @@ -1056,7 +1057,7 @@ class QPDF
1056 1057 QPDFObjGen exp_og,
1057 1058 QPDFObjGen& og,
1058 1059 bool skip_cache_if_in_xref);
1059   - void resolve(QPDFObjGen og);
  1060 + QPDFObject* resolve(QPDFObjGen og);
1060 1061 void resolveObjectsInStream(int obj_stream_number);
1061 1062 void stopOnError(std::string const& message);
1062 1063 QPDFObjectHandle reserveObjectIfNotExists(QPDFObjGen const& og);
... ...
include/qpdf/QPDFObjectHandle.hh
... ... @@ -1363,24 +1363,23 @@ class QPDFObjectHandle
1363 1363 void writeJSON(int json_version, JSON::Writer& p, bool dereference_indirect = false);
1364 1364  
1365 1365 private:
1366   - QPDF_Array* asArray();
1367   - QPDF_Bool* asBool();
1368   - QPDF_Dictionary* asDictionary();
1369   - QPDF_InlineImage* asInlineImage();
1370   - QPDF_Integer* asInteger();
1371   - QPDF_Name* asName();
1372   - QPDF_Null* asNull();
1373   - QPDF_Operator* asOperator();
1374   - QPDF_Real* asReal();
1375   - QPDF_Reserved* asReserved();
1376   - QPDF_Stream* asStream();
  1366 + QPDF_Array* asArray() const;
  1367 + QPDF_Bool* asBool() const;
  1368 + QPDF_Dictionary* asDictionary() const;
  1369 + QPDF_InlineImage* asInlineImage() const;
  1370 + QPDF_Integer* asInteger() const;
  1371 + QPDF_Name* asName() const;
  1372 + QPDF_Null* asNull() const;
  1373 + QPDF_Operator* asOperator() const;
  1374 + QPDF_Real* asReal() const;
  1375 + QPDF_Reserved* asReserved() const;
  1376 + QPDF_Stream* asStream() const;
1377 1377 QPDF_Stream* asStreamWithAssert();
1378   - QPDF_String* asString();
  1378 + QPDF_String* asString() const;
1379 1379  
1380 1380 void typeWarning(char const* expected_type, std::string const& warning);
1381 1381 void objectWarning(std::string const& warning);
1382 1382 void assertType(char const* type_name, bool istype);
1383   - inline bool dereference();
1384 1383 void makeDirect(QPDFObjGen::set& visited, bool stop_at_streams);
1385 1384 void disconnect();
1386 1385 void setParsedOffset(qpdf_offset_t offset);
... ...
libqpdf/QPDF.cc
... ... @@ -1728,11 +1728,11 @@ QPDF::readObjectAtOffset(
1728 1728 return oh;
1729 1729 }
1730 1730  
1731   -void
  1731 +QPDFObject*
1732 1732 QPDF::resolve(QPDFObjGen og)
1733 1733 {
1734 1734 if (!isUnresolved(og)) {
1735   - return;
  1735 + return m->obj_cache[og].object.get();
1736 1736 }
1737 1737  
1738 1738 if (m->resolving.count(og)) {
... ... @@ -1741,7 +1741,7 @@ QPDF::resolve(QPDFObjGen og)
1741 1741 QTC::TC("qpdf", "QPDF recursion loop in resolve");
1742 1742 warn(damagedPDF("", "loop detected resolving object " + og.unparse(' ')));
1743 1743 updateCache(og, QPDF_Null::create(), -1, -1);
1744   - return;
  1744 + return m->obj_cache[og].object.get();
1745 1745 }
1746 1746 ResolveRecorder rr(this, og);
1747 1747  
... ... @@ -1782,6 +1782,7 @@ QPDF::resolve(QPDFObjGen og)
1782 1782  
1783 1783 auto result(m->obj_cache[og].object);
1784 1784 result->setDefaultDescription(this, og);
  1785 + return result.get();
1785 1786 }
1786 1787  
1787 1788 void
... ...
libqpdf/QPDFObject.cc
... ... @@ -4,13 +4,6 @@
4 4 #include <qpdf/QPDF_Destroyed.hh>
5 5  
6 6 void
7   -QPDFObject::doResolve()
8   -{
9   - auto og = value->og;
10   - QPDF::Resolver::resolve(value->qpdf, og);
11   -}
12   -
13   -void
14 7 QPDFObject::destroy()
15 8 {
16 9 value = QPDF_Destroyed::getInstance();
... ...
libqpdf/QPDFObjectHandle.cc
... ... @@ -240,79 +240,79 @@ QPDFObjectHandle::disconnect()
240 240 qpdf_object_type_e
241 241 QPDFObjectHandle::getTypeCode()
242 242 {
243   - return dereference() ? this->obj->getTypeCode() : ::ot_uninitialized;
  243 + return obj ? obj->getResolvedTypeCode() : ::ot_uninitialized;
244 244 }
245 245  
246 246 char const*
247 247 QPDFObjectHandle::getTypeName()
248 248 {
249   - return dereference() ? this->obj->getTypeName() : "uninitialized";
  249 + return obj ? obj->getTypeName() : "uninitialized";
250 250 }
251 251  
252 252 QPDF_Array*
253   -QPDFObjectHandle::asArray()
  253 +QPDFObjectHandle::asArray() const
254 254 {
255   - return dereference() ? obj->as<QPDF_Array>() : nullptr;
  255 + return obj ? obj->as<QPDF_Array>() : nullptr;
256 256 }
257 257  
258 258 QPDF_Bool*
259   -QPDFObjectHandle::asBool()
  259 +QPDFObjectHandle::asBool() const
260 260 {
261   - return dereference() ? obj->as<QPDF_Bool>() : nullptr;
  261 + return obj ? obj->as<QPDF_Bool>() : nullptr;
262 262 }
263 263  
264 264 QPDF_Dictionary*
265   -QPDFObjectHandle::asDictionary()
  265 +QPDFObjectHandle::asDictionary() const
266 266 {
267   - return dereference() ? obj->as<QPDF_Dictionary>() : nullptr;
  267 + return obj ? obj->as<QPDF_Dictionary>() : nullptr;
268 268 }
269 269  
270 270 QPDF_InlineImage*
271   -QPDFObjectHandle::asInlineImage()
  271 +QPDFObjectHandle::asInlineImage() const
272 272 {
273   - return dereference() ? obj->as<QPDF_InlineImage>() : nullptr;
  273 + return obj ? obj->as<QPDF_InlineImage>() : nullptr;
274 274 }
275 275  
276 276 QPDF_Integer*
277   -QPDFObjectHandle::asInteger()
  277 +QPDFObjectHandle::asInteger() const
278 278 {
279   - return dereference() ? obj->as<QPDF_Integer>() : nullptr;
  279 + return obj ? obj->as<QPDF_Integer>() : nullptr;
280 280 }
281 281  
282 282 QPDF_Name*
283   -QPDFObjectHandle::asName()
  283 +QPDFObjectHandle::asName() const
284 284 {
285   - return dereference() ? obj->as<QPDF_Name>() : nullptr;
  285 + return obj ? obj->as<QPDF_Name>() : nullptr;
286 286 }
287 287  
288 288 QPDF_Null*
289   -QPDFObjectHandle::asNull()
  289 +QPDFObjectHandle::asNull() const
290 290 {
291   - return dereference() ? obj->as<QPDF_Null>() : nullptr;
  291 + return obj ? obj->as<QPDF_Null>() : nullptr;
292 292 }
293 293  
294 294 QPDF_Operator*
295   -QPDFObjectHandle::asOperator()
  295 +QPDFObjectHandle::asOperator() const
296 296 {
297   - return dereference() ? obj->as<QPDF_Operator>() : nullptr;
  297 + return obj ? obj->as<QPDF_Operator>() : nullptr;
298 298 }
299 299  
300 300 QPDF_Real*
301   -QPDFObjectHandle::asReal()
  301 +QPDFObjectHandle::asReal() const
302 302 {
303   - return dereference() ? obj->as<QPDF_Real>() : nullptr;
  303 + return obj ? obj->as<QPDF_Real>() : nullptr;
304 304 }
305 305  
306 306 QPDF_Reserved*
307   -QPDFObjectHandle::asReserved()
  307 +QPDFObjectHandle::asReserved() const
308 308 {
309   - return dereference() ? obj->as<QPDF_Reserved>() : nullptr;
  309 + return obj ? obj->as<QPDF_Reserved>() : nullptr;
310 310 }
311 311  
312 312 QPDF_Stream*
313   -QPDFObjectHandle::asStream()
  313 +QPDFObjectHandle::asStream() const
314 314 {
315   - return dereference() ? obj->as<QPDF_Stream>() : nullptr;
  315 + return obj ? obj->as<QPDF_Stream>() : nullptr;
316 316 }
317 317  
318 318 QPDF_Stream*
... ... @@ -324,21 +324,21 @@ QPDFObjectHandle::asStreamWithAssert()
324 324 }
325 325  
326 326 QPDF_String*
327   -QPDFObjectHandle::asString()
  327 +QPDFObjectHandle::asString() const
328 328 {
329   - return dereference() ? obj->as<QPDF_String>() : nullptr;
  329 + return obj ? obj->as<QPDF_String>() : nullptr;
330 330 }
331 331  
332 332 bool
333 333 QPDFObjectHandle::isDestroyed()
334 334 {
335   - return dereference() && (obj->getTypeCode() == ::ot_destroyed);
  335 + return obj && obj->getResolvedTypeCode() == ::ot_destroyed;
336 336 }
337 337  
338 338 bool
339 339 QPDFObjectHandle::isBool()
340 340 {
341   - return dereference() && (obj->getTypeCode() == ::ot_boolean);
  341 + return obj && obj->getResolvedTypeCode() == ::ot_boolean;
342 342 }
343 343  
344 344 bool
... ... @@ -346,25 +346,25 @@ QPDFObjectHandle::isDirectNull() const
346 346 {
347 347 // Don't call dereference() -- this is a const method, and we know
348 348 // objid == 0, so there's nothing to resolve.
349   - return (isInitialized() && (getObjectID() == 0) && (obj->getTypeCode() == ::ot_null));
  349 + return (obj && getObjectID() == 0 && obj->getTypeCode() == ::ot_null);
350 350 }
351 351  
352 352 bool
353 353 QPDFObjectHandle::isNull()
354 354 {
355   - return dereference() && (obj->getTypeCode() == ::ot_null);
  355 + return obj && obj->getResolvedTypeCode() == ::ot_null;
356 356 }
357 357  
358 358 bool
359 359 QPDFObjectHandle::isInteger()
360 360 {
361   - return dereference() && (obj->getTypeCode() == ::ot_integer);
  361 + return obj && obj->getResolvedTypeCode() == ::ot_integer;
362 362 }
363 363  
364 364 bool
365 365 QPDFObjectHandle::isReal()
366 366 {
367   - return dereference() && (obj->getTypeCode() == ::ot_real);
  367 + return obj && obj->getResolvedTypeCode() == ::ot_real;
368 368 }
369 369  
370 370 bool
... ... @@ -401,49 +401,49 @@ QPDFObjectHandle::getValueAsNumber(double&amp; value)
401 401 bool
402 402 QPDFObjectHandle::isName()
403 403 {
404   - return dereference() && (obj->getTypeCode() == ::ot_name);
  404 + return obj && obj->getResolvedTypeCode() == ::ot_name;
405 405 }
406 406  
407 407 bool
408 408 QPDFObjectHandle::isString()
409 409 {
410   - return dereference() && (obj->getTypeCode() == ::ot_string);
  410 + return obj && obj->getResolvedTypeCode() == ::ot_string;
411 411 }
412 412  
413 413 bool
414 414 QPDFObjectHandle::isOperator()
415 415 {
416   - return dereference() && (obj->getTypeCode() == ::ot_operator);
  416 + return obj && obj->getResolvedTypeCode() == ::ot_operator;
417 417 }
418 418  
419 419 bool
420 420 QPDFObjectHandle::isInlineImage()
421 421 {
422   - return dereference() && (obj->getTypeCode() == ::ot_inlineimage);
  422 + return obj && obj->getResolvedTypeCode() == ::ot_inlineimage;
423 423 }
424 424  
425 425 bool
426 426 QPDFObjectHandle::isArray()
427 427 {
428   - return dereference() && (obj->getTypeCode() == ::ot_array);
  428 + return obj && obj->getResolvedTypeCode() == ::ot_array;
429 429 }
430 430  
431 431 bool
432 432 QPDFObjectHandle::isDictionary()
433 433 {
434   - return dereference() && (obj->getTypeCode() == ::ot_dictionary);
  434 + return obj && obj->getResolvedTypeCode() == ::ot_dictionary;
435 435 }
436 436  
437 437 bool
438 438 QPDFObjectHandle::isStream()
439 439 {
440   - return dereference() && (obj->getTypeCode() == ::ot_stream);
  440 + return obj && obj->getResolvedTypeCode() == ::ot_stream;
441 441 }
442 442  
443 443 bool
444 444 QPDFObjectHandle::isReserved()
445 445 {
446   - return dereference() && (obj->getTypeCode() == ::ot_reserved);
  446 + return obj && obj->getResolvedTypeCode() == ::ot_reserved;
447 447 }
448 448  
449 449 bool
... ... @@ -1586,7 +1586,7 @@ QPDFObjectHandle::unparse()
1586 1586 std::string
1587 1587 QPDFObjectHandle::unparseResolved()
1588 1588 {
1589   - if (!dereference()) {
  1589 + if (!obj) {
1590 1590 throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle");
1591 1591 }
1592 1592 return obj->unparse();
... ... @@ -1615,7 +1615,7 @@ QPDFObjectHandle::getJSON(int json_version, bool dereference_indirect)
1615 1615 {
1616 1616 if ((!dereference_indirect) && isIndirect()) {
1617 1617 return JSON::makeString(unparse());
1618   - } else if (!dereference()) {
  1618 + } else if (!obj) {
1619 1619 throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle");
1620 1620 } else {
1621 1621 Pl_Buffer p{"json"};
... ... @@ -1631,7 +1631,7 @@ QPDFObjectHandle::writeJSON(int json_version, JSON::Writer&amp; p, bool dereference_
1631 1631 {
1632 1632 if (!dereference_indirect && isIndirect()) {
1633 1633 p << "\"" << getObjGen().unparse(' ') << " R\"";
1634   - } else if (!dereference()) {
  1634 + } else if (!obj) {
1635 1635 throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle");
1636 1636 } else {
1637 1637 obj->writeJSON(json_version, p);
... ... @@ -1874,11 +1874,7 @@ QPDFObjectHandle::parse(
1874 1874 qpdf_offset_t
1875 1875 QPDFObjectHandle::getParsedOffset()
1876 1876 {
1877   - if (dereference()) {
1878   - return this->obj->getParsedOffset();
1879   - } else {
1880   - return -1;
1881   - }
  1877 + return obj ? obj->getParsedOffset() : -1;
1882 1878 }
1883 1879  
1884 1880 QPDFObjectHandle
... ... @@ -2055,9 +2051,7 @@ QPDFObjectHandle::newReserved(QPDF* qpdf)
2055 2051 void
2056 2052 QPDFObjectHandle::setObjectDescription(QPDF* owning_qpdf, std::string const& object_description)
2057 2053 {
2058   - // This is called during parsing on newly created direct objects, so we can't call dereference()
2059   - // here.
2060   - if (isInitialized() && obj.get()) {
  2054 + if (obj) {
2061 2055 auto descr = std::make_shared<QPDFValue::Description>(object_description);
2062 2056 obj->setDescription(owning_qpdf, descr);
2063 2057 }
... ... @@ -2066,13 +2060,13 @@ QPDFObjectHandle::setObjectDescription(QPDF* owning_qpdf, std::string const&amp; obj
2066 2060 bool
2067 2061 QPDFObjectHandle::hasObjectDescription()
2068 2062 {
2069   - return dereference() && obj.get() && obj->hasDescription();
  2063 + return obj && obj->hasDescription();
2070 2064 }
2071 2065  
2072 2066 QPDFObjectHandle
2073 2067 QPDFObjectHandle::shallowCopy()
2074 2068 {
2075   - if (!dereference()) {
  2069 + if (!obj) {
2076 2070 throw std::logic_error("operation attempted on uninitialized QPDFObjectHandle");
2077 2071 }
2078 2072 return {obj->copy()};
... ... @@ -2081,7 +2075,7 @@ QPDFObjectHandle::shallowCopy()
2081 2075 QPDFObjectHandle
2082 2076 QPDFObjectHandle::unsafeShallowCopy()
2083 2077 {
2084   - if (!dereference()) {
  2078 + if (!obj) {
2085 2079 throw std::logic_error("operation attempted on uninitialized QPDFObjectHandle");
2086 2080 }
2087 2081 return {obj->copy(true)};
... ... @@ -2172,10 +2166,10 @@ QPDFObjectHandle::typeWarning(char const* expected_type, std::string const&amp; warn
2172 2166 std::string description;
2173 2167 // Type checks above guarantee that the object has been dereferenced. Nevertheless, dereference
2174 2168 // throws exceptions in the test suite
2175   - if (!dereference()) {
  2169 + if (!obj) {
2176 2170 throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle");
2177 2171 }
2178   - this->obj->getDescription(context, description);
  2172 + obj->getDescription(context, description);
2179 2173 // Null context handled by warn
2180 2174 warn(
2181 2175 context,
... ... @@ -2193,7 +2187,7 @@ QPDFObjectHandle::warnIfPossible(std::string const&amp; warning)
2193 2187 {
2194 2188 QPDF* context = nullptr;
2195 2189 std::string description;
2196   - if (dereference() && obj->getDescription(context, description)) {
  2190 + if (obj && obj->getDescription(context, description)) {
2197 2191 warn(context, QPDFExc(qpdf_e_damaged_pdf, "", description, 0, warning));
2198 2192 } else {
2199 2193 *QPDFLogger::defaultLogger()->getError() << warning << "\n";
... ... @@ -2205,8 +2199,8 @@ QPDFObjectHandle::objectWarning(std::string const&amp; warning)
2205 2199 {
2206 2200 QPDF* context = nullptr;
2207 2201 std::string description;
2208   - // Type checks above guarantee that the object has been dereferenced.
2209   - this->obj->getDescription(context, description);
  2202 + // Type checks above guarantee that the object is initialized.
  2203 + obj->getDescription(context, description);
2210 2204 // Null context handled by warn
2211 2205 warn(context, QPDFExc(qpdf_e_object, "", description, 0, warning));
2212 2206 }
... ... @@ -2372,16 +2366,6 @@ QPDFObjectHandle::assertPageObject()
2372 2366 }
2373 2367 }
2374 2368  
2375   -inline bool
2376   -QPDFObjectHandle::dereference()
2377   -{
2378   - if (!isInitialized()) {
2379   - return false;
2380   - }
2381   - this->obj->resolve();
2382   - return true;
2383   -}
2384   -
2385 2369 void
2386 2370 QPDFObjectHandle::warn(QPDF* qpdf, QPDFExc const& e)
2387 2371 {
... ...
libqpdf/QPDF_Array.cc
... ... @@ -130,8 +130,7 @@ QPDF_Array::unparse()
130 130 for (int j = next; j < key; ++j) {
131 131 result += "null ";
132 132 }
133   - item.second->resolve();
134   - auto og = item.second->getObjGen();
  133 + auto og = item.second->resolved_object()->getObjGen();
135 134 result += og.isIndirect() ? og.unparse(' ') + " R " : item.second->unparse() + " ";
136 135 next = ++key;
137 136 }
... ... @@ -140,8 +139,7 @@ QPDF_Array::unparse()
140 139 }
141 140 } else {
142 141 for (auto const& item: elements) {
143   - item->resolve();
144   - auto og = item->getObjGen();
  142 + auto og = item->resolved_object()->getObjGen();
145 143 result += og.isIndirect() ? og.unparse(' ') + " R " : item->unparse() + " ";
146 144 }
147 145 }
... ...
libqpdf/QPDF_Unresolved.cc
1 1 #include <qpdf/QPDF_Unresolved.hh>
2 2  
3   -#include <stdexcept>
  3 +#include <qpdf/QPDF.hh>
  4 +#include <qpdf/QPDFObject_private.hh>
4 5  
5 6 QPDF_Unresolved::QPDF_Unresolved(QPDF* qpdf, QPDFObjGen const& og) :
6 7 QPDFValue(::ot_unresolved, "unresolved", qpdf, og)
... ... @@ -16,19 +17,23 @@ QPDF_Unresolved::create(QPDF* qpdf, QPDFObjGen const&amp; og)
16 17 std::shared_ptr<QPDFObject>
17 18 QPDF_Unresolved::copy(bool shallow)
18 19 {
19   - throw std::logic_error("attempted to shallow copy an unresolved QPDFObjectHandle");
20   - return nullptr;
  20 + return QPDF::Resolver::resolved(qpdf, og)->copy(shallow);
21 21 }
22 22  
23 23 std::string
24 24 QPDF_Unresolved::unparse()
25 25 {
26   - throw std::logic_error("attempted to unparse an unresolved QPDFObjectHandle");
27   - return "";
  26 + return QPDF::Resolver::resolved(qpdf, og)->unparse();
28 27 }
29 28  
30 29 void
31 30 QPDF_Unresolved::writeJSON(int json_version, JSON::Writer& p)
32 31 {
33   - throw std::logic_error("attempted to get JSON from an unresolved QPDFObjectHandle");
  32 + QPDF::Resolver::resolved(qpdf, og)->writeJSON(json_version, p);
  33 +}
  34 +
  35 +std::string
  36 +QPDF_Unresolved::getStringValue() const
  37 +{
  38 + return QPDF::Resolver::resolved(qpdf, og)->getStringValue();
34 39 }
... ...
libqpdf/qpdf/QPDFObject_private.hh
... ... @@ -5,8 +5,8 @@
5 5 // include/qpdf/QPDFObject.hh. See comments there for an explanation.
6 6  
7 7 #include <qpdf/Constants.h>
8   -#include <qpdf/DLL.h>
9 8 #include <qpdf/JSON.hh>
  9 +#include <qpdf/QPDF.hh>
10 10 #include <qpdf/QPDFValue.hh>
11 11 #include <qpdf/Types.h>
12 12  
... ... @@ -43,18 +43,26 @@ class QPDFObject
43 43 {
44 44 return value->getStringValue();
45 45 }
  46 + // Return a unique type code for the resolved object
  47 + qpdf_object_type_e
  48 + getResolvedTypeCode() const
  49 + {
  50 + auto tc = value->type_code;
  51 + return tc == ::ot_unresolved
  52 + ? QPDF::Resolver::resolved(value->qpdf, value->og)->value->type_code
  53 + : tc;
  54 + }
46 55 // Return a unique type code for the object
47 56 qpdf_object_type_e
48   - getTypeCode() const
  57 + getTypeCode() const noexcept
49 58 {
50 59 return value->type_code;
51 60 }
52   -
53 61 // Return a string literal that describes the type, useful for debugging and testing
54 62 char const*
55 63 getTypeName() const
56 64 {
57   - return value->type_name;
  65 + return resolved_object()->value->type_name;
58 66 }
59 67  
60 68 QPDF*
... ... @@ -157,20 +165,23 @@ class QPDFObject
157 165 {
158 166 return value->type_code == ::ot_unresolved;
159 167 }
160   - void
161   - resolve()
  168 + const QPDFObject*
  169 + resolved_object() const
162 170 {
163   - if (isUnresolved()) {
164   - doResolve();
165   - }
  171 + return isUnresolved() ? QPDF::Resolver::resolved(value->qpdf, value->og) : this;
166 172 }
167   - void doResolve();
168 173  
169 174 template <typename T>
170 175 T*
171   - as()
172   - {
173   - return dynamic_cast<T*>(value.get());
  176 + as() const
  177 + {
  178 + if (auto result = dynamic_cast<T*>(value.get())) {
  179 + return result;
  180 + } else {
  181 + return isUnresolved()
  182 + ? dynamic_cast<T*>(QPDF::Resolver::resolved(value->qpdf, value->og)->value.get())
  183 + : nullptr;
  184 + }
174 185 }
175 186  
176 187 private:
... ...
libqpdf/qpdf/QPDF_Unresolved.hh
... ... @@ -11,6 +11,7 @@ class QPDF_Unresolved: public QPDFValue
11 11 std::shared_ptr<QPDFObject> copy(bool shallow = false) override;
12 12 std::string unparse() override;
13 13 void writeJSON(int json_version, JSON::Writer& p) override;
  14 + std::string getStringValue() const override;
14 15  
15 16 private:
16 17 QPDF_Unresolved(QPDF* qpdf, QPDFObjGen const& og);
... ...