From 9641626cae44ed17ef4af4e72e89276065dd85ed Mon Sep 17 00:00:00 2001 From: m-holger Date: Sun, 10 Mar 2024 15:34:58 +0000 Subject: [PATCH 1/3] Refactor resolving of objects --- include/qpdf/QPDF.hh | 9 ++- include/qpdf/QPDFObjectHandle.hh | 25 +++--- libqpdf/QPDF.cc | 7 +- libqpdf/QPDFObject.cc | 7 -- libqpdf/QPDFObjectHandle.cc | 122 +++++++++++++---------------- libqpdf/QPDF_Array.cc | 6 +- libqpdf/QPDF_Unresolved.cc | 17 ++-- libqpdf/qpdf/QPDFObject_private.hh | 35 ++++++--- libqpdf/qpdf/QPDF_Unresolved.hh | 1 + 9 files changed, 111 insertions(+), 118 deletions(-) diff --git a/include/qpdf/QPDF.hh b/include/qpdf/QPDF.hh index f936d401..e637434a 100644 --- a/include/qpdf/QPDF.hh +++ b/include/qpdf/QPDF.hh @@ -792,12 +792,13 @@ class QPDF class Resolver { friend class QPDFObject; + friend class QPDF_Unresolved; private: - static void - resolve(QPDF* qpdf, QPDFObjGen const& og) + static QPDFObject* + resolved(QPDF* qpdf, QPDFObjGen og) { - qpdf->resolve(og); + return qpdf->resolve(og); } }; @@ -1056,7 +1057,7 @@ class QPDF QPDFObjGen exp_og, QPDFObjGen& og, bool skip_cache_if_in_xref); - void resolve(QPDFObjGen og); + QPDFObject* resolve(QPDFObjGen og); void resolveObjectsInStream(int obj_stream_number); void stopOnError(std::string const& message); QPDFObjectHandle reserveObjectIfNotExists(QPDFObjGen const& og); diff --git a/include/qpdf/QPDFObjectHandle.hh b/include/qpdf/QPDFObjectHandle.hh index 9ea329ff..0a03c035 100644 --- a/include/qpdf/QPDFObjectHandle.hh +++ b/include/qpdf/QPDFObjectHandle.hh @@ -1363,24 +1363,23 @@ class QPDFObjectHandle void writeJSON(int json_version, JSON::Writer& p, bool dereference_indirect = false); private: - QPDF_Array* asArray(); - QPDF_Bool* asBool(); - QPDF_Dictionary* asDictionary(); - QPDF_InlineImage* asInlineImage(); - QPDF_Integer* asInteger(); - QPDF_Name* asName(); - QPDF_Null* asNull(); - QPDF_Operator* asOperator(); - QPDF_Real* asReal(); - QPDF_Reserved* asReserved(); - QPDF_Stream* asStream(); + QPDF_Array* asArray() const; + QPDF_Bool* asBool() const; + QPDF_Dictionary* asDictionary() const; + QPDF_InlineImage* asInlineImage() const; + QPDF_Integer* asInteger() const; + QPDF_Name* asName() const; + QPDF_Null* asNull() const; + QPDF_Operator* asOperator() const; + QPDF_Real* asReal() const; + QPDF_Reserved* asReserved() const; + QPDF_Stream* asStream() const; QPDF_Stream* asStreamWithAssert(); - QPDF_String* asString(); + QPDF_String* asString() const; void typeWarning(char const* expected_type, std::string const& warning); void objectWarning(std::string const& warning); void assertType(char const* type_name, bool istype); - inline bool dereference(); void makeDirect(QPDFObjGen::set& visited, bool stop_at_streams); void disconnect(); void setParsedOffset(qpdf_offset_t offset); diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index d43fec16..8276b68f 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -1728,11 +1728,11 @@ QPDF::readObjectAtOffset( return oh; } -void +QPDFObject* QPDF::resolve(QPDFObjGen og) { if (!isUnresolved(og)) { - return; + return m->obj_cache[og].object.get(); } if (m->resolving.count(og)) { @@ -1741,7 +1741,7 @@ QPDF::resolve(QPDFObjGen og) QTC::TC("qpdf", "QPDF recursion loop in resolve"); warn(damagedPDF("", "loop detected resolving object " + og.unparse(' '))); updateCache(og, QPDF_Null::create(), -1, -1); - return; + return m->obj_cache[og].object.get(); } ResolveRecorder rr(this, og); @@ -1782,6 +1782,7 @@ QPDF::resolve(QPDFObjGen og) auto result(m->obj_cache[og].object); result->setDefaultDescription(this, og); + return result.get(); } void diff --git a/libqpdf/QPDFObject.cc b/libqpdf/QPDFObject.cc index f6869925..7e3cc218 100644 --- a/libqpdf/QPDFObject.cc +++ b/libqpdf/QPDFObject.cc @@ -3,13 +3,6 @@ #include #include -void -QPDFObject::doResolve() -{ - auto og = value->og; - QPDF::Resolver::resolve(value->qpdf, og); -} - void QPDFObject::destroy() { diff --git a/libqpdf/QPDFObjectHandle.cc b/libqpdf/QPDFObjectHandle.cc index 163ebe88..b8ce416f 100644 --- a/libqpdf/QPDFObjectHandle.cc +++ b/libqpdf/QPDFObjectHandle.cc @@ -240,79 +240,79 @@ QPDFObjectHandle::disconnect() qpdf_object_type_e QPDFObjectHandle::getTypeCode() { - return dereference() ? this->obj->getTypeCode() : ::ot_uninitialized; + return obj ? obj->getResolvedTypeCode() : ::ot_uninitialized; } char const* QPDFObjectHandle::getTypeName() { - return dereference() ? this->obj->getTypeName() : "uninitialized"; + return obj ? obj->getTypeName() : "uninitialized"; } QPDF_Array* -QPDFObjectHandle::asArray() +QPDFObjectHandle::asArray() const { - return dereference() ? obj->as() : nullptr; + return obj ? obj->as() : nullptr; } QPDF_Bool* -QPDFObjectHandle::asBool() +QPDFObjectHandle::asBool() const { - return dereference() ? obj->as() : nullptr; + return obj ? obj->as() : nullptr; } QPDF_Dictionary* -QPDFObjectHandle::asDictionary() +QPDFObjectHandle::asDictionary() const { - return dereference() ? obj->as() : nullptr; + return obj ? obj->as() : nullptr; } QPDF_InlineImage* -QPDFObjectHandle::asInlineImage() +QPDFObjectHandle::asInlineImage() const { - return dereference() ? obj->as() : nullptr; + return obj ? obj->as() : nullptr; } QPDF_Integer* -QPDFObjectHandle::asInteger() +QPDFObjectHandle::asInteger() const { - return dereference() ? obj->as() : nullptr; + return obj ? obj->as() : nullptr; } QPDF_Name* -QPDFObjectHandle::asName() +QPDFObjectHandle::asName() const { - return dereference() ? obj->as() : nullptr; + return obj ? obj->as() : nullptr; } QPDF_Null* -QPDFObjectHandle::asNull() +QPDFObjectHandle::asNull() const { - return dereference() ? obj->as() : nullptr; + return obj ? obj->as() : nullptr; } QPDF_Operator* -QPDFObjectHandle::asOperator() +QPDFObjectHandle::asOperator() const { - return dereference() ? obj->as() : nullptr; + return obj ? obj->as() : nullptr; } QPDF_Real* -QPDFObjectHandle::asReal() +QPDFObjectHandle::asReal() const { - return dereference() ? obj->as() : nullptr; + return obj ? obj->as() : nullptr; } QPDF_Reserved* -QPDFObjectHandle::asReserved() +QPDFObjectHandle::asReserved() const { - return dereference() ? obj->as() : nullptr; + return obj ? obj->as() : nullptr; } QPDF_Stream* -QPDFObjectHandle::asStream() +QPDFObjectHandle::asStream() const { - return dereference() ? obj->as() : nullptr; + return obj ? obj->as() : nullptr; } QPDF_Stream* @@ -324,21 +324,21 @@ QPDFObjectHandle::asStreamWithAssert() } QPDF_String* -QPDFObjectHandle::asString() +QPDFObjectHandle::asString() const { - return dereference() ? obj->as() : nullptr; + return obj ? obj->as() : nullptr; } bool QPDFObjectHandle::isDestroyed() { - return dereference() && (obj->getTypeCode() == ::ot_destroyed); + return obj && obj->getResolvedTypeCode() == ::ot_destroyed; } bool QPDFObjectHandle::isBool() { - return dereference() && (obj->getTypeCode() == ::ot_boolean); + return obj && obj->getResolvedTypeCode() == ::ot_boolean; } bool @@ -346,25 +346,25 @@ QPDFObjectHandle::isDirectNull() const { // Don't call dereference() -- this is a const method, and we know // objid == 0, so there's nothing to resolve. - return (isInitialized() && (getObjectID() == 0) && (obj->getTypeCode() == ::ot_null)); + return (obj && getObjectID() == 0 && obj->getTypeCode() == ::ot_null); } bool QPDFObjectHandle::isNull() { - return dereference() && (obj->getTypeCode() == ::ot_null); + return obj && obj->getResolvedTypeCode() == ::ot_null; } bool QPDFObjectHandle::isInteger() { - return dereference() && (obj->getTypeCode() == ::ot_integer); + return obj && obj->getResolvedTypeCode() == ::ot_integer; } bool QPDFObjectHandle::isReal() { - return dereference() && (obj->getTypeCode() == ::ot_real); + return obj && obj->getResolvedTypeCode() == ::ot_real; } bool @@ -401,49 +401,49 @@ QPDFObjectHandle::getValueAsNumber(double& value) bool QPDFObjectHandle::isName() { - return dereference() && (obj->getTypeCode() == ::ot_name); + return obj && obj->getResolvedTypeCode() == ::ot_name; } bool QPDFObjectHandle::isString() { - return dereference() && (obj->getTypeCode() == ::ot_string); + return obj && obj->getResolvedTypeCode() == ::ot_string; } bool QPDFObjectHandle::isOperator() { - return dereference() && (obj->getTypeCode() == ::ot_operator); + return obj && obj->getResolvedTypeCode() == ::ot_operator; } bool QPDFObjectHandle::isInlineImage() { - return dereference() && (obj->getTypeCode() == ::ot_inlineimage); + return obj && obj->getResolvedTypeCode() == ::ot_inlineimage; } bool QPDFObjectHandle::isArray() { - return dereference() && (obj->getTypeCode() == ::ot_array); + return obj && obj->getResolvedTypeCode() == ::ot_array; } bool QPDFObjectHandle::isDictionary() { - return dereference() && (obj->getTypeCode() == ::ot_dictionary); + return obj && obj->getResolvedTypeCode() == ::ot_dictionary; } bool QPDFObjectHandle::isStream() { - return dereference() && (obj->getTypeCode() == ::ot_stream); + return obj && obj->getResolvedTypeCode() == ::ot_stream; } bool QPDFObjectHandle::isReserved() { - return dereference() && (obj->getTypeCode() == ::ot_reserved); + return obj && obj->getResolvedTypeCode() == ::ot_reserved; } bool @@ -1586,7 +1586,7 @@ QPDFObjectHandle::unparse() std::string QPDFObjectHandle::unparseResolved() { - if (!dereference()) { + if (!obj) { throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle"); } return obj->unparse(); @@ -1615,7 +1615,7 @@ QPDFObjectHandle::getJSON(int json_version, bool dereference_indirect) { if ((!dereference_indirect) && isIndirect()) { return JSON::makeString(unparse()); - } else if (!dereference()) { + } else if (!obj) { throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle"); } else { Pl_Buffer p{"json"}; @@ -1631,7 +1631,7 @@ QPDFObjectHandle::writeJSON(int json_version, JSON::Writer& p, bool dereference_ { if (!dereference_indirect && isIndirect()) { p << "\"" << getObjGen().unparse(' ') << " R\""; - } else if (!dereference()) { + } else if (!obj) { throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle"); } else { obj->writeJSON(json_version, p); @@ -1874,11 +1874,7 @@ QPDFObjectHandle::parse( qpdf_offset_t QPDFObjectHandle::getParsedOffset() { - if (dereference()) { - return this->obj->getParsedOffset(); - } else { - return -1; - } + return obj ? obj->getParsedOffset() : -1; } QPDFObjectHandle @@ -2055,9 +2051,7 @@ QPDFObjectHandle::newReserved(QPDF* qpdf) void QPDFObjectHandle::setObjectDescription(QPDF* owning_qpdf, std::string const& object_description) { - // This is called during parsing on newly created direct objects, so we can't call dereference() - // here. - if (isInitialized() && obj.get()) { + if (obj) { auto descr = std::make_shared(object_description); obj->setDescription(owning_qpdf, descr); } @@ -2066,13 +2060,13 @@ QPDFObjectHandle::setObjectDescription(QPDF* owning_qpdf, std::string const& obj bool QPDFObjectHandle::hasObjectDescription() { - return dereference() && obj.get() && obj->hasDescription(); + return obj && obj->hasDescription(); } QPDFObjectHandle QPDFObjectHandle::shallowCopy() { - if (!dereference()) { + if (!obj) { throw std::logic_error("operation attempted on uninitialized QPDFObjectHandle"); } return {obj->copy()}; @@ -2081,7 +2075,7 @@ QPDFObjectHandle::shallowCopy() QPDFObjectHandle QPDFObjectHandle::unsafeShallowCopy() { - if (!dereference()) { + if (!obj) { throw std::logic_error("operation attempted on uninitialized QPDFObjectHandle"); } return {obj->copy(true)}; @@ -2172,10 +2166,10 @@ QPDFObjectHandle::typeWarning(char const* expected_type, std::string const& warn std::string description; // Type checks above guarantee that the object has been dereferenced. Nevertheless, dereference // throws exceptions in the test suite - if (!dereference()) { + if (!obj) { throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle"); } - this->obj->getDescription(context, description); + obj->getDescription(context, description); // Null context handled by warn warn( context, @@ -2193,7 +2187,7 @@ QPDFObjectHandle::warnIfPossible(std::string const& warning) { QPDF* context = nullptr; std::string description; - if (dereference() && obj->getDescription(context, description)) { + if (obj && obj->getDescription(context, description)) { warn(context, QPDFExc(qpdf_e_damaged_pdf, "", description, 0, warning)); } else { *QPDFLogger::defaultLogger()->getError() << warning << "\n"; @@ -2205,8 +2199,8 @@ QPDFObjectHandle::objectWarning(std::string const& warning) { QPDF* context = nullptr; std::string description; - // Type checks above guarantee that the object has been dereferenced. - this->obj->getDescription(context, description); + // Type checks above guarantee that the object is initialized. + obj->getDescription(context, description); // Null context handled by warn warn(context, QPDFExc(qpdf_e_object, "", description, 0, warning)); } @@ -2372,16 +2366,6 @@ QPDFObjectHandle::assertPageObject() } } -inline bool -QPDFObjectHandle::dereference() -{ - if (!isInitialized()) { - return false; - } - this->obj->resolve(); - return true; -} - void QPDFObjectHandle::warn(QPDF* qpdf, QPDFExc const& e) { diff --git a/libqpdf/QPDF_Array.cc b/libqpdf/QPDF_Array.cc index ab2a4c8c..c1c373ae 100644 --- a/libqpdf/QPDF_Array.cc +++ b/libqpdf/QPDF_Array.cc @@ -130,8 +130,7 @@ QPDF_Array::unparse() for (int j = next; j < key; ++j) { result += "null "; } - item.second->resolve(); - auto og = item.second->getObjGen(); + auto og = item.second->resolved_object()->getObjGen(); result += og.isIndirect() ? og.unparse(' ') + " R " : item.second->unparse() + " "; next = ++key; } @@ -140,8 +139,7 @@ QPDF_Array::unparse() } } else { for (auto const& item: elements) { - item->resolve(); - auto og = item->getObjGen(); + auto og = item->resolved_object()->getObjGen(); result += og.isIndirect() ? og.unparse(' ') + " R " : item->unparse() + " "; } } diff --git a/libqpdf/QPDF_Unresolved.cc b/libqpdf/QPDF_Unresolved.cc index dc14c2f2..c0c24fda 100644 --- a/libqpdf/QPDF_Unresolved.cc +++ b/libqpdf/QPDF_Unresolved.cc @@ -1,6 +1,7 @@ #include -#include +#include +#include QPDF_Unresolved::QPDF_Unresolved(QPDF* qpdf, QPDFObjGen const& og) : QPDFValue(::ot_unresolved, "unresolved", qpdf, og) @@ -16,19 +17,23 @@ QPDF_Unresolved::create(QPDF* qpdf, QPDFObjGen const& og) std::shared_ptr QPDF_Unresolved::copy(bool shallow) { - throw std::logic_error("attempted to shallow copy an unresolved QPDFObjectHandle"); - return nullptr; + return QPDF::Resolver::resolved(qpdf, og)->copy(shallow); } std::string QPDF_Unresolved::unparse() { - throw std::logic_error("attempted to unparse an unresolved QPDFObjectHandle"); - return ""; + return QPDF::Resolver::resolved(qpdf, og)->unparse(); } void QPDF_Unresolved::writeJSON(int json_version, JSON::Writer& p) { - throw std::logic_error("attempted to get JSON from an unresolved QPDFObjectHandle"); + QPDF::Resolver::resolved(qpdf, og)->writeJSON(json_version, p); +} + +std::string +QPDF_Unresolved::getStringValue() const +{ + return QPDF::Resolver::resolved(qpdf, og)->getStringValue(); } diff --git a/libqpdf/qpdf/QPDFObject_private.hh b/libqpdf/qpdf/QPDFObject_private.hh index 1c3dadc9..e5748006 100644 --- a/libqpdf/qpdf/QPDFObject_private.hh +++ b/libqpdf/qpdf/QPDFObject_private.hh @@ -5,8 +5,8 @@ // include/qpdf/QPDFObject.hh. See comments there for an explanation. #include -#include #include +#include #include #include @@ -43,18 +43,26 @@ class QPDFObject { return value->getStringValue(); } + // Return a unique type code for the resolved object + qpdf_object_type_e + getResolvedTypeCode() const + { + auto tc = value->type_code; + return tc == ::ot_unresolved + ? QPDF::Resolver::resolved(value->qpdf, value->og)->value->type_code + : tc; + } // Return a unique type code for the object qpdf_object_type_e - getTypeCode() const + getTypeCode() const noexcept { return value->type_code; } - // Return a string literal that describes the type, useful for debugging and testing char const* getTypeName() const { - return value->type_name; + return resolved_object()->value->type_name; } QPDF* @@ -157,20 +165,23 @@ class QPDFObject { return value->type_code == ::ot_unresolved; } - void - resolve() + const QPDFObject* + resolved_object() const { - if (isUnresolved()) { - doResolve(); - } + return isUnresolved() ? QPDF::Resolver::resolved(value->qpdf, value->og) : this; } - void doResolve(); template T* - as() + as() const { - return dynamic_cast(value.get()); + if (auto result = dynamic_cast(value.get())) { + return result; + } else { + return isUnresolved() + ? dynamic_cast(QPDF::Resolver::resolved(value->qpdf, value->og)->value.get()) + : nullptr; + } } private: diff --git a/libqpdf/qpdf/QPDF_Unresolved.hh b/libqpdf/qpdf/QPDF_Unresolved.hh index b4c1d9e4..df443d34 100644 --- a/libqpdf/qpdf/QPDF_Unresolved.hh +++ b/libqpdf/qpdf/QPDF_Unresolved.hh @@ -11,6 +11,7 @@ class QPDF_Unresolved: public QPDFValue std::shared_ptr copy(bool shallow = false) override; std::string unparse() override; void writeJSON(int json_version, JSON::Writer& p) override; + std::string getStringValue() const override; private: QPDF_Unresolved(QPDF* qpdf, QPDFObjGen const& og); From f49f43fed125eef4f7dd72d1ad73d4c2e306c5bd Mon Sep 17 00:00:00 2001 From: m-holger Date: Mon, 17 Jun 2024 13:57:21 +0100 Subject: [PATCH 2/3] Add separate FUTURE header file for QPDFObjectHandle --- include/qpdf/QPDFObjectHandle.hh | 46 +- include/qpdf/QPDFObjectHandle_future.hh | 1637 +++++++++++++++++++++++ 2 files changed, 1659 insertions(+), 24 deletions(-) create mode 100644 include/qpdf/QPDFObjectHandle_future.hh diff --git a/include/qpdf/QPDFObjectHandle.hh b/include/qpdf/QPDFObjectHandle.hh index 0a03c035..5e02fca2 100644 --- a/include/qpdf/QPDFObjectHandle.hh +++ b/include/qpdf/QPDFObjectHandle.hh @@ -22,23 +22,27 @@ #ifndef QPDFOBJECTHANDLE_HH #define QPDFOBJECTHANDLE_HH -#include -#include -#include +#ifdef QPDF_FUTURE +# include +#else -#include -#include -#include -#include -#include -#include +# include +# include +# include -#include -#include -#include -#include // unused -- remove in qpdf 12 (see #785) -#include -#include +# include +# include +# include +# include +# include +# include + +# include +# include +# include +# include // unused -- remove in qpdf 12 (see #785) +# include +# include class Pipeline; class QPDF; @@ -291,13 +295,6 @@ class QPDFObjectHandle QPDF_DLL QPDFObjectHandle& operator=(QPDFObjectHandle const&) = default; -#ifdef QPDF_FUTURE - QPDF_DLL - QPDFObjectHandle(QPDFObjectHandle&&) = default; - QPDF_DLL - QPDFObjectHandle& operator=(QPDFObjectHandle&&) = default; -#endif - QPDF_DLL inline bool isInitialized() const; @@ -1399,7 +1396,7 @@ class QPDFObjectHandle std::shared_ptr obj; }; -#ifndef QPDF_NO_QPDF_STRING +# ifndef QPDF_NO_QPDF_STRING // This is short for QPDFObjectHandle::parse, so you can do // auto oh = "<< /Key (value) >>"_qpdf; @@ -1415,7 +1412,7 @@ QPDF_DLL QPDFObjectHandle operator ""_qpdf(char const* v, size_t len); /* clang-format on */ -#endif // QPDF_NO_QPDF_STRING +# endif // QPDF_NO_QPDF_STRING class QPDFObjectHandle::QPDFDictItems { @@ -1637,4 +1634,5 @@ QPDFObjectHandle::isInitialized() const return obj != nullptr; } +#endif // QPDF_FUTURE #endif // QPDFOBJECTHANDLE_HH diff --git a/include/qpdf/QPDFObjectHandle_future.hh b/include/qpdf/QPDFObjectHandle_future.hh new file mode 100644 index 00000000..5a110c40 --- /dev/null +++ b/include/qpdf/QPDFObjectHandle_future.hh @@ -0,0 +1,1637 @@ +// Copyright (c) 2005-2024 Jay Berkenbilt +// +// This file is part of qpdf. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Versions of qpdf prior to version 7 were released under the terms +// of version 2.0 of the Artistic License. At your option, you may +// continue to consider qpdf to be licensed under those terms. Please +// see the manual for additional information. + +#ifndef QPDFOBJECTHANDLE_FUTURE_HH +#define QPDFOBJECTHANDLE_FUTURE_HH + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +class Pipeline; +class QPDF; +class QPDF_Array; +class QPDF_Bool; +class QPDF_Dictionary; +class QPDF_InlineImage; +class QPDF_Integer; +class QPDF_Name; +class QPDF_Null; +class QPDF_Operator; +class QPDF_Real; +class QPDF_Reserved; +class QPDF_Stream; +class QPDF_String; +class QPDFObject; +class QPDFTokenizer; +class QPDFExc; +class Pl_QPDFTokenizer; +class QPDFMatrix; +class QPDFParser; + +class QPDFObjectHandle +{ + friend class QPDFParser; + + public: + // This class is used by replaceStreamData. It provides an alternative way of associating + // stream data with a stream. See comments on replaceStreamData and newStream for additional + // details. + class QPDF_DLL_CLASS StreamDataProvider + { + public: + QPDF_DLL + StreamDataProvider(bool supports_retry = false); + + QPDF_DLL + virtual ~StreamDataProvider(); + // The implementation of this function must write stream data to the given pipeline. The + // stream data must conform to whatever filters are explicitly associated with the stream. + // QPDFWriter may, in some cases, add compression, but if it does, it will update the + // filters as needed. Every call to provideStreamData for a given stream must write the same + // data. Note that, when writing linearized files, qpdf will call your provideStreamData + // twice, and if it generates different output, you risk generating invalid output or having + // qpdf throw an exception. The object ID and generation passed to this method are those + // that belong to the stream on behalf of which the provider is called. They may be ignored + // or used by the implementation for indexing or other purposes. This information is made + // available just to make it more convenient to use a single StreamDataProvider object to + // provide data for multiple streams. + + // A few things to keep in mind: + // + // * Stream data providers must not modify any objects since they may be called after some + // parts of the file have already been written. + // + // * Since qpdf may call provideStreamData multiple times when writing linearized files, if + // the work done by your stream data provider is slow or computationally intensive, you + // might want to implement your own cache. + // + // * Once you have called replaceStreamData, the original stream data is no longer directly + // accessible from the stream, but this is easy to work around by copying the stream to + // a separate QPDF object. The qpdf library implements this very efficiently without + // actually making a copy of the stream data. You can find examples of this pattern in + // some of the examples, including pdf-custom-filter.cc and pdf-invert-images.cc. + + // Prior to qpdf 10.0.0, it was not possible to handle errors the way pipeStreamData does or + // to pass back success. Starting in qpdf 10.0.0, those capabilities have been added by + // allowing an alternative provideStreamData to be implemented. You must implement at least + // one of the versions of provideStreamData below. If you implement the version that + // supports retry and returns a value, you should pass true as the value of supports_retry + // in the base class constructor. This will cause the library to call that version of the + // method, which should also return a boolean indicating whether it ran without errors. + QPDF_DLL + virtual void provideStreamData(QPDFObjGen const& og, Pipeline* pipeline); + QPDF_DLL + virtual bool provideStreamData( + QPDFObjGen const& og, Pipeline* pipeline, bool suppress_warnings, bool will_retry); + QPDF_DLL virtual void provideStreamData(int objid, int generation, Pipeline* pipeline); + QPDF_DLL virtual bool provideStreamData( + int objid, int generation, Pipeline* pipeline, bool suppress_warnings, bool will_retry); + QPDF_DLL + bool supportsRetry(); + + private: + bool supports_retry; + }; + + // The TokenFilter class provides a way to filter content streams in a lexically aware fashion. + // TokenFilters can be attached to streams using the addTokenFilter or addContentTokenFilter + // methods or can be applied on the spot by filterPageContents. You may also use + // Pl_QPDFTokenizer directly if you need full control. + // + // The handleToken method is called for each token, including the eof token, and then handleEOF + // is called at the very end. Handlers may call write (or writeToken) to pass data downstream. + // Please see examples/pdf-filter-tokens.cc and examples/pdf-count-strings.cc for examples of + // using TokenFilters. + // + // Please note that when you call token.getValue() on a token of type tt_string or tt_name, you + // get the canonical, "parsed" representation of the token. For a string, this means that there + // are no delimiters, and for a name, it means that all escaping (# followed by two hex digits) + // has been resolved. qpdf's internal representation of a name includes the leading slash. As + // such, you can't write the value of token.getValue() directly to output that is supposed to be + // valid PDF syntax. If you want to do that, you need to call writeToken() instead, or you can + // retrieve the token as it appeared in the input with token.getRawValue(). To construct a new + // string or name token from a canonical representation, use + // QPDFTokenizer::Token(QPDFTokenizer::tt_string, "parsed-str") or + // QPDFTokenizer::Token(QPDFTokenizer::tt_name, + // "/Canonical-Name"). Tokens created this way won't have a PDF-syntax raw value, but you can + // still write them with writeToken(). Example: + // writeToken(QPDFTokenizer::Token(QPDFTokenizer::tt_name, "/text/plain")) + // would write `/text#2fplain`, and + // writeToken(QPDFTokenizer::Token(QPDFTokenizer::tt_string, "a\\(b")) would write `(a\(b)`. + class QPDF_DLL_CLASS TokenFilter + { + public: + QPDF_DLL + TokenFilter() = default; + QPDF_DLL + virtual ~TokenFilter() = default; + virtual void handleToken(QPDFTokenizer::Token const&) = 0; + QPDF_DLL + virtual void handleEOF(); + + class PipelineAccessor + { + friend class Pl_QPDFTokenizer; + + private: + static void + setPipeline(TokenFilter* f, Pipeline* p) + { + f->setPipeline(p); + } + }; + + protected: + QPDF_DLL + void write(char const* data, size_t len); + QPDF_DLL + void write(std::string const& str); + QPDF_DLL + void writeToken(QPDFTokenizer::Token const&); + + private: + QPDF_DLL_PRIVATE + void setPipeline(Pipeline*); + + Pipeline* pipeline; + }; + + // This class is used by parse to decrypt strings when reading an object that contains encrypted + // strings. + class StringDecrypter + { + public: + QPDF_DLL + virtual ~StringDecrypter() = default; + virtual void decryptString(std::string& val) = 0; + }; + + // This class is used by parsePageContents. Callers must instantiate a subclass of this with + // handlers defined to accept QPDFObjectHandles that are parsed from the stream. + class QPDF_DLL_CLASS ParserCallbacks + { + public: + QPDF_DLL + virtual ~ParserCallbacks() = default; + // One of the handleObject methods must be overridden. + QPDF_DLL + virtual void handleObject(QPDFObjectHandle); + QPDF_DLL + virtual void handleObject(QPDFObjectHandle, size_t offset, size_t length); + + virtual void handleEOF() = 0; + + // Override this if you want to know the full size of the contents, possibly after + // concatenation of multiple streams. This is called before the first call to handleObject. + QPDF_DLL + virtual void contentSize(size_t); + + protected: + // Implementors may call this method during parsing to terminate parsing early. This method + // throws an exception that is caught by parsePageContents, so its effect is immediate. + QPDF_DLL + void terminateParsing(); + }; + + // Convenience object for rectangles + class Rectangle + { + public: + Rectangle() : + llx(0.0), + lly(0.0), + urx(0.0), + ury(0.0) + { + } + Rectangle(double llx, double lly, double urx, double ury) : + llx(llx), + lly(lly), + urx(urx), + ury(ury) + { + } + + double llx; + double lly; + double urx; + double ury; + }; + + // Convenience object for transformation matrices. See also QPDFMatrix. Unfortunately we can't + // replace this with QPDFMatrix because QPDFMatrix's default constructor creates the identity + // transform matrix and this one is all zeroes. + class Matrix + { + public: + Matrix() : + a(0.0), + b(0.0), + c(0.0), + d(0.0), + e(0.0), + f(0.0) + { + } + Matrix(double a, double b, double c, double d, double e, double f) : + a(a), + b(b), + c(c), + d(d), + e(e), + f(f) + { + } + + double a; + double b; + double c; + double d; + double e; + double f; + }; + + QPDF_DLL + QPDFObjectHandle() = default; + QPDF_DLL + QPDFObjectHandle(QPDFObjectHandle const&) = default; + QPDF_DLL + QPDFObjectHandle& operator=(QPDFObjectHandle const&) = default; + + QPDF_DLL + QPDFObjectHandle(QPDFObjectHandle&&) = default; + QPDF_DLL + QPDFObjectHandle& operator=(QPDFObjectHandle&&) = default; + + QPDF_DLL + inline bool isInitialized() const; + + // This method returns true if the QPDFObjectHandle objects point to exactly the same underlying + // object, meaning that changes to one are reflected in the other, or "if you paint one, the + // other one changes color." This does not perform a structural comparison of the contents of + // the objects. + QPDF_DLL + bool isSameObjectAs(QPDFObjectHandle const&) const; + + // Return type code and type name of underlying object. These are useful for doing rapid type + // tests (like switch statements) or for testing and debugging. + QPDF_DLL + qpdf_object_type_e getTypeCode(); + QPDF_DLL + char const* getTypeName(); + + // Exactly one of these will return true for any initialized object. Operator and InlineImage + // are only allowed in content streams. + QPDF_DLL + bool isBool(); + QPDF_DLL + bool isNull(); + QPDF_DLL + bool isInteger(); + QPDF_DLL + bool isReal(); + QPDF_DLL + bool isName(); + QPDF_DLL + bool isString(); + QPDF_DLL + bool isOperator(); + QPDF_DLL + bool isInlineImage(); + QPDF_DLL + bool isArray(); + QPDF_DLL + bool isDictionary(); + QPDF_DLL + bool isStream(); + QPDF_DLL + bool isReserved(); + + // True for objects that are direct nulls. Does not attempt to resolve objects. This is intended + // for internal use, but it can be used as an efficient way to check for nulls that are not + // indirect objects. + QPDF_DLL + bool isDirectNull() const; + + // This returns true in addition to the query for the specific type for indirect objects. + QPDF_DLL + inline bool isIndirect() const; + + // This returns true for indirect objects from a QPDF that has been destroyed. Trying unparse + // such an object will throw a logic_error. + QPDF_DLL + bool isDestroyed(); + + // True for everything except array, dictionary, stream, word, and inline image. + QPDF_DLL + bool isScalar(); + + // True if the object is a name object representing the provided name. + QPDF_DLL + bool isNameAndEquals(std::string const& name); + + // True if the object is a dictionary of the specified type and subtype, if any. + QPDF_DLL + bool isDictionaryOfType(std::string const& type, std::string const& subtype = ""); + + // True if the object is a stream of the specified type and subtype, if any. + QPDF_DLL + bool isStreamOfType(std::string const& type, std::string const& subtype = ""); + + // Public factory methods + + // Wrap an object in an array if it is not already an array. This is a helper for cases in which + // something in a PDF may either be a single item or an array of items, which is a common idiom. + QPDF_DLL + QPDFObjectHandle wrapInArray(); + + // Construct an object of any type from a string representation of the object. Throws QPDFExc + // with an empty filename and an offset into the string if there is an error. Any indirect + // object syntax (obj gen R) will cause a logic_error exception to be thrown. If + // object_description is provided, it will appear in the message of any QPDFExc exception thrown + // for invalid syntax. See also the global `operator ""_qpdf` defined below. + QPDF_DLL + static QPDFObjectHandle + parse(std::string const& object_str, std::string const& object_description = ""); + + // Construct an object of any type from a string representation of the object. Indirect object + // syntax (obj gen R) is allowed and will create indirect references within the passed-in + // context. If object_description is provided, it will appear in the message of any QPDFExc + // exception thrown for invalid syntax. Note that you can't parse an indirect object reference + // all by itself as parse will stop at the end of the first complete object, which will just be + // the first number and will report that there is trailing data at the end of the string. + QPDF_DLL + static QPDFObjectHandle + parse(QPDF* context, std::string const& object_str, std::string const& object_description = ""); + + // Construct an object as above by reading from the given InputSource at its current position + // and using the tokenizer you supply. Indirect objects and encrypted strings are permitted. + // This method was intended to be called by QPDF for parsing objects that are ready from the + // object's input stream. + QPDF_DLL + static QPDFObjectHandle parse( + std::shared_ptr input, + std::string const& object_description, + QPDFTokenizer&, + bool& empty, + StringDecrypter* decrypter, + QPDF* context); + + // Return the offset where the object was found when parsed. A negative value means that the + // object was created without parsing. If the object is in a stream, the offset is from the + // beginning of the stream. Otherwise, the offset is from the beginning of the file. + QPDF_DLL + qpdf_offset_t getParsedOffset(); + + // Older method: stream_or_array should be the value of /Contents from a page object. It's more + // convenient to just call QPDFPageObjectHelper::parsePageContents on the page object, and error + // messages will also be more useful because the page object information will be known. + QPDF_DLL + static void parseContentStream(QPDFObjectHandle stream_or_array, ParserCallbacks* callbacks); + + // When called on a stream or stream array that is some page's content streams, do the same as + // pipePageContents. This method is a lower level way to do what + // QPDFPageObjectHelper::pipePageContents does, but it allows you to perform this operation on a + // contents object that is disconnected from a page object. The description argument should + // describe the containing page and is used in error messages. The all_description argument is + // initialized to something that could be used to describe the result of the pipeline. It is the + // description amended with the identifiers of the underlying objects. Please note that if there + // is an array of content streams, p->finish() is called after each stream. If you pass a + // pipeline that doesn't allow write() to be called after finish(), you can wrap it in an + // instance of Pl_Concatenate and then call manualFinish() on the Pl_Concatenate pipeline at the + // end. + QPDF_DLL + void + pipeContentStreams(Pipeline* p, std::string const& description, std::string& all_description); + + // As of qpdf 8, it is possible to add custom token filters to a stream. The tokenized stream + // data is passed through the token filter after all original filters but before content stream + // normalization if requested. This is a low-level interface to add it to a stream. You will + // usually want to call QPDFPageObjectHelper::addContentTokenFilter instead, which can be + // applied to a page object, and which will automatically handle the case of pages whose + // contents are split across multiple streams. + QPDF_DLL + void addTokenFilter(std::shared_ptr token_filter); + + // Legacy helpers for parsing content streams. These methods are not going away, but newer code + // should call the correspond methods in QPDFPageObjectHelper instead. The specification and + // behavior of these methods are the same as the identically named methods in that class, but + // newer functionality will be added there. + QPDF_DLL + void parsePageContents(ParserCallbacks* callbacks); + QPDF_DLL + void filterPageContents(TokenFilter* filter, Pipeline* next = nullptr); + // See comments for QPDFPageObjectHelper::pipeContents. + QPDF_DLL + void pipePageContents(Pipeline* p); + QPDF_DLL + void addContentTokenFilter(std::shared_ptr token_filter); + // End legacy content stream helpers + + // Called on a stream to filter the stream as if it were page contents. This can be used to + // apply a TokenFilter to a form XObject, whose data is in the same format as a content stream. + QPDF_DLL + void filterAsContents(TokenFilter* filter, Pipeline* next = nullptr); + // Called on a stream to parse the stream as page contents. This can be used to parse a form + // XObject. + QPDF_DLL + void parseAsContents(ParserCallbacks* callbacks); + + // Type-specific factories + QPDF_DLL + static QPDFObjectHandle newNull(); + QPDF_DLL + static QPDFObjectHandle newBool(bool value); + QPDF_DLL + static QPDFObjectHandle newInteger(long long value); + QPDF_DLL + static QPDFObjectHandle newReal(std::string const& value); + QPDF_DLL + static QPDFObjectHandle + newReal(double value, int decimal_places = 0, bool trim_trailing_zeroes = true); + // Note about name objects: qpdf's internal representation of a PDF name is a sequence of bytes, + // excluding the NUL character, and starting with a slash. Name objects as represented in the + // PDF specification can contain characters escaped with #, but such escaping is not of concern + // when calling QPDFObjectHandle methods not directly relating to parsing. For example, + // newName("/text/plain").getName() and parse("/text#2fplain").getName() both return + // "/text/plain", while newName("/text/plain").unparse() and parse("/text#2fplain").unparse() + // both return "/text#2fplain". When working with the qpdf API for creating, retrieving, and + // modifying objects, you want to work with the internal, canonical representation. For names + // containing alphanumeric characters, dashes, and underscores, there is no difference between + // the two representations. For a lengthy discussion, see + // https://github.com/qpdf/qpdf/discussions/625. + QPDF_DLL + static QPDFObjectHandle newName(std::string const& name); + QPDF_DLL + static QPDFObjectHandle newString(std::string const& str); + // Create a string encoded from the given utf8-encoded string appropriately encoded to appear in + // PDF files outside of content streams, such as in document metadata form field values, page + // labels, outlines, and similar locations. We try ASCII first, then PDFDocEncoding, then UTF-16 + // as needed to successfully encode all the characters. + QPDF_DLL + static QPDFObjectHandle newUnicodeString(std::string const& utf8_str); + QPDF_DLL + static QPDFObjectHandle newOperator(std::string const&); + QPDF_DLL + static QPDFObjectHandle newInlineImage(std::string const&); + QPDF_DLL + static QPDFObjectHandle newArray(); + QPDF_DLL + static QPDFObjectHandle newArray(std::vector const& items); + QPDF_DLL + static QPDFObjectHandle newArray(Rectangle const&); + QPDF_DLL + static QPDFObjectHandle newArray(Matrix const&); + QPDF_DLL + static QPDFObjectHandle newArray(QPDFMatrix const&); + QPDF_DLL + static QPDFObjectHandle newDictionary(); + QPDF_DLL + static QPDFObjectHandle newDictionary(std::map const& items); + + // Create an array from a rectangle. Equivalent to the rectangle form of newArray. + QPDF_DLL + static QPDFObjectHandle newFromRectangle(Rectangle const&); + // Create an array from a matrix. Equivalent to the matrix form of newArray. + QPDF_DLL + static QPDFObjectHandle newFromMatrix(Matrix const&); + QPDF_DLL + static QPDFObjectHandle newFromMatrix(QPDFMatrix const&); + + // Note: new stream creation methods have were added to the QPDF class starting with + // version 11.2.0. The ones in this class are here for backward compatibility. + + // Create a new stream and associate it with the given qpdf object. A subsequent call must be + // made to replaceStreamData() to provide data for the stream. The stream's dictionary may be + // retrieved by calling getDict(), and the resulting dictionary may be modified. Alternatively, + // you can create a new dictionary and call replaceDict to install it. From QPDF 11.2, you can + // call QPDF::newStream() instead. + QPDF_DLL + static QPDFObjectHandle newStream(QPDF* qpdf); + + // Create a new stream and associate it with the given qpdf object. Use the given buffer as the + // stream data. The stream dictionary's /Length key will automatically be set to the size of the + // data buffer. If additional keys are required, the stream's dictionary may be retrieved by + // calling getDict(), and the resulting dictionary may be modified. This method is just a + // convenient wrapper around the newStream() and replaceStreamData(). It is a convenience + // methods for streams that require no parameters beyond the stream length. Note that you don't + // have to deal with compression yourself if you use QPDFWriter. By default, QPDFWriter will + // automatically compress uncompressed stream data. Example programs are provided that + // illustrate this. From QPDF 11.2, you can call QPDF::newStream() + // instead. + QPDF_DLL + static QPDFObjectHandle newStream(QPDF* qpdf, std::shared_ptr data); + + // Create new stream with data from string. This method will create a copy of the data rather + // than using the user-provided buffer as in the std::shared_ptr version of newStream. + // From QPDF 11.2, you can call QPDF::newStream() instead. + QPDF_DLL + static QPDFObjectHandle newStream(QPDF* qpdf, std::string const& data); + + // A reserved object is a special sentinel used for qpdf to reserve a spot for an object that is + // going to be added to the QPDF object. Normally you don't have to use this type since you can + // just call QPDF::makeIndirectObject. However, in some cases, if you have to create objects + // with circular references, you may need to create a reserved object so that you can have a + // reference to it and then replace the object later. Reserved objects have the special + // property that they can't be resolved to direct objects. This makes it possible to replace a + // reserved object with a new object while preserving existing references to them. When you are + // ready to replace a reserved object with its replacement, use QPDF::replaceReserved for this + // purpose rather than the more general QPDF::replaceObject. It is an error to try to write a + // QPDF with QPDFWriter if it has any reserved objects in it. From QPDF 11.4, you can call + // QPDF::newReserved() instead. + QPDF_DLL + static QPDFObjectHandle newReserved(QPDF* qpdf); + + // Provide an owning qpdf and object description. The library does this automatically with + // objects that are read from the input PDF and with objects that are created programmatically + // and inserted into the QPDF as a new indirect object. Most end user code will not need to call + // this. If an object has an owning qpdf and object description, it enables qpdf to give + // warnings with proper context in some cases where it would otherwise raise exceptions. It is + // okay to add objects without an owning_qpdf to objects that have one, but it is an error to + // have a QPDF contain objects with owning_qpdf set to something else. To add objects from + // another qpdf, use copyForeignObject instead. + QPDF_DLL + void setObjectDescription(QPDF* owning_qpdf, std::string const& object_description); + QPDF_DLL + bool hasObjectDescription(); + + // Accessor methods + // + // (Note: this comment is referenced in qpdf-c.h and the manual.) + // + // In PDF files, objects have specific types, but there is nothing that prevents PDF files from + // containing objects of types that aren't expected by the specification. + // + // There are two flavors of accessor methods: + // + // * getSomethingValue() returns the value and issues a type warning if the type is incorrect. + // + // * getValueAsSomething() returns false if the value is the wrong type. Otherwise, it returns + // true and initializes a reference of the appropriate type. These methods never issue type + // warnings. + // + // The getSomethingValue() accessors and some of the other methods expect objects of a + // particular type. Prior to qpdf 8, calling an accessor on a method of the wrong type, such as + // trying to get a dictionary key from an array, trying to get the string value of a number, + // etc., would throw an exception, but since qpdf 8, qpdf issues a warning and recovers using + // the following behavior: + // + // * Requesting a value of the wrong type (int value from string, array item from a scalar or + // dictionary, etc.) will return a zero-like value for that type: false for boolean, 0 for + // number, the empty string for string, or the null object for an object handle. + // + // * Accessing an array item that is out of bounds will return a null object. + // + // * Attempts to mutate an object of the wrong type (e.g., attempting to add a dictionary key to + // a scalar or array) will be ignored. + // + // When any of these fallback behaviors are used, qpdf issues a warning. Starting in qpdf 10.5, + // these warnings have the error code qpdf_e_object. Prior to 10.5, they had the error code + // qpdf_e_damaged_pdf. If the QPDFObjectHandle is associated with a QPDF object (as is the case + // for all objects whose origin was a PDF file), the warning is issued using the normal warning + // mechanism (as described in QPDF.hh), making it possible to suppress or otherwise detect them. + // If the QPDFObjectHandle is not associated with a QPDF object (meaning it was created + // programmatically), an exception will be thrown. + // + // The way to avoid getting any type warnings or exceptions, even when working with malformed + // PDF files, is to always check the type of a QPDFObjectHandle before accessing it (for + // example, make sure that isString() returns true before calling getStringValue()) and to + // always be sure that any array indices are in bounds. + // + // For additional discussion and rationale for this behavior, see the section in the QPDF manual + // entitled "Object Accessor Methods". + + // Methods for bool objects + QPDF_DLL + bool getBoolValue(); + QPDF_DLL + bool getValueAsBool(bool&); + + // Methods for integer objects. Note: if an integer value is too big (too far away from zero in + // either direction) to fit in the requested return type, the maximum or minimum value for that + // return type may be returned. For example, on a system with 32-bit int, a numeric object with + // a value of 2^40 (or anything too big for 32 bits) will be returned as INT_MAX. + QPDF_DLL + long long getIntValue(); + QPDF_DLL + bool getValueAsInt(long long&); + QPDF_DLL + int getIntValueAsInt(); + QPDF_DLL + bool getValueAsInt(int&); + QPDF_DLL + unsigned long long getUIntValue(); + QPDF_DLL + bool getValueAsUInt(unsigned long long&); + QPDF_DLL + unsigned int getUIntValueAsUInt(); + QPDF_DLL + bool getValueAsUInt(unsigned int&); + + // Methods for real objects + QPDF_DLL + std::string getRealValue(); + QPDF_DLL + bool getValueAsReal(std::string&); + + // Methods that work for both integer and real objects + QPDF_DLL + bool isNumber(); + QPDF_DLL + double getNumericValue(); + QPDF_DLL + bool getValueAsNumber(double&); + + // Methods for name objects. The returned name value is in qpdf's canonical form with all + // escaping resolved. See comments for newName() for details. + QPDF_DLL + std::string getName(); + QPDF_DLL + bool getValueAsName(std::string&); + + // Methods for string objects + QPDF_DLL + std::string getStringValue(); + QPDF_DLL + bool getValueAsString(std::string&); + + // If a string starts with the UTF-16 marker, it is converted from UTF-16 to UTF-8. Otherwise, + // it is treated as a string encoded with PDF Doc Encoding. PDF Doc Encoding is identical to + // ISO-8859-1 except in the range from 0200 through 0240, where there is a mapping of characters + // to Unicode. QPDF versions prior to version 8.0.0 erroneously left characters in that range + // unmapped. + QPDF_DLL + std::string getUTF8Value(); + QPDF_DLL + bool getValueAsUTF8(std::string&); + + // Methods for content stream objects + QPDF_DLL + std::string getOperatorValue(); + QPDF_DLL + bool getValueAsOperator(std::string&); + QPDF_DLL + std::string getInlineImageValue(); + QPDF_DLL + bool getValueAsInlineImage(std::string&); + + // Methods for array objects; see also name and array objects. + + // Return an object that enables iteration over members. You can do + // + // for (auto iter: obj.aitems()) + // { + // // iter is an array element + // } + class QPDFArrayItems; + QPDF_DLL + QPDFArrayItems aitems(); + + QPDF_DLL + int getArrayNItems(); + QPDF_DLL + QPDFObjectHandle getArrayItem(int n); + // Note: QPDF arrays internally optimize memory for arrays containing lots of nulls. Calling + // getArrayAsVector may cause a lot of memory to be allocated for very large arrays with lots of + // nulls. + QPDF_DLL + std::vector getArrayAsVector(); + QPDF_DLL + bool isRectangle(); + // If the array is an array of four numeric values, return as a rectangle. Otherwise, return the + // rectangle [0, 0, 0, 0] + QPDF_DLL + Rectangle getArrayAsRectangle(); + QPDF_DLL + bool isMatrix(); + // If the array is an array of six numeric values, return as a matrix. Otherwise, return the + // matrix [1, 0, 0, 1, 0, 0] + QPDF_DLL + Matrix getArrayAsMatrix(); + + // Methods for dictionary objects. In all dictionary methods, keys are specified/represented as + // canonical name strings starting with a leading slash and not containing any PDF syntax + // escaping. See comments for getName() for details. + + // Return an object that enables iteration over members. You can do + // + // for (auto iter: obj.ditems()) + // { + // // iter.first is the key + // // iter.second is the value + // } + class QPDFDictItems; + QPDF_DLL + QPDFDictItems ditems(); + + // Return true if key is present. Keys with null values are treated as if they are not present. + // This is as per the PDF spec. + QPDF_DLL + bool hasKey(std::string const&); + // Return the value for the key. If the key is not present, null is returned. + QPDF_DLL + QPDFObjectHandle getKey(std::string const&); + // If the object is null, return null. Otherwise, call getKey(). This makes it easier to access + // lower-level dictionaries, as in + // auto font = page.getKeyIfDict("/Resources").getKeyIfDict("/Font"); + QPDF_DLL + QPDFObjectHandle getKeyIfDict(std::string const&); + // Return all keys. Keys with null values are treated as if they are not present. This is as + // per the PDF spec. + QPDF_DLL + std::set getKeys(); + // Return dictionary as a map. Entries with null values are included. + QPDF_DLL + std::map getDictAsMap(); + + // Methods for name and array objects. The name value is in qpdf's canonical form with all + // escaping resolved. See comments for newName() for details. + QPDF_DLL + bool isOrHasName(std::string const&); + + // Make all resources in a resource dictionary indirect. This just goes through all entries of + // top-level subdictionaries and converts any direct objects to indirect objects. This can be + // useful to call before mergeResources if it is going to be called multiple times to prevent + // resources from being copied multiple times. + QPDF_DLL + void makeResourcesIndirect(QPDF& owning_qpdf); + + // Merge resource dictionaries. If the "conflicts" parameter is provided, conflicts in + // dictionary subitems are resolved, and "conflicts" is initialized to a map such that + // conflicts[resource_type][old_key] == [new_key] + // + // See also makeResourcesIndirect, which can be useful to call before calling this. + // + // This method does nothing if both this object and the other object are not dictionaries. + // Otherwise, it has following behavior, where "object" refers to the object whose method is + // invoked, and "other" refers to the argument: + // + // * For each key in "other" whose value is an array: + // * If "object" does not have that entry, shallow copy it. + // * Otherwise, if "object" has an array in the same place, append to that array any objects + // in "other"'s array that are not already present. + // * For each key in "other" whose value is a dictionary: + // * If "object" does not have that entry, shallow copy it. + // * Otherwise, for each key in the subdictionary: + // * If key is not present in "object"'s entry, shallow copy it if direct or just add it if + // indirect. + // * Otherwise, if conflicts are being detected: + // * If there is a key (oldkey) already in the dictionary that points to the same indirect + // destination as key, indicate that key was replaced by oldkey. This would happen if + // these two resource dictionaries have previously been merged. + // * Otherwise pick a new key (newkey) that is unique within the resource dictionary, + // store that in the resource dictionary with key's destination as its destination, and + // indicate that key was replaced by newkey. + // + // The primary purpose of this method is to facilitate merging of resource dictionaries that are + // supposed to have the same scope as each other. For example, this can be used to merge a form + // XObject's /Resources dictionary with a form field's /DR or to merge two /DR dictionaries. The + // "conflicts" parameter may be previously initialized. This method adds to whatever is already + // there, which can be useful when merging with multiple things. + QPDF_DLL + void mergeResources( + QPDFObjectHandle other, + std::map>* conflicts = nullptr); + + // Get all resource names from a resource dictionary. If this object is a dictionary, this + // method returns a set of all the keys in all top-level subdictionaries. For resources + // dictionaries, this is the collection of names that may be referenced in the content stream. + QPDF_DLL + std::set getResourceNames(); + + // Find a unique name within a resource dictionary starting with a given prefix. This method + // works by appending a number to the given prefix. It searches starting with min_suffix and + // sets min_suffix to selected value upon return. This can be used to increase efficiency if + // adding multiple items with the same prefix. (Why doesn't it set min_suffix to the next + // number? Well, maybe you aren't going to actually use the name it returns.) If you are calling + // this multiple times on the same resource dictionary, you can initialize resource_names by + // calling getResourceNames(), incrementally update it as you add resources, and keep passing it + // in so that getUniqueResourceName doesn't have to traverse the resource dictionary each time + // it's called. + QPDF_DLL + std::string getUniqueResourceName( + std::string const& prefix, + int& min_suffix, + std::set* resource_names = nullptr); + + // A QPDFObjectHandle has an owning QPDF if it is associated with ("owned by") a specific QPDF + // object. Indirect objects always have an owning QPDF. Direct objects that are read from the + // input source will also have an owning QPDF. Programmatically created objects will only have + // one if setObjectDescription was called. + // + // When the QPDF object that owns an object is destroyed, the object is changed into a null, and + // its owner is cleared. Therefore you should not retain the value of an owning QPDF beyond the + // life of the QPDF. If in doubt, ask for it each time you need it. + + // getOwningQPDF returns a pointer to the owning QPDF is the object has one. Otherwise, it + // returns a null pointer. Use this when you are able to handle the case of an object that + // doesn't have an owning QPDF. + QPDF_DLL + QPDF* getOwningQPDF() const; + // getQPDF, new in qpdf 11, returns a reference owning QPDF. If there is none, it throws a + // runtime_error. Use this when you know the object has to have an owning QPDF, such as when + // it's a known indirect object. Since streams are always indirect objects, this method can be + // used safely for streams. If error_msg is specified, it will be used at the contents of the + // runtime_error if there is now owner. + QPDF_DLL + QPDF& getQPDF(std::string const& error_msg = "") const; + + // Create a shallow copy of an object as a direct object, but do not traverse across indirect + // object boundaries. That means that, for dictionaries and arrays, any keys or items that were + // indirect objects will still be indirect objects that point to the same place. In the + // strictest sense, this is not a shallow copy because it recursively descends arrays and + // dictionaries; it just doesn't cross over indirect objects. See also unsafeShallowCopy(). You + // can't copy a stream this way. See copyStream() instead. + QPDF_DLL + QPDFObjectHandle shallowCopy(); + + // Create a true shallow copy of an array or dictionary, just copying the immediate items + // (array) or keys (dictionary). This is "unsafe" because, if you *modify* any of the items in + // the copy, you are modifying the original, which is almost never what you want. However, if + // your intention is merely to *replace* top-level items or keys and not to modify lower-level + // items in the copy, this method is much faster than shallowCopy(). + QPDF_DLL + QPDFObjectHandle unsafeShallowCopy(); + + // Create a copy of this stream. The new stream and the old stream are independent: after the + // copy, either the original or the copy's dictionary or data can be modified without affecting + // the other. This uses StreamDataProvider internally, so no unnecessary copies of the stream's + // data are made. If the source stream's data is already being provided by a StreamDataProvider, + // the new stream will use the same one, so you have to make sure your StreamDataProvider can + // handle that case. But if you're already using a StreamDataProvider, you probably don't need + // to call this method. + QPDF_DLL + QPDFObjectHandle copyStream(); + + // Mutator methods. + + // Since qpdf 11: for mutators that may add or remove an item, there are additional versions + // whose names contain "AndGet" that return the added or removed item. For example: + // + // auto new_dict = dict.replaceKeyAndGetNew( + // "/New", QPDFObjectHandle::newDictionary()); + // + // auto old_value = dict.replaceKeyAndGetOld( + // "/New", "(something)"_qpdf); + + // Recursively copy this object, making it direct. An exception is thrown if a loop is detected. + // With allow_streams true, keep indirect object references to streams. Otherwise, throw an + // exception if any sub-object is a stream. Note that, when allow_streams is true and a stream + // is found, the resulting object is still associated with the containing qpdf. When + // allow_streams is false, the object will no longer be connected to the original QPDF object + // after this call completes successfully. + QPDF_DLL + void makeDirect(bool allow_streams = false); + + // Mutator methods for array objects + QPDF_DLL + void setArrayItem(int, QPDFObjectHandle const&); + QPDF_DLL + void setArrayFromVector(std::vector const& items); + // Insert an item before the item at the given position ("at") so that it has that position + // after insertion. If "at" is equal to the size of the array, insert the item at the end. + QPDF_DLL + void insertItem(int at, QPDFObjectHandle const& item); + // Like insertItem but return the item that was inserted. + QPDF_DLL + QPDFObjectHandle insertItemAndGetNew(int at, QPDFObjectHandle const& item); + // Append an item to an array. + QPDF_DLL + void appendItem(QPDFObjectHandle const& item); + // Append an item, and return the newly added item. + QPDF_DLL + QPDFObjectHandle appendItemAndGetNew(QPDFObjectHandle const& item); + // Remove the item at that position, reducing the size of the array by one. + QPDF_DLL + void eraseItem(int at); + // Erase and item and return the item that was removed. + QPDF_DLL + QPDFObjectHandle eraseItemAndGetOld(int at); + + // Mutator methods for dictionary objects + + // Replace value of key, adding it if it does not exist. If value is null, remove the key. + QPDF_DLL + void replaceKey(std::string const& key, QPDFObjectHandle const& value); + // Replace value of key and return the value. + QPDF_DLL + QPDFObjectHandle replaceKeyAndGetNew(std::string const& key, QPDFObjectHandle const& value); + // Replace value of key and return the old value, or null if the key was previously not present. + QPDF_DLL + QPDFObjectHandle replaceKeyAndGetOld(std::string const& key, QPDFObjectHandle const& value); + // Remove key, doing nothing if key does not exist. + QPDF_DLL + void removeKey(std::string const& key); + // Remove key and return the old value. If the old value didn't exist, return a null object. + QPDF_DLL + QPDFObjectHandle removeKeyAndGetOld(std::string const& key); + + // ABI: Remove in qpdf 12 + [[deprecated("use replaceKey -- it does the same thing")]] QPDF_DLL void + replaceOrRemoveKey(std::string const& key, QPDFObjectHandle const&); + + // Methods for stream objects + QPDF_DLL + QPDFObjectHandle getDict(); + + // By default, or if true passed, QPDFWriter will attempt to filter a stream based on decode + // level, whether compression is enabled, and its ability to filter. Passing false will prevent + // QPDFWriter from attempting to filter the stream even if it can. This includes both decoding + // and compressing. This makes it possible for you to prevent QPDFWriter from uncompressing and + // recompressing a stream that it knows how to operate on for any application-specific reason, + // such as that you have already optimized its filtering. Note that this doesn't affect any + // other ways to get the stream's data, such as pipeStreamData or getStreamData. + QPDF_DLL + void setFilterOnWrite(bool); + QPDF_DLL + bool getFilterOnWrite(); + + // If addTokenFilter has been called for this stream, then the original data should be + // considered to be modified. This means we should avoid optimizations such as not filtering a + // stream that is already compressed. + QPDF_DLL + bool isDataModified(); + + // Returns filtered (uncompressed) stream data. Throws an exception if the stream is filtered + // and we can't decode it. + QPDF_DLL + std::shared_ptr getStreamData(qpdf_stream_decode_level_e level = qpdf_dl_generalized); + + // Returns unfiltered (raw) stream data. + QPDF_DLL + std::shared_ptr getRawStreamData(); + + // Write stream data through the given pipeline. A null pipeline value may be used if all you + // want to do is determine whether a stream is filterable and would be filtered based on the + // provided flags. If flags is 0, write raw stream data and return false. Otherwise, the flags + // alter the behavior in the following way: + // + // encode_flags: + // + // qpdf_sf_compress -- compress data with /FlateDecode if no other compression filters are + // applied. + // + // qpdf_sf_normalize -- tokenize as content stream and normalize tokens + // + // decode_level: + // + // qpdf_dl_none -- do not decode any streams. + // + // qpdf_dl_generalized -- decode supported general-purpose filters. This includes + // /ASCIIHexDecode, /ASCII85Decode, /LZWDecode, and /FlateDecode. + // + // qpdf_dl_specialized -- in addition to generalized filters, also decode supported non-lossy + // specialized filters. This includes /RunLengthDecode. + // + // qpdf_dl_all -- in addition to generalized and non-lossy specialized filters, decode supported + // lossy filters. This includes /DCTDecode. + // + // If, based on the flags and the filters and decode parameters, we determine that we know how + // to apply all requested filters, do so and return true if we are successful. + // + // The exact meaning of the return value differs the different versions of this function, but + // for any version, the meaning has been the same. For the main version, added in qpdf 10, the + // return value indicates whether the overall operation succeeded. The filter parameter, if + // specified, will be set to whether or not filtering was attempted. If filtering was not + // requested, this value will be false even if the overall operation succeeded. + // + // If filtering is requested but this method returns false, it means there was some error in the + // filtering, in which case the resulting data is likely partially filtered and/or incomplete + // and may not be consistent with the configured filters. QPDFWriter handles this by attempting + // to get the stream data without filtering, but callers should consider a false return value + // when decode_level is not qpdf_dl_none to be a potential loss of data. If you intend to retry + // in that case, pass true as the value of will_retry. This changes the warning issued by the + // library to indicate that the operation will be retried without filtering to avoid data loss. + + // Return value is overall success, even if filtering is not requested. + QPDF_DLL + bool pipeStreamData( + Pipeline*, + bool* filtering_attempted, + int encode_flags, + qpdf_stream_decode_level_e decode_level, + bool suppress_warnings = false, + bool will_retry = false); + + // Legacy version. Return value is whether filtering was attempted. There is no way to determine + // success if filtering was not attempted. + QPDF_DLL + bool pipeStreamData( + Pipeline*, + int encode_flags, + qpdf_stream_decode_level_e decode_level, + bool suppress_warnings = false, + bool will_retry = false); + + // Legacy pipeStreamData. This maps to the the flags-based pipeStreamData as follows: + // filter = false -> encode_flags = 0 + // filter = true -> decode_level = qpdf_dl_generalized + // normalize = true -> encode_flags |= qpdf_sf_normalize + // compress = true -> encode_flags |= qpdf_sf_compress + // Return value is whether filtering was attempted. + QPDF_DLL + bool pipeStreamData(Pipeline*, bool filter, bool normalize, bool compress); + + // Replace a stream's dictionary. The new dictionary must be consistent with the stream's data. + // This is most appropriately used when creating streams from scratch that will use a stream + // data provider and therefore start with an empty dictionary. It may be more convenient in + // this case than calling getDict and modifying it for each key. The pdf-create example does + // this. + QPDF_DLL + void replaceDict(QPDFObjectHandle const&); + + // REPLACING STREAM DATA + + // Note about all replaceStreamData methods: whatever values are passed as filter and + // decode_parms will overwrite /Filter and /DecodeParms in the stream. Passing a null object + // (QPDFObjectHandle::newNull()) will remove those values from the stream dictionary. From qpdf + // 11, passing an *uninitialized* QPDFObjectHandle (QPDFObjectHandle()) will leave any existing + // values untouched. + + // Replace this stream's stream data with the given data buffer. The stream's /Length key is + // replaced with the length of the data buffer. The stream is interpreted as if the data read + // from the file, after any decryption filters have been applied, is as presented. + QPDF_DLL + void replaceStreamData( + std::shared_ptr data, + QPDFObjectHandle const& filter, + QPDFObjectHandle const& decode_parms); + + // Replace the stream's stream data with the given string. This method will create a copy of the + // data rather than using the user-provided buffer as in the std::shared_ptr version of + // replaceStreamData. + QPDF_DLL + void replaceStreamData( + std::string const& data, + QPDFObjectHandle const& filter, + QPDFObjectHandle const& decode_parms); + + // As above, replace this stream's stream data. Instead of directly providing a buffer with the + // stream data, call the given provider's provideStreamData method. See comments on the + // StreamDataProvider class (defined above) for details on the method. The data must be + // consistent with filter and decode_parms as provided. Although it is more complex to use this + // form of replaceStreamData than the one that takes a buffer, it makes it possible to avoid + // allocating memory for the stream data. Example programs are provided that use both forms of + // replaceStreamData. + + // Note about stream length: for any given stream, the provider must provide the same amount of + // data each time it is called. This is critical for making linearization work properly. + // Versions of qpdf before 3.0.0 required a length to be specified here. Starting with + // version 3.0.0, this is no longer necessary (or permitted). The first time the stream data + // provider is invoked for a given stream, the actual length is stored. Subsequent times, it is + // enforced that the length be the same as the first time. + + // If you have gotten a compile error here while building code that worked with older versions + // of qpdf, just omit the length parameter. You can also simplify your code by not having to + // compute the length in advance. + QPDF_DLL + void replaceStreamData( + std::shared_ptr provider, + QPDFObjectHandle const& filter, + QPDFObjectHandle const& decode_parms); + + // Starting in qpdf 10.2, you can use C++-11 function objects instead of StreamDataProvider. + + // The provider should write the stream data to the pipeline. For a one-liner to replace stream + // data with the contents of a file, pass QUtil::file_provider(filename) as provider. + QPDF_DLL + void replaceStreamData( + std::function provider, + QPDFObjectHandle const& filter, + QPDFObjectHandle const& decode_parms); + // The provider should write the stream data to the pipeline, returning true if it succeeded + // without errors. + QPDF_DLL + void replaceStreamData( + std::function provider, + QPDFObjectHandle const& filter, + QPDFObjectHandle const& decode_parms); + + // Access object ID and generation. For direct objects, return object ID 0. + + // NOTE: Be careful about calling getObjectID() and getGeneration() directly as this can lead to + // the pattern of depending on object ID or generation without the other. In general, when + // keeping track of object IDs, it's better to use QPDFObjGen instead. + + QPDF_DLL + QPDFObjGen getObjGen() const; + QPDF_DLL + inline int getObjectID() const; + QPDF_DLL + inline int getGeneration() const; + + QPDF_DLL + std::string unparse(); + QPDF_DLL + std::string unparseResolved(); + // For strings only, force binary representation. Otherwise, same as unparse. + QPDF_DLL + std::string unparseBinary(); + + // Return encoded as JSON. The constant JSON::LATEST can be used to specify the latest available + // JSON version. The JSON is generated as follows: + // * Arrays, dictionaries, booleans, nulls, integers, and real numbers are represented by their + // native JSON types. + // * Names are encoded as strings representing the canonical representation (after parsing #xx) + // and preceded by a slash, just as unparse() returns. For example, the JSON for the + // PDF-syntax name /Text#2fPlain would be "/Text/Plain". + // * Indirect references are encoded as strings containing "obj gen R" + // * Strings + // * JSON v1: Strings are encoded as UTF-8 strings with unrepresentable binary characters + // encoded as \uHHHH. Characters in PDF Doc encoding that don't have bidirectional unicode + // mappings are not reversible. There is no way to tell the difference between a string that + // looks like a name or indirect object from an actual name or indirect object. + // * JSON v2: + // * Unicode strings and strings encoded with PDF Doc encoding that can be bidirectionally + // mapped to Unicode (which is all strings without undefined characters) are represented + // as "u:" followed by the UTF-8 encoded string. Example: + // "u:potato". + // * All other strings are represented as "b:" followed by a hexadecimal encoding of the + // string. Example: "b:0102cacb" + // * Streams + // * JSON v1: Only the stream's dictionary is encoded. There is no way to tell a stream from a + // dictionary other than context. + // * JSON v2: A stream is encoded as {"dict": {...}} with the value being the encoding of the + // stream's dictionary. Since "dict" does not otherwise represent anything, this is + // unambiguous. The getStreamJSON() call can be used to add encoding of the stream's data. + // * Object types that are only valid in content streams (inline image, operator) are serialized + // as "null". Attempting to serialize a "reserved" object is an error. + // If dereference_indirect is true and this is an indirect object, show the actual contents of + // the object. The effect of dereference_indirect applies only to this object. It is not + // recursive. + QPDF_DLL + JSON getJSON(int json_version, bool dereference_indirect = false); + + // Write the object encoded as JSON to a pipeline. This is equivalent to, but more efficient + // than, calling getJSON(json_version, dereference_indirect).write(p, depth). See the + // documentation for getJSON and JSON::write for further detail. + QPDF_DLL + void + writeJSON(int json_version, Pipeline* p, bool dereference_indirect = false, size_t depth = 0); + + // Deprecated version uses v1 for backward compatibility. + // ABI: remove for qpdf 12 + [[deprecated("Use getJSON(int version)")]] QPDF_DLL JSON + getJSON(bool dereference_indirect = false); + + // This method can be called on a stream to get a more extended JSON representation of the + // stream that includes the stream's data. The JSON object returned is always a dictionary whose + // "dict" key is an encoding of the stream's dictionary. The representation of the data is + // determined by the json_data field. + // + // The json_data field may have the value qpdf_sj_none, qpdf_sj_inline, or qpdf_sj_file. + // + // If json_data is qpdf_sj_none, stream data is not represented. + // + // If json_data is qpdf_sj_inline or qpdf_sj_file, then stream data is filtered or not based on + // the value of decode_level, which has the same meaning as with pipeStreamData. + // + // If json_data is qpdf_sj_inline, the base64-encoded stream data is included in the "data" + // field of the dictionary that is returned. + // + // If json_data is qpdf_sj_file, then the Pipeline ("p") and data_filename argument must be + // supplied. The value of data_filename is stored in the resulting json in the "datafile" key + // but is not otherwise use. The stream data itself (raw or filtered depending on decode level), + // is written to the pipeline via pipeStreamData(). + // + // NOTE: When json_data is qpdf_sj_inline, the QPDF object from which the stream originates must + // remain valid until after the JSON object is written. + QPDF_DLL + JSON getStreamJSON( + int json_version, + qpdf_json_stream_data_e json_data, + qpdf_stream_decode_level_e decode_level, + Pipeline* p, + std::string const& data_filename); + + // Legacy helper methods for commonly performed operations on pages. Newer code should use + // QPDFPageObjectHelper instead. The specification and behavior of these methods are the same as + // the identically named methods in that class, but newer functionality will be added there. + QPDF_DLL + std::map getPageImages(); + QPDF_DLL + std::vector getPageContents(); + QPDF_DLL + void addPageContents(QPDFObjectHandle contents, bool first); + QPDF_DLL + void rotatePage(int angle, bool relative); + QPDF_DLL + void coalesceContentStreams(); + // End legacy page helpers + + // Issue a warning about this object if possible. If the object has a description, a warning + // will be issued using the owning QPDF as context. Otherwise, a message will be written to the + // default logger's error stream, which is standard error if not overridden. Objects read + // normally from the file have descriptions. See comments on setObjectDescription for additional + // details. + QPDF_DLL + void warnIfPossible(std::string const& warning); + + // Provide access to specific classes for recursive disconnected(). + class DisconnectAccess + { + friend class QPDF_Dictionary; + friend class QPDF_Stream; + + private: + static void + disconnect(QPDFObjectHandle o) + { + o.disconnect(); + } + }; + + // Convenience routine: Throws if the assumption is violated. Your code will be better if you + // call one of the isType methods and handle the case of the type being wrong, but these can be + // convenient if you have already verified the type. + QPDF_DLL + void assertInitialized() const; + + QPDF_DLL + void assertNull(); + QPDF_DLL + void assertBool(); + QPDF_DLL + void assertInteger(); + QPDF_DLL + void assertReal(); + QPDF_DLL + void assertName(); + QPDF_DLL + void assertString(); + QPDF_DLL + void assertOperator(); + QPDF_DLL + void assertInlineImage(); + QPDF_DLL + void assertArray(); + QPDF_DLL + void assertDictionary(); + QPDF_DLL + void assertStream(); + QPDF_DLL + void assertReserved(); + + QPDF_DLL + void assertIndirect(); + QPDF_DLL + void assertScalar(); + QPDF_DLL + void assertNumber(); + + // The isPageObject method checks the /Type key of the object. This is not completely reliable + // as there are some otherwise valid files whose /Type is wrong for page objects. qpdf is + // slightly more accepting but may still return false here when treating the object as a page + // would work. Use this sparingly. + QPDF_DLL + bool isPageObject(); + QPDF_DLL + bool isPagesObject(); + QPDF_DLL + void assertPageObject(); + + QPDF_DLL + bool isFormXObject(); + + // Indicate if this is an image. If exclude_imagemask is true, don't count image masks as + // images. + QPDF_DLL + bool isImage(bool exclude_imagemask = true); + + // The following methods do not form part of the public API and are for internal use only. + + QPDFObjectHandle(std::shared_ptr const& obj) : + obj(obj) + { + } + std::shared_ptr + getObj() + { + return obj; + } + std::shared_ptr + getObj() const + { + return obj; + } + QPDFObject* + getObjectPtr() + { + return obj.get(); + } + QPDFObject* const + getObjectPtr() const + { + return obj.get(); + } + + void writeJSON(int json_version, JSON::Writer& p, bool dereference_indirect = false); + + private: + QPDF_Array* asArray() const; + QPDF_Bool* asBool() const; + QPDF_Dictionary* asDictionary() const; + QPDF_InlineImage* asInlineImage() const; + QPDF_Integer* asInteger() const; + QPDF_Name* asName() const; + QPDF_Null* asNull() const; + QPDF_Operator* asOperator() const; + QPDF_Real* asReal() const; + QPDF_Reserved* asReserved() const; + QPDF_Stream* asStream() const; + QPDF_Stream* asStreamWithAssert(); + QPDF_String* asString() const; + + void typeWarning(char const* expected_type, std::string const& warning); + void objectWarning(std::string const& warning); + void assertType(char const* type_name, bool istype); + void makeDirect(QPDFObjGen::set& visited, bool stop_at_streams); + void disconnect(); + void setParsedOffset(qpdf_offset_t offset); + void parseContentStream_internal(std::string const& description, ParserCallbacks* callbacks); + static void parseContentStream_data( + std::shared_ptr, + std::string const& description, + ParserCallbacks* callbacks, + QPDF* context); + std::vector + arrayOrStreamToStreamArray(std::string const& description, std::string& all_description); + static void warn(QPDF*, QPDFExc const&); + void checkOwnership(QPDFObjectHandle const&) const; + + // Moving members of QPDFObjectHandle into a smart pointer incurs a substantial performance + // penalty since QPDFObjectHandle objects are copied around so frequently. + std::shared_ptr obj; +}; + +#ifndef QPDF_NO_QPDF_STRING +// This is short for QPDFObjectHandle::parse, so you can do + +// auto oh = "<< /Key (value) >>"_qpdf; + +// If this is causing problems in your code, define QPDF_NO_QPDF_STRING to prevent the declaration +// from being here. + +/* clang-format off */ +// Disable formatting for this declaration: emacs font-lock in cc-mode (as of 28.1) treats the rest +// of the file as a string if clang-format removes the space after "operator", and as of +// clang-format 15, there's no way to prevent it from doing so. +QPDF_DLL +QPDFObjectHandle operator ""_qpdf(char const* v, size_t len); +/* clang-format on */ + +#endif // QPDF_NO_QPDF_STRING + +class QPDFObjectHandle::QPDFDictItems +{ + // This class allows C++-style iteration, including range-for iteration, around dictionaries. + // You can write + + // for (auto iter: QPDFDictItems(dictionary_obj)) + // { + // // iter.first is a string + // // iter.second is a QPDFObjectHandle + // } + + // See examples/pdf-name-number-tree.cc for a demonstration of using this API. + + public: + QPDF_DLL + QPDFDictItems(QPDFObjectHandle const& oh); + + class iterator + { + friend class QPDFDictItems; + + public: + typedef std::pair T; + using iterator_category = std::bidirectional_iterator_tag; + using value_type = T; + using difference_type = long; + using pointer = T*; + using reference = T&; + + QPDF_DLL + virtual ~iterator() = default; + QPDF_DLL + iterator& operator++(); + QPDF_DLL + iterator + operator++(int) + { + iterator t = *this; + ++(*this); + return t; + } + QPDF_DLL + iterator& operator--(); + QPDF_DLL + iterator + operator--(int) + { + iterator t = *this; + --(*this); + return t; + } + QPDF_DLL + reference operator*(); + QPDF_DLL + pointer operator->(); + QPDF_DLL + bool operator==(iterator const& other) const; + QPDF_DLL + bool + operator!=(iterator const& other) const + { + return !operator==(other); + } + + private: + iterator(QPDFObjectHandle& oh, bool for_begin); + void updateIValue(); + + class Members + { + friend class QPDFDictItems::iterator; + + public: + QPDF_DLL + ~Members() = default; + + private: + Members(QPDFObjectHandle& oh, bool for_begin); + Members() = delete; + Members(Members const&) = delete; + + QPDFObjectHandle& oh; + std::set keys; + std::set::iterator iter; + bool is_end; + }; + std::shared_ptr m; + value_type ivalue; + }; + + QPDF_DLL + iterator begin(); + QPDF_DLL + iterator end(); + + private: + QPDFObjectHandle oh; +}; + +class QPDFObjectHandle::QPDFArrayItems +{ + // This class allows C++-style iteration, including range-for iteration, around arrays. You can + // write + + // for (auto iter: QPDFArrayItems(array_obj)) + // { + // // iter is a QPDFObjectHandle + // } + + // See examples/pdf-name-number-tree.cc for a demonstration of using this API. + + public: + QPDF_DLL + QPDFArrayItems(QPDFObjectHandle const& oh); + + class iterator + { + friend class QPDFArrayItems; + + public: + typedef QPDFObjectHandle T; + using iterator_category = std::bidirectional_iterator_tag; + using value_type = T; + using difference_type = long; + using pointer = T*; + using reference = T&; + + QPDF_DLL + virtual ~iterator() = default; + QPDF_DLL + iterator& operator++(); + QPDF_DLL + iterator + operator++(int) + { + iterator t = *this; + ++(*this); + return t; + } + QPDF_DLL + iterator& operator--(); + QPDF_DLL + iterator + operator--(int) + { + iterator t = *this; + --(*this); + return t; + } + QPDF_DLL + reference operator*(); + QPDF_DLL + pointer operator->(); + QPDF_DLL + bool operator==(iterator const& other) const; + QPDF_DLL + bool + operator!=(iterator const& other) const + { + return !operator==(other); + } + + private: + iterator(QPDFObjectHandle& oh, bool for_begin); + void updateIValue(); + + class Members + { + friend class QPDFArrayItems::iterator; + + public: + QPDF_DLL + ~Members() = default; + + private: + Members(QPDFObjectHandle& oh, bool for_begin); + Members() = delete; + Members(Members const&) = delete; + + QPDFObjectHandle& oh; + int item_number; + bool is_end; + }; + std::shared_ptr m; + value_type ivalue; + }; + + QPDF_DLL + iterator begin(); + QPDF_DLL + iterator end(); + + private: + QPDFObjectHandle oh; +}; + +inline int +QPDFObjectHandle::getObjectID() const +{ + return getObjGen().getObj(); +} + +inline int +QPDFObjectHandle::getGeneration() const +{ + return getObjGen().getGen(); +} + +inline bool +QPDFObjectHandle::isIndirect() const +{ + return (obj != nullptr) && (getObjectID() != 0); +} + +inline bool +QPDFObjectHandle::isInitialized() const +{ + return obj != nullptr; +} + +#endif // QPDFOBJECTHANDLE_FUTURE_HH From 6ad16cd1fdd25b195cec64582be589622dbb0f16 Mon Sep 17 00:00:00 2001 From: m-holger Date: Mon, 17 Jun 2024 16:14:27 +0100 Subject: [PATCH 3/3] In FUTURE make QPDFObjectHandle methods const and noexcept where possible --- include/qpdf/QPDFObjectHandle.hh | 8 +- include/qpdf/QPDFObjectHandle_future.hh | 206 +++++----- libqpdf/QPDFObjectHandle.cc | 504 ++++++++++++++++++++++-- 3 files changed, 585 insertions(+), 133 deletions(-) diff --git a/include/qpdf/QPDFObjectHandle.hh b/include/qpdf/QPDFObjectHandle.hh index 5e02fca2..9df0dd52 100644 --- a/include/qpdf/QPDFObjectHandle.hh +++ b/include/qpdf/QPDFObjectHandle.hh @@ -1371,12 +1371,12 @@ class QPDFObjectHandle QPDF_Real* asReal() const; QPDF_Reserved* asReserved() const; QPDF_Stream* asStream() const; - QPDF_Stream* asStreamWithAssert(); + QPDF_Stream* asStreamWithAssert() const; QPDF_String* asString() const; - void typeWarning(char const* expected_type, std::string const& warning); - void objectWarning(std::string const& warning); - void assertType(char const* type_name, bool istype); + void typeWarning(char const* expected_type, std::string const& warning) const; + void objectWarning(std::string const& warning) const; + void assertType(char const* type_name, bool istype) const; void makeDirect(QPDFObjGen::set& visited, bool stop_at_streams); void disconnect(); void setParsedOffset(qpdf_offset_t offset); diff --git a/include/qpdf/QPDFObjectHandle_future.hh b/include/qpdf/QPDFObjectHandle_future.hh index 5a110c40..2a8a0ba6 100644 --- a/include/qpdf/QPDFObjectHandle_future.hh +++ b/include/qpdf/QPDFObjectHandle_future.hh @@ -296,48 +296,48 @@ class QPDFObjectHandle QPDFObjectHandle& operator=(QPDFObjectHandle&&) = default; QPDF_DLL - inline bool isInitialized() const; + inline bool isInitialized() const noexcept; // This method returns true if the QPDFObjectHandle objects point to exactly the same underlying // object, meaning that changes to one are reflected in the other, or "if you paint one, the // other one changes color." This does not perform a structural comparison of the contents of // the objects. QPDF_DLL - bool isSameObjectAs(QPDFObjectHandle const&) const; + bool isSameObjectAs(QPDFObjectHandle const&) const noexcept; // Return type code and type name of underlying object. These are useful for doing rapid type // tests (like switch statements) or for testing and debugging. QPDF_DLL - qpdf_object_type_e getTypeCode(); + qpdf_object_type_e getTypeCode() const; QPDF_DLL - char const* getTypeName(); + char const* getTypeName() const; // Exactly one of these will return true for any initialized object. Operator and InlineImage // are only allowed in content streams. QPDF_DLL - bool isBool(); + bool isBool() const; QPDF_DLL - bool isNull(); + bool isNull() const; QPDF_DLL - bool isInteger(); + bool isInteger() const; QPDF_DLL - bool isReal(); + bool isReal() const; QPDF_DLL - bool isName(); + bool isName() const; QPDF_DLL - bool isString(); + bool isString() const; QPDF_DLL - bool isOperator(); + bool isOperator() const; QPDF_DLL - bool isInlineImage(); + bool isInlineImage() const; QPDF_DLL - bool isArray(); + bool isArray() const; QPDF_DLL - bool isDictionary(); + bool isDictionary() const; QPDF_DLL - bool isStream(); + bool isStream() const; QPDF_DLL - bool isReserved(); + bool isReserved() const; // True for objects that are direct nulls. Does not attempt to resolve objects. This is intended // for internal use, but it can be used as an efficient way to check for nulls that are not @@ -352,23 +352,23 @@ class QPDFObjectHandle // This returns true for indirect objects from a QPDF that has been destroyed. Trying unparse // such an object will throw a logic_error. QPDF_DLL - bool isDestroyed(); + bool isDestroyed() const; // True for everything except array, dictionary, stream, word, and inline image. QPDF_DLL - bool isScalar(); + bool isScalar() const; // True if the object is a name object representing the provided name. QPDF_DLL - bool isNameAndEquals(std::string const& name); + bool isNameAndEquals(std::string const& name) const; // True if the object is a dictionary of the specified type and subtype, if any. QPDF_DLL - bool isDictionaryOfType(std::string const& type, std::string const& subtype = ""); + bool isDictionaryOfType(std::string const& type, std::string const& subtype = "") const; // True if the object is a stream of the specified type and subtype, if any. QPDF_DLL - bool isStreamOfType(std::string const& type, std::string const& subtype = ""); + bool isStreamOfType(std::string const& type, std::string const& subtype = "") const; // Public factory methods @@ -413,7 +413,7 @@ class QPDFObjectHandle // object was created without parsing. If the object is in a stream, the offset is from the // beginning of the stream. Otherwise, the offset is from the beginning of the file. QPDF_DLL - qpdf_offset_t getParsedOffset(); + qpdf_offset_t getParsedOffset() const; // Older method: stream_or_array should be the value of /Contents from a page object. It's more // convenient to just call QPDFPageObjectHelper::parsePageContents on the page object, and error @@ -585,7 +585,7 @@ class QPDFObjectHandle QPDF_DLL void setObjectDescription(QPDF* owning_qpdf, std::string const& object_description); QPDF_DLL - bool hasObjectDescription(); + bool hasObjectDescription() const; // Accessor methods // @@ -635,57 +635,57 @@ class QPDFObjectHandle // Methods for bool objects QPDF_DLL - bool getBoolValue(); + bool getBoolValue() const; QPDF_DLL - bool getValueAsBool(bool&); + bool getValueAsBool(bool&) const; // Methods for integer objects. Note: if an integer value is too big (too far away from zero in // either direction) to fit in the requested return type, the maximum or minimum value for that // return type may be returned. For example, on a system with 32-bit int, a numeric object with // a value of 2^40 (or anything too big for 32 bits) will be returned as INT_MAX. QPDF_DLL - long long getIntValue(); + long long getIntValue() const; QPDF_DLL - bool getValueAsInt(long long&); + bool getValueAsInt(long long&) const; QPDF_DLL - int getIntValueAsInt(); + int getIntValueAsInt() const; QPDF_DLL - bool getValueAsInt(int&); + bool getValueAsInt(int&) const; QPDF_DLL - unsigned long long getUIntValue(); + unsigned long long getUIntValue() const; QPDF_DLL - bool getValueAsUInt(unsigned long long&); + bool getValueAsUInt(unsigned long long&) const; QPDF_DLL - unsigned int getUIntValueAsUInt(); + unsigned int getUIntValueAsUInt() const; QPDF_DLL - bool getValueAsUInt(unsigned int&); + bool getValueAsUInt(unsigned int&) const; // Methods for real objects QPDF_DLL - std::string getRealValue(); + std::string getRealValue() const; QPDF_DLL - bool getValueAsReal(std::string&); + bool getValueAsReal(std::string&) const; // Methods that work for both integer and real objects QPDF_DLL - bool isNumber(); + bool isNumber() const; QPDF_DLL - double getNumericValue(); + double getNumericValue() const; QPDF_DLL - bool getValueAsNumber(double&); + bool getValueAsNumber(double&) const; // Methods for name objects. The returned name value is in qpdf's canonical form with all // escaping resolved. See comments for newName() for details. QPDF_DLL - std::string getName(); + std::string getName() const; QPDF_DLL - bool getValueAsName(std::string&); + bool getValueAsName(std::string&) const; // Methods for string objects QPDF_DLL - std::string getStringValue(); + std::string getStringValue() const; QPDF_DLL - bool getValueAsString(std::string&); + bool getValueAsString(std::string&) const; // If a string starts with the UTF-16 marker, it is converted from UTF-16 to UTF-8. Otherwise, // it is treated as a string encoded with PDF Doc Encoding. PDF Doc Encoding is identical to @@ -693,19 +693,19 @@ class QPDFObjectHandle // to Unicode. QPDF versions prior to version 8.0.0 erroneously left characters in that range // unmapped. QPDF_DLL - std::string getUTF8Value(); + std::string getUTF8Value() const; QPDF_DLL - bool getValueAsUTF8(std::string&); + bool getValueAsUTF8(std::string&) const; // Methods for content stream objects QPDF_DLL - std::string getOperatorValue(); + std::string getOperatorValue() const; QPDF_DLL - bool getValueAsOperator(std::string&); + bool getValueAsOperator(std::string&) const; QPDF_DLL - std::string getInlineImageValue(); + std::string getInlineImageValue() const; QPDF_DLL - bool getValueAsInlineImage(std::string&); + bool getValueAsInlineImage(std::string&) const; // Methods for array objects; see also name and array objects. @@ -720,26 +720,26 @@ class QPDFObjectHandle QPDFArrayItems aitems(); QPDF_DLL - int getArrayNItems(); + int getArrayNItems() const; QPDF_DLL - QPDFObjectHandle getArrayItem(int n); + QPDFObjectHandle getArrayItem(int n) const; // Note: QPDF arrays internally optimize memory for arrays containing lots of nulls. Calling // getArrayAsVector may cause a lot of memory to be allocated for very large arrays with lots of // nulls. QPDF_DLL - std::vector getArrayAsVector(); + std::vector getArrayAsVector() const; QPDF_DLL - bool isRectangle(); + bool isRectangle() const; // If the array is an array of four numeric values, return as a rectangle. Otherwise, return the // rectangle [0, 0, 0, 0] QPDF_DLL - Rectangle getArrayAsRectangle(); + Rectangle getArrayAsRectangle() const; QPDF_DLL - bool isMatrix(); + bool isMatrix() const; // If the array is an array of six numeric values, return as a matrix. Otherwise, return the // matrix [1, 0, 0, 1, 0, 0] QPDF_DLL - Matrix getArrayAsMatrix(); + Matrix getArrayAsMatrix() const; // Methods for dictionary objects. In all dictionary methods, keys are specified/represented as // canonical name strings starting with a leading slash and not containing any PDF syntax @@ -759,27 +759,27 @@ class QPDFObjectHandle // Return true if key is present. Keys with null values are treated as if they are not present. // This is as per the PDF spec. QPDF_DLL - bool hasKey(std::string const&); + bool hasKey(std::string const&) const; // Return the value for the key. If the key is not present, null is returned. QPDF_DLL - QPDFObjectHandle getKey(std::string const&); + QPDFObjectHandle getKey(std::string const&) const; // If the object is null, return null. Otherwise, call getKey(). This makes it easier to access // lower-level dictionaries, as in // auto font = page.getKeyIfDict("/Resources").getKeyIfDict("/Font"); QPDF_DLL - QPDFObjectHandle getKeyIfDict(std::string const&); + QPDFObjectHandle getKeyIfDict(std::string const&) const; // Return all keys. Keys with null values are treated as if they are not present. This is as // per the PDF spec. QPDF_DLL - std::set getKeys(); + std::set getKeys() const; // Return dictionary as a map. Entries with null values are included. QPDF_DLL - std::map getDictAsMap(); + std::map getDictAsMap() const; // Methods for name and array objects. The name value is in qpdf's canonical form with all // escaping resolved. See comments for newName() for details. QPDF_DLL - bool isOrHasName(std::string const&); + bool isOrHasName(std::string const&) const; // Make all resources in a resource dictionary indirect. This just goes through all entries of // top-level subdictionaries and converts any direct objects to indirect objects. This can be @@ -829,7 +829,7 @@ class QPDFObjectHandle // method returns a set of all the keys in all top-level subdictionaries. For resources // dictionaries, this is the collection of names that may be referenced in the content stream. QPDF_DLL - std::set getResourceNames(); + std::set getResourceNames() const; // Find a unique name within a resource dictionary starting with a given prefix. This method // works by appending a number to the given prefix. It searches starting with min_suffix and @@ -844,7 +844,7 @@ class QPDFObjectHandle std::string getUniqueResourceName( std::string const& prefix, int& min_suffix, - std::set* resource_names = nullptr); + std::set* resource_names = nullptr) const; // A QPDFObjectHandle has an owning QPDF if it is associated with ("owned by") a specific QPDF // object. Indirect objects always have an owning QPDF. Direct objects that are read from the @@ -964,7 +964,7 @@ class QPDFObjectHandle // Methods for stream objects QPDF_DLL - QPDFObjectHandle getDict(); + QPDFObjectHandle getDict() const; // By default, or if true passed, QPDFWriter will attempt to filter a stream based on decode // level, whether compression is enabled, and its ability to filter. Passing false will prevent @@ -1153,12 +1153,12 @@ class QPDFObjectHandle inline int getGeneration() const; QPDF_DLL - std::string unparse(); + std::string unparse() const; QPDF_DLL - std::string unparseResolved(); + std::string unparseResolved() const; // For strings only, force binary representation. Otherwise, same as unparse. QPDF_DLL - std::string unparseBinary(); + std::string unparseBinary() const; // Return encoded as JSON. The constant JSON::LATEST can be used to specify the latest available // JSON version. The JSON is generated as follows: @@ -1192,19 +1192,19 @@ class QPDFObjectHandle // the object. The effect of dereference_indirect applies only to this object. It is not // recursive. QPDF_DLL - JSON getJSON(int json_version, bool dereference_indirect = false); + JSON getJSON(int json_version, bool dereference_indirect = false) const; // Write the object encoded as JSON to a pipeline. This is equivalent to, but more efficient // than, calling getJSON(json_version, dereference_indirect).write(p, depth). See the // documentation for getJSON and JSON::write for further detail. QPDF_DLL - void - writeJSON(int json_version, Pipeline* p, bool dereference_indirect = false, size_t depth = 0); + void writeJSON( + int json_version, Pipeline* p, bool dereference_indirect = false, size_t depth = 0) const; // Deprecated version uses v1 for backward compatibility. // ABI: remove for qpdf 12 [[deprecated("Use getJSON(int version)")]] QPDF_DLL JSON - getJSON(bool dereference_indirect = false); + getJSON(bool dereference_indirect = false) const; // This method can be called on a stream to get a more extended JSON representation of the // stream that includes the stream's data. The JSON object returned is always a dictionary whose @@ -1257,7 +1257,7 @@ class QPDFObjectHandle // normally from the file have descriptions. See comments on setObjectDescription for additional // details. QPDF_DLL - void warnIfPossible(std::string const& warning); + void warnIfPossible(std::string const& warning) const; // Provide access to specific classes for recursive disconnected(). class DisconnectAccess @@ -1280,55 +1280,55 @@ class QPDFObjectHandle void assertInitialized() const; QPDF_DLL - void assertNull(); + void assertNull() const; QPDF_DLL - void assertBool(); + void assertBool() const; QPDF_DLL - void assertInteger(); + void assertInteger() const; QPDF_DLL - void assertReal(); + void assertReal() const; QPDF_DLL - void assertName(); + void assertName() const; QPDF_DLL - void assertString(); + void assertString() const; QPDF_DLL - void assertOperator(); + void assertOperator() const; QPDF_DLL - void assertInlineImage(); + void assertInlineImage() const; QPDF_DLL - void assertArray(); + void assertArray() const; QPDF_DLL - void assertDictionary(); + void assertDictionary() const; QPDF_DLL - void assertStream(); + void assertStream() const; QPDF_DLL - void assertReserved(); + void assertReserved() const; QPDF_DLL - void assertIndirect(); + void assertIndirect() const; QPDF_DLL - void assertScalar(); + void assertScalar() const; QPDF_DLL - void assertNumber(); + void assertNumber() const; // The isPageObject method checks the /Type key of the object. This is not completely reliable // as there are some otherwise valid files whose /Type is wrong for page objects. qpdf is // slightly more accepting but may still return false here when treating the object as a page // would work. Use this sparingly. QPDF_DLL - bool isPageObject(); + bool isPageObject() const; QPDF_DLL - bool isPagesObject(); + bool isPagesObject() const; QPDF_DLL - void assertPageObject(); + void assertPageObject() const; QPDF_DLL - bool isFormXObject(); + bool isFormXObject() const; // Indicate if this is an image. If exclude_imagemask is true, don't count image masks as // images. QPDF_DLL - bool isImage(bool exclude_imagemask = true); + bool isImage(bool exclude_imagemask = true) const; // The following methods do not form part of the public API and are for internal use only. @@ -1357,7 +1357,7 @@ class QPDFObjectHandle return obj.get(); } - void writeJSON(int json_version, JSON::Writer& p, bool dereference_indirect = false); + void writeJSON(int json_version, JSON::Writer& p, bool dereference_indirect = false) const; private: QPDF_Array* asArray() const; @@ -1371,12 +1371,12 @@ class QPDFObjectHandle QPDF_Real* asReal() const; QPDF_Reserved* asReserved() const; QPDF_Stream* asStream() const; - QPDF_Stream* asStreamWithAssert(); + QPDF_Stream* asStreamWithAssert() const; QPDF_String* asString() const; - void typeWarning(char const* expected_type, std::string const& warning); - void objectWarning(std::string const& warning); - void assertType(char const* type_name, bool istype); + void typeWarning(char const* expected_type, std::string const& warning) const; + void objectWarning(std::string const& warning) const; + void assertType(char const* type_name, bool istype) const; void makeDirect(QPDFObjGen::set& visited, bool stop_at_streams); void disconnect(); void setParsedOffset(qpdf_offset_t offset); @@ -1405,11 +1405,11 @@ class QPDFObjectHandle // from being here. /* clang-format off */ -// Disable formatting for this declaration: emacs font-lock in cc-mode (as of 28.1) treats the rest -// of the file as a string if clang-format removes the space after "operator", and as of -// clang-format 15, there's no way to prevent it from doing so. -QPDF_DLL -QPDFObjectHandle operator ""_qpdf(char const* v, size_t len); + // Disable formatting for this declaration: emacs font-lock in cc-mode (as of 28.1) treats the rest + // of the file as a string if clang-format removes the space after "operator", and as of + // clang-format 15, there's no way to prevent it from doing so. + QPDF_DLL + QPDFObjectHandle operator ""_qpdf(char const* v, size_t len); /* clang-format on */ #endif // QPDF_NO_QPDF_STRING @@ -1629,7 +1629,7 @@ QPDFObjectHandle::isIndirect() const } inline bool -QPDFObjectHandle::isInitialized() const +QPDFObjectHandle::isInitialized() const noexcept { return obj != nullptr; } diff --git a/libqpdf/QPDFObjectHandle.cc b/libqpdf/QPDFObjectHandle.cc index b8ce416f..4eaa2236 100644 --- a/libqpdf/QPDFObjectHandle.cc +++ b/libqpdf/QPDFObjectHandle.cc @@ -220,12 +220,19 @@ LastChar::getLastChar() return this->last_char; } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::isSameObjectAs(QPDFObjectHandle const& rhs) const { return this->obj == rhs.obj; } - +#else +bool +QPDFObjectHandle::isSameObjectAs(QPDFObjectHandle const& rhs) const noexcept +{ + return this->obj == rhs.obj; +} +#endif void QPDFObjectHandle::disconnect() { @@ -237,14 +244,24 @@ QPDFObjectHandle::disconnect() } } +#ifndef QPDF_FUTURE qpdf_object_type_e QPDFObjectHandle::getTypeCode() +#else +qpdf_object_type_e +QPDFObjectHandle::getTypeCode() const +#endif { return obj ? obj->getResolvedTypeCode() : ::ot_uninitialized; } +#ifndef QPDF_FUTURE char const* QPDFObjectHandle::getTypeName() +#else +char const* +QPDFObjectHandle::getTypeName() const +#endif { return obj ? obj->getTypeName() : "uninitialized"; } @@ -316,7 +333,7 @@ QPDFObjectHandle::asStream() const } QPDF_Stream* -QPDFObjectHandle::asStreamWithAssert() +QPDFObjectHandle::asStreamWithAssert() const { auto stream = asStream(); assertType("stream", stream); @@ -329,14 +346,24 @@ QPDFObjectHandle::asString() const return obj ? obj->as() : nullptr; } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::isDestroyed() +#else +bool +QPDFObjectHandle::isDestroyed() const +#endif { return obj && obj->getResolvedTypeCode() == ::ot_destroyed; } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::isBool() +#else +bool +QPDFObjectHandle::isBool() const +#endif { return obj && obj->getResolvedTypeCode() == ::ot_boolean; } @@ -349,47 +376,76 @@ QPDFObjectHandle::isDirectNull() const return (obj && getObjectID() == 0 && obj->getTypeCode() == ::ot_null); } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::isNull() +#else +bool +QPDFObjectHandle::isNull() const +#endif { return obj && obj->getResolvedTypeCode() == ::ot_null; } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::isInteger() +#else +bool +QPDFObjectHandle::isInteger() const +#endif { return obj && obj->getResolvedTypeCode() == ::ot_integer; } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::isReal() +#else +bool +QPDFObjectHandle::isReal() const +#endif { return obj && obj->getResolvedTypeCode() == ::ot_real; } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::isNumber() +#else +bool +QPDFObjectHandle::isNumber() const +#endif { return (isInteger() || isReal()); } +#ifndef QPDF_FUTURE double QPDFObjectHandle::getNumericValue() +#else +double +QPDFObjectHandle::getNumericValue() const +#endif { - double result = 0.0; if (isInteger()) { - result = static_cast(getIntValue()); + return static_cast(getIntValue()); } else if (isReal()) { - result = atof(getRealValue().c_str()); + return atof(getRealValue().c_str()); } else { typeWarning("number", "returning 0"); QTC::TC("qpdf", "QPDFObjectHandle numeric non-numeric"); + return 0; } - return result; } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::getValueAsNumber(double& value) +#else +bool +QPDFObjectHandle::getValueAsNumber(double& value) const +#endif { if (!isNumber()) { return false; @@ -398,83 +454,148 @@ QPDFObjectHandle::getValueAsNumber(double& value) return true; } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::isName() +#else +bool +QPDFObjectHandle::isName() const +#endif { return obj && obj->getResolvedTypeCode() == ::ot_name; } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::isString() +#else +bool +QPDFObjectHandle::isString() const +#endif { return obj && obj->getResolvedTypeCode() == ::ot_string; } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::isOperator() +#else +bool +QPDFObjectHandle::isOperator() const +#endif { return obj && obj->getResolvedTypeCode() == ::ot_operator; } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::isInlineImage() +#else +bool +QPDFObjectHandle::isInlineImage() const +#endif { return obj && obj->getResolvedTypeCode() == ::ot_inlineimage; } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::isArray() +#else +bool +QPDFObjectHandle::isArray() const +#endif { return obj && obj->getResolvedTypeCode() == ::ot_array; } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::isDictionary() +#else +bool +QPDFObjectHandle::isDictionary() const +#endif { return obj && obj->getResolvedTypeCode() == ::ot_dictionary; } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::isStream() +#else +bool +QPDFObjectHandle::isStream() const +#endif { return obj && obj->getResolvedTypeCode() == ::ot_stream; } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::isReserved() +#else +bool +QPDFObjectHandle::isReserved() const +#endif { return obj && obj->getResolvedTypeCode() == ::ot_reserved; } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::isScalar() +#else +bool +QPDFObjectHandle::isScalar() const +#endif { return isBool() || isInteger() || isName() || isNull() || isReal() || isString(); } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::isNameAndEquals(std::string const& name) +#else +bool +QPDFObjectHandle::isNameAndEquals(std::string const& name) const +#endif { return isName() && (getName() == name); } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::isDictionaryOfType(std::string const& type, std::string const& subtype) +#else +bool +QPDFObjectHandle::isDictionaryOfType(std::string const& type, std::string const& subtype) const +#endif { return isDictionary() && (type.empty() || getKey("/Type").isNameAndEquals(type)) && (subtype.empty() || getKey("/Subtype").isNameAndEquals(subtype)); } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::isStreamOfType(std::string const& type, std::string const& subtype) +#else +bool +QPDFObjectHandle::isStreamOfType(std::string const& type, std::string const& subtype) const +#endif { return isStream() && getDict().isDictionaryOfType(type, subtype); } // Bool accessors +#ifndef QPDF_FUTURE bool QPDFObjectHandle::getBoolValue() +#else +bool +QPDFObjectHandle::getBoolValue() const +#endif { auto boolean = asBool(); if (boolean) { @@ -486,8 +607,13 @@ QPDFObjectHandle::getBoolValue() } } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::getValueAsBool(bool& value) +#else +bool +QPDFObjectHandle::getValueAsBool(bool& value) const +#endif { auto boolean = asBool(); if (boolean == nullptr) { @@ -499,8 +625,13 @@ QPDFObjectHandle::getValueAsBool(bool& value) // Integer accessors +#ifndef QPDF_FUTURE long long QPDFObjectHandle::getIntValue() +#else +long long +QPDFObjectHandle::getIntValue() const +#endif { auto integer = asInteger(); if (integer) { @@ -512,8 +643,13 @@ QPDFObjectHandle::getIntValue() } } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::getValueAsInt(long long& value) +#else +bool +QPDFObjectHandle::getValueAsInt(long long& value) const +#endif { auto integer = asInteger(); if (integer == nullptr) { @@ -523,8 +659,13 @@ QPDFObjectHandle::getValueAsInt(long long& value) return true; } +#ifndef QPDF_FUTURE int QPDFObjectHandle::getIntValueAsInt() +#else +int +QPDFObjectHandle::getIntValueAsInt() const +#endif { int result = 0; long long v = getIntValue(); @@ -542,8 +683,13 @@ QPDFObjectHandle::getIntValueAsInt() return result; } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::getValueAsInt(int& value) +#else +bool +QPDFObjectHandle::getValueAsInt(int& value) const +#endif { if (!isInteger()) { return false; @@ -552,22 +698,31 @@ QPDFObjectHandle::getValueAsInt(int& value) return true; } +#ifndef QPDF_FUTURE unsigned long long QPDFObjectHandle::getUIntValue() +#else +unsigned long long +QPDFObjectHandle::getUIntValue() const +#endif { - unsigned long long result = 0; long long v = getIntValue(); if (v < 0) { QTC::TC("qpdf", "QPDFObjectHandle uint returning 0"); warnIfPossible("unsigned value request for negative number; returning 0"); + return 0; } else { - result = static_cast(v); + return static_cast(v); } - return result; } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::getValueAsUInt(unsigned long long& value) +#else +bool +QPDFObjectHandle::getValueAsUInt(unsigned long long& value) const +#endif { if (!isInteger()) { return false; @@ -576,27 +731,35 @@ QPDFObjectHandle::getValueAsUInt(unsigned long long& value) return true; } +#ifndef QPDF_FUTURE unsigned int QPDFObjectHandle::getUIntValueAsUInt() +#else +unsigned int +QPDFObjectHandle::getUIntValueAsUInt() const +#endif { - unsigned int result = 0; long long v = getIntValue(); if (v < 0) { QTC::TC("qpdf", "QPDFObjectHandle uint uint returning 0"); warnIfPossible("unsigned integer value request for negative number; returning 0"); - result = 0; + return 0; } else if (v > UINT_MAX) { QTC::TC("qpdf", "QPDFObjectHandle uint returning UINT_MAX"); warnIfPossible("requested value of unsigned integer is too big; returning UINT_MAX"); - result = UINT_MAX; + return UINT_MAX; } else { - result = static_cast(v); + return static_cast(v); } - return result; } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::getValueAsUInt(unsigned int& value) +#else +bool +QPDFObjectHandle::getValueAsUInt(unsigned int& value) const +#endif { if (!isInteger()) { return false; @@ -607,8 +770,13 @@ QPDFObjectHandle::getValueAsUInt(unsigned int& value) // Real accessors +#ifndef QPDF_FUTURE std::string QPDFObjectHandle::getRealValue() +#else +std::string +QPDFObjectHandle::getRealValue() const +#endif { if (isReal()) { return obj->getStringValue(); @@ -619,8 +787,13 @@ QPDFObjectHandle::getRealValue() } } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::getValueAsReal(std::string& value) +#else +bool +QPDFObjectHandle::getValueAsReal(std::string& value) const +#endif { if (!isReal()) { return false; @@ -631,8 +804,13 @@ QPDFObjectHandle::getValueAsReal(std::string& value) // Name accessors +#ifndef QPDF_FUTURE std::string QPDFObjectHandle::getName() +#else +std::string +QPDFObjectHandle::getName() const +#endif { if (isName()) { return obj->getStringValue(); @@ -643,8 +821,13 @@ QPDFObjectHandle::getName() } } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::getValueAsName(std::string& value) +#else +bool +QPDFObjectHandle::getValueAsName(std::string& value) const +#endif { if (!isName()) { return false; @@ -655,8 +838,13 @@ QPDFObjectHandle::getValueAsName(std::string& value) // String accessors +#ifndef QPDF_FUTURE std::string QPDFObjectHandle::getStringValue() +#else +std::string +QPDFObjectHandle::getStringValue() const +#endif { if (isString()) { return obj->getStringValue(); @@ -667,8 +855,13 @@ QPDFObjectHandle::getStringValue() } } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::getValueAsString(std::string& value) +#else +bool +QPDFObjectHandle::getValueAsString(std::string& value) const +#endif { if (!isString()) { return false; @@ -677,8 +870,13 @@ QPDFObjectHandle::getValueAsString(std::string& value) return true; } +#ifndef QPDF_FUTURE std::string QPDFObjectHandle::getUTF8Value() +#else +std::string +QPDFObjectHandle::getUTF8Value() const +#endif { auto str = asString(); if (str) { @@ -690,8 +888,13 @@ QPDFObjectHandle::getUTF8Value() } } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::getValueAsUTF8(std::string& value) +#else +bool +QPDFObjectHandle::getValueAsUTF8(std::string& value) const +#endif { auto str = asString(); if (str == nullptr) { @@ -703,8 +906,13 @@ QPDFObjectHandle::getValueAsUTF8(std::string& value) // Operator and Inline Image accessors +#ifndef QPDF_FUTURE std::string QPDFObjectHandle::getOperatorValue() +#else +std::string +QPDFObjectHandle::getOperatorValue() const +#endif { if (isOperator()) { return obj->getStringValue(); @@ -715,8 +923,13 @@ QPDFObjectHandle::getOperatorValue() } } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::getValueAsOperator(std::string& value) +#else +bool +QPDFObjectHandle::getValueAsOperator(std::string& value) const +#endif { if (!isOperator()) { return false; @@ -725,8 +938,13 @@ QPDFObjectHandle::getValueAsOperator(std::string& value) return true; } +#ifndef QPDF_FUTURE std::string QPDFObjectHandle::getInlineImageValue() +#else +std::string +QPDFObjectHandle::getInlineImageValue() const +#endif { if (isInlineImage()) { return obj->getStringValue(); @@ -737,8 +955,13 @@ QPDFObjectHandle::getInlineImageValue() } } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::getValueAsInlineImage(std::string& value) +#else +bool +QPDFObjectHandle::getValueAsInlineImage(std::string& value) const +#endif { if (!isInlineImage()) { return false; @@ -755,8 +978,13 @@ QPDFObjectHandle::aitems() return *this; } +#ifndef QPDF_FUTURE int QPDFObjectHandle::getArrayNItems() +#else +int +QPDFObjectHandle::getArrayNItems() const +#endif { if (auto array = asArray()) { return array->size(); @@ -767,8 +995,13 @@ QPDFObjectHandle::getArrayNItems() } } +#ifndef QPDF_FUTURE QPDFObjectHandle QPDFObjectHandle::getArrayItem(int n) +#else +QPDFObjectHandle +QPDFObjectHandle::getArrayItem(int n) const +#endif { if (auto array = asArray()) { if (auto result = array->at(n); result.obj != nullptr) { @@ -785,8 +1018,13 @@ QPDFObjectHandle::getArrayItem(int n) return QPDF_Null::create(obj, msg, ""); } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::isRectangle() +#else +bool +QPDFObjectHandle::isRectangle() const +#endif { if (auto array = asArray()) { for (int i = 0; i < 4; ++i) { @@ -799,8 +1037,13 @@ QPDFObjectHandle::isRectangle() return false; } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::isMatrix() +#else +bool +QPDFObjectHandle::isMatrix() const +#endif { if (auto array = asArray()) { for (int i = 0; i < 6; ++i) { @@ -813,8 +1056,13 @@ QPDFObjectHandle::isMatrix() return false; } +#ifndef QPDF_FUTURE QPDFObjectHandle::Rectangle QPDFObjectHandle::getArrayAsRectangle() +#else +QPDFObjectHandle::Rectangle +QPDFObjectHandle::getArrayAsRectangle() const +#endif { if (auto array = asArray()) { if (array->size() != 4) { @@ -835,8 +1083,13 @@ QPDFObjectHandle::getArrayAsRectangle() return {}; } +#ifndef QPDF_FUTURE QPDFObjectHandle::Matrix QPDFObjectHandle::getArrayAsMatrix() +#else +QPDFObjectHandle::Matrix +QPDFObjectHandle::getArrayAsMatrix() const +#endif { if (auto array = asArray()) { if (array->size() != 6) { @@ -853,8 +1106,13 @@ QPDFObjectHandle::getArrayAsMatrix() return {}; } +#ifndef QPDF_FUTURE std::vector QPDFObjectHandle::getArrayAsVector() +#else +std::vector +QPDFObjectHandle::getArrayAsVector() const +#endif { auto array = asArray(); if (array) { @@ -962,8 +1220,13 @@ QPDFObjectHandle::ditems() return {*this}; } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::hasKey(std::string const& key) +#else +bool +QPDFObjectHandle::hasKey(std::string const& key) const +#endif { auto dict = asDictionary(); if (dict) { @@ -975,8 +1238,13 @@ QPDFObjectHandle::hasKey(std::string const& key) } } +#ifndef QPDF_FUTURE QPDFObjectHandle QPDFObjectHandle::getKey(std::string const& key) +#else +QPDFObjectHandle +QPDFObjectHandle::getKey(std::string const& key) const +#endif { if (auto dict = asDictionary()) { return dict->getKey(key); @@ -988,14 +1256,24 @@ QPDFObjectHandle::getKey(std::string const& key) } } +#ifndef QPDF_FUTURE QPDFObjectHandle QPDFObjectHandle::getKeyIfDict(std::string const& key) +#else +QPDFObjectHandle +QPDFObjectHandle::getKeyIfDict(std::string const& key) const +#endif { return isNull() ? newNull() : getKey(key); } +#ifndef QPDF_FUTURE std::set QPDFObjectHandle::getKeys() +#else +std::set +QPDFObjectHandle::getKeys() const +#endif { std::set result; auto dict = asDictionary(); @@ -1008,8 +1286,13 @@ QPDFObjectHandle::getKeys() return result; } +#ifndef QPDF_FUTURE std::map QPDFObjectHandle::getDictAsMap() +#else +std::map +QPDFObjectHandle::getDictAsMap() const +#endif { std::map result; auto dict = asDictionary(); @@ -1023,13 +1306,18 @@ QPDFObjectHandle::getDictAsMap() } // Array and Name accessors +#ifndef QPDF_FUTURE bool QPDFObjectHandle::isOrHasName(std::string const& value) +#else +bool +QPDFObjectHandle::isOrHasName(std::string const& value) const +#endif { if (isNameAndEquals(value)) { return true; } else if (isArray()) { - for (auto& item: aitems()) { + for (auto& item: getArrayAsVector()) { if (item.isNameAndEquals(value)) { return true; } @@ -1152,8 +1440,13 @@ QPDFObjectHandle::mergeResources( } } +#ifndef QPDF_FUTURE std::set QPDFObjectHandle::getResourceNames() +#else +std::set +QPDFObjectHandle::getResourceNames() const +#endif { // Return second-level dictionary keys std::set result; @@ -1171,9 +1464,15 @@ QPDFObjectHandle::getResourceNames() return result; } +#ifndef QPDF_FUTURE std::string QPDFObjectHandle::getUniqueResourceName( std::string const& prefix, int& min_suffix, std::set* namesp) +#else +std::string +QPDFObjectHandle::getUniqueResourceName( + std::string const& prefix, int& min_suffix, std::set* namesp) const +#endif { std::set names = (namesp ? *namesp : getResourceNames()); @@ -1255,8 +1554,13 @@ QPDFObjectHandle::replaceOrRemoveKey(std::string const& key, QPDFObjectHandle co } // Stream accessors +#ifndef QPDF_FUTURE QPDFObjectHandle QPDFObjectHandle::getDict() +#else +QPDFObjectHandle +QPDFObjectHandle::getDict() const +#endif { return asStreamWithAssert()->getDict(); } @@ -1571,20 +1875,28 @@ QPDFObjectHandle::coalesceContentStreams() new_contents.replaceStreamData(provider, newNull(), newNull()); } +#ifndef QPDF_FUTURE std::string QPDFObjectHandle::unparse() +#else +std::string +QPDFObjectHandle::unparse() const +#endif { - std::string result; if (this->isIndirect()) { - result = getObjGen().unparse(' ') + " R"; + return getObjGen().unparse(' ') + " R"; } else { - result = unparseResolved(); + return unparseResolved(); } - return result; } +#ifndef QPDF_FUTURE std::string QPDFObjectHandle::unparseResolved() +#else +std::string +QPDFObjectHandle::unparseResolved() const +#endif { if (!obj) { throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle"); @@ -1592,11 +1904,15 @@ QPDFObjectHandle::unparseResolved() return obj->unparse(); } +#ifndef QPDF_FUTURE std::string QPDFObjectHandle::unparseBinary() +#else +std::string +QPDFObjectHandle::unparseBinary() const +#endif { - auto str = asString(); - if (str) { + if (auto str = asString()) { return str->unparse(true); } else { return unparse(); @@ -1604,14 +1920,24 @@ QPDFObjectHandle::unparseBinary() } // Deprecated versionless getJSON to be removed in qpdf 12 +#ifndef QPDF_FUTURE JSON QPDFObjectHandle::getJSON(bool dereference_indirect) +#else +JSON +QPDFObjectHandle::getJSON(bool dereference_indirect) const +#endif { return getJSON(1, dereference_indirect); } +#ifndef QPDF_FUTURE JSON QPDFObjectHandle::getJSON(int json_version, bool dereference_indirect) +#else +JSON +QPDFObjectHandle::getJSON(int json_version, bool dereference_indirect) const +#endif { if ((!dereference_indirect) && isIndirect()) { return JSON::makeString(unparse()); @@ -1626,8 +1952,13 @@ QPDFObjectHandle::getJSON(int json_version, bool dereference_indirect) } } +#ifndef QPDF_FUTURE void QPDFObjectHandle::writeJSON(int json_version, JSON::Writer& p, bool dereference_indirect) +#else +void +QPDFObjectHandle::writeJSON(int json_version, JSON::Writer& p, bool dereference_indirect) const +#endif { if (!dereference_indirect && isIndirect()) { p << "\"" << getObjGen().unparse(' ') << " R\""; @@ -1638,8 +1969,14 @@ QPDFObjectHandle::writeJSON(int json_version, JSON::Writer& p, bool dereference_ } } +#ifndef QPDF_FUTURE void QPDFObjectHandle::writeJSON(int json_version, Pipeline* p, bool dereference_indirect, size_t depth) +#else +void +QPDFObjectHandle::writeJSON( + int json_version, Pipeline* p, bool dereference_indirect, size_t depth) const +#endif { JSON::Writer jw{p, depth}; writeJSON(json_version, jw, dereference_indirect); @@ -1871,8 +2208,13 @@ QPDFObjectHandle::parse( return QPDFParser(input, object_description, tokenizer, decrypter, context).parse(empty, false); } +#ifndef QPDF_FUTURE qpdf_offset_t QPDFObjectHandle::getParsedOffset() +#else +qpdf_offset_t +QPDFObjectHandle::getParsedOffset() const +#endif { return obj ? obj->getParsedOffset() : -1; } @@ -2057,8 +2399,13 @@ QPDFObjectHandle::setObjectDescription(QPDF* owning_qpdf, std::string const& obj } } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::hasObjectDescription() +#else +bool +QPDFObjectHandle::hasObjectDescription() const +#endif { return obj && obj->hasDescription(); } @@ -2160,7 +2507,7 @@ QPDFObjectHandle::assertInitialized() const } void -QPDFObjectHandle::typeWarning(char const* expected_type, std::string const& warning) +QPDFObjectHandle::typeWarning(char const* expected_type, std::string const& warning) const { QPDF* context = nullptr; std::string description; @@ -2179,11 +2526,16 @@ QPDFObjectHandle::typeWarning(char const* expected_type, std::string const& warn description, 0, std::string("operation for ") + expected_type + " attempted on object of type " + - getTypeName() + ": " + warning)); + obj->getTypeName() + ": " + warning)); } +#ifndef QPDF_FUTURE void QPDFObjectHandle::warnIfPossible(std::string const& warning) +#else +void +QPDFObjectHandle::warnIfPossible(std::string const& warning) const +#endif { QPDF* context = nullptr; std::string description; @@ -2195,7 +2547,7 @@ QPDFObjectHandle::warnIfPossible(std::string const& warning) } void -QPDFObjectHandle::objectWarning(std::string const& warning) +QPDFObjectHandle::objectWarning(std::string const& warning) const { QPDF* context = nullptr; std::string description; @@ -2206,109 +2558,189 @@ QPDFObjectHandle::objectWarning(std::string const& warning) } void -QPDFObjectHandle::assertType(char const* type_name, bool istype) +QPDFObjectHandle::assertType(char const* type_name, bool istype) const { if (!istype) { throw std::runtime_error( std::string("operation for ") + type_name + " attempted on object of type " + - getTypeName()); + (obj ? obj->getTypeName() : "uninitialized")); } } +#ifndef QPDF_FUTURE void QPDFObjectHandle::assertNull() +#else +void +QPDFObjectHandle::assertNull() const +#endif { assertType("null", isNull()); } +#ifndef QPDF_FUTURE void QPDFObjectHandle::assertBool() +#else +void +QPDFObjectHandle::assertBool() const +#endif { assertType("boolean", isBool()); } +#ifndef QPDF_FUTURE void QPDFObjectHandle::assertInteger() +#else +void +QPDFObjectHandle::assertInteger() const +#endif { assertType("integer", isInteger()); } +#ifndef QPDF_FUTURE void QPDFObjectHandle::assertReal() +#else +void +QPDFObjectHandle::assertReal() const +#endif { assertType("real", isReal()); } +#ifndef QPDF_FUTURE void QPDFObjectHandle::assertName() +#else +void +QPDFObjectHandle::assertName() const +#endif { assertType("name", isName()); } +#ifndef QPDF_FUTURE void QPDFObjectHandle::assertString() +#else +void +QPDFObjectHandle::assertString() const +#endif { assertType("string", isString()); } +#ifndef QPDF_FUTURE void QPDFObjectHandle::assertOperator() +#else +void +QPDFObjectHandle::assertOperator() const +#endif { assertType("operator", isOperator()); } +#ifndef QPDF_FUTURE void QPDFObjectHandle::assertInlineImage() +#else +void +QPDFObjectHandle::assertInlineImage() const +#endif { assertType("inlineimage", isInlineImage()); } +#ifndef QPDF_FUTURE void QPDFObjectHandle::assertArray() +#else +void +QPDFObjectHandle::assertArray() const +#endif { assertType("array", isArray()); } +#ifndef QPDF_FUTURE void QPDFObjectHandle::assertDictionary() +#else +void +QPDFObjectHandle::assertDictionary() const +#endif { assertType("dictionary", isDictionary()); } +#ifndef QPDF_FUTURE void QPDFObjectHandle::assertStream() +#else +void +QPDFObjectHandle::assertStream() const +#endif { assertType("stream", isStream()); } +#ifndef QPDF_FUTURE void QPDFObjectHandle::assertReserved() +#else +void +QPDFObjectHandle::assertReserved() const +#endif { assertType("reserved", isReserved()); } +#ifndef QPDF_FUTURE void QPDFObjectHandle::assertIndirect() +#else +void +QPDFObjectHandle::assertIndirect() const +#endif { if (!isIndirect()) { throw std::logic_error("operation for indirect object attempted on direct object"); } } +#ifndef QPDF_FUTURE void QPDFObjectHandle::assertScalar() +#else +void +QPDFObjectHandle::assertScalar() const +#endif { assertType("scalar", isScalar()); } +#ifndef QPDF_FUTURE void QPDFObjectHandle::assertNumber() +#else +void +QPDFObjectHandle::assertNumber() const +#endif { assertType("number", isNumber()); } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::isPageObject() +#else +bool +QPDFObjectHandle::isPageObject() const +#endif { // See comments in QPDFObjectHandle.hh. if (getOwningQPDF() == nullptr) { @@ -2319,8 +2751,13 @@ QPDFObjectHandle::isPageObject() return isDictionaryOfType("/Page"); } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::isPagesObject() +#else +bool +QPDFObjectHandle::isPagesObject() const +#endif { if (getOwningQPDF() == nullptr) { return false; @@ -2330,14 +2767,24 @@ QPDFObjectHandle::isPagesObject() return isDictionaryOfType("/Pages"); } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::isFormXObject() +#else +bool +QPDFObjectHandle::isFormXObject() const +#endif { return isStreamOfType("", "/Form"); } +#ifndef QPDF_FUTURE bool QPDFObjectHandle::isImage(bool exclude_imagemask) +#else +bool +QPDFObjectHandle::isImage(bool exclude_imagemask) const +#endif { return ( isStreamOfType("", "/Image") && @@ -2358,8 +2805,13 @@ QPDFObjectHandle::checkOwnership(QPDFObjectHandle const& item) const } } +#ifndef QPDF_FUTURE void QPDFObjectHandle::assertPageObject() +#else +void +QPDFObjectHandle::assertPageObject() const +#endif { if (!isPageObject()) { throw std::runtime_error("page operation called on non-Page object");