From cc9facdb645e163ed67d1311a670c68958e8ac4c Mon Sep 17 00:00:00 2001 From: m-holger Date: Mon, 13 Oct 2025 13:40:34 +0100 Subject: [PATCH] Refactor `QPDF`: move warnings and damaged PDF handling logic to `Common` class, streamline error handling, and improve encapsulation. --- include/qpdf/QPDF.hh | 13 ------------- libqpdf/QPDF.cc | 49 ++++++++++++++++++++++++++++++++++--------------- libqpdf/QPDF_encryption.cc | 16 +++++++++------- libqpdf/QPDF_linearization.cc | 71 +++++++++++++++++++++++++++++++++++------------------------------------ libqpdf/QPDF_objects.cc | 153 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------------------- libqpdf/QPDF_optimization.cc | 2 +- libqpdf/qpdf/QPDF_private.hh | 39 ++++++++++++++++++++++++++++++--------- 7 files changed, 183 insertions(+), 160 deletions(-) diff --git a/include/qpdf/QPDF.hh b/include/qpdf/QPDF.hh index 7dfcbeb..cecb242 100644 --- a/include/qpdf/QPDF.hh +++ b/include/qpdf/QPDF.hh @@ -747,20 +747,7 @@ class QPDF class ResolveRecorder; class JSONReactor; - void stopOnError(std::string const& message); - inline void - no_ci_stop_if(bool condition, std::string const& message, std::string const& context = {}); void removeObject(QPDFObjGen og); - static QPDFExc damagedPDF( - InputSource& input, - std::string const& object, - qpdf_offset_t offset, - std::string const& message); - QPDFExc damagedPDF(InputSource& input, qpdf_offset_t offset, std::string const& message); - QPDFExc damagedPDF(std::string const& object, qpdf_offset_t offset, std::string const& message); - QPDFExc damagedPDF(std::string const& object, std::string const& message); - QPDFExc damagedPDF(qpdf_offset_t offset, std::string const& message); - QPDFExc damagedPDF(std::string const& message); // Calls finish() on the pipeline when done but does not delete it bool pipeStreamData( diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index 5ebeffa..768da3d 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -27,7 +27,9 @@ using namespace qpdf; using namespace std::literals; -using Objects = QPDF::Doc::Objects; +using Doc = QPDF::Doc; +using Common = Doc::Common; +using Objects = Doc::Objects; using Foreign = Objects::Foreign; using Streams = Objects::Streams; @@ -364,6 +366,12 @@ QPDF::findHeader() void QPDF::warn(QPDFExc const& e) { + m->c.warn(e); +} + +void +Common::warn(QPDFExc const& e) +{ if (m->max_warnings > 0 && m->warnings.size() >= m->max_warnings) { stopOnError("Too many warnings - file is too badly damaged"); } @@ -380,7 +388,17 @@ QPDF::warn( qpdf_offset_t offset, std::string const& message) { - warn(QPDFExc(error_code, getFilename(), object, offset, message)); + m->c.warn(QPDFExc(error_code, getFilename(), object, offset, message)); +} + +void +Common::warn( + qpdf_error_code_e error_code, + std::string const& object, + qpdf_offset_t offset, + std::string const& message) +{ + warn(QPDFExc(error_code, qpdf.getFilename(), object, offset, message)); } QPDFObjectHandle @@ -515,7 +533,7 @@ Objects::Foreign::Copier::copied(QPDFObjectHandle const& foreign) auto og = foreign.getObjGen(); if (!object_map.contains(og)) { - qpdf.warn(qpdf.damagedPDF( + warn(damagedPDF( foreign.qpdf()->getFilename() + " object " + og.unparse(' '), foreign.offset(), "unexpected reference to /Pages object while copying foreign object; replacing with " @@ -693,12 +711,12 @@ QPDF::getRoot() { QPDFObjectHandle root = m->trailer.getKey("/Root"); if (!root.isDictionary()) { - throw damagedPDF("", -1, "unable to find /Root dictionary"); + throw m->c.damagedPDF("", -1, "unable to find /Root dictionary"); } else if ( // Check_mode is an interim solution to request #810 pending a more comprehensive review of // the approach to more extensive checks and warning levels. m->check_mode && !root.getKey("/Type").isNameAndEquals("/Catalog")) { - warn(damagedPDF("", -1, "catalog /Type entry missing or invalid")); + warn(m->c.damagedPDF("", -1, "catalog /Type entry missing or invalid")); root.replaceKey("/Type", "/Catalog"_qpdf); } return root; @@ -744,7 +762,7 @@ QPDF::pipeStreamData( try { auto buf = file->read(length, offset); if (buf.size() != length) { - throw damagedPDF( + throw qpdf_for_warning.m->c.damagedPDF( *file, "", offset + toO(buf.size()), "unexpected EOF reading stream data"); } pipeline->write(buf.data(), length); @@ -760,7 +778,7 @@ QPDF::pipeStreamData( QTC::TC("qpdf", "QPDF decoding error warning"); qpdf_for_warning.warn( // line-break - damagedPDF( + qpdf_for_warning.m->c.damagedPDF( *file, "", file->getLastOffset(), @@ -769,7 +787,7 @@ QPDF::pipeStreamData( if (will_retry) { qpdf_for_warning.warn( // line-break - damagedPDF( + qpdf_for_warning.m->c.damagedPDF( *file, "", file->getLastOffset(), @@ -815,14 +833,14 @@ QPDF::pipeStreamData( // Throw a generic exception when we lack context for something more specific. New code should not // use this. void -QPDF::stopOnError(std::string const& message) +Common::stopOnError(std::string const& message) { throw damagedPDF("", message); } // Return an exception of type qpdf_e_damaged_pdf. QPDFExc -QPDF::damagedPDF( +Common::damagedPDF( InputSource& input, std::string const& object, qpdf_offset_t offset, std::string const& message) { return {qpdf_e_damaged_pdf, input.getName(), object, offset, message, true}; @@ -831,14 +849,15 @@ QPDF::damagedPDF( // Return an exception of type qpdf_e_damaged_pdf. The object is taken from // m->last_object_description. QPDFExc -QPDF::damagedPDF(InputSource& input, qpdf_offset_t offset, std::string const& message) +Common::damagedPDF(InputSource& input, qpdf_offset_t offset, std::string const& message) const { return damagedPDF(input, m->last_object_description, offset, message); } // Return an exception of type qpdf_e_damaged_pdf. The filename is taken from m->file. QPDFExc -QPDF::damagedPDF(std::string const& object, qpdf_offset_t offset, std::string const& message) +Common::damagedPDF( + std::string const& object, qpdf_offset_t offset, std::string const& message) const { return {qpdf_e_damaged_pdf, m->file->getName(), object, offset, message, true}; } @@ -846,7 +865,7 @@ QPDF::damagedPDF(std::string const& object, qpdf_offset_t offset, std::string co // Return an exception of type qpdf_e_damaged_pdf. The filename is taken from m->file and the // offset from .m->file->getLastOffset(). QPDFExc -QPDF::damagedPDF(std::string const& object, std::string const& message) +Common::damagedPDF(std::string const& object, std::string const& message) const { return damagedPDF(object, m->file->getLastOffset(), message); } @@ -854,7 +873,7 @@ QPDF::damagedPDF(std::string const& object, std::string const& message) // Return an exception of type qpdf_e_damaged_pdf. The filename is taken from m->file and the object // from .m->last_object_description. QPDFExc -QPDF::damagedPDF(qpdf_offset_t offset, std::string const& message) +Common::damagedPDF(qpdf_offset_t offset, std::string const& message) const { return damagedPDF(m->last_object_description, offset, message); } @@ -862,7 +881,7 @@ QPDF::damagedPDF(qpdf_offset_t offset, std::string const& message) // Return an exception of type qpdf_e_damaged_pdf. The filename is taken from m->file, the object // from m->last_object_description and the offset from m->file->getLastOffset(). QPDFExc -QPDF::damagedPDF(std::string const& message) +Common::damagedPDF(std::string const& message) const { return damagedPDF(m->last_object_description, m->file->getLastOffset(), message); } diff --git a/libqpdf/QPDF_encryption.cc b/libqpdf/QPDF_encryption.cc index f45a72c..91b3819 100644 --- a/libqpdf/QPDF_encryption.cc +++ b/libqpdf/QPDF_encryption.cc @@ -734,15 +734,16 @@ QPDF::EncryptionParameters::initialize(QPDF& qpdf) } encryption_initialized = true; + auto& c = qpdf.m->c; auto& qm = *qpdf.m; auto& trailer = qm.trailer; auto& file = qm.file; - auto warn_damaged_pdf = [&qpdf](std::string const& msg) { - qpdf.warn(qpdf.damagedPDF("encryption dictionary", msg)); + auto warn_damaged_pdf = [&qpdf, c](std::string const& msg) { + qpdf.warn(c.damagedPDF("encryption dictionary", msg)); }; auto throw_damaged_pdf = [&qpdf](std::string const& msg) { - throw qpdf.damagedPDF("encryption dictionary", msg); + throw qpdf.m->c.damagedPDF("encryption dictionary", msg); }; auto unsupported = [&file](std::string const& msg) -> QPDFExc { return { @@ -770,14 +771,14 @@ QPDF::EncryptionParameters::initialize(QPDF& qpdf) 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")); + qpdf.warn(qpdf.m->c.damagedPDF("trailer", "invalid /ID in trailer dictionary")); } else { id1 = id_obj.getArrayItem(0).getStringValue(); } auto encryption_dict = trailer.getKey("/Encrypt"); if (!encryption_dict.isDictionary()) { - throw qpdf.damagedPDF("/Encrypt in trailer dictionary is not a dictionary"); + throw qpdf.m->c.damagedPDF("/Encrypt in trailer dictionary is not a dictionary"); } if (Name(encryption_dict["/Filter"]) != "/Standard") { @@ -984,7 +985,7 @@ QPDF::decryptString(std::string& str, QPDFObjGen og) break; default: - warn(damagedPDF( + warn(m->c.damagedPDF( "unknown encryption filter for strings (check /StrF in " "/Encrypt dictionary); strings may be decrypted improperly")); // To avoid repeated warnings, reset cf_string. Assume we'd want to use AES if V == 4. @@ -1017,7 +1018,8 @@ QPDF::decryptString(std::string& str, QPDFObjGen og) } catch (QPDFExc&) { throw; } catch (std::runtime_error& e) { - throw damagedPDF("error decrypting string for object " + og.unparse() + ": " + e.what()); + throw m->c.damagedPDF( + "error decrypting string for object " + og.unparse() + ": " + e.what()); } } diff --git a/libqpdf/QPDF_linearization.cc b/libqpdf/QPDF_linearization.cc index 3ac797b..358435e 100644 --- a/libqpdf/QPDF_linearization.cc +++ b/libqpdf/QPDF_linearization.cc @@ -73,7 +73,7 @@ void Lin::linearizationWarning(std::string_view msg) { m->linearization_warnings = true; - qpdf.warn(qpdf_e_linearization, "", 0, std::string(msg)); + warn(qpdf_e_linearization, "", 0, std::string(msg)); } bool @@ -166,19 +166,19 @@ Lin::readLinearizationData() Integer P = P_oh; // first page number QTC::TC("qpdf", "QPDF P absent in lindict", P ? 0 : 1); - qpdf.no_ci_stop_if( + no_ci_stop_if( !(H && O && E && N && T && (P || P_oh.null())), "some keys in linearization dictionary are of the wrong type", "linearization dictionary" // ); - qpdf.no_ci_stop_if( + no_ci_stop_if( !(H_size == 2 || H_size == 4), "H has the wrong number of items", "linearization dictionary" // ); - qpdf.no_ci_stop_if( + no_ci_stop_if( !(H_0 && H_1 && (H_size == 2 || (H_2 && H_3))), "some H items are of the wrong type", "linearization dictionary" // @@ -188,7 +188,7 @@ Lin::readLinearizationData() // Various places in the code use linp.npages, which is initialized from N, to pre-allocate // memory, so make sure it's accurate and bail right now if it's not. - qpdf.no_ci_stop_if( + no_ci_stop_if( N != qpdf.getAllPages().size(), "/N does not match number of pages", "linearization dictionary" // @@ -234,13 +234,12 @@ Lin::readLinearizationData() size_t HSi = HS; if (HSi < 0 || HSi >= h_size) { - throw qpdf.damagedPDF( - "linearization hint table", "/S (shared object) offset is out of bounds"); + throw damagedPDF("linearization hint table", "/S (shared object) offset is out of bounds"); } readHSharedObject(BitStream(h_buf + HSi, h_size - HSi)); if (HO) { - qpdf.no_ci_stop_if( + no_ci_stop_if( HO < 0 || HO >= h_size, "/O (outline) offset is out of bounds", "linearization dictionary" // @@ -257,7 +256,7 @@ Lin::readHintStream(Pipeline& pl, qpdf_offset_t offset, size_t length) ObjCache& oc = m->obj_cache[H]; qpdf_offset_t min_end_offset = oc.end_before_space; qpdf_offset_t max_end_offset = oc.end_after_space; - qpdf.no_ci_stop_if( + no_ci_stop_if( !H.isStream(), "hint table is not a stream", "linearization dictionary" // ); @@ -275,7 +274,7 @@ Lin::readHintStream(Pipeline& pl, qpdf_offset_t offset, size_t length) QTC::TC("qpdf", "QPDF hint table length direct"); } qpdf_offset_t computed_end = offset + toO(length); - qpdf.no_ci_stop_if( + no_ci_stop_if( computed_end < min_end_offset || computed_end > max_end_offset, "hint table length mismatch (expected = " + std::to_string(computed_end) + "; actual = " + std::to_string(min_end_offset) + ".." + std::to_string(max_end_offset) + ")", @@ -464,7 +463,7 @@ Lin::checkLinearizationInternal() // are present. In that case, it would probably agree with pdlin. As of this writing, the test // suite doesn't contain any files with threads. - qpdf.no_ci_stop_if( + no_ci_stop_if( m->part6.empty(), "linearization part 6 unexpectedly empty" // ); qpdf_offset_t min_E = -1; @@ -494,14 +493,14 @@ Lin::checkLinearizationInternal() qpdf_offset_t Lin::maxEnd(ObjUser const& ou) { - qpdf.no_ci_stop_if( + no_ci_stop_if( !m->obj_user_to_objects.contains(ou), "no entry in object user table for requested object user" // ); qpdf_offset_t end = 0; for (auto const& og: m->obj_user_to_objects[ou]) { - qpdf.no_ci_stop_if( + no_ci_stop_if( !m->obj_cache.contains(og), "unknown object referenced in object user table" // ); end = std::max(end, m->obj_cache[og].end_after_space); @@ -517,7 +516,7 @@ Lin::getLinearizationOffset(QPDFObjGen og) if (typ == 1) { return entry.getOffset(); } - qpdf.no_ci_stop_if( + no_ci_stop_if( typ != 2, "getLinearizationOffset called for xref entry not of type 1 or 2" // ); // For compressed objects, return the offset of the object stream that contains them. @@ -551,7 +550,7 @@ Lin::lengthNextN(int first_object, int n) for (int i = 0; i < n; ++i) { QPDFObjGen og(first_object + i, 0); if (m->xref_table.contains(og)) { - qpdf.no_ci_stop_if( + no_ci_stop_if( !m->obj_cache.contains(og), "found unknown object while calculating length for linearization data" // ); @@ -585,7 +584,7 @@ Lin::checkHPageOffset( qpdf_offset_t table_offset = adjusted_offset(m->page_offset_hints.first_page_offset); QPDFObjGen first_page_og(pages.at(0).getObjGen()); if (!m->xref_table.contains(first_page_og)) { - qpdf.stopOnError("supposed first page object is not known"); + stopOnError("supposed first page object is not known"); } qpdf_offset_t offset = getLinearizationOffset(first_page_og); if (table_offset != offset) { @@ -596,7 +595,7 @@ Lin::checkHPageOffset( QPDFObjGen page_og(pages.at(pageno).getObjGen()); int first_object = page_og.getObj(); if (!m->xref_table.contains(page_og)) { - qpdf.stopOnError("unknown object in page offset hint table"); + stopOnError("unknown object in page offset hint table"); } offset = getLinearizationOffset(page_og); @@ -636,7 +635,7 @@ Lin::checkHPageOffset( for (size_t i = 0; i < toS(he.nshared_objects); ++i) { int idx = he.shared_identifiers.at(i); - qpdf.no_ci_stop_if( + no_ci_stop_if( !shared_idx_to_obj.contains(idx), "unable to get object for item in shared objects hint table"); @@ -645,7 +644,7 @@ Lin::checkHPageOffset( for (size_t i = 0; i < toS(ce.nshared_objects); ++i) { int idx = ce.shared_identifiers.at(i); - qpdf.no_ci_stop_if( + no_ci_stop_if( idx >= m->c_shared_object_data.nshared_total, "index out of bounds for shared object hint table" // ); @@ -718,7 +717,7 @@ Lin::checkHSharedObject(std::vector const& pages, std::mapxref_table.contains(og)) { - qpdf.stopOnError("unknown object in shared object hint table"); + stopOnError("unknown object in shared object hint table"); } qpdf_offset_t offset = getLinearizationOffset(og); qpdf_offset_t h_offset = adjusted_offset(so.first_shared_offset); @@ -768,7 +767,7 @@ Lin::checkHOutlines() return; } QPDFObjGen og(outlines.getObjGen()); - qpdf.no_ci_stop_if( + no_ci_stop_if( !m->xref_table.contains(og), "unknown object in outlines hint table" // ); qpdf_offset_t offset = getLinearizationOffset(og); @@ -1128,7 +1127,7 @@ Lin::calculateLinearizationData(T const& object_stream_data) // Part 4: open document objects. We don't care about the order. - qpdf.no_ci_stop_if( + no_ci_stop_if( lc_root.size() != 1, "found other than one root while calculating linearization data" // ); @@ -1142,11 +1141,11 @@ Lin::calculateLinearizationData(T const& object_stream_data) // any option to set this and also disregards /OpenAction. We will do the same. // First, place the actual first page object itself. - qpdf.no_ci_stop_if( + no_ci_stop_if( pages.empty(), "no pages found while calculating linearization data" // ); QPDFObjGen first_page_og(pages.at(0).getObjGen()); - qpdf.no_ci_stop_if( + no_ci_stop_if( !lc_first_page_private.erase(first_page_og), "unable to linearize first page" // ); m->c_linp.first_page_object = pages.at(0).getObjectID(); @@ -1182,7 +1181,7 @@ Lin::calculateLinearizationData(T const& object_stream_data) // Place this page's page object QPDFObjGen page_og(pages.at(i).getObjGen()); - qpdf.no_ci_stop_if( + no_ci_stop_if( !lc_other_page_private.erase(page_og), "unable to linearize page " + std::to_string(i) // ); @@ -1195,7 +1194,7 @@ Lin::calculateLinearizationData(T const& object_stream_data) m->c_page_offset_data.entries.at(i).nobjects = 1; ObjUser ou(ObjUser::ou_page, i); - qpdf.no_ci_stop_if( + no_ci_stop_if( !m->obj_user_to_objects.contains(ou), "found unreferenced page while calculating linearization data" // ); @@ -1231,7 +1230,7 @@ Lin::calculateLinearizationData(T const& object_stream_data) // Place the pages tree. std::set pages_ogs = m->obj_user_to_objects[ObjUser(ObjUser::ou_root_key, "/Pages")]; - qpdf.no_ci_stop_if( + no_ci_stop_if( pages_ogs.empty(), "found empty pages tree while calculating linearization data" // ); for (auto const& og: pages_ogs) { @@ -1288,7 +1287,7 @@ Lin::calculateLinearizationData(T const& object_stream_data) size_t num_placed = m->part4.size() + m->part6.size() + m->part7.size() + m->part8.size() + m->part9.size(); size_t num_wanted = m->object_to_obj_users.size(); - qpdf.no_ci_stop_if( + no_ci_stop_if( // This can happen with damaged files, e.g. if the root is part of the the pages tree. num_placed != num_wanted, "QPDF::calculateLinearizationData: wrong number of objects placed (num_placed = " + @@ -1326,7 +1325,7 @@ Lin::calculateLinearizationData(T const& object_stream_data) shared.emplace_back(obj); } } - qpdf.no_ci_stop_if( + no_ci_stop_if( std::cmp_not_equal( m->c_shared_object_data.nshared_total, m->c_shared_object_data.entries.size()), "shared object hint table has wrong number of entries" // @@ -1337,7 +1336,7 @@ Lin::calculateLinearizationData(T const& object_stream_data) for (size_t i = 1; i < npages; ++i) { CHPageOffsetEntry& pe = m->c_page_offset_data.entries.at(i); ObjUser ou(ObjUser::ou_page, i); - qpdf.no_ci_stop_if( + no_ci_stop_if( !m->obj_user_to_objects.contains(ou), "found unreferenced page while calculating linearization data" // ); @@ -1420,12 +1419,12 @@ Lin::outputLengthNextN( int first = obj[in_object].renumber; int last = first + n; - qpdf.no_ci_stop_if( + no_ci_stop_if( first <= 0, "found object that is not renumbered while writing linearization data"); qpdf_offset_t length = 0; for (int i = first; i < last; ++i) { auto l = new_obj[i].length; - qpdf.no_ci_stop_if( + no_ci_stop_if( l == 0, "found item with unknown length while writing linearization data" // ); length += l; @@ -1502,7 +1501,7 @@ Lin::calculateHPageOffset(QPDFWriter::NewObjTable const& new_obj, QPDFWriter::Ob for (auto& phe_i: phe) { // Adjust delta entries if (phe_i.delta_nobjects < min_nobjects || phe_i.delta_page_length < min_length) { - qpdf.stopOnError( + stopOnError( "found too small delta nobjects or delta page length while writing " "linearization data"); } @@ -1537,7 +1536,7 @@ Lin::calculateHSharedObject(QPDFWriter::NewObjTable const& new_obj, QPDFWriter:: soe.emplace_back(); soe.at(i).delta_group_length = length; } - qpdf.no_ci_stop_if( + no_ci_stop_if( soe.size() != toS(cso.nshared_total), "soe has wrong size after initialization" // ); @@ -1553,7 +1552,7 @@ Lin::calculateHSharedObject(QPDFWriter::NewObjTable const& new_obj, QPDFWriter:: for (size_t i = 0; i < toS(cso.nshared_total); ++i) { // Adjust deltas - qpdf.no_ci_stop_if( + no_ci_stop_if( soe.at(i).delta_group_length < min_length, "found too small group length while writing linearization data" // ); @@ -1687,7 +1686,7 @@ Lin::writeHSharedObject(BitWriter& w) for (size_t i = 0; i < toS(nitems); ++i) { // If signature were present, we'd have to write a 128-bit hash. if (entries.at(i).signature_present != 0) { - qpdf.stopOnError("found unexpected signature present while writing linearization data"); + stopOnError("found unexpected signature present while writing linearization data"); } } write_vector_int(w, nitems, entries, t.nbits_nobjects, &HSharedObjectEntry::nobjects_minus_one); diff --git a/libqpdf/QPDF_objects.cc b/libqpdf/QPDF_objects.cc index 8d399f3..0ff6bfc 100644 --- a/libqpdf/QPDF_objects.cc +++ b/libqpdf/QPDF_objects.cc @@ -123,7 +123,7 @@ Objects::parse(char const* password) // Find the header anywhere in the first 1024 bytes of the file. PatternFinder hf(qpdf, &QPDF::findHeader); if (!m->file->findFirst("%PDF-", 0, 1024, hf)) { - qpdf.warn(qpdf.damagedPDF("", -1, "can't find PDF header")); + warn(damagedPDF("", -1, "can't find PDF header")); // QPDFWriter writes files that usually require at least version 1.2 for /FlateDecode m->pdf_version = "1.2"; } @@ -147,14 +147,14 @@ Objects::parse(char const* password) try { if (xref_offset == 0) { - throw qpdf.damagedPDF("", -1, "can't find startxref"); + throw damagedPDF("", -1, "can't find startxref"); } try { read_xref(xref_offset); } catch (QPDFExc&) { throw; } catch (std::exception& e) { - throw qpdf.damagedPDF("", -1, std::string("error reading xref: ") + e.what()); + throw damagedPDF("", -1, std::string("error reading xref: ") + e.what()); } } catch (QPDFExc& e) { if (m->attempt_recovery) { @@ -168,7 +168,7 @@ Objects::parse(char const* password) m->parsed = true; if (!m->xref_table.empty() && !qpdf.getRoot().getKey("/Pages").isDictionary()) { // QPDFs created from JSON have an empty xref table and no root object yet. - throw qpdf.damagedPDF("", -1, "unable to find page tree"); + throw damagedPDF("", -1, "unable to find page tree"); } } @@ -208,8 +208,7 @@ Objects::reconstruct_xref(QPDFExc& e, bool found_startxref) const auto max_warnings = m->warnings.size() + 1000U; auto check_warnings = [this, max_warnings]() { if (m->warnings.size() > max_warnings) { - throw qpdf.damagedPDF( - "", -1, "too many errors while reconstructing cross-reference table"); + throw damagedPDF("", -1, "too many errors while reconstructing cross-reference table"); } }; @@ -217,9 +216,9 @@ Objects::reconstruct_xref(QPDFExc& e, bool found_startxref) // We may find more objects, which may contain dangling references. m->fixed_dangling_refs = false; - qpdf.warn(qpdf.damagedPDF("", -1, "file is damaged")); - qpdf.warn(e); - qpdf.warn(qpdf.damagedPDF("", -1, "Attempting to reconstruct cross-reference table")); + warn(damagedPDF("", -1, "file is damaged")); + warn(e); + warn(damagedPDF("", -1, "Attempting to reconstruct cross-reference table")); // Delete all references to type 1 (uncompressed) objects std::vector to_delete; @@ -253,7 +252,7 @@ Objects::reconstruct_xref(QPDFExc& e, bool found_startxref) if (obj <= m->xref_table_max_id) { found_objects.emplace_back(obj, gen, token_start); } else { - qpdf.warn(qpdf.damagedPDF( + warn(damagedPDF( "", -1, "ignoring object with impossibly large id " + std::to_string(obj))); } } @@ -278,7 +277,7 @@ Objects::reconstruct_xref(QPDFExc& e, bool found_startxref) if (qpdf.getRoot().getKey("/Pages").isDictionary()) { QTC::TC("qpdf", "QPDF startxref more than 1024 before end"); - qpdf.warn(qpdf.damagedPDF( + warn(damagedPDF( "", -1, "startxref was more than 1024 bytes before end of file")); qpdf.initializeEncryption(); m->parsed = true; @@ -313,7 +312,7 @@ Objects::reconstruct_xref(QPDFExc& e, bool found_startxref) m->trailer = t; break; } - qpdf.warn(qpdf.damagedPDF("trailer", *it, "recovered trailer has no /Root entry")); + warn(damagedPDF("trailer", *it, "recovered trailer has no /Root entry")); } check_warnings(); } @@ -347,7 +346,7 @@ Objects::reconstruct_xref(QPDFExc& e, bool found_startxref) try { read_xref(max_offset, true); } catch (std::exception&) { - qpdf.warn(qpdf.damagedPDF( + warn(damagedPDF( "", -1, "error decoding candidate xref stream while recovering damaged file")); } QTC::TC("qpdf", "QPDF recover xref stream"); @@ -368,7 +367,7 @@ Objects::reconstruct_xref(QPDFExc& e, bool found_startxref) } if (root) { if (!m->trailer) { - qpdf.warn(qpdf.damagedPDF( + warn(damagedPDF( "", -1, "unable to find trailer dictionary while recovering damaged file")); m->trailer = QPDFObjectHandle::newDictionary(); } @@ -381,13 +380,12 @@ Objects::reconstruct_xref(QPDFExc& e, bool found_startxref) // could try to get the trailer from there. This may make it possible to recover files with // bad startxref pointers even when they have object streams. - throw qpdf.damagedPDF( - "", -1, "unable to find trailer dictionary while recovering damaged file"); + throw damagedPDF("", -1, "unable to find trailer dictionary while recovering damaged file"); } if (m->xref_table.empty()) { // We cannot check for an empty xref table in parse because empty tables are valid when // creating QPDF objects from JSON. - throw qpdf.damagedPDF("", -1, "unable to find objects while recovering damaged file"); + throw damagedPDF("", -1, "unable to find objects while recovering damaged file"); } check_warnings(); if (!m->parsed) { @@ -396,7 +394,7 @@ Objects::reconstruct_xref(QPDFExc& e, bool found_startxref) check_warnings(); if (m->all_pages.empty()) { m->parsed = false; - throw qpdf.damagedPDF("", -1, "unable to find any pages while recovering damaged file"); + throw damagedPDF("", -1, "unable to find any pages while recovering damaged file"); } } @@ -443,7 +441,7 @@ Objects::read_xref(qpdf_offset_t xref_offset, bool in_stream_recovery) // where it is terminated by arbitrary whitespace. if ((strncmp(buf, "xref", 4) == 0) && util::is_space(buf[4])) { if (skipped_space) { - qpdf.warn(qpdf.damagedPDF("", -1, "extraneous whitespace seen before xref")); + warn(damagedPDF("", -1, "extraneous whitespace seen before xref")); } QTC::TC( "qpdf", @@ -462,12 +460,12 @@ Objects::read_xref(qpdf_offset_t xref_offset, bool in_stream_recovery) xref_offset = read_xrefStream(xref_offset, in_stream_recovery); } if (visited.contains(xref_offset)) { - throw qpdf.damagedPDF("", -1, "loop detected following xref tables"); + throw damagedPDF("", -1, "loop detected following xref tables"); } } if (!m->trailer) { - throw qpdf.damagedPDF("", -1, "unable to find trailer while reading xref"); + throw damagedPDF("", -1, "unable to find trailer while reading xref"); } int size = m->trailer.getKey("/Size").getIntValueAsInt(); int max_obj = 0; @@ -478,7 +476,7 @@ Objects::read_xref(qpdf_offset_t xref_offset, bool in_stream_recovery) max_obj = std::max(max_obj, *(m->deleted_objects.rbegin())); } if ((size < 1) || (size - 1 != max_obj)) { - qpdf.warn(qpdf.damagedPDF( + warn(damagedPDF( "", -1, ("reported number of objects (" + std::to_string(size) + @@ -615,7 +613,7 @@ Objects::read_bad_xrefEntry(qpdf_offset_t& f1, int& f2, char& type) } if (invalid) { - qpdf.warn(qpdf.damagedPDF("xref table", "accepting invalid xref table entry")); + warn(damagedPDF("xref table", "accepting invalid xref table entry")); } f1 = QUtil::string_to_ll(f1_str.c_str()); @@ -692,7 +690,7 @@ Objects::read_xrefTable(qpdf_offset_t xref_offset) int num = 0; int bytes = 0; if (!parse_xrefFirst(line, obj, num, bytes)) { - throw qpdf.damagedPDF("xref table", "xref syntax invalid"); + throw damagedPDF("xref table", "xref syntax invalid"); } m->file->seek(m->file->getLastOffset() + bytes, SEEK_SET); for (qpdf_offset_t i = obj; i - num < obj; ++i) { @@ -705,7 +703,7 @@ Objects::read_xrefTable(qpdf_offset_t xref_offset) int f2 = 0; char type = '\0'; if (!read_xrefEntry(f1, f2, type)) { - throw qpdf.damagedPDF( + throw damagedPDF( "xref table", "invalid xref entry (obj=" + std::to_string(i) + ")"); } if (type == 'f') { @@ -725,17 +723,17 @@ Objects::read_xrefTable(qpdf_offset_t xref_offset) // Set offset to previous xref table if any QPDFObjectHandle cur_trailer = m->objects.readTrailer(); if (!cur_trailer.isDictionary()) { - throw qpdf.damagedPDF("", "expected trailer dictionary"); + throw damagedPDF("", "expected trailer dictionary"); } if (!m->trailer) { setTrailer(cur_trailer); if (!m->trailer.hasKey("/Size")) { - throw qpdf.damagedPDF("trailer", "trailer dictionary lacks /Size key"); + throw damagedPDF("trailer", "trailer dictionary lacks /Size key"); } if (!m->trailer.getKey("/Size").isInteger()) { - throw qpdf.damagedPDF("trailer", "/Size key in trailer dictionary is not an integer"); + throw damagedPDF("trailer", "/Size key in trailer dictionary is not an integer"); } } @@ -748,14 +746,14 @@ Objects::read_xrefTable(qpdf_offset_t xref_offset) // /Prev key instead of the xref stream's. (void)read_xrefStream(cur_trailer.getKey("/XRefStm").getIntValue()); } else { - throw qpdf.damagedPDF("xref stream", xref_offset, "invalid /XRefStm"); + throw damagedPDF("xref stream", xref_offset, "invalid /XRefStm"); } } } if (cur_trailer.hasKey("/Prev")) { if (!cur_trailer.getKey("/Prev").isInteger()) { - throw qpdf.damagedPDF("trailer", "/Prev key in trailer dictionary is not an integer"); + throw damagedPDF("trailer", "/Prev key in trailer dictionary is not an integer"); } return cur_trailer.getKey("/Prev").getIntValue(); } @@ -781,7 +779,7 @@ Objects::read_xrefStream(qpdf_offset_t xref_offset, bool in_stream_recovery) } } - throw qpdf.damagedPDF("", xref_offset, "xref not found"); + throw damagedPDF("", xref_offset, "xref not found"); return 0; // unreachable } @@ -912,7 +910,7 @@ Objects::processXRefStream( qpdf_offset_t xref_offset, QPDFObjectHandle& xref_obj, bool in_stream_recovery) { auto damaged = [this, xref_offset](std::string_view msg) -> QPDFExc { - return qpdf.damagedPDF("xref stream", xref_offset, msg.data()); + return damagedPDF("xref stream", xref_offset, msg.data()); }; auto dict = xref_obj.getDict(); @@ -932,7 +930,7 @@ Objects::processXRefStream( if (expected_size > actual_size) { throw x; } else { - qpdf.warn(x); + warn(x); } } @@ -992,7 +990,7 @@ Objects::processXRefStream( if (dict.hasKey("/Prev")) { if (!dict.getKey("/Prev").isInteger()) { - throw qpdf.damagedPDF( + throw damagedPDF( "xref stream", "/Prev key in xref stream dictionary is not an integer"); } return dict.getKey("/Prev").getIntValue(); @@ -1030,13 +1028,13 @@ Objects::insertXrefEntry(int obj, int f0, qpdf_offset_t f1, int f2) if (f0 == 2) { if (f1 == obj) { - qpdf.warn(qpdf.damagedPDF( - "xref stream", "self-referential object stream " + std::to_string(obj))); + warn( + damagedPDF("xref stream", "self-referential object stream " + std::to_string(obj))); return; } if (f1 > m->xref_table_max_id) { // ignore impossibly large object stream ids - qpdf.warn(qpdf.damagedPDF( + warn(damagedPDF( "xref stream", "object stream id " + std::to_string(f1) + " for object " + std::to_string(obj) + " is impossibly large")); @@ -1061,8 +1059,7 @@ Objects::insertXrefEntry(int obj, int f0, qpdf_offset_t f1, int f2) break; default: - throw qpdf.damagedPDF( - "xref stream", "unknown xref stream entry type " + std::to_string(f0)); + throw damagedPDF("xref stream", "unknown xref stream entry type " + std::to_string(f0)); break; } } @@ -1182,9 +1179,9 @@ Objects::readTrailer() if (empty) { // Nothing in the PDF spec appears to allow empty objects, but they have been encountered in // actual PDF files and Adobe Reader appears to ignore them. - qpdf.warn(qpdf.damagedPDF("trailer", "empty object treated as null")); + warn(damagedPDF("trailer", "empty object treated as null")); } else if (object.isDictionary() && m->objects.readToken(*m->file).isWord("stream")) { - qpdf.warn(qpdf.damagedPDF("trailer", m->file->tell(), "stream keyword found in trailer")); + warn(damagedPDF("trailer", m->file->tell(), "stream keyword found in trailer")); } // Override last_offset so that it points to the beginning of the object we just read m->file->setLastOffset(offset); @@ -1210,8 +1207,7 @@ Objects::readObject(std::string const& description, QPDFObjGen og) if (empty) { // Nothing in the PDF spec appears to allow empty objects, but they have been encountered in // actual PDF files and Adobe Reader appears to ignore them. - qpdf.warn( - qpdf.damagedPDF(*m->file, m->file->getLastOffset(), "empty object treated as null")); + warn(damagedPDF(*m->file, m->file->getLastOffset(), "empty object treated as null")); return object; } auto token = readToken(*m->file); @@ -1220,7 +1216,7 @@ Objects::readObject(std::string const& description, QPDFObjGen og) token = readToken(*m->file); } if (!token.isWord("endobj")) { - qpdf.warn(qpdf.damagedPDF("expected endobj")); + warn(damagedPDF("expected endobj")); } return object; } @@ -1241,9 +1237,9 @@ Objects::readStream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offse if (!length_obj.isInteger()) { if (length_obj.null()) { - throw qpdf.damagedPDF(offset, "stream dictionary lacks /Length key"); + throw damagedPDF(offset, "stream dictionary lacks /Length key"); } - throw qpdf.damagedPDF(offset, "/Length key in stream dictionary is not an integer"); + throw damagedPDF(offset, "/Length key in stream dictionary is not an integer"); } length = toS(length_obj.getUIntValue()); @@ -1251,11 +1247,11 @@ Objects::readStream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offse m->file->seek(stream_offset, SEEK_SET); m->file->seek(toO(length), SEEK_CUR); if (!readToken(*m->file).isWord("endstream")) { - throw qpdf.damagedPDF("expected endstream"); + throw damagedPDF("expected endstream"); } } catch (QPDFExc& e) { if (m->attempt_recovery) { - qpdf.warn(e); + warn(e); length = recoverStreamLength(m->file, og, stream_offset); } else { throw; @@ -1295,7 +1291,7 @@ Objects::validateStreamLineEnd(QPDFObjectHandle& object, QPDFObjGen og, qpdf_off // Treat the \r by itself as the whitespace after endstream and start reading // stream data in spite of not having seen a newline. m->file->unreadCh(ch); - qpdf.warn(qpdf.damagedPDF( + warn(damagedPDF( m->file->tell(), "stream keyword followed by carriage return only")); } } @@ -1303,12 +1299,11 @@ Objects::validateStreamLineEnd(QPDFObjectHandle& object, QPDFObjGen og, qpdf_off } if (!util::is_space(ch)) { m->file->unreadCh(ch); - qpdf.warn(qpdf.damagedPDF( + warn(damagedPDF( m->file->tell(), "stream keyword not followed by proper line terminator")); return; } - qpdf.warn( - qpdf.damagedPDF(m->file->tell(), "stream keyword followed by extraneous whitespace")); + warn(damagedPDF(m->file->tell(), "stream keyword followed by extraneous whitespace")); } } @@ -1319,7 +1314,7 @@ Objects::readObjectInStream(is::OffsetBuffer& input, int stream_id, int obj_id) if (empty) { // Nothing in the PDF spec appears to allow empty objects, but they have been encountered in // actual PDF files and Adobe Reader appears to ignore them. - qpdf.warn(QPDFExc( + warn(QPDFExc( qpdf_e_damaged_pdf, m->file->getName() + " object stream " + std::to_string(stream_id), +"object " + std::to_string(obj_id) + " 0, offset " + @@ -1347,7 +1342,7 @@ Objects::recoverStreamLength( std::shared_ptr input, QPDFObjGen og, qpdf_offset_t stream_offset) { // Try to reconstruct stream length by looking for endstream or endobj - qpdf.warn(qpdf.damagedPDF(*input, stream_offset, "attempting to recover stream length")); + warn(damagedPDF(*input, stream_offset, "attempting to recover stream length")); PatternFinder ef(qpdf, &QPDF::findEndstream); size_t length = 0; @@ -1386,10 +1381,10 @@ Objects::recoverStreamLength( } if (length == 0) { - qpdf.warn(qpdf.damagedPDF( + warn(damagedPDF( *input, stream_offset, "unable to recover stream data; treating stream as empty")); } else { - qpdf.warn(qpdf.damagedPDF( + warn(damagedPDF( *input, stream_offset, "recovered stream length: " + std::to_string(length))); } @@ -1409,24 +1404,24 @@ Objects::read_object_start(qpdf_offset_t offset) QPDFTokenizer::Token tobjid = readToken(*m->file); bool objidok = tobjid.isInteger(); if (!objidok) { - throw qpdf.damagedPDF(offset, "expected n n obj"); + throw damagedPDF(offset, "expected n n obj"); } QPDFTokenizer::Token tgen = readToken(*m->file); bool genok = tgen.isInteger(); if (!genok) { - throw qpdf.damagedPDF(offset, "expected n n obj"); + throw damagedPDF(offset, "expected n n obj"); } QPDFTokenizer::Token tobj = readToken(*m->file); bool objok = tobj.isWord("obj"); if (!objok) { - throw qpdf.damagedPDF(offset, "expected n n obj"); + throw damagedPDF(offset, "expected n n obj"); } int objid = QUtil::string_to_int(tobjid.getValue().c_str()); int generation = QUtil::string_to_int(tgen.getValue().c_str()); if (objid == 0) { - throw qpdf.damagedPDF(offset, "object with ID 0"); + throw damagedPDF(offset, "object with ID 0"); } return {objid, generation}; } @@ -1447,20 +1442,20 @@ Objects::readObjectAtOffset( // "0000000000 00000 n", which is not correct, but it won't hurt anything for us to ignore // these. if (offset == 0) { - qpdf.warn(qpdf.damagedPDF(-1, "object has offset 0")); + warn(damagedPDF(-1, "object has offset 0")); return; } try { og = read_object_start(offset); if (exp_og != og) { - QPDFExc e = qpdf.damagedPDF(offset, "expected " + exp_og.unparse(' ') + " obj"); + QPDFExc e = damagedPDF(offset, "expected " + exp_og.unparse(' ') + " obj"); if (try_recovery) { // Will be retried below throw e; } else { // We can try reading the object anyway even if the ID doesn't match. - qpdf.warn(e); + warn(e); } } } catch (QPDFExc& e) { @@ -1474,7 +1469,7 @@ Objects::readObjectAtOffset( readObjectAtOffset(false, new_offset, description, exp_og); return; } - qpdf.warn(qpdf.damagedPDF( + warn(damagedPDF( "", -1, ("object " + exp_og.unparse(' ') + @@ -1493,7 +1488,7 @@ Objects::readObjectAtOffset( while (true) { char ch; if (!m->file->read(&ch, 1)) { - throw qpdf.damagedPDF(m->file->tell(), "EOF after endobj"); + throw damagedPDF(m->file->tell(), "EOF after endobj"); } if (!isspace(static_cast(ch))) { m->file->seek(-1, SEEK_CUR); @@ -1552,7 +1547,7 @@ Objects::readObjectAtOffset( while (true) { char ch; if (!m->file->read(&ch, 1)) { - throw qpdf.damagedPDF(m->file->tell(), "EOF after endobj"); + throw damagedPDF(m->file->tell(), "EOF after endobj"); } if (!isspace(static_cast(ch))) { m->file->seek(-1, SEEK_CUR); @@ -1574,7 +1569,7 @@ Objects::resolve(QPDFObjGen og) if (m->resolving.contains(og)) { // This can happen if an object references itself directly or indirectly in some key that // has to be resolved during object parsing, such as stream length. - qpdf.warn(qpdf.damagedPDF("", "loop detected resolving object " + og.unparse(' '))); + warn(damagedPDF("", "loop detected resolving object " + og.unparse(' '))); updateCache(og, QPDFObject::create(), -1, -1); return m->obj_cache[og].object; } @@ -1594,13 +1589,13 @@ Objects::resolve(QPDFObjGen og) break; default: - throw qpdf.damagedPDF( + throw damagedPDF( "", -1, ("object " + og.unparse('/') + " has unexpected xref entry type")); } } catch (QPDFExc& e) { - qpdf.warn(e); + warn(e); } catch (std::exception& e) { - qpdf.warn(qpdf.damagedPDF( + warn(damagedPDF( "", -1, ("object " + og.unparse('/') + ": error reading object: " + e.what()))); } } @@ -1636,7 +1631,7 @@ Objects::resolveObjectsInStream(int obj_stream_number) // Force resolution of object stream Stream obj_stream = qpdf.getObject(obj_stream_number, 0); if (!obj_stream) { - throw qpdf.damagedPDF( + throw damagedPDF( "object " + std::to_string(obj_stream_number) + " 0", "supposed object stream " + std::to_string(obj_stream_number) + " is not a stream"); } @@ -1649,7 +1644,7 @@ Objects::resolveObjectsInStream(int obj_stream_number) QPDFObjectHandle dict = obj_stream.getDict(); if (!dict.isDictionaryOfType("/ObjStm")) { - qpdf.warn(qpdf.damagedPDF( + warn(damagedPDF( "object " + std::to_string(obj_stream_number) + " 0", "supposed object stream " + std::to_string(obj_stream_number) + " has wrong type")); } @@ -1657,7 +1652,7 @@ Objects::resolveObjectsInStream(int obj_stream_number) unsigned int n{0}; int first{0}; if (!(dict.getKey("/N").getValueAsUInt(n) && dict.getKey("/First").getValueAsInt(first))) { - throw qpdf.damagedPDF( + throw damagedPDF( "object " + std::to_string(obj_stream_number) + " 0", "object stream " + std::to_string(obj_stream_number) + " has incorrect keys"); } @@ -1674,7 +1669,7 @@ Objects::resolveObjectsInStream(int obj_stream_number) auto b_start = stream_data.data(); if (first >= end_offset) { - throw qpdf.damagedPDF( + throw damagedPDF( "object " + std::to_string(obj_stream_number) + " 0", "object stream " + std::to_string(obj_stream_number) + " has invalid /First entry"); } @@ -1694,17 +1689,17 @@ Objects::resolveObjectsInStream(int obj_stream_number) long long offset = QUtil::string_to_int(toffset.getValue().c_str()); if (num == obj_stream_number) { - qpdf.warn(damaged(num, id_offset, "object stream claims to contain itself")); + warn(damaged(num, id_offset, "object stream claims to contain itself")); continue; } if (num < 1) { - qpdf.warn(damaged(num, id_offset, "object id is invalid"s)); + warn(damaged(num, id_offset, "object id is invalid"s)); continue; } if (offset <= last_offset) { - qpdf.warn(damaged( + warn(damaged( num, input.getLastOffset(), "offset " + std::to_string(offset) + @@ -1718,7 +1713,7 @@ Objects::resolveObjectsInStream(int obj_stream_number) } if (first + offset >= end_offset) { - qpdf.warn(damaged( + warn(damaged( num, input.getLastOffset(), "offset " + std::to_string(offset) + " is too large")); continue; } @@ -1934,7 +1929,7 @@ Objects::tableSize() // Temporary fix. Long-term solution is // - QPDFObjGen to enforce objgens are valid and sensible // - xref table and obj cache to protect against insertion of impossibly large obj ids - qpdf.stopOnError("Impossibly large object id encountered."); + stopOnError("Impossibly large object id encountered."); } if (max_obj < 1.1 * std::max(toI(m->obj_cache.size()), max_xref)) { return toS(++max_obj); diff --git a/libqpdf/QPDF_optimization.cc b/libqpdf/QPDF_optimization.cc index 9cfb605..4cd246f 100644 --- a/libqpdf/QPDF_optimization.cc +++ b/libqpdf/QPDF_optimization.cc @@ -218,7 +218,7 @@ Pages ::pushInheritedAttributesToPageInternal( // Warn when flattening, but not if the key is at the top level (i.e. "/Parent" not // set), as we don't change these; but flattening removes intermediate /Pages nodes. if (warn_skipped_keys && cur_pages.hasKey("/Parent")) { - qpdf.warn( + warn( qpdf_e_pages, "Pages object: object " + cur_pages.id_gen().unparse(' '), 0, diff --git a/libqpdf/qpdf/QPDF_private.hh b/libqpdf/qpdf/QPDF_private.hh index 35699da..6c0ec2b 100644 --- a/libqpdf/qpdf/QPDF_private.hh +++ b/libqpdf/qpdf/QPDF_private.hh @@ -322,6 +322,36 @@ class QPDF::Doc { } + void stopOnError(std::string const& message); + void warn(QPDFExc const& e); + void warn( + qpdf_error_code_e error_code, + std::string const& object, + qpdf_offset_t offset, + std::string const& message); + + static QPDFExc damagedPDF( + InputSource& input, + std::string const& object, + qpdf_offset_t offset, + std::string const& message); + QPDFExc + damagedPDF(InputSource& input, qpdf_offset_t offset, std::string const& message) const; + QPDFExc damagedPDF( + std::string const& object, qpdf_offset_t offset, std::string const& message) const; + QPDFExc damagedPDF(std::string const& object, std::string const& message) const; + QPDFExc damagedPDF(qpdf_offset_t offset, std::string const& message) const; + QPDFExc damagedPDF(std::string const& message) const; + + void + no_ci_stop_if( + bool condition, std::string const& message, std::string const& context = {}) const + { + if (condition) { + throw damagedPDF(context, message); + } + } + protected: QPDF& qpdf; QPDF::Members* m; @@ -989,13 +1019,4 @@ inline QPDF::Doc::Objects::Foreign::Copier::Copier(QPDF& qpdf) : { } -// Throw a generic exception for unusual error conditions that do not be covered during CI testing. -inline void -QPDF::no_ci_stop_if(bool condition, std::string const& message, std::string const& context) -{ - if (condition) { - throw damagedPDF(context, message); - } -} - #endif // QPDF_PRIVATE_HH -- libgit2 0.21.4