Commit 0132261ee06e9b94bdc011eb4dc3fcd3a403e5f3

Authored by Jay Berkenbilt
1 parent bac55955

Revert getOwningQPDF, and add getQPDF that returns a reference

ChangeLog
  1 +2022-09-07 Jay Berkenbilt <ejb@ql.org>
  2 +
  3 + * Add QPDFObjectHandle::getQPDF(), which returns a reference, as
  4 + an alternative to QPDFObjectHandle::getOwningQPDF().
  5 +
1 6 2022-09-06 Jay Berkenbilt <ejb@ql.org>
2 7  
3 8 * For all bounding box methods in QPDFPageObjectHelper other than
... ...
include/qpdf/QPDFObjectHandle.hh
... ... @@ -661,14 +661,17 @@ class QPDFObjectHandle
661 661 static QPDFObjectHandle newReserved(QPDF* qpdf);
662 662  
663 663 // Provide an owning qpdf and object description. The library does
664   - // this automatically with objects that are read from the
665   - // input PDF and with objects that are created programmatically
666   - // and inserted into the QPDF by adding them to an array or a
667   - // dictionary or creating a new indirect object. Most end user
  664 + // this automatically with objects that are read from the input
  665 + // PDF and with objects that are created programmatically and
  666 + // inserted into the QPDF as a new indirect object. Most end user
668 667 // code will not need to call this. If an object has an owning
669 668 // qpdf and object description, it enables qpdf to give warnings
670 669 // with proper context in some cases where it would otherwise
671   - // raise exceptions.
  670 + // raise exceptions. It is okay to add objects without an
  671 + // owning_qpdf to objects that have one, but it is an error to
  672 + // have a QPDF contain objects with owning_qpdf set to something
  673 + // else. To add objects from another qpdf, use copyForeignObject
  674 + // instead.
672 675 QPDF_DLL
673 676 void setObjectDescription(
674 677 QPDF* owning_qpdf, std::string const& object_description);
... ... @@ -978,23 +981,34 @@ class QPDFObjectHandle
978 981 int& min_suffix,
979 982 std::set<std::string>* resource_names = nullptr);
980 983  
981   - // If this is an indirect object, return a pointer to the QPDF
982   - // object that owns an indirect object. Direct objects are not
983   - // owned by a QPDF. Usage notes:
984   - //
985   - // * When allow_nullptr is true, this method will return a null
986   - // pointer if the object is not owned by a QPDF. Otherwise, an
987   - // exception is thrown.
  984 + // A QPDFObjectHandle has an owning QPDF if it is associated with
  985 + // ("owned by") a specific QPDF object. Indirect objects always
  986 + // have an owning QPDF. Direct objects that are read from the
  987 + // input source will also have an owning QPDF. Programmatically
  988 + // created objects will only have one if setObjectDescription was
  989 + // called.
988 990 //
989   - // * You should not retain the value returned by this method for
990   - // longer than the lifetime of the owning QPDF object. If you
991   - // retain a QPDFObjectHandle longer than the owning QPDF, when
992   - // the QPDF object is destroyed, the QPDFObjectHandle will turn
993   - // into a direct "null" object, and getOwningQPDF() called on it
994   - // at that time will return a null pointer.
995   - QPDF_DLL
996   - QPDF* getOwningQPDF(
997   - bool allow_nullptr = true, std::string const& error_msg = "") const;
  991 + // When the QPDF object that owns an object is destroyed, the
  992 + // object is changed into a null, and its owner is cleared.
  993 + // Therefore you should not retain the value of an owning QPDF
  994 + // beyond the life of the QPDF. If in doubt, ask for it each time
  995 + // you need it.
  996 +
  997 + // getOwningQPDF returns a pointer to the owning QPDF is the
  998 + // object has one. Otherwise, it returns a null pointer. Use this
  999 + // when you are able to handle the case of an object that doesn't
  1000 + // have an owning QPDF.
  1001 + QPDF_DLL
  1002 + QPDF* getOwningQPDF() const;
  1003 + // getQPDF, new in qpdf 11, returns a reference owning QPDF. If
  1004 + // there is none, it throws a runtime_error. Use this when you
  1005 + // know the object has to have an owning QPDF, such as when it's a
  1006 + // known indirect object. Since streams are always indirect
  1007 + // objects, this method can be used safely for streams. If
  1008 + // error_msg is specified, it will be used at the contents of the
  1009 + // runtime_error if there is now owner.
  1010 + QPDF_DLL
  1011 + QPDF& getQPDF(std::string const& error_msg = "") const;
998 1012  
999 1013 // Create a shallow copy of an object as a direct object, but do not
1000 1014 // traverse across indirect object boundaries. That means that,
... ...
libqpdf/QPDF.cc
... ... @@ -2299,14 +2299,14 @@ QPDF::copyForeignObject(QPDFObjectHandle foreign)
2299 2299 throw std::logic_error(
2300 2300 "QPDF::copyForeign called with direct object handle");
2301 2301 }
2302   - QPDF* other = foreign.getOwningQPDF(false);
2303   - if (other == this) {
  2302 + QPDF& other = foreign.getQPDF();
  2303 + if (&other == this) {
2304 2304 QTC::TC("qpdf", "QPDF copyForeign not foreign");
2305 2305 throw std::logic_error(
2306 2306 "QPDF::copyForeign called with object from this QPDF");
2307 2307 }
2308 2308  
2309   - ObjCopier& obj_copier = this->m->object_copiers[other->m->unique_id];
  2309 + ObjCopier& obj_copier = this->m->object_copiers[other.m->unique_id];
2310 2310 if (!obj_copier.visiting.empty()) {
2311 2311 throw std::logic_error("obj_copier.visiting is not empty"
2312 2312 " at the beginning of copyForeignObject");
... ... @@ -2490,8 +2490,8 @@ QPDF::copyStreamData(QPDFObjectHandle result, QPDFObjectHandle foreign)
2490 2490 // Copy information from the foreign stream so we can pipe its
2491 2491 // data later without keeping the original QPDF object around.
2492 2492  
2493   - QPDF* foreign_stream_qpdf = foreign.getOwningQPDF(
2494   - false, "unable to retrieve owning qpdf from foreign stream");
  2493 + QPDF& foreign_stream_qpdf =
  2494 + foreign.getQPDF("unable to retrieve owning qpdf from foreign stream");
2495 2495  
2496 2496 auto stream = QPDFObjectHandle::ObjAccessor::asStream(foreign);
2497 2497 if (stream == nullptr) {
... ... @@ -2499,7 +2499,7 @@ QPDF::copyStreamData(QPDFObjectHandle result, QPDFObjectHandle foreign)
2499 2499 " stream object from foreign stream");
2500 2500 }
2501 2501 std::shared_ptr<Buffer> stream_buffer = stream->getStreamDataBuffer();
2502   - if ((foreign_stream_qpdf->m->immediate_copy_from) &&
  2502 + if ((foreign_stream_qpdf.m->immediate_copy_from) &&
2503 2503 (stream_buffer == nullptr)) {
2504 2504 // Pull the stream data into a buffer before attempting
2505 2505 // the copy operation. Do it on the source stream so that
... ... @@ -2529,8 +2529,8 @@ QPDF::copyStreamData(QPDFObjectHandle result, QPDFObjectHandle foreign)
2529 2529 dict.getKey("/DecodeParms"));
2530 2530 } else {
2531 2531 auto foreign_stream_data = std::make_shared<ForeignStreamData>(
2532   - foreign_stream_qpdf->m->encp,
2533   - foreign_stream_qpdf->m->file,
  2532 + foreign_stream_qpdf.m->encp,
  2533 + foreign_stream_qpdf.m->file,
2534 2534 foreign.getObjGen(),
2535 2535 stream->getOffset(),
2536 2536 stream->getLength(),
... ...
libqpdf/QPDFFormFieldObjectHelper.cc
... ... @@ -362,12 +362,10 @@ QPDFFormFieldObjectHelper::setV(QPDFObjectHandle value, bool need_appearances)
362 362 setFieldAttribute("/V", value);
363 363 }
364 364 if (need_appearances) {
365   - QPDF* qpdf = this->oh.getOwningQPDF(
366   - false,
  365 + QPDF& qpdf = this->oh.getQPDF(
367 366 "QPDFFormFieldObjectHelper::setV called with need_appearances = "
368 367 "true on an object that is not associated with an owning QPDF");
369   -
370   - QPDFAcroFormDocumentHelper(*qpdf).setNeedAppearances(true);
  368 + QPDFAcroFormDocumentHelper(qpdf).setNeedAppearances(true);
371 369 }
372 370 }
373 371  
... ... @@ -881,7 +879,7 @@ QPDFFormFieldObjectHelper::generateTextAppearance(
881 879 if (found_font_in_dr && resources.isDictionary()) {
882 880 QTC::TC("qpdf", "QPDFFormFieldObjectHelper get font from /DR");
883 881 if (resources.isIndirect()) {
884   - resources = resources.getOwningQPDF(false)->makeIndirectObject(
  882 + resources = resources.getQPDF().makeIndirectObject(
885 883 resources.shallowCopy());
886 884 AS.getDict().replaceKey("/Resources", resources);
887 885 }
... ...
libqpdf/QPDFJob.cc
... ... @@ -2194,8 +2194,8 @@ QPDFJob::doUnderOverlayForPage(
2194 2194 std::map<unsigned long long, std::shared_ptr<QPDFAcroFormDocumentHelper>>
2195 2195 afdh;
2196 2196 auto make_afdh = [&](QPDFPageObjectHelper& ph) {
2197   - QPDF* q = ph.getObjectHandle().getOwningQPDF(false);
2198   - return get_afdh_for_qpdf(afdh, q);
  2197 + QPDF& q = ph.getObjectHandle().getQPDF();
  2198 + return get_afdh_for_qpdf(afdh, &q);
2199 2199 };
2200 2200 auto dest_afdh = make_afdh(dest_page);
2201 2201  
... ... @@ -2635,7 +2635,7 @@ static QPDFObjectHandle
2635 2635 added_page(QPDF& pdf, QPDFObjectHandle page)
2636 2636 {
2637 2637 QPDFObjectHandle result = page;
2638   - if (page.getOwningQPDF(false) != &pdf) {
  2638 + if (&page.getQPDF() != &pdf) {
2639 2639 // Calling copyForeignObject on an object we already copied
2640 2640 // will give us the already existing copy.
2641 2641 result = pdf.copyForeignObject(page);
... ...
libqpdf/QPDFObjectHandle.cc
... ... @@ -1668,11 +1668,10 @@ QPDFObjectHandle::coalesceContentStreams()
1668 1668 // incorrect way. However, it can happen in a PDF file whose
1669 1669 // page structure is direct, which is against spec but still
1670 1670 // possible to hand construct, as in fuzz issue 27393.
1671   - QPDF* qpdf = getOwningQPDF(
1672   - false,
  1671 + QPDF& qpdf = getQPDF(
1673 1672 "coalesceContentStreams called on object with no associated PDF file");
1674 1673  
1675   - QPDFObjectHandle new_contents = newStream(qpdf);
  1674 + QPDFObjectHandle new_contents = newStream(&qpdf);
1676 1675 this->replaceKey("/Contents", new_contents);
1677 1676  
1678 1677 auto provider = std::shared_ptr<StreamDataProvider>(
... ... @@ -2775,16 +2774,20 @@ QPDFObjectHandle::getObjGen() const
2775 2774  
2776 2775 // Indirect object accessors
2777 2776 QPDF*
2778   -QPDFObjectHandle::getOwningQPDF(
2779   - bool allow_nullptr, std::string const& error_msg) const
  2777 +QPDFObjectHandle::getOwningQPDF() const
  2778 +{
  2779 + return isInitialized() ? this->obj->getQPDF() : nullptr;
  2780 +}
  2781 +
  2782 +QPDF&
  2783 +QPDFObjectHandle::getQPDF(std::string const& error_msg) const
2780 2784 {
2781   - // Will be null for direct objects
2782 2785 auto result = isInitialized() ? this->obj->getQPDF() : nullptr;
2783   - if (!allow_nullptr && (result == nullptr)) {
  2786 + if (result == nullptr) {
2784 2787 throw std::runtime_error(
2785 2788 error_msg == "" ? "attempt to use a null qpdf object" : error_msg);
2786 2789 }
2787   - return result;
  2790 + return *result;
2788 2791 }
2789 2792  
2790 2793 void
... ...
libqpdf/QPDFPageObjectHelper.cc
... ... @@ -477,8 +477,7 @@ QPDFPageObjectHelper::externalizeInlineImages(size_t min_size, bool shallow)
477 477 this->oh.replaceKey(
478 478 "/Contents",
479 479 QPDFObjectHandle::newStream(
480   - this->oh.getOwningQPDF(false),
481   - b.getBufferSharedPointer()));
  480 + &this->oh.getQPDF(), b.getBufferSharedPointer()));
482 481 }
483 482 }
484 483 } else {
... ... @@ -729,12 +728,10 @@ QPDFPageObjectHelper::removeUnreferencedResources()
729 728 QPDFPageObjectHelper
730 729 QPDFPageObjectHelper::shallowCopyPage()
731 730 {
732   - QPDF* qpdf = this->oh.getOwningQPDF(
733   - false,
  731 + QPDF& qpdf = this->oh.getQPDF(
734 732 "QPDFPageObjectHelper::shallowCopyPage called with a direct object");
735   -
736 733 QPDFObjectHandle new_page = this->oh.shallowCopy();
737   - return QPDFPageObjectHelper(qpdf->makeIndirectObject(new_page));
  734 + return QPDFPageObjectHelper(qpdf.makeIndirectObject(new_page));
738 735 }
739 736  
740 737 QPDFObjectHandle::Matrix
... ... @@ -788,11 +785,10 @@ QPDFPageObjectHelper::getMatrixForTransformations(bool invert)
788 785 QPDFObjectHandle
789 786 QPDFPageObjectHelper::getFormXObjectForPage(bool handle_transformations)
790 787 {
791   - QPDF* qpdf = this->oh.getOwningQPDF(
792   - false,
  788 + QPDF& qpdf = this->oh.getQPDF(
793 789 "QPDFPageObjectHelper::getFormXObjectForPage called with a direct "
794 790 "object");
795   - QPDFObjectHandle result = QPDFObjectHandle::newStream(qpdf);
  791 + QPDFObjectHandle result = QPDFObjectHandle::newStream(&qpdf);
796 792 QPDFObjectHandle newdict = result.getDict();
797 793 newdict.replaceKey("/Type", QPDFObjectHandle::newName("/XObject"));
798 794 newdict.replaceKey("/Subtype", QPDFObjectHandle::newName("/Form"));
... ... @@ -961,10 +957,8 @@ QPDFPageObjectHelper::placeFormXObject(
961 957 void
962 958 QPDFPageObjectHelper::flattenRotation(QPDFAcroFormDocumentHelper* afdh)
963 959 {
964   - QPDF* qpdf = this->oh.getOwningQPDF(
965   - false,
  960 + QPDF& qpdf = this->oh.getQPDF(
966 961 "QPDFPageObjectHelper::flattenRotation called with a direct object");
967   -
968 962 auto rotate_oh = this->oh.getKey("/Rotate");
969 963 int rotate = 0;
970 964 if (rotate_oh.isInteger()) {
... ... @@ -1067,8 +1061,9 @@ QPDFPageObjectHelper::flattenRotation(QPDFAcroFormDocumentHelper* afdh)
1067 1061 break;
1068 1062 }
1069 1063 std::string cm_str = std::string("q\n") + cm.unparse() + " cm\n";
1070   - this->oh.addPageContents(QPDFObjectHandle::newStream(qpdf, cm_str), true);
1071   - this->oh.addPageContents(QPDFObjectHandle::newStream(qpdf, "\nQ\n"), false);
  1064 + this->oh.addPageContents(QPDFObjectHandle::newStream(&qpdf, cm_str), true);
  1065 + this->oh.addPageContents(
  1066 + QPDFObjectHandle::newStream(&qpdf, "\nQ\n"), false);
1072 1067 this->oh.removeKey("/Rotate");
1073 1068 QPDFObjectHandle rotate_obj = getAttribute("/Rotate", false);
1074 1069 if (!rotate_obj.isNull()) {
... ... @@ -1083,7 +1078,7 @@ QPDFPageObjectHelper::flattenRotation(QPDFAcroFormDocumentHelper* afdh)
1083 1078 std::set<QPDFObjGen> old_fields;
1084 1079 std::shared_ptr<QPDFAcroFormDocumentHelper> afdhph;
1085 1080 if (!afdh) {
1086   - afdhph = std::make_shared<QPDFAcroFormDocumentHelper>(*qpdf);
  1081 + afdhph = std::make_shared<QPDFAcroFormDocumentHelper>(qpdf);
1087 1082 afdh = afdhph.get();
1088 1083 }
1089 1084 afdh->transformAnnotations(
... ... @@ -1108,11 +1103,9 @@ QPDFPageObjectHelper::copyAnnotations(
1108 1103 return;
1109 1104 }
1110 1105  
1111   - QPDF* from_qpdf = from_page.getObjectHandle().getOwningQPDF(
1112   - false,
  1106 + QPDF& from_qpdf = from_page.getObjectHandle().getQPDF(
1113 1107 "QPDFPageObjectHelper::copyAnnotations: from page is a direct object");
1114   - QPDF* this_qpdf = this->oh.getOwningQPDF(
1115   - false,
  1108 + QPDF& this_qpdf = this->oh.getQPDF(
1116 1109 "QPDFPageObjectHelper::copyAnnotations: this page is a direct object");
1117 1110  
1118 1111 std::vector<QPDFObjectHandle> new_annots;
... ... @@ -1121,19 +1114,19 @@ QPDFPageObjectHelper::copyAnnotations(
1121 1114 std::shared_ptr<QPDFAcroFormDocumentHelper> afdhph;
1122 1115 std::shared_ptr<QPDFAcroFormDocumentHelper> from_afdhph;
1123 1116 if (!afdh) {
1124   - afdhph = std::make_shared<QPDFAcroFormDocumentHelper>(*this_qpdf);
  1117 + afdhph = std::make_shared<QPDFAcroFormDocumentHelper>(this_qpdf);
1125 1118 afdh = afdhph.get();
1126 1119 }
1127   - if (this_qpdf == from_qpdf) {
  1120 + if (&this_qpdf == &from_qpdf) {
1128 1121 from_afdh = afdh;
1129 1122 } else if (from_afdh) {
1130   - if (from_afdh->getQPDF().getUniqueId() != from_qpdf->getUniqueId()) {
  1123 + if (from_afdh->getQPDF().getUniqueId() != from_qpdf.getUniqueId()) {
1131 1124 throw std::logic_error(
1132 1125 "QPDFAcroFormDocumentHelper::copyAnnotations: from_afdh"
1133 1126 " is not from the same QPDF as from_page");
1134 1127 }
1135 1128 } else {
1136   - from_afdhph = std::make_shared<QPDFAcroFormDocumentHelper>(*from_qpdf);
  1129 + from_afdhph = std::make_shared<QPDFAcroFormDocumentHelper>(from_qpdf);
1137 1130 from_afdh = from_afdhph.get();
1138 1131 }
1139 1132  
... ... @@ -1143,7 +1136,7 @@ QPDFPageObjectHelper::copyAnnotations(
1143 1136 new_fields,
1144 1137 old_fields,
1145 1138 cm,
1146   - from_qpdf,
  1139 + &from_qpdf,
1147 1140 from_afdh);
1148 1141 afdh->addAndRenameFormFields(new_fields);
1149 1142 auto annots = this->oh.getKey("/Annots");
... ...
libqpdf/QPDF_pages.cc
... ... @@ -233,7 +233,7 @@ QPDF::insertPage(QPDFObjectHandle newpage, int pos)
233 233 newpage = makeIndirectObject(newpage);
234 234 } else if (newpage.getOwningQPDF() != this) {
235 235 QTC::TC("qpdf", "QPDF insert foreign page");
236   - newpage.getOwningQPDF(false)->pushInheritedAttributesToPage();
  236 + newpage.getQPDF().pushInheritedAttributesToPage();
237 237 newpage = copyForeignObject(newpage);
238 238 } else {
239 239 QTC::TC("qpdf", "QPDF insert indirect page");
... ...
manual/release-notes.rst
... ... @@ -208,6 +208,11 @@ For a detailed list of changes, please see the file
208 208 generally not happen for correct code, but at least the
209 209 situation is detectible now.
210 210  
  211 + - The method ``QPDFObjectHandle::getQPDF`` returns a ``QPDF&``
  212 + (rather than a ``QPDF*``) and is an alternative to
  213 + ``QPDFObjectHandle::getOwningQPDF`` for when the object is known
  214 + to have an owning ``QPDF``.
  215 +
211 216 - It is now possible to test ``QPDFObjectHandle`` equality with
212 217 ``==`` and ``!=``. Two ``QPDFObjectHandle`` objects are equal if
213 218 they point to the same underlying object, meaning changes to one
... ...