diff --git a/include/qpdf/QPDFObjectHandle.hh b/include/qpdf/QPDFObjectHandle.hh index 5d9f52d5..4b054928 100644 --- a/include/qpdf/QPDFObjectHandle.hh +++ b/include/qpdf/QPDFObjectHandle.hh @@ -957,9 +957,11 @@ class QPDFObjectHandle std::set* resource_names = nullptr); // Return the QPDF object that owns an indirect object. Returns - // null for a direct object. + // null for a direct object if allow_nullptr is set to true or + // throws a runtime error otherwise. QPDF_DLL - QPDF* getOwningQPDF(); + inline QPDF* + getOwningQPDF(bool allow_nullptr = true, std::string const& error_msg = ""); // Create a shallow copy of an object as a direct object, but do not // traverse across indirect object boundaries. That means that, @@ -1876,4 +1878,17 @@ QPDFObjectHandle::isInitialized() const return initialized; } +// Indirect object accessors +inline QPDF* +QPDFObjectHandle::getOwningQPDF( + bool allow_nullptr, std::string const& error_msg) +{ + // Will be null for direct objects + if (!allow_nullptr && (this->qpdf == nullptr)) { + throw std::runtime_error( + error_msg == "" ? "attempt to use a null qpdf object" : error_msg); + } + return this->qpdf; +} + #endif // QPDFOBJECTHANDLE_HH diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index b96b0674..6ae74b25 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -2266,7 +2266,7 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign) throw std::logic_error( "QPDF::copyForeign called with direct object handle"); } - QPDF* other = foreign.getOwningQPDF(); + QPDF* other = foreign.getOwningQPDF(false); if (other == this) { QTC::TC("qpdf", "QPDF copyForeign not foreign"); throw std::logic_error( @@ -2456,11 +2456,9 @@ QPDF::copyStreamData(QPDFObjectHandle result, QPDFObjectHandle foreign) QPDFObjGen local_og(result.getObjGen()); // Copy information from the foreign stream so we can pipe its // data later without keeping the original QPDF object around. - QPDF* foreign_stream_qpdf = foreign.getOwningQPDF(); - if (!foreign_stream_qpdf) { - throw std::logic_error("unable to retrieve owning qpdf" - " from foreign stream"); - } + QPDF* foreign_stream_qpdf = foreign.getOwningQPDF( + false, "unable to retrieve owning qpdf from foreign stream"); + QPDF_Stream* stream = dynamic_cast( QPDFObjectHandle::ObjAccessor::getObject(foreign).get()); if (!stream) { diff --git a/libqpdf/QPDFFormFieldObjectHelper.cc b/libqpdf/QPDFFormFieldObjectHelper.cc index 5ff41edd..e56024af 100644 --- a/libqpdf/QPDFFormFieldObjectHelper.cc +++ b/libqpdf/QPDFFormFieldObjectHelper.cc @@ -362,13 +362,11 @@ QPDFFormFieldObjectHelper::setV(QPDFObjectHandle value, bool need_appearances) setFieldAttribute("/V", value); } if (need_appearances) { - QPDF* qpdf = this->oh.getOwningQPDF(); - if (!qpdf) { - throw std::logic_error( - "QPDFFormFieldObjectHelper::setV called with" - " need_appearances = true on an object that is" - " not associated with an owning QPDF"); - } + QPDF* qpdf = this->oh.getOwningQPDF( + false, + "QPDFFormFieldObjectHelper::setV called with need_appearances = " + "true on an object that is not associated with an owning QPDF"); + QPDFAcroFormDocumentHelper(*qpdf).setNeedAppearances(true); } } @@ -883,7 +881,7 @@ QPDFFormFieldObjectHelper::generateTextAppearance( if (found_font_in_dr && resources.isDictionary()) { QTC::TC("qpdf", "QPDFFormFieldObjectHelper get font from /DR"); if (resources.isIndirect()) { - resources = resources.getOwningQPDF()->makeIndirectObject( + resources = resources.getOwningQPDF(false)->makeIndirectObject( resources.shallowCopy()); AS.getDict().replaceKey("/Resources", resources); } diff --git a/libqpdf/QPDFJob.cc b/libqpdf/QPDFJob.cc index f8d963b2..15a81854 100644 --- a/libqpdf/QPDFJob.cc +++ b/libqpdf/QPDFJob.cc @@ -2161,7 +2161,7 @@ QPDFJob::doUnderOverlayForPage( std::map> afdh; auto make_afdh = [&](QPDFPageObjectHelper& ph) { - QPDF* q = ph.getObjectHandle().getOwningQPDF(); + QPDF* q = ph.getObjectHandle().getOwningQPDF(false); return get_afdh_for_qpdf(afdh, q); }; auto dest_afdh = make_afdh(dest_page); @@ -2597,7 +2597,7 @@ static QPDFObjectHandle added_page(QPDF& pdf, QPDFObjectHandle page) { QPDFObjectHandle result = page; - if (page.getOwningQPDF() != &pdf) { + if (page.getOwningQPDF(false) != &pdf) { // Calling copyForeignObject on an object we already copied // will give us the already existing copy. result = pdf.copyForeignObject(page); diff --git a/libqpdf/QPDFObjectHandle.cc b/libqpdf/QPDFObjectHandle.cc index cf5460d7..8a2d59e3 100644 --- a/libqpdf/QPDFObjectHandle.cc +++ b/libqpdf/QPDFObjectHandle.cc @@ -1207,14 +1207,6 @@ QPDFObjectHandle::getUniqueResourceName( " QPDFObjectHandle::getUniqueResourceName"); } -// Indirect object accessors -QPDF* -QPDFObjectHandle::getOwningQPDF() -{ - // Will be null for direct objects - return this->qpdf; -} - // Dictionary mutators void @@ -1634,16 +1626,15 @@ QPDFObjectHandle::coalesceContentStreams() // files may have pages that are invalid in other ways. return; } - QPDF* qpdf = getOwningQPDF(); - if (qpdf == nullptr) { - // Should not be possible for a page object to not have an - // owning PDF unless it was manually constructed in some - // incorrect way. However, it can happen in a PDF file whose - // page structure is direct, which is against spec but still - // possible to hand construct, as in fuzz issue 27393. - throw std::runtime_error("coalesceContentStreams called on object" - " with no associated PDF file"); - } + // Should not be possible for a page object to not have an + // owning PDF unless it was manually constructed in some + // incorrect way. However, it can happen in a PDF file whose + // page structure is direct, which is against spec but still + // possible to hand construct, as in fuzz issue 27393. + QPDF* qpdf = getOwningQPDF( + false, + "coalesceContentStreams called on object with no associated PDF file"); + QPDFObjectHandle new_contents = newStream(qpdf); this->replaceKey("/Contents", new_contents); diff --git a/libqpdf/QPDFPageObjectHelper.cc b/libqpdf/QPDFPageObjectHelper.cc index 9ad75cf8..23a54231 100644 --- a/libqpdf/QPDFPageObjectHelper.cc +++ b/libqpdf/QPDFPageObjectHelper.cc @@ -432,7 +432,8 @@ QPDFPageObjectHelper::externalizeInlineImages(size_t min_size, bool shallow) this->oh.replaceKey( "/Contents", QPDFObjectHandle::newStream( - this->oh.getOwningQPDF(), b.getBufferSharedPointer())); + this->oh.getOwningQPDF(false), + b.getBufferSharedPointer())); } } } else { @@ -683,11 +684,10 @@ QPDFPageObjectHelper::removeUnreferencedResources() QPDFPageObjectHelper QPDFPageObjectHelper::shallowCopyPage() { - QPDF* qpdf = this->oh.getOwningQPDF(); - if (!qpdf) { - throw std::runtime_error("QPDFPageObjectHelper::shallowCopyPage" - " called with a direct object"); - } + QPDF* qpdf = this->oh.getOwningQPDF( + false, + "QPDFPageObjectHelper::shallowCopyPage called with a direct object"); + QPDFObjectHandle new_page = this->oh.shallowCopy(); return QPDFPageObjectHelper(qpdf->makeIndirectObject(new_page)); } @@ -743,11 +743,10 @@ QPDFPageObjectHelper::getMatrixForTransformations(bool invert) QPDFObjectHandle QPDFPageObjectHelper::getFormXObjectForPage(bool handle_transformations) { - QPDF* qpdf = this->oh.getOwningQPDF(); - if (!qpdf) { - throw std::runtime_error("QPDFPageObjectHelper::getFormXObjectForPage" - " called with a direct object"); - } + QPDF* qpdf = this->oh.getOwningQPDF( + false, + "QPDFPageObjectHelper::getFormXObjectForPage called with a direct " + "object"); QPDFObjectHandle result = QPDFObjectHandle::newStream(qpdf); QPDFObjectHandle newdict = result.getDict(); newdict.replaceKey("/Type", QPDFObjectHandle::newName("/XObject")); @@ -917,11 +916,9 @@ QPDFPageObjectHelper::placeFormXObject( void QPDFPageObjectHelper::flattenRotation(QPDFAcroFormDocumentHelper* afdh) { - QPDF* qpdf = this->oh.getOwningQPDF(); - if (!qpdf) { - throw std::runtime_error("QPDFPageObjectHelper::flattenRotation" - " called with a direct object"); - } + QPDF* qpdf = this->oh.getOwningQPDF( + false, + "QPDFPageObjectHelper::flattenRotation called with a direct object"); auto rotate_oh = this->oh.getKey("/Rotate"); int rotate = 0; @@ -1066,16 +1063,12 @@ QPDFPageObjectHelper::copyAnnotations( return; } - QPDF* from_qpdf = from_page.getObjectHandle().getOwningQPDF(); - if (!from_qpdf) { - throw std::runtime_error("QPDFPageObjectHelper::copyAnnotations:" - " from page is a direct object"); - } - QPDF* this_qpdf = this->oh.getOwningQPDF(); - if (!this_qpdf) { - throw std::runtime_error("QPDFPageObjectHelper::copyAnnotations:" - " this page is a direct object"); - } + QPDF* from_qpdf = from_page.getObjectHandle().getOwningQPDF( + false, + "QPDFPageObjectHelper::copyAnnotations: from page is a direct object"); + QPDF* this_qpdf = this->oh.getOwningQPDF( + false, + "QPDFPageObjectHelper::copyAnnotations: this page is a direct object"); std::vector new_annots; std::vector new_fields; diff --git a/libqpdf/QPDF_pages.cc b/libqpdf/QPDF_pages.cc index e1a3b2c1..8c9bfbaa 100644 --- a/libqpdf/QPDF_pages.cc +++ b/libqpdf/QPDF_pages.cc @@ -234,7 +234,7 @@ QPDF::insertPage(QPDFObjectHandle newpage, int pos) newpage = makeIndirectObject(newpage); } else if (newpage.getOwningQPDF() != this) { QTC::TC("qpdf", "QPDF insert foreign page"); - newpage.getOwningQPDF()->pushInheritedAttributesToPage(); + newpage.getOwningQPDF(false)->pushInheritedAttributesToPage(); newpage = copyForeignObject(newpage); } else { QTC::TC("qpdf", "QPDF insert indirect page"); diff --git a/libqpdf/QTC.cc b/libqpdf/QTC.cc index 1ca79c05..8188846c 100644 --- a/libqpdf/QTC.cc +++ b/libqpdf/QTC.cc @@ -1,8 +1,8 @@ #include #include -#include #include +#include #include static bool