diff --git a/include/qpdf/ObjectHandle.hh b/include/qpdf/ObjectHandle.hh index b8978e2..213124b 100644 --- a/include/qpdf/ObjectHandle.hh +++ b/include/qpdf/ObjectHandle.hh @@ -22,7 +22,10 @@ #include #include +#include + #include +#include #include #include @@ -58,6 +61,18 @@ namespace qpdf // The rest of the header file is for qpdf internal use only. + // For arrays, return the number of items in the array. + // For null-like objects, return 0. + // For all other objects, return 1. + size_t size() const; + + // Return 'true' if size() == 0. + bool + empty() const + { + return size() == 0; + } + std::shared_ptr copy(bool shallow = false) const; // Recursively remove association with any QPDF object. This method may only be called // during final destruction. @@ -71,6 +86,9 @@ namespace qpdf inline qpdf_object_type_e type_code() const; std::string unparse() const; void write_json(int json_version, JSON::Writer& p) const; + static void warn(QPDF*, QPDFExc&&); + void warn(QPDFExc&&) const; + void warn(std::string const& warning) const; protected: BaseHandle() = default; @@ -87,6 +105,11 @@ namespace qpdf template T* as() const; + std::string description() const; + std::runtime_error type_error(char const* expected_type) const; + QPDFExc type_error(char const* expected_type, std::string const& message) const; + char const* type_name() const; + std::shared_ptr obj; }; diff --git a/include/qpdf/QPDFObjectHandle.hh b/include/qpdf/QPDFObjectHandle.hh index 6eaeedb..afa3204 100644 --- a/include/qpdf/QPDFObjectHandle.hh +++ b/include/qpdf/QPDFObjectHandle.hh @@ -1357,7 +1357,6 @@ class QPDFObjectHandle: public qpdf::BaseHandle QPDF* context); std::vector arrayOrStreamToStreamArray(std::string const& description, std::string& all_description); - static void warn(QPDF*, QPDFExc const&); void checkOwnership(QPDFObjectHandle const&) const; }; diff --git a/libqpdf/QPDFAcroFormDocumentHelper.cc b/libqpdf/QPDFAcroFormDocumentHelper.cc index 18997e8..9ed850b 100644 --- a/libqpdf/QPDFAcroFormDocumentHelper.cc +++ b/libqpdf/QPDFAcroFormDocumentHelper.cc @@ -7,7 +7,10 @@ #include #include +#include + using namespace qpdf; +using namespace std::literals; QPDFAcroFormDocumentHelper::QPDFAcroFormDocumentHelper(QPDF& qpdf) : QPDFDocumentHelper(qpdf), @@ -138,7 +141,7 @@ QPDFAcroFormDocumentHelper::removeFormFields(std::set const& to_remo } int i = 0; - while (i < fields.getArrayNItems()) { + while (std::cmp_less(i, fields.size())) { auto field = fields.getArrayItem(i); if (to_remove.contains(field.getObjGen())) { fields.eraseItem(i); @@ -251,7 +254,7 @@ QPDFAcroFormDocumentHelper::analyze() } } else { QTC::TC("qpdf", "QPDFAcroFormDocumentHelper fields not array"); - acroform.warnIfPossible("/Fields key of /AcroForm dictionary is not an array; ignoring"); + acroform.warn("/Fields key of /AcroForm dictionary is not an array; ignoring"); fields = QPDFObjectHandle::newArray(); } @@ -273,9 +276,9 @@ QPDFAcroFormDocumentHelper::analyze() // case such as a PDF creator adding a self-contained annotation (merged with the // field dictionary) to the page's /Annots array and forgetting to also put it in // /AcroForm. - annot.warnIfPossible( - "this widget annotation is not" - " reachable from /AcroForm in the document catalog"); + annot.warn( + "this widget annotation is not reachable from /AcroForm in the document " + "catalog"); m->annotation_to_field[og] = QPDFFormFieldObjectHelper(annot); m->field_to_annotations[og].emplace_back(annot); } @@ -292,16 +295,16 @@ QPDFAcroFormDocumentHelper::traverseField( // could cause stack overflow. return; } - if (!field.isIndirect()) { + if (!field.indirect()) { QTC::TC("qpdf", "QPDFAcroFormDocumentHelper direct field"); - field.warnIfPossible( + field.warn( "encountered a direct object as a field or annotation while " "traversing /AcroForm; ignoring field or annotation"); return; } if (!field.isDictionary()) { QTC::TC("qpdf", "QPDFAcroFormDocumentHelper non-dictionary field"); - field.warnIfPossible( + field.warn( "encountered a non-dictionary as a field or annotation while" " traversing /AcroForm; ignoring field or annotation"); return; @@ -309,7 +312,7 @@ QPDFAcroFormDocumentHelper::traverseField( QPDFObjGen og(field.getObjGen()); if (!visited.add(og)) { QTC::TC("qpdf", "QPDFAcroFormDocumentHelper loop"); - field.warnIfPossible("loop detected while traversing /AcroForm"); + field.warn("loop detected while traversing /AcroForm"); return; } @@ -375,7 +378,7 @@ QPDFAcroFormDocumentHelper::setNeedAppearances(bool val) { QPDFObjectHandle acroform = qpdf.getRoot().getKey("/AcroForm"); if (!acroform.isDictionary()) { - qpdf.getRoot().warnIfPossible( + qpdf.getRoot().warn( "ignoring call to QPDFAcroFormDocumentHelper::setNeedAppearances" " on a file that lacks an /AcroForm dictionary"); return; @@ -592,9 +595,7 @@ QPDFAcroFormDocumentHelper::adjustDefaultAppearances( } catch (std::exception& e) { // No way to reproduce in test suite right now since error conditions are converted to // warnings. - obj.warnIfPossible( - std::string("Unable to parse /DA: ") + e.what() + - "; this form field may not update properly"); + obj.warn("Unable to parse /DA: "s + e.what() + "; this form field may not update properly"); return; } @@ -677,16 +678,17 @@ QPDFAcroFormDocumentHelper::adjustAppearanceStream( try { auto nwarnings = qpdf.numWarnings(); stream.parseAsContents(&rf); - if (qpdf.numWarnings() > nwarnings) { - QTC::TC("qpdf", "QPDFAcroFormDocumentHelper AP parse error"); - } + QTC::TC( + "qpdf", + "QPDFAcroFormDocumentHelper AP parse error", + qpdf.numWarnings() > nwarnings ? 0 : 1); auto rr = new ResourceReplacer(dr_map, rf.getNamesByResourceType()); auto tf = std::shared_ptr(rr); stream.addTokenFilter(tf); } catch (std::exception& e) { // No way to reproduce in test suite right now since error conditions are converted to // warnings. - stream.warnIfPossible(std::string("Unable to parse appearance stream: ") + e.what()); + stream.warn("Unable to parse appearance stream: "s + e.what()); } } @@ -814,7 +816,7 @@ QPDFAcroFormDocumentHelper::transformAnnotations( QPDFObjGen::set added_new_fields; for (auto annot: old_annots.aitems()) { if (annot.isStream()) { - annot.warnIfPossible("ignoring annotation that's a stream"); + annot.warn("ignoring annotation that's a stream"); continue; } @@ -847,10 +849,10 @@ QPDFAcroFormDocumentHelper::transformAnnotations( bool have_field = false; bool have_parent = false; if (ffield_oh.isStream()) { - ffield_oh.warnIfPossible("ignoring form field that's a stream"); + ffield.warn("ignoring form field that's a stream"); } else if ((!ffield_oh.isNull()) && (!ffield_oh.isIndirect())) { - ffield_oh.warnIfPossible("ignoring form field not indirect"); - } else if (!ffield_oh.isNull()) { + ffield.warn("ignoring form field not indirect"); + } else if (!ffield.null()) { // A field and its associated annotation can be the same object. This matters because we // don't want to clone the annotation and field separately in this case. have_field = true; @@ -888,7 +890,7 @@ QPDFAcroFormDocumentHelper::transformAnnotations( if (orig_to_copy.contains(parent_og)) { obj.replaceKey("/Parent", orig_to_copy[parent_og]); } else { - parent.warnIfPossible( + parent.warn( "while traversing field " + obj.getObjGen().unparse(',') + ", found parent (" + parent_og.unparse(',') + ") that had not been seen, indicating likely invalid field " @@ -896,12 +898,13 @@ QPDFAcroFormDocumentHelper::transformAnnotations( } } auto kids = obj.getKey("/Kids"); - if (kids.isArray()) { - for (int i = 0; i < kids.getArrayNItems(); ++i) { + int sz = static_cast(kids.size()); + if (sz != 1 || kids.isArray()) { + for (int i = 0; i < sz; ++i) { auto kid = kids.getArrayItem(i); if (maybe_copy_object(kid)) { kids.setArrayItem(i, kid); - queue.push_back(kid); + queue.emplace_back(kid); } } } @@ -1013,7 +1016,7 @@ QPDFAcroFormDocumentHelper::fixCopiedAnnotations( std::set* added_fields) { auto old_annots = from_page.getKey("/Annots"); - if ((!old_annots.isArray()) || (old_annots.getArrayNItems() == 0)) { + if (old_annots.empty() || !old_annots.isArray()) { return; } diff --git a/libqpdf/QPDFEFStreamObjectHelper.cc b/libqpdf/QPDFEFStreamObjectHelper.cc index 8728617..dd6dbf0 100644 --- a/libqpdf/QPDFEFStreamObjectHelper.cc +++ b/libqpdf/QPDFEFStreamObjectHelper.cc @@ -138,7 +138,7 @@ QPDFEFStreamObjectHelper::newFromStream(QPDFObjectHandle stream) Pl_MD5 md5("EF md5", &discard); Pl_Count count("EF size", &md5); if (!stream.pipeStreamData(&count, nullptr, 0, qpdf_dl_all)) { - stream.warnIfPossible("unable to get stream data for new embedded file stream"); + stream.warn("unable to get stream data for new embedded file stream"); } else { result.setParam("/Size", QPDFObjectHandle::newInteger(count.getCount())); result.setParam( diff --git a/libqpdf/QPDFFileSpecObjectHelper.cc b/libqpdf/QPDFFileSpecObjectHelper.cc index 8fac3ba..3932466 100644 --- a/libqpdf/QPDFFileSpecObjectHelper.cc +++ b/libqpdf/QPDFFileSpecObjectHelper.cc @@ -4,32 +4,33 @@ #include #include +#include #include -#include + +using namespace std::literals; QPDFFileSpecObjectHelper::QPDFFileSpecObjectHelper(QPDFObjectHandle oh) : QPDFObjectHelper(oh) { if (!oh.isDictionary()) { - oh.warnIfPossible("Embedded file object is not a dictionary"); + warn("Embedded file object is not a dictionary"); return; } if (!oh.isDictionaryOfType("/Filespec")) { - oh.warnIfPossible("Embedded file object's type is not /Filespec"); + warn("Embedded file object's type is not /Filespec"); } } -static std::vector name_keys = {"/UF", "/F", "/Unix", "/DOS", "/Mac"}; +static const std::array name_keys = {"/UF"s, "/F"s, "/Unix"s, "/DOS"s, "/Mac"s}; std::string QPDFFileSpecObjectHelper::getDescription() { - std::string result; auto desc = oh().getKey("/Desc"); if (desc.isString()) { - result = desc.getUTF8Value(); + return desc.getUTF8Value(); } - return result; + return {}; } std::string diff --git a/libqpdf/QPDFFormFieldObjectHelper.cc b/libqpdf/QPDFFormFieldObjectHelper.cc index f806888..113e98d 100644 --- a/libqpdf/QPDFFormFieldObjectHelper.cc +++ b/libqpdf/QPDFFormFieldObjectHelper.cc @@ -281,7 +281,7 @@ QPDFFormFieldObjectHelper::getChoices() for (auto const& item: getInheritableFieldValue("/Opt").as_array()) { if (item.isString()) { result.emplace_back(item.getUTF8Value()); - } else if (item.isArray() && item.getArrayNItems() == 2) { + } else if (item.size() == 2) { auto display = item.getArrayItem(1); if (display.isString()) { result.emplace_back(display.getUTF8Value()); @@ -317,18 +317,17 @@ QPDFFormFieldObjectHelper::setV(QPDFObjectHandle value, bool need_appearances) setCheckBoxValue((name != "/Off")); } if (!okay) { - oh().warnIfPossible( - "ignoring attempt to set a checkbox field to a value whose type is not name"); + warn("ignoring attempt to set a checkbox field to a value whose type is not name"); } } else if (isRadioButton()) { if (value.isName()) { setRadioButtonValue(value); } else { - oh().warnIfPossible( + warn( "ignoring attempt to set a radio button field to an object that is not a name"); } } else if (isPushbutton()) { - oh().warnIfPossible("ignoring attempt set the value of a pushbutton field"); + warn("ignoring attempt set the value of a pushbutton field"); } return; } @@ -375,7 +374,7 @@ QPDFFormFieldObjectHelper::setRadioButtonValue(QPDFObjectHandle name) QPDFObjectHandle kids = oh().getKey("/Kids"); if (!(isRadioButton() && parent.isNull() && kids.isArray())) { - oh().warnIfPossible("don't know how to set the value of this field as a radio button"); + warn("don't know how to set the value of this field as a radio button"); return; } setFieldAttribute("/V", name); @@ -397,7 +396,7 @@ QPDFFormFieldObjectHelper::setRadioButtonValue(QPDFObjectHandle name) } if (!annot) { QTC::TC("qpdf", "QPDFObjectHandle broken radio button"); - oh().warnIfPossible("unable to set the value of this radio button"); + warn("unable to set the value of this radio button"); continue; } if (AP.isDictionary() && AP.getKey("/N").isDictionary() && @@ -453,7 +452,7 @@ QPDFFormFieldObjectHelper::setCheckBoxValue(bool value) setFieldAttribute("/V", name); if (!annot) { QTC::TC("qpdf", "QPDFObjectHandle broken checkbox"); - oh().warnIfPossible("unable to set the value of this checkbox"); + warn("unable to set the value of this checkbox"); return; } QTC::TC("qpdf", "QPDFFormFieldObjectHelper set checkbox AS"); @@ -770,18 +769,17 @@ QPDFFormFieldObjectHelper::generateTextAppearance(QPDFAnnotationObjectHelper& ao AP.replaceKey("/N", AS); } if (!AS.isStream()) { - aoh.getObjectHandle().warnIfPossible("unable to get normal appearance stream for update"); + aoh.warn("unable to get normal appearance stream for update"); return; } if (AS.getObj().use_count() > 4) { - aoh.getObjectHandle().warnIfPossible( - "unable to generate text appearance from shared appearance stream for update"); + aoh.warn("unable to generate text appearance from shared appearance stream for update"); return; } QPDFObjectHandle bbox_obj = AS.getDict().getKey("/BBox"); if (!bbox_obj.isRectangle()) { - aoh.getObjectHandle().warnIfPossible("unable to get appearance stream bounding box"); + aoh.warn("unable to get appearance stream bounding box"); return; } QPDFObjectHandle::Rectangle bbox = bbox_obj.getArrayAsRectangle(); diff --git a/libqpdf/QPDFJob.cc b/libqpdf/QPDFJob.cc index e483623..c6feb31 100644 --- a/libqpdf/QPDFJob.cc +++ b/libqpdf/QPDFJob.cc @@ -1082,7 +1082,7 @@ QPDFJob::doJSONPages(Pipeline* p, bool& first, QPDF& pdf) dp_array = decode_parms; } else { dp_array = QPDFObjectHandle::newArray(); - for (int i = 0; i < filters.getArrayNItems(); ++i) { + for (size_t i = 0; i < filters.size(); ++i) { dp_array.appendItem(decode_parms); } } @@ -2322,7 +2322,7 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF& pdf) }); std::list queue; - queue.push_back(pdf.getRoot().getKey("/Pages")); + queue.emplace_back(pdf.getRoot().getKey("/Pages")); while (!queue.empty()) { QPDFObjectHandle node = *queue.begin(); queue.pop_front(); @@ -2341,9 +2341,8 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF& pdf) }); return true; } - int n = kids.getArrayNItems(); - for (int i = 0; i < n; ++i) { - queue.push_back(kids.getArrayItem(i)); + for (auto const& kid: kids.as_array()) { + queue.emplace_back(kid); } } else { // This is a leaf node or a form XObject. @@ -2670,12 +2669,10 @@ QPDFJob::handlePageSpecs(QPDF& pdf, std::vector>& page_hea new_fields.appendItem(field); } } - if (new_fields.getArrayNItems() > 0) { - QTC::TC("qpdf", "QPDFJob keep some fields in pages"); - acroform.replaceKey("/Fields", new_fields); - } else { - QTC::TC("qpdf", "QPDFJob no more fields in pages"); + if (new_fields.empty()) { pdf.getRoot().removeKey("/AcroForm"); + } else { + acroform.replaceKey("/Fields", new_fields); } } } diff --git a/libqpdf/QPDFObjectHandle.cc b/libqpdf/QPDFObjectHandle.cc index 5127ebe..5e70ece 100644 --- a/libqpdf/QPDFObjectHandle.cc +++ b/libqpdf/QPDFObjectHandle.cc @@ -352,16 +352,15 @@ BaseHandle::unparse() const auto const& a = std::get(obj->value); std::string result = "[ "; if (a.sp) { - int next = 0; - for (auto& item: a.sp->elements) { - int key = item.first; - for (int j = next; j < key; ++j) { + size_t next = 0; + for (auto& [key, value]: a.sp->elements) { + for (size_t j = next; j < key; ++j) { result += "null "; } - result += item.second.unparse() + " "; - next = ++key; + result += value.unparse() + " "; + next = key + 1; } - for (int j = next; j < a.sp->size; ++j) { + for (size_t j = next; j < a.sp->size; ++j) { result += "null "; } } else { @@ -467,22 +466,21 @@ BaseHandle::write_json(int json_version, JSON::Writer& p) const auto const& a = std::get(obj->value); p.writeStart('['); if (a.sp) { - int next = 0; - for (auto& item: a.sp->elements) { - int key = item.first; - for (int j = next; j < key; ++j) { + size_t next = 0; + for (auto& [key, value]: a.sp->elements) { + for (size_t j = next; j < key; ++j) { p.writeNext() << "null"; } p.writeNext(); - auto item_og = item.second.getObj()->getObjGen(); + auto item_og = value.getObj()->getObjGen(); if (item_og.isIndirect()) { p << "\"" << item_og.unparse(' ') << " R\""; } else { - item.second.write_json(json_version, p); + value.write_json(json_version, p); } - next = ++key; + next = key + 1; } - for (int j = next; j < a.sp->size; ++j) { + for (size_t j = next; j < a.sp->size; ++j) { p.writeNext() << "null"; } } else { @@ -611,11 +609,11 @@ QPDFObjectHandle::isSameObjectAs(QPDFObjectHandle const& rhs) const qpdf_object_type_e QPDFObjectHandle::getTypeCode() const { - return obj ? obj->getResolvedTypeCode() : ::ot_uninitialized; + return type_code(); } char const* -QPDFObjectHandle::getTypeName() const +BaseHandle::type_name() const { static constexpr std::array tn{ "uninitialized", @@ -634,7 +632,13 @@ QPDFObjectHandle::getTypeName() const "unresolved", "destroyed", "reference"}; - return obj ? tn[getTypeCode()] : "uninitialized"; + return tn[type_code()]; +} + +char const* +QPDFObjectHandle::getTypeName() const +{ + return type_name(); } bool @@ -829,20 +833,16 @@ QPDFObjectHandle::getValueAsInt(long long& value) const int QPDFObjectHandle::getIntValueAsInt() const { - int result = 0; long long v = getIntValue(); if (v < INT_MIN) { - QTC::TC("qpdf", "QPDFObjectHandle int returning INT_MIN"); - warnIfPossible("requested value of integer is too small; returning INT_MIN"); - result = INT_MIN; - } else if (v > INT_MAX) { - QTC::TC("qpdf", "QPDFObjectHandle int returning INT_MAX"); - warnIfPossible("requested value of integer is too big; returning INT_MAX"); - result = INT_MAX; - } else { - result = static_cast(v); + warn("requested value of integer is too small; returning INT_MIN"); + return INT_MIN; } - return result; + if (v > INT_MAX) { + warn("requested value of integer is too big; returning INT_MAX"); + return INT_MAX; + } + return static_cast(v); } bool @@ -860,8 +860,7 @@ QPDFObjectHandle::getUIntValue() const { long long v = getIntValue(); if (v < 0) { - QTC::TC("qpdf", "QPDFObjectHandle uint returning 0"); - warnIfPossible("unsigned value request for negative number; returning 0"); + warn("unsigned value request for negative number; returning 0"); return 0; } else { return static_cast(v); @@ -883,16 +882,14 @@ QPDFObjectHandle::getUIntValueAsUInt() const { 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"); + warn("unsigned integer value request for negative number; returning 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"); + } + if (v > UINT_MAX) { + warn("requested value of unsigned integer is too big; returning UINT_MAX"); return UINT_MAX; - } else { - return static_cast(v); } + return static_cast(v); } bool @@ -1235,34 +1232,30 @@ QPDFObjectHandle::arrayOrStreamToStreamArray( all_description = description; std::vector result; if (auto array = as_array(strict)) { - int n_items = array.size(); + int n_items = static_cast(array.size()); for (int i = 0; i < n_items; ++i) { QPDFObjectHandle item = array.at(i).second; if (item.isStream()) { result.emplace_back(item); } else { QTC::TC("qpdf", "QPDFObjectHandle non-stream in stream array"); - warn( - item.getOwningQPDF(), - QPDFExc( - qpdf_e_damaged_pdf, - "", - description + ": item index " + std::to_string(i) + " (from 0)", - 0, - "ignoring non-stream in an array of streams")); + item.warn( + {qpdf_e_damaged_pdf, + "", + description + ": item index " + std::to_string(i) + " (from 0)", + 0, + "ignoring non-stream in an array of streams"}); } } } else if (isStream()) { result.emplace_back(*this); - } else if (!isNull()) { + } else if (!null()) { warn( - getOwningQPDF(), - QPDFExc( - qpdf_e_damaged_pdf, - "", - description, - 0, - " object is supposed to be a stream or an array of streams but is neither")); + {qpdf_e_damaged_pdf, + "", + description, + 0, + " object is supposed to be a stream or an array of streams but is neither"}); } bool first = true; @@ -1824,6 +1817,12 @@ QPDFObjectHandle::newReserved(QPDF* qpdf) return qpdf->newReserved(); } +std::string +BaseHandle::description() const +{ + return obj ? obj->getDescription() : ""s; +} + void QPDFObjectHandle::setObjectDescription(QPDF* owning_qpdf, std::string const& object_description) { @@ -1934,50 +1933,44 @@ QPDFObjectHandle::assertInitialized() const } } +std::runtime_error +BaseHandle::type_error(char const* expected_type) const +{ + return std::runtime_error( + "operation for "s + expected_type + " attempted on object of type " + type_name()); +} + +QPDFExc +BaseHandle::type_error(char const* expected_type, std::string const& message) const +{ + return { + qpdf_e_object, + "", + description(), + 0, + "operation for "s + expected_type + " attempted on object of type " + type_name() + ": " + + message}; +} + void -QPDFObjectHandle::typeWarning(char const* expected_type, std::string const& warning) const +QPDFObjectHandle::typeWarning(char const* expected_type, std::string const& message) const { - QPDF* context = nullptr; - std::string description; - // Type checks above guarantee that the object has been dereferenced. Nevertheless, dereference - // throws exceptions in the test suite if (!obj) { throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle"); } - obj->getDescription(context, description); - // Null context handled by warn - warn( - context, - QPDFExc( - qpdf_e_object, - "", - description, - 0, - std::string("operation for ") + expected_type + " attempted on object of type " + - QPDFObjectHandle(*this).getTypeName() + ": " + warning)); + warn(type_error(expected_type, message)); } void QPDFObjectHandle::warnIfPossible(std::string const& warning) const { - QPDF* context = nullptr; - std::string description; - if (obj && obj->getDescription(context, description)) { - warn(context, QPDFExc(qpdf_e_damaged_pdf, "", description, 0, warning)); - } else { - *QPDFLogger::defaultLogger()->getError() << warning << "\n"; - } + warn(warning); } void QPDFObjectHandle::objectWarning(std::string const& warning) const { - QPDF* context = nullptr; - std::string 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)); + warn({qpdf_e_object, "", description(), 0, warning}); } void @@ -2130,15 +2123,30 @@ QPDFObjectHandle::assertPageObject() const } void -QPDFObjectHandle::warn(QPDF* qpdf, QPDFExc const& e) +BaseHandle::warn(QPDF* qpdf, QPDFExc&& e) { - // If parsing on behalf of a QPDF object and want to give a warning, we can warn through the - // object. If parsing for some other reason, such as an explicit creation of an object from a - // string, then just throw the exception. - if (qpdf) { - qpdf->warn(e); + if (!qpdf) { + throw std::move(e); + } + qpdf->warn(std::move(e)); +} + +void +BaseHandle::warn(QPDFExc&& e) const +{ + if (!qpdf()) { + throw std::move(e); + } + qpdf()->warn(std::move(e)); +} + +void +BaseHandle::warn(std::string const& warning) const +{ + if (qpdf()) { + warn({qpdf_e_damaged_pdf, "", description(), 0, warning}); } else { - throw e; + *QPDFLogger::defaultLogger()->getError() << warning << "\n"; } } diff --git a/libqpdf/QPDFOutlineObjectHelper.cc b/libqpdf/QPDFOutlineObjectHelper.cc index 8d561f8..640826a 100644 --- a/libqpdf/QPDFOutlineObjectHelper.cc +++ b/libqpdf/QPDFOutlineObjectHelper.cc @@ -75,7 +75,7 @@ QPDFObjectHandle QPDFOutlineObjectHelper::getDestPage() { QPDFObjectHandle dest = getDest(); - if ((dest.isArray()) && (dest.getArrayNItems() > 0)) { + if (!dest.empty() && dest.isArray()) { return dest.getArrayItem(0); } return QPDFObjectHandle::newNull(); diff --git a/libqpdf/QPDFPageDocumentHelper.cc b/libqpdf/QPDFPageDocumentHelper.cc index f88f3dc..59189d6 100644 --- a/libqpdf/QPDFPageDocumentHelper.cc +++ b/libqpdf/QPDFPageDocumentHelper.cc @@ -59,7 +59,7 @@ QPDFPageDocumentHelper::flattenAnnotations(int required_flags, int forbidden_fla if (afdh.getNeedAppearances()) { qpdf.getRoot() .getKey("/AcroForm") - .warnIfPossible( + .warn( "document does not have updated appearance streams, so form fields " "will not be flattened"); } diff --git a/libqpdf/QPDFPageObjectHelper.cc b/libqpdf/QPDFPageObjectHelper.cc index 6a095d3..1a0098c 100644 --- a/libqpdf/QPDFPageObjectHelper.cc +++ b/libqpdf/QPDFPageObjectHelper.cc @@ -12,6 +12,8 @@ #include #include +using namespace std::literals; + namespace { class ContentProvider: public QPDFObjectHandle::StreamDataProvider @@ -118,7 +120,7 @@ InlineImageTracker::convertIIDict(QPDFObjectHandle odict) QTC::TC("qpdf", "QPDFPageObjectHelper colorspace lookup"); value = colorspace.getKey(name); } else { - resources.warnIfPossible("unable to resolve colorspace " + name); + resources.warn("unable to resolve colorspace " + name); } name.clear(); } @@ -414,8 +416,8 @@ QPDFPageObjectHelper::externalizeInlineImages(size_t min_size, bool shallow) filterContents(&iit, &b); filtered = true; } catch (std::exception& e) { - oh().warnIfPossible( - std::string("Unable to filter content stream: ") + e.what() + + warn( + "Unable to filter content stream: "s + e.what() + "; not attempting to externalize inline images from this stream"); } if (filtered && iit.any_images) { @@ -443,14 +445,9 @@ std::vector QPDFPageObjectHelper::getAnnotations(std::string const& only_subtype) { std::vector result; - QPDFObjectHandle annots = oh().getKey("/Annots"); - if (annots.isArray()) { - int nannots = annots.getArrayNItems(); - for (int i = 0; i < nannots; ++i) { - QPDFObjectHandle annot = annots.getArrayItem(i); - if (annot.isDictionaryOfType("", only_subtype)) { - result.emplace_back(annot); - } + for (auto const& annot: oh().getKey("/Annots").as_array()) { + if (annot.isDictionaryOfType("", only_subtype)) { + result.emplace_back(annot); } } return result; @@ -555,15 +552,15 @@ QPDFPageObjectHelper::removeUnreferencedResourcesHelper( ph.parseContents(&rf); size_t after_nw = (q ? q->numWarnings() : 0); if (after_nw > before_nw) { - ph.oh().warnIfPossible( + ph.warn( "Bad token found while scanning content stream; " "not attempting to remove unreferenced objects from this object"); return false; } } catch (std::exception& e) { QTC::TC("qpdf", "QPDFPageObjectHelper bad token finding names"); - ph.oh().warnIfPossible( - std::string("Unable to parse content stream: ") + e.what() + + ph.warn( + "Unable to parse content stream: "s + e.what() + "; not attempting to remove unreferenced objects from this object"); return false; } @@ -719,8 +716,7 @@ QPDFPageObjectHelper::getFormXObjectForPage(bool handle_transformations) newdict.replaceKey("/Group", getAttribute("/Group", false).shallowCopy()); QPDFObjectHandle bbox = getTrimBox(false).shallowCopy(); if (!bbox.isRectangle()) { - oh().warnIfPossible( - "bounding box is invalid; form XObject created from page will not work"); + warn("bounding box is invalid; form XObject created from page will not work"); } newdict.replaceKey("/BBox", bbox); auto provider = diff --git a/libqpdf/QPDFWriter.cc b/libqpdf/QPDFWriter.cc index 44a3aeb..81d894f 100644 --- a/libqpdf/QPDFWriter.cc +++ b/libqpdf/QPDFWriter.cc @@ -1396,8 +1396,8 @@ QPDFWriter::willFilterStream( } } catch (std::runtime_error& e) { if (filter && first_attempt) { - stream.warnIfPossible("error while getting stream data: "s + e.what()); - stream.warnIfPossible("qpdf will attempt to write the damaged stream unchanged"); + stream.warn("error while getting stream data: "s + e.what()); + stream.warn("qpdf will attempt to write the damaged stream unchanged"); filter = false; stream.setFilterOnWrite(false); continue; @@ -1538,9 +1538,7 @@ QPDFWriter::unparseObject( object.removeKey("/Length"); // If /DecodeParms is an empty list, remove it. - if (object.getKey("/DecodeParms").isArray() && - (0 == object.getKey("/DecodeParms").getArrayNItems())) { - QTC::TC("qpdf", "QPDFWriter remove empty DecodeParms"); + if (object.getKey("/DecodeParms").empty()) { object.removeKey("/DecodeParms"); } @@ -1558,22 +1556,19 @@ QPDFWriter::unparseObject( object.removeKey("/Filter"); object.removeKey("/DecodeParms"); } else { - int idx = -1; - for (int i = 0; i < filter.getArrayNItems(); ++i) { - QPDFObjectHandle item = filter.getArrayItem(i); + int idx = 0; + for (auto const& item: filter.as_array()) { if (item.isNameAndEquals("/Crypt")) { - idx = i; + // If filter is an array, then the code in QPDF_Stream has already + // verified that DecodeParms and Filters are arrays of the same + // length, but if they weren't for some reason, eraseItem does type + // and bounds checking. Fuzzing tells us that this can actually + // happen. + filter.eraseItem(idx); + decode_parms.eraseItem(idx); break; } - } - if (idx >= 0) { - // If filter is an array, then the code in QPDF_Stream has already - // verified that DecodeParms and Filters are arrays of the same length, - // but if they weren't for some reason, eraseItem does type and bounds - // checking. - QTC::TC("qpdf", "QPDFWriter remove Crypt"); - filter.eraseItem(idx); - decode_parms.eraseItem(idx); + ++idx; } } } @@ -1742,7 +1737,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) if (obj_to_write.isStream()) { // This condition occurred in a fuzz input. Ideally we should block it at parse // time, but it's not clear to me how to construct a case for this. - obj_to_write.warnIfPossible("stream found inside object stream; treating as null"); + obj_to_write.warn("stream found inside object stream; treating as null"); obj_to_write = QPDFObjectHandle::newNull(); } writeObject(obj_to_write, count); @@ -1965,7 +1960,7 @@ QPDFWriter::initializeSpecialStreams() QPDFObjectHandle contents = page.getKey("/Contents"); std::vector contents_objects; if (contents.isArray()) { - int n = contents.getArrayNItems(); + int n = static_cast(contents.size()); for (int i = 0; i < n; ++i) { contents_objects.push_back(contents.getArrayItem(i).getObjGen()); } diff --git a/libqpdf/QPDF_Array.cc b/libqpdf/QPDF_Array.cc index 2c4104c..1057528 100644 --- a/libqpdf/QPDF_Array.cc +++ b/libqpdf/QPDF_Array.cc @@ -2,11 +2,25 @@ #include +#include + using namespace std::literals; using namespace qpdf; static const QPDFObjectHandle null_oh = QPDFObjectHandle::newNull(); +static size_t +to_s(int n) +{ + return static_cast(n); +} + +static int +to_i(size_t n) +{ + return static_cast(n); +} + inline void Array::checkOwnership(QPDFObjectHandle const& item) const { @@ -142,24 +156,24 @@ Array::null() const return null_oh; } -int +size_t Array::size() const { auto a = array(); - return a->sp ? a->sp->size : int(a->elements.size()); + return a->sp ? a->sp->size : a->elements.size(); } std::pair Array::at(int n) const { auto a = array(); - if (n < 0 || n >= size()) { + if (n < 0 || std::cmp_greater_equal(n, size())) { return {false, {}}; } if (!a->sp) { - return {true, a->elements[size_t(n)]}; + return {true, a->elements[to_s(n)]}; } - auto const& iter = a->sp->elements.find(n); + auto const& iter = a->sp->elements.find(to_s(n)); return {true, iter == a->sp->elements.end() ? null() : iter->second}; } @@ -169,12 +183,12 @@ Array::getAsVector() const auto a = array(); if (a->sp) { std::vector v; - v.reserve(size_t(size())); + v.reserve(size()); for (auto const& item: a->sp->elements) { - v.resize(size_t(item.first), null_oh); + v.resize(item.first, null_oh); v.emplace_back(item.second); } - v.resize(size_t(size()), null_oh); + v.resize(size(), null_oh); return v; } else { return a->elements; @@ -184,15 +198,15 @@ Array::getAsVector() const bool Array::setAt(int at, QPDFObjectHandle const& oh) { - if (at < 0 || at >= size()) { + if (at < 0 || std::cmp_greater_equal(at, size())) { return false; } auto a = array(); checkOwnership(oh); if (a->sp) { - a->sp->elements[at] = oh; + a->sp->elements[to_s(at)] = oh; } else { - a->elements[size_t(at)] = oh; + a->elements[to_s(at)] = oh; } return true; } @@ -210,34 +224,39 @@ Array::setFromVector(std::vector const& v) } bool -Array::insert(int at, QPDFObjectHandle const& item) +Array::insert(int at_i, QPDFObjectHandle const& item) { auto a = array(); - int sz = size(); - if (at < 0 || at > sz) { - // As special case, also allow insert beyond the end + size_t sz = size(); + if (at_i < 0) { + return false; + } + size_t at = to_s(at_i); + if (at > sz) { return false; - } else if (at == sz) { + } + if (at == sz) { + // As special case, also allow insert beyond the end push_back(item); - } else { - checkOwnership(item); - if (a->sp) { - auto iter = a->sp->elements.crbegin(); - while (iter != a->sp->elements.crend()) { - auto key = (iter++)->first; - if (key >= at) { - auto nh = a->sp->elements.extract(key); - ++nh.key(); - a->sp->elements.insert(std::move(nh)); - } else { - break; - } + return true; + } + checkOwnership(item); + if (a->sp) { + auto iter = a->sp->elements.crbegin(); + while (iter != a->sp->elements.crend()) { + auto key = (iter++)->first; + if (key >= at) { + auto nh = a->sp->elements.extract(key); + ++nh.key(); + a->sp->elements.insert(std::move(nh)); + } else { + break; } - a->sp->elements[at] = item.getObj(); - ++a->sp->size; - } else { - a->elements.insert(a->elements.cbegin() + at, item.getObj()); } + a->sp->elements[at] = item.getObj(); + ++a->sp->size; + } else { + a->elements.insert(a->elements.cbegin() + at_i, item.getObj()); } return true; } @@ -255,10 +274,14 @@ Array::push_back(QPDFObjectHandle const& item) } bool -Array::erase(int at) +Array::erase(int at_i) { auto a = array(); - if (at < 0 || at >= size()) { + if (at_i < 0) { + return false; + } + size_t at = to_s(at_i); + if (at >= size()) { return false; } if (a->sp) { @@ -277,7 +300,7 @@ Array::erase(int at) } --(a->sp->size); } else { - a->elements.erase(a->elements.cbegin() + at); + a->elements.erase(a->elements.cbegin() + at_i); } return true; } @@ -286,7 +309,7 @@ int QPDFObjectHandle::getArrayNItems() const { if (auto array = as_array(strict)) { - return array.size(); + return to_i(array.size()); } typeWarning("array", "treating as empty"); QTC::TC("qpdf", "QPDFObjectHandle array treating as empty"); @@ -471,7 +494,37 @@ QPDFObjectHandle QPDFObjectHandle::eraseItemAndGetOld(int at) { auto array = as_array(strict); - auto result = (array && at < array.size() && at >= 0) ? array.at(at).second : newNull(); + auto result = + (array && std::cmp_less(at, array.size()) && at >= 0) ? array.at(at).second : newNull(); eraseItem(at); return result; } + +size_t +BaseHandle::size() const +{ + switch (resolved_type_code()) { + case ::ot_array: + return as()->size(); + case ::ot_uninitialized: + case ::ot_reserved: + case ::ot_null: + case ::ot_destroyed: + case ::ot_unresolved: + case ::ot_reference: + return 0; + case ::ot_boolean: + case ::ot_integer: + case ::ot_real: + case ::ot_string: + case ::ot_name: + case ::ot_dictionary: + case ::ot_stream: + case ::ot_inlineimage: + case ::ot_operator: + return 1; + default: + throw std::logic_error("Unexpected type code in size"); // unreachable + return 0; // unreachable + } +} diff --git a/libqpdf/QPDF_encryption.cc b/libqpdf/QPDF_encryption.cc index 935c0a1..e5dca43 100644 --- a/libqpdf/QPDF_encryption.cc +++ b/libqpdf/QPDF_encryption.cc @@ -664,7 +664,7 @@ QPDF::EncryptionParameters::initialize(QPDF& qpdf) std::string id1; auto id_obj = trailer.getKey("/ID"); - if (!id_obj.isArray() || id_obj.getArrayNItems() != 2 || !id_obj.getArrayItem(0).isString()) { + if (id_obj.size() != 2 || !id_obj.getArrayItem(0).isString()) { // Treating a missing ID as the empty string enables qpdf to decrypt some invalid encrypted // files with no /ID that poppler can read but Adobe Reader can't. qpdf.warn(qpdf.damagedPDF("trailer", "invalid /ID in trailer dictionary")); @@ -961,19 +961,21 @@ QPDF::decryptStream( } else if ( stream_dict.getKey("/DecodeParms").isArray() && stream_dict.getKey("/Filter").isArray()) { - QPDFObjectHandle filter = stream_dict.getKey("/Filter"); - QPDFObjectHandle decode = stream_dict.getKey("/DecodeParms"); - if (filter.getArrayNItems() == decode.getArrayNItems()) { - for (int i = 0; i < filter.getArrayNItems(); ++i) { - if (filter.getArrayItem(i).isNameAndEquals("/Crypt")) { - QPDFObjectHandle crypt_params = decode.getArrayItem(i); + auto filter = stream_dict.getKey("/Filter"); + auto decode = stream_dict.getKey("/DecodeParms"); + if (filter.size() == decode.size()) { + int i = 0; + for (auto const& item: filter.as_array()) { + if (item.isNameAndEquals("/Crypt")) { + auto crypt_params = decode.getArrayItem(i); if (crypt_params.isDictionary() && crypt_params.getKey("/Name").isName()) { - QTC::TC("qpdf", "QPDF_encrypt crypt array"); method = encp->interpretCF(crypt_params.getKey("/Name")); method_source = "stream's Crypt decode parameters (array)"; } + break; } + ++i; } } } diff --git a/libqpdf/QPDF_linearization.cc b/libqpdf/QPDF_linearization.cc index 2dc21fc..c0f5585 100644 --- a/libqpdf/QPDF_linearization.cc +++ b/libqpdf/QPDF_linearization.cc @@ -174,14 +174,13 @@ QPDF::readLinearizationData() } // Hint table array: offset length [ offset length ] - size_t n_H_items = toS(H.getArrayNItems()); - if (!((n_H_items == 2) || (n_H_items == 4))) { + size_t n_H_items = H.size(); + if (!(n_H_items == 2 || n_H_items == 4)) { throw damagedPDF("linearization dictionary", "H has the wrong number of items"); } std::vector H_items; - for (size_t i = 0; i < n_H_items; ++i) { - QPDFObjectHandle oh(H.getArrayItem(toI(i))); + for (auto const& oh: H.as_array()) { if (oh.isInteger()) { H_items.push_back(oh.getIntValueAsInt()); } else { diff --git a/libqpdf/QPDF_objects.cc b/libqpdf/QPDF_objects.cc index ecae23d..168c1c8 100644 --- a/libqpdf/QPDF_objects.cc +++ b/libqpdf/QPDF_objects.cc @@ -782,7 +782,7 @@ std::pair> QPDF::processXRefW(QPDFObjectHandle& dict, std::function damaged) { auto W_obj = dict.getKey("/W"); - if (!(W_obj.isArray() && (W_obj.getArrayNItems() >= 3) && W_obj.getArrayItem(0).isInteger() && + if (!(W_obj.size() >= 3 && W_obj.getArrayItem(0).isInteger() && W_obj.getArrayItem(1).isInteger() && W_obj.getArrayItem(2).isInteger())) { throw damaged("Cross-reference stream does not have a proper /W key"); } diff --git a/libqpdf/QPDF_pages.cc b/libqpdf/QPDF_pages.cc index b2907f0..b3564ef 100644 --- a/libqpdf/QPDF_pages.cc +++ b/libqpdf/QPDF_pages.cc @@ -57,7 +57,7 @@ QPDF::getAllPages() // Files have been found in the wild where /Pages in the catalog points to the first // page. Try to work around this and similar cases with this heuristic. if (!warned) { - root.warnIfPossible( + root.warn( "document page tree root (root -> /Pages) doesn't point" " to the root of the page tree; attempting to correct"); warned = true; @@ -109,7 +109,7 @@ QPDF::getAllPagesInternal( // During fuzzing files were encountered where the root object appeared in the pages tree. // Unconditionally setting the /Type to /Pages could cause problems, but trying to // accommodate the possibility may be excessive. - cur_node.warnIfPossible("/Type key should be /Pages but is not; overriding"); + cur_node.warn("/Type key should be /Pages but is not; overriding"); cur_node.replaceKey("/Type", "/Pages"_qpdf); } if (!media_box) { @@ -134,13 +134,13 @@ QPDF::getAllPagesInternal( int errors = 0; if (!kid.isDictionary()) { - kid.warnIfPossible("Pages tree includes non-dictionary object; ignoring"); + kid.warn("Pages tree includes non-dictionary object; ignoring"); m->invalid_page_found = true; continue; } if (!kid.isIndirect()) { QTC::TC("qpdf", "QPDF handle direct page object"); - cur_node.warnIfPossible( + cur_node.warn( "kid " + std::to_string(i) + " (from 0) is direct; converting to indirect"); kid = makeIndirectObject(kid); ++errors; @@ -150,7 +150,7 @@ QPDF::getAllPagesInternal( } else { if (!media_box && !kid.getKey("/MediaBox").isRectangle()) { QTC::TC("qpdf", "QPDF missing mediabox"); - kid.warnIfPossible( + kid.warn( "kid " + std::to_string(i) + " (from 0) MediaBox is undefined; setting to letter / ANSI A"); kid.replaceKey( @@ -167,7 +167,7 @@ QPDF::getAllPagesInternal( // QPDFPageObjectHelper. QTC::TC("qpdf", "QPDF resolve duplicated page object"); if (!m->reconstructed_xref) { - cur_node.warnIfPossible( + cur_node.warn( "kid " + std::to_string(i) + " (from 0) appears more than once in the pages tree;" " creating a new page object as a copy"); @@ -176,7 +176,7 @@ QPDF::getAllPagesInternal( kid = makeIndirectObject(QPDFObjectHandle(kid).shallowCopy()); seen.add(kid); } else { - cur_node.warnIfPossible( + cur_node.warn( "kid " + std::to_string(i) + " (from 0) appears more than once in the pages tree; ignoring duplicate"); m->invalid_page_found = true; @@ -189,12 +189,12 @@ QPDF::getAllPagesInternal( } } if (!kid.isDictionaryOfType("/Page")) { - kid.warnIfPossible("/Type key should be /Page but is not; overriding"); + kid.warn("/Type key should be /Page but is not; overriding"); kid.replaceKey("/Type", "/Page"_qpdf); ++errors; } if (m->reconstructed_xref && errors > 2) { - cur_node.warnIfPossible( + cur_node.warn( "kid " + std::to_string(i) + " (from 0) has too many errors; ignoring page"); m->invalid_page_found = true; kid = QPDFObjectHandle::newNull(); @@ -313,7 +313,7 @@ QPDF::insertPage(QPDFObjectHandle newpage, int pos) newpage.replaceKey("/Parent", pages); kids.insertItem(pos, newpage); - int npages = kids.getArrayNItems(); + int npages = static_cast(kids.size()); pages.replaceKey("/Count", QPDFObjectHandle::newInteger(npages)); m->all_pages.insert(m->all_pages.begin() + pos, newpage); for (int i = pos + 1; i < npages; ++i) { @@ -337,7 +337,7 @@ QPDF::removePage(QPDFObjectHandle page) QPDFObjectHandle kids = pages.getKey("/Kids"); kids.eraseItem(pos); - int npages = kids.getArrayNItems(); + int npages = static_cast(kids.size()); pages.replaceKey("/Count", QPDFObjectHandle::newInteger(npages)); m->all_pages.erase(m->all_pages.begin() + pos); m->pageobj_to_pages_pos.erase(page.getObjGen()); diff --git a/libqpdf/qpdf/QPDFObjectHandle_private.hh b/libqpdf/qpdf/QPDFObjectHandle_private.hh index c73ea1b..9fa67cd 100644 --- a/libqpdf/qpdf/QPDFObjectHandle_private.hh +++ b/libqpdf/qpdf/QPDFObjectHandle_private.hh @@ -38,7 +38,7 @@ namespace qpdf const_reverse_iterator crend(); - int size() const; + size_t size() const; std::pair at(int n) const; bool setAt(int at, QPDFObjectHandle const& oh); bool insert(int at, QPDFObjectHandle const& item); diff --git a/libqpdf/qpdf/QPDFObject_private.hh b/libqpdf/qpdf/QPDFObject_private.hh index 8518665..0637069 100644 --- a/libqpdf/qpdf/QPDFObject_private.hh +++ b/libqpdf/qpdf/QPDFObject_private.hh @@ -35,8 +35,8 @@ class QPDF_Array final private: struct Sparse { - int size{0}; - std::map elements; + size_t size{0}; + std::map elements; }; public: @@ -65,10 +65,10 @@ class QPDF_Array final { } - int + size_t size() const { - return sp ? sp->size : int(elements.size()); + return sp ? sp->size : elements.size(); } std::unique_ptr sp; @@ -434,13 +434,6 @@ class QPDFObject parsed_offset = offset; } } - bool - getDescription(QPDF*& a_qpdf, std::string& description) - { - a_qpdf = qpdf; - description = getDescription(); - return qpdf != nullptr; - } qpdf_offset_t getParsedOffset() { diff --git a/libtests/sparse_array.cc b/libtests/sparse_array.cc index 449cfd8..d0fee40 100644 --- a/libtests/sparse_array.cc +++ b/libtests/sparse_array.cc @@ -7,6 +7,12 @@ #include int +to_i(size_t n) +{ + return static_cast(n); +} + +int main() { auto obj = QPDFObject::create(std::vector(), true); @@ -65,20 +71,20 @@ main() a.setAt(4, QPDFObjectHandle::newNull()); assert(a.at(4).second.isNull()); - a.erase(a.size() - 1); + a.erase(to_i(a.size()) - 1); assert(a.size() == 5); assert(a.at(0).second.isName() && (a.at(0).second.getName() == "/First")); assert(a.at(1).second.isInteger() && (a.at(1).second.getIntValue() == 1)); assert(a.at(3).second.isName() && (a.at(3).second.getName() == "/Third")); assert(a.at(4).second.isNull()); - a.erase(a.size() - 1); + a.erase(to_i(a.size()) - 1); assert(a.size() == 4); assert(a.at(0).second.isName() && (a.at(0).second.getName() == "/First")); assert(a.at(1).second.isInteger() && (a.at(1).second.getIntValue() == 1)); assert(a.at(3).second.isName() && (a.at(3).second.getName() == "/Third")); - a.erase(a.size() - 1); + a.erase(to_i(a.size()) - 1); assert(a.size() == 3); assert(a.at(0).second.isName() && (a.at(0).second.getName() == "/First")); assert(a.at(1).second.isInteger() && (a.at(1).second.getIntValue() == 1)); diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov index c9bc18d..e39e04f 100644 --- a/qpdf/qpdf.testcov +++ b/qpdf/qpdf.testcov @@ -237,9 +237,7 @@ QPDFWriter remove ADBE 0 QPDFWriter remove existing Extensions 0 QPDFWriter preserve ADBE 0 QPDF_encryption skip 0x28 0 -QPDF_encrypt crypt array 0 QPDF_encryption CFM AESV3 0 -QPDFWriter remove Crypt 0 qpdf-c called qpdf_get_pdf_extension_level 0 qpdf-c called qpdf_set_r5_encryption_parameters 0 qpdf-c called qpdf_set_r6_encryption_parameters 0 @@ -423,12 +421,6 @@ QPDFPageObjectHelper externalize inline image 0 QPDFPageObjectHelper keep inline image 0 QPDFJob image optimize colorspace 0 QPDFJob image optimize bits per component 0 -QPDFWriter remove empty DecodeParms 0 -QPDFObjectHandle uint returning 0 0 -QPDFObjectHandle int returning INT_MIN 0 -QPDFObjectHandle int returning INT_MAX 0 -QPDFObjectHandle uint returning UINT_MAX 0 -QPDFObjectHandle uint uint returning 0 0 QPDF xref skipped space 0 QPDF eof skipping spaces before xref 1 QPDF_encryption user matches owner V < 5 0 @@ -580,9 +572,7 @@ QPDFAcroFormDocumentHelper copy annotation 3 QPDFAcroFormDocumentHelper field with parent 3 QPDFAcroFormDocumentHelper modify ap matrix 0 QPDFJob copy form fields in split_pages 0 -QPDFJob keep some fields in pages 0 QPDFJob pages keeping field from original 0 -QPDFJob no more fields in pages 0 QPDFObjectHandle merge reuse 0 QPDFObjectHandle merge generate 0 QPDFFormFieldObjectHelper get font from /DR 0 @@ -592,7 +582,7 @@ QPDFAcroFormDocumentHelper replaced DA token 0 QPDFAcroFormDocumentHelper ap conflict 0 QPDFAcroFormDocumentHelper ap rename 0 QPDFAcroFormDocumentHelper /DA parse error 0 -QPDFAcroFormDocumentHelper AP parse error 0 +QPDFAcroFormDocumentHelper AP parse error 1 QPDFJob copy fields not this file 0 QPDFJob copy fields non-first from orig 0 QPDF resolve duplicated page in insert 0