diff --git a/ChangeLog b/ChangeLog index 299b6272..341ae8e4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2021-12-10 Jay Berkenbilt + + * C API: Overhaul how errors are handle the C API's object handle + interfaces. Clarify documentation regarding object accessors and + how type errors and warnings are handled. Many cases that used to + crash code that used the C API can now be trapped and will be + written stderr if not trapped. The new method + qpdf_register_oh_error_handler can be used to specifically handle + errors that occur when accessing object handles. See qpdf-c.h for + details. + + * C API: Add qpdf_oh_new_uninitialized to explicitly create + uninitialized object handles. + + * Add new error code qpdf_e_object that is used for exceptions + (including warnings) that are caused by using QPDFObjectHandle + methods on object handles of the wrong type. + 2021-12-02 Jay Berkenbilt * C API: Add qpdf_oh_is_initialized. diff --git a/include/qpdf/qpdf-c.h b/include/qpdf/qpdf-c.h index 294166ac..90219123 100644 --- a/include/qpdf/qpdf-c.h +++ b/include/qpdf/qpdf-c.h @@ -62,22 +62,27 @@ * string was just returned. * * Many functions defined here merely set parameters and therefore - * never return error conditions. Functions that may cause PDF - * files to be read or written may return error conditions. Such - * functions return an error code. If there were no errors or - * warnings, they return QPDF_SUCCESS. If there were warnings, - * the return value has the QPDF_WARNINGS bit set. If there - * errors, the QPDF_ERRORS bit is set. In other words, if there - * are both warnings and errors, then the return status will be - * QPDF_WARNINGS | QPDF_ERRORS. You may also call the + * never return error conditions. Functions that access or return + * qpdf_oh object handles may generate warnings but have no way to + * return errors, but the errors may be checked afterwards or + * handled using a registered handler. This is discussed in more + * detail in the section on object handling. Functions that may + * cause PDF files to be read or written may return error + * conditions. Such functions return an error code. If there were + * no errors or warnings, they return QPDF_SUCCESS. If there were + * warnings, the return value has the QPDF_WARNINGS bit set. If + * there were errors, the QPDF_ERRORS bit is set. In other words, + * if there are both warnings and errors, then the return status + * will be QPDF_WARNINGS | QPDF_ERRORS. You may also call the * qpdf_more_warnings and qpdf_more_errors functions to test - * whether there are unseen warning or error conditions. By + * whether there are unseen warning or error conditions. By * default, warnings are written to stderr when detected, but this - * behavior can be suppressed. In all cases, errors and warnings + * behavior can be suppressed. In all cases, errors and warnings * may be retrieved by calling qpdf_next_warning and - * qpdf_next_error. All exceptions thrown by the C++ interface - * are caught and converted into error messages by the C - * interface. + * qpdf_get_error. All exceptions thrown by the C++ interface are + * caught and converted into error messages by the C interface. + * Any exceptions to this are qpdf bugs and should be reported at + * https://github.com/qpdf/qpdf/issues/new. * * Most functions defined here have obvious counterparts that are * methods to either QPDF or QPDFWriter. Please see comments in @@ -550,13 +555,51 @@ extern "C" { * handle, the object is safely part of the dictionary or array. * Similarly, any other object handle refering to the object remains * valid. Explicitly releasing an object handle is essentially the - * same as letting a QPDFObjectHandle go out of scope in the C++ API. + * same as letting a QPDFObjectHandle go out of scope in the C++ + * API. + * + * Important note about error handling: + * + * While many of the functions that operate on the QPDF object + * return error codes, the qpdf_oh functions return values such as + * object handles or data. They have no way to return error codes. + * If they generate warnings, the warnings are handled using the + * error/warning handling functions described above. If the + * underlying C++ call throws an exception, the error handler + * registered with qpdf_register_oh_error_handler() will be + * called. If no handler is registered, the exception is written + * to STDERR. In either case, a sensible fallback value is + * returned (0 for numbers, QPDF_FALSE for booleans, "" for + * strings, or a null object). It is sensible for a C program to + * use setjmp and longjmp with this error handler since the C++ + * code has raised an exception, but you can also just set a flag + * and check it after each call. + * + * All conditions under which exceptions would be thrown by object + * accessors are caused by programmer error or major problems such + * as running out of memory or not being able to read the input + * file. If they are ever caused by invalid data in the PDF file, + * it is a bug in qpdf, which should be reported at + * https://github.com/qpdf/qpdf/issues/new. */ /* For examples of using this API, see examples/pdf-c-objects.c */ typedef unsigned int qpdf_oh; + /* If an exception is thrown by the C++ code when any of the + * qpdf_oh functions are called, the registered handle_error + * function will be called. The value passed to data will be + * passed along to the error handler function. If any errors occur + * and no error handler is accessed, a single warning will be + * issued, and the error will be written to stderr. + */ + QPDF_DLL + void qpdf_register_oh_error_handler( + qpdf_data qpdf, + void (*handle_error)(qpdf_data qpdf, qpdf_error error, void* data), + void* data); + /* Releasing objects -- see comments above. These functions have no * equivalent in the C++ API. */ @@ -659,7 +702,7 @@ extern "C" { /* The memory returned by qpdf_oh_dict_next_key is owned by * qpdf_data. It is good until the next call to * qpdf_oh_dict_next_key with the same qpdf_data object. Calling - * the method again, even with a different dict, invalidates + * the function again, even with a different dict, invalidates * previous return values. */ QPDF_DLL @@ -676,6 +719,8 @@ extern "C" { QPDF_BOOL qpdf_oh_is_or_has_name( qpdf_data data, qpdf_oh oh, char const* key); + QPDF_DLL + qpdf_oh qpdf_oh_new_uninitialized(qpdf_data qpdf); QPDF_DLL qpdf_oh qpdf_oh_new_null(qpdf_data data); QPDF_DLL diff --git a/libqpdf/qpdf-c.cc b/libqpdf/qpdf-c.cc index 063adaf2..b7e67e85 100644 --- a/libqpdf/qpdf-c.cc +++ b/libqpdf/qpdf-c.cc @@ -41,6 +41,9 @@ struct _qpdf_data PointerHolder output_buffer; // QPDFObjectHandle support + void (*oh_error_handler)(qpdf_data, qpdf_error, void*); + void* oh_error_handler_data; + bool default_oh_error_handler_called; std::map> oh_cache; qpdf_oh next_oh; std::set cur_iter_dict_keys; @@ -48,8 +51,32 @@ struct _qpdf_data std::string cur_dict_key; }; +static void default_oh_error_handler(qpdf_data qpdf, qpdf_error e, void* data) +{ + bool* called = reinterpret_cast(data); + if (called != nullptr) + { + QTC::TC("qpdf", "qpdf-c warn about oh error", *called ? 0 : 1); + if (! *called) + { + qpdf->warnings.push_back( + QPDFExc( + qpdf_e_internal, + qpdf->qpdf->getFilename(), + "", 0, + "C API object handle accessor errors occurred," + " and the application did not define an error handler")); + *called = true; + } + } + std::cerr << e->exc->what() << std::endl; +} + _qpdf_data::_qpdf_data() : write_memory(false), + oh_error_handler(default_oh_error_handler), + oh_error_handler_data(&this->default_oh_error_handler_called), + default_oh_error_handler_called(false), next_oh(0) { } @@ -170,6 +197,13 @@ void qpdf_cleanup(qpdf_data* qpdf) { QTC::TC("qpdf", "qpdf-c called qpdf_cleanup"); qpdf_oh_release_all(*qpdf); + if ((*qpdf)->error.getPointer()) + { + QTC::TC("qpdf", "qpdf-c cleanup warned about unhandled error"); + std::cerr << "WARNING: application did not handle error: " + << (*qpdf)->error->what() << std::endl; + + } delete *qpdf; *qpdf = 0; } @@ -841,6 +875,38 @@ QPDF_ERROR_CODE qpdf_write(qpdf_data qpdf) return status; } +void qpdf_register_oh_error_handler( + qpdf_data qpdf, + void (*handle_error)(qpdf_data qpdf, qpdf_error error, void* data), + void* data) +{ + QTC::TC("qpdf", "qpdf-c registered oh error handler"); + qpdf->oh_error_handler = handle_error; + qpdf->oh_error_handler_data = data; +} + +template +static RET trap_oh_errors( + qpdf_data qpdf, + std::function fallback, + std::function fn) +{ + // Note: fallback is a function so we don't have to evaluate it + // unless needed. This is important because sometimes the fallback + // creates an object. + RET ret; + QPDF_ERROR_CODE status = trap_errors(qpdf, [&ret, &fn] (qpdf_data q) { + ret = fn(q); + }); + if (status & QPDF_ERRORS) + { + (*qpdf->oh_error_handler)( + qpdf, qpdf_get_error(qpdf), qpdf->oh_error_handler_data); + return fallback(); + } + return ret; +} + static qpdf_oh new_object(qpdf_data qpdf, QPDFObjectHandle const& qoh) { @@ -867,310 +933,367 @@ void qpdf_oh_release_all(qpdf_data qpdf) qpdf->oh_cache.clear(); } +template +static std::function return_T(T const& r) +{ + return [&r]() { return r; }; +} + +static QPDF_BOOL return_false() +{ + return QPDF_FALSE; +} + +static std::function return_uninitialized(qpdf_data qpdf) +{ + return [qpdf]() { return qpdf_oh_new_uninitialized(qpdf); }; +} + +static std::function return_null(qpdf_data qpdf) +{ + return [qpdf]() { return qpdf_oh_new_null(qpdf); }; +} + qpdf_oh qpdf_get_trailer(qpdf_data qpdf) { QTC::TC("qpdf", "qpdf-c called qpdf_get_trailer"); - return new_object(qpdf, qpdf->qpdf->getTrailer()); + return trap_oh_errors( + qpdf, return_uninitialized(qpdf), [] (qpdf_data q) { + return new_object(q, q->qpdf->getTrailer()); + }); } qpdf_oh qpdf_get_root(qpdf_data qpdf) { QTC::TC("qpdf", "qpdf-c called qpdf_get_root"); - return new_object(qpdf, qpdf->qpdf->getRoot()); + return trap_oh_errors( + qpdf, return_uninitialized(qpdf), [] (qpdf_data q) { + return new_object(q, q->qpdf->getRoot()); + }); } -static bool -qpdf_oh_valid_internal(qpdf_data qpdf, qpdf_oh oh) +template +static RET do_with_oh( + qpdf_data qpdf, qpdf_oh oh, + std::function fallback, + std::function fn) { - auto i = qpdf->oh_cache.find(oh); - bool result = ((i != qpdf->oh_cache.end()) && - (i->second).getPointer()); - if (! result) - { - QTC::TC("qpdf", "qpdf-c invalid object handle"); - qpdf->warnings.push_back( - QPDFExc( - qpdf_e_damaged_pdf, - qpdf->qpdf->getFilename(), - std::string("C API object handle ") + - QUtil::uint_to_string(oh), - 0, "attempted access to unknown object handle")); - } - return result; + return trap_oh_errors( + qpdf, fallback, [&fn, &oh](qpdf_data q) { + auto i = q->oh_cache.find(oh); + bool result = ((i != q->oh_cache.end()) && + (i->second).getPointer()); + if (! result) + { + QTC::TC("qpdf", "qpdf-c invalid object handle"); + throw QPDFExc( + qpdf_e_internal, + q->qpdf->getFilename(), + std::string("C API object handle ") + + QUtil::uint_to_string(oh), + 0, "attempted access to unknown object handle"); + } + return fn(*(q->oh_cache[oh])); + }); +} + +static void do_with_oh_void( + qpdf_data qpdf, qpdf_oh oh, + std::function fn) +{ + do_with_oh( + qpdf, oh, return_T(false), [&fn](QPDFObjectHandle& o) { + fn(o); + return true; // unused + }); } QPDF_BOOL qpdf_oh_is_initialized(qpdf_data qpdf, qpdf_oh oh) { QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_initialized"); - return (qpdf_oh_valid_internal(qpdf, oh) && - qpdf->oh_cache[oh]->isInitialized()); + return do_with_oh( + qpdf, oh, return_false, [](QPDFObjectHandle& o) { + return o.isInitialized(); + }); } QPDF_BOOL qpdf_oh_is_bool(qpdf_data qpdf, qpdf_oh oh) { QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_bool"); - return (qpdf_oh_valid_internal(qpdf, oh) && - qpdf->oh_cache[oh]->isBool()); + return do_with_oh( + qpdf, oh, return_false, [](QPDFObjectHandle& o) { + return o.isBool(); + }); } QPDF_BOOL qpdf_oh_is_null(qpdf_data qpdf, qpdf_oh oh) { QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_null"); - return (qpdf_oh_valid_internal(qpdf, oh) && - qpdf->oh_cache[oh]->isNull()); + return do_with_oh( + qpdf, oh, return_false, [](QPDFObjectHandle& o) { + return o.isNull(); + }); } QPDF_BOOL qpdf_oh_is_integer(qpdf_data qpdf, qpdf_oh oh) { QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_integer"); - return (qpdf_oh_valid_internal(qpdf, oh) && - qpdf->oh_cache[oh]->isInteger()); + return do_with_oh( + qpdf, oh, return_false, [](QPDFObjectHandle& o) { + return o.isInteger(); + }); } QPDF_BOOL qpdf_oh_is_real(qpdf_data qpdf, qpdf_oh oh) { QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_real"); - return (qpdf_oh_valid_internal(qpdf, oh) && - qpdf->oh_cache[oh]->isReal()); + return do_with_oh( + qpdf, oh, return_false, [](QPDFObjectHandle& o) { + return o.isReal(); + }); } QPDF_BOOL qpdf_oh_is_name(qpdf_data qpdf, qpdf_oh oh) { QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_name"); - return (qpdf_oh_valid_internal(qpdf, oh) && - qpdf->oh_cache[oh]->isName()); + return do_with_oh( + qpdf, oh, return_false, [](QPDFObjectHandle& o) { + return o.isName(); + }); } QPDF_BOOL qpdf_oh_is_string(qpdf_data qpdf, qpdf_oh oh) { QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_string"); - return (qpdf_oh_valid_internal(qpdf, oh) && - qpdf->oh_cache[oh]->isString()); + return do_with_oh( + qpdf, oh, return_false, [](QPDFObjectHandle& o) { + return o.isString(); + }); } QPDF_BOOL qpdf_oh_is_operator(qpdf_data qpdf, qpdf_oh oh) { QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_operator"); - return (qpdf_oh_valid_internal(qpdf, oh) && - qpdf->oh_cache[oh]->isOperator()); + return do_with_oh( + qpdf, oh, return_false, [](QPDFObjectHandle& o) { + return o.isOperator(); + }); } QPDF_BOOL qpdf_oh_is_inline_image(qpdf_data qpdf, qpdf_oh oh) { QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_inline_image"); - return (qpdf_oh_valid_internal(qpdf, oh) && - qpdf->oh_cache[oh]->isInlineImage()); + return do_with_oh( + qpdf, oh, return_false, [](QPDFObjectHandle& o) { + return o.isInlineImage(); + }); } QPDF_BOOL qpdf_oh_is_array(qpdf_data qpdf, qpdf_oh oh) { QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_array"); - return (qpdf_oh_valid_internal(qpdf, oh) && - qpdf->oh_cache[oh]->isArray()); + return do_with_oh( + qpdf, oh, return_false, [](QPDFObjectHandle& o) { + return o.isArray(); + }); } QPDF_BOOL qpdf_oh_is_dictionary(qpdf_data qpdf, qpdf_oh oh) { QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_dictionary"); - return (qpdf_oh_valid_internal(qpdf, oh) && - qpdf->oh_cache[oh]->isDictionary()); + return do_with_oh( + qpdf, oh, return_false, [](QPDFObjectHandle& o) { + return o.isDictionary(); + }); } QPDF_BOOL qpdf_oh_is_stream(qpdf_data qpdf, qpdf_oh oh) { QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_stream"); - return (qpdf_oh_valid_internal(qpdf, oh) && - qpdf->oh_cache[oh]->isStream()); + return do_with_oh( + qpdf, oh, return_false, [](QPDFObjectHandle& o) { + return o.isStream(); + }); } QPDF_BOOL qpdf_oh_is_indirect(qpdf_data qpdf, qpdf_oh oh) { QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_indirect"); - return (qpdf_oh_valid_internal(qpdf, oh) && - qpdf->oh_cache[oh]->isIndirect()); + return do_with_oh( + qpdf, oh, return_false, [](QPDFObjectHandle& o) { + return o.isIndirect(); + }); } QPDF_BOOL qpdf_oh_is_scalar(qpdf_data qpdf, qpdf_oh oh) { QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_scalar"); - return (qpdf_oh_valid_internal(qpdf, oh) && - qpdf->oh_cache[oh]->isScalar()); + return do_with_oh( + qpdf, oh, return_false, [](QPDFObjectHandle& o) { + return o.isScalar(); + }); +} + +QPDF_BOOL qpdf_oh_is_number(qpdf_data qpdf, qpdf_oh oh) +{ + return do_with_oh( + qpdf, oh, return_false, [](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_number"); + return o.isNumber(); + }); } qpdf_oh qpdf_oh_wrap_in_array(qpdf_data qpdf, qpdf_oh oh) { - if (! qpdf_oh_valid_internal(qpdf, oh)) - { - return qpdf_oh_new_array(qpdf); - } - auto qoh = qpdf->oh_cache[oh]; - if (qoh->isArray()) - { - QTC::TC("qpdf", "qpdf-c array to wrap_in_array"); - return new_object(qpdf, *qoh); - } - else - { - QTC::TC("qpdf", "qpdf-c non-array to wrap_in_array"); - return new_object(qpdf, - QPDFObjectHandle::newArray( - std::vector{ - *qpdf->oh_cache[oh]})); - } + return do_with_oh( + qpdf, oh, + [&qpdf](){ return qpdf_oh_new_array(qpdf); }, + [&qpdf](QPDFObjectHandle& qoh) { + if (qoh.isArray()) + { + QTC::TC("qpdf", "qpdf-c array to wrap_in_array"); + return new_object(qpdf, qoh); + } + else + { + QTC::TC("qpdf", "qpdf-c non-array to wrap_in_array"); + return new_object(qpdf, + QPDFObjectHandle::newArray( + std::vector{qoh})); + } + }); } qpdf_oh qpdf_oh_parse(qpdf_data qpdf, char const* object_str) { QTC::TC("qpdf", "qpdf-c called qpdf_oh_parse"); - return new_object(qpdf, QPDFObjectHandle::parse(object_str)); + return trap_oh_errors( + qpdf, return_uninitialized(qpdf), [&object_str] (qpdf_data q) { + return new_object(q, QPDFObjectHandle::parse(object_str)); + }); } QPDF_BOOL qpdf_oh_get_bool_value(qpdf_data qpdf, qpdf_oh oh) { - if (! qpdf_oh_valid_internal(qpdf, oh)) - { - return QPDF_FALSE; - } - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_bool_value"); - return qpdf->oh_cache[oh]->getBoolValue(); + return do_with_oh( + qpdf, oh, return_false, [](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_bool_value"); + return o.getBoolValue(); + }); } long long qpdf_oh_get_int_value(qpdf_data qpdf, qpdf_oh oh) { - if (! qpdf_oh_valid_internal(qpdf, oh)) - { - return 0LL; - } - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_int_value"); - return qpdf->oh_cache[oh]->getIntValue(); + return do_with_oh( + qpdf, oh, return_T(0LL), [](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_int_value"); + return o.getIntValue(); + }); } int qpdf_oh_get_int_value_as_int(qpdf_data qpdf, qpdf_oh oh) { - if (! qpdf_oh_valid_internal(qpdf, oh)) - { - return 0; - } - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_int_value_as_int"); - return qpdf->oh_cache[oh]->getIntValueAsInt(); + return do_with_oh( + qpdf, oh, return_T(0), [](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_int_value_as_int"); + return o.getIntValueAsInt(); + }); } unsigned long long qpdf_oh_get_uint_value(qpdf_data qpdf, qpdf_oh oh) { - if (! qpdf_oh_valid_internal(qpdf, oh)) - { - return 0ULL; - } - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_uint_value"); - return qpdf->oh_cache[oh]->getUIntValue(); + return do_with_oh( + qpdf, oh, return_T(0ULL), [](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_uint_value"); + return o.getUIntValue(); + }); } unsigned int qpdf_oh_get_uint_value_as_uint(qpdf_data qpdf, qpdf_oh oh) { - if (! qpdf_oh_valid_internal(qpdf, oh)) - { - return 0U; - } - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_uint_value_as_uint"); - return qpdf->oh_cache[oh]->getUIntValueAsUInt(); + return do_with_oh( + qpdf, oh, return_T(0U), [](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_uint_value_as_uint"); + return o.getUIntValueAsUInt(); + }); } char const* qpdf_oh_get_real_value(qpdf_data qpdf, qpdf_oh oh) { - if (! qpdf_oh_valid_internal(qpdf, oh)) - { - return ""; - } - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_real_value"); - qpdf->tmp_string = qpdf->oh_cache[oh]->getRealValue(); - return qpdf->tmp_string.c_str(); -} - -QPDF_BOOL qpdf_oh_is_number(qpdf_data qpdf, qpdf_oh oh) -{ - if (! qpdf_oh_valid_internal(qpdf, oh)) - { - return QPDF_FALSE; - } - QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_number"); - return qpdf->oh_cache[oh]->isNumber(); + return do_with_oh( + qpdf, oh, return_T(""), [&qpdf](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_real_value"); + qpdf->tmp_string = o.getRealValue(); + return qpdf->tmp_string.c_str(); + }); } double qpdf_oh_get_numeric_value(qpdf_data qpdf, qpdf_oh oh) { - if (! qpdf_oh_valid_internal(qpdf, oh)) - { - return 0.0; - } - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_numeric_value"); - return qpdf->oh_cache[oh]->getNumericValue(); + return do_with_oh( + qpdf, oh, return_T(0.0), [](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_numeric_value"); + return o.getNumericValue(); + }); } char const* qpdf_oh_get_name(qpdf_data qpdf, qpdf_oh oh) { - if (! qpdf_oh_valid_internal(qpdf, oh)) - { - return ""; - } - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_name"); - qpdf->tmp_string = qpdf->oh_cache[oh]->getName(); - return qpdf->tmp_string.c_str(); + return do_with_oh( + qpdf, oh, return_T(""), [&qpdf](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_name"); + qpdf->tmp_string = o.getName(); + return qpdf->tmp_string.c_str(); + }); } char const* qpdf_oh_get_string_value(qpdf_data qpdf, qpdf_oh oh) { - if (! qpdf_oh_valid_internal(qpdf, oh)) - { - return ""; - } - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_string_value"); - qpdf->tmp_string = qpdf->oh_cache[oh]->getStringValue(); - return qpdf->tmp_string.c_str(); + return do_with_oh( + qpdf, oh, return_T(""), [&qpdf](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_string_value"); + qpdf->tmp_string = o.getStringValue(); + return qpdf->tmp_string.c_str(); + }); } char const* qpdf_oh_get_utf8_value(qpdf_data qpdf, qpdf_oh oh) { - if (! qpdf_oh_valid_internal(qpdf, oh)) - { - return ""; - } - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_utf8_value"); - qpdf->tmp_string = qpdf->oh_cache[oh]->getUTF8Value(); - return qpdf->tmp_string.c_str(); + return do_with_oh( + qpdf, oh, return_T(""), [&qpdf](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_utf8_value"); + qpdf->tmp_string = o.getUTF8Value(); + return qpdf->tmp_string.c_str(); + }); } int qpdf_oh_get_array_n_items(qpdf_data qpdf, qpdf_oh oh) { - if (! qpdf_oh_valid_internal(qpdf, oh)) - { - return 0; - } - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_array_n_items"); - return qpdf->oh_cache[oh]->getArrayNItems(); + return do_with_oh( + qpdf, oh, return_T(0), [](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_array_n_items"); + return o.getArrayNItems(); + }); } qpdf_oh qpdf_oh_get_array_item(qpdf_data qpdf, qpdf_oh oh, int n) { - if (! qpdf_oh_valid_internal(qpdf, oh)) - { - return qpdf_oh_new_null(qpdf); - } - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_array_item"); - return new_object(qpdf, qpdf->oh_cache[oh]->getArrayItem(n)); + return do_with_oh( + qpdf, oh, return_null(qpdf), [&qpdf, &n](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_array_item"); + return new_object(qpdf, o.getArrayItem(n)); + }); } void qpdf_oh_begin_dict_key_iter(qpdf_data qpdf, qpdf_oh oh) { - if (qpdf_oh_valid_internal(qpdf, oh) && - qpdf_oh_is_dictionary(qpdf, oh)) - { - QTC::TC("qpdf", "qpdf-c called qpdf_oh_begin_dict_key_iter"); - qpdf->cur_iter_dict_keys = qpdf->oh_cache[oh]->getKeys(); - } - else - { - qpdf->cur_iter_dict_keys = {}; - } + qpdf->cur_iter_dict_keys = do_with_oh>( + qpdf, oh, + [](){ return std::set(); }, + [](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_begin_dict_key_iter"); + return o.getKeys(); + }); qpdf->dict_iter = qpdf->cur_iter_dict_keys.begin(); } @@ -1197,32 +1320,35 @@ char const* qpdf_oh_dict_next_key(qpdf_data qpdf) QPDF_BOOL qpdf_oh_has_key(qpdf_data qpdf, qpdf_oh oh, char const* key) { - if (! qpdf_oh_valid_internal(qpdf, oh)) - { - return QPDF_FALSE; - } - QTC::TC("qpdf", "qpdf-c called qpdf_oh_has_key"); - return qpdf->oh_cache[oh]->hasKey(key); + return do_with_oh( + qpdf, oh, return_false, [&key](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_has_key"); + return o.hasKey(key); + }); } qpdf_oh qpdf_oh_get_key(qpdf_data qpdf, qpdf_oh oh, char const* key) { - if (! qpdf_oh_valid_internal(qpdf, oh)) - { - return qpdf_oh_new_null(qpdf); - } - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_key"); - return new_object(qpdf, qpdf->oh_cache[oh]->getKey(key)); + return do_with_oh( + qpdf, oh, return_null(qpdf), [&qpdf, &key](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_key"); + return new_object(qpdf, o.getKey(key)); + }); } QPDF_BOOL qpdf_oh_is_or_has_name(qpdf_data qpdf, qpdf_oh oh, char const* key) { - if (! qpdf_oh_valid_internal(qpdf, oh)) - { - return QPDF_FALSE; - } - QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_or_has_name"); - return qpdf->oh_cache[oh]->isOrHasName(key); + return do_with_oh( + qpdf, oh, return_false, [&key](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_or_has_name"); + return o.isOrHasName(key); + }); +} + +qpdf_oh qpdf_oh_new_uninitialized(qpdf_data qpdf) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_oh_new_uninitialized"); + return new_object(qpdf, QPDFObjectHandle()); } qpdf_oh qpdf_oh_new_null(qpdf_data qpdf) @@ -1288,156 +1414,143 @@ qpdf_oh qpdf_oh_new_dictionary(qpdf_data qpdf) void qpdf_oh_make_direct(qpdf_data qpdf, qpdf_oh oh) { - if (qpdf_oh_valid_internal(qpdf, oh)) - { - QTC::TC("qpdf", "qpdf-c called qpdf_oh_make_direct"); - qpdf->oh_cache[oh]->makeDirect(); - } + do_with_oh_void( + qpdf, oh, [](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_make_direct"); + o.makeDirect(); + }); } static QPDFObjectHandle qpdf_oh_item_internal(qpdf_data qpdf, qpdf_oh item) { - if (qpdf_oh_valid_internal(qpdf, item)) - { - return *(qpdf->oh_cache[item]); - } - else - { - return QPDFObjectHandle::newNull(); - } + return do_with_oh( + qpdf, item, + [](){return QPDFObjectHandle::newNull();}, + [](QPDFObjectHandle& o) { + return o; + }); } void qpdf_oh_set_array_item(qpdf_data qpdf, qpdf_oh oh, int at, qpdf_oh item) { - if (qpdf_oh_is_array(qpdf, oh)) - { - QTC::TC("qpdf", "qpdf-c called qpdf_oh_set_array_item"); - qpdf->oh_cache[oh]->setArrayItem( - at, qpdf_oh_item_internal(qpdf, item)); - } + do_with_oh_void( + qpdf, oh, [&qpdf, &at, &item](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_set_array_item"); + o.setArrayItem(at, qpdf_oh_item_internal(qpdf, item)); + }); } void qpdf_oh_insert_item(qpdf_data qpdf, qpdf_oh oh, int at, qpdf_oh item) { - if (qpdf_oh_is_array(qpdf, oh)) - { - QTC::TC("qpdf", "qpdf-c called qpdf_oh_insert_item"); - qpdf->oh_cache[oh]->insertItem( - at, qpdf_oh_item_internal(qpdf, item)); - } + do_with_oh_void( + qpdf, oh, [&qpdf, &at, &item](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_insert_item"); + o.insertItem(at, qpdf_oh_item_internal(qpdf, item)); + }); } void qpdf_oh_append_item(qpdf_data qpdf, qpdf_oh oh, qpdf_oh item) { - if (qpdf_oh_is_array(qpdf, oh)) - { - QTC::TC("qpdf", "qpdf-c called qpdf_oh_append_item"); - qpdf->oh_cache[oh]->appendItem( - qpdf_oh_item_internal(qpdf, item)); - } + do_with_oh_void( + qpdf, oh, [&qpdf, &item](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_append_item"); + o.appendItem(qpdf_oh_item_internal(qpdf, item)); + }); } void qpdf_oh_erase_item(qpdf_data qpdf, qpdf_oh oh, int at) { - if (qpdf_oh_is_array(qpdf, oh)) - { - QTC::TC("qpdf", "qpdf-c called qpdf_oh_erase_item"); - qpdf->oh_cache[oh]->eraseItem(at); - } + do_with_oh_void( + qpdf, oh, [&at](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_erase_item"); + o.eraseItem(at); + }); } void qpdf_oh_replace_key(qpdf_data qpdf, qpdf_oh oh, char const* key, qpdf_oh item) { - if (qpdf_oh_is_dictionary(qpdf, oh)) - { - QTC::TC("qpdf", "qpdf-c called qpdf_oh_replace_key"); - qpdf->oh_cache[oh]->replaceKey( - key, qpdf_oh_item_internal(qpdf, item)); - } + do_with_oh_void( + qpdf, oh, [&qpdf, &key, &item](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_replace_key"); + o.replaceKey(key, qpdf_oh_item_internal(qpdf, item)); + }); } void qpdf_oh_remove_key(qpdf_data qpdf, qpdf_oh oh, char const* key) { - if (qpdf_oh_is_dictionary(qpdf, oh)) - { - QTC::TC("qpdf", "qpdf-c called qpdf_oh_remove_key"); - qpdf->oh_cache[oh]->removeKey(key); - } + do_with_oh_void( + qpdf, oh, [&key](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_remove_key"); + o.removeKey(key); + }); } void qpdf_oh_replace_or_remove_key(qpdf_data qpdf, qpdf_oh oh, char const* key, qpdf_oh item) { - if (qpdf_oh_is_dictionary(qpdf, oh)) - { - QTC::TC("qpdf", "qpdf-c called qpdf_oh_replace_or_remove_key"); - qpdf->oh_cache[oh]->replaceOrRemoveKey( - key, qpdf_oh_item_internal(qpdf, item)); - } + do_with_oh_void( + qpdf, oh, [&qpdf, &key, &item](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_replace_or_remove_key"); + o.replaceOrRemoveKey(key, qpdf_oh_item_internal(qpdf, item)); + }); } qpdf_oh qpdf_oh_get_dict(qpdf_data qpdf, qpdf_oh oh) { - if (! qpdf_oh_valid_internal(qpdf, oh)) - { - return qpdf_oh_new_null(qpdf); - } - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_dict"); - return new_object(qpdf, qpdf->oh_cache[oh]->getDict()); + return do_with_oh( + qpdf, oh, return_null(qpdf), [&qpdf](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_dict"); + return new_object(qpdf, o.getDict()); + }); } int qpdf_oh_get_object_id(qpdf_data qpdf, qpdf_oh oh) { - if (! qpdf_oh_valid_internal(qpdf, oh)) - { - return 0; - } - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_object_id"); - return qpdf->oh_cache[oh]->getObjectID(); + return do_with_oh( + qpdf, oh, return_T(0), [](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_object_id"); + return o.getObjectID(); + }); } int qpdf_oh_get_generation(qpdf_data qpdf, qpdf_oh oh) { - if (! qpdf_oh_valid_internal(qpdf, oh)) - { - return 0; - } - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_generation"); - return qpdf->oh_cache[oh]->getGeneration(); + return do_with_oh( + qpdf, oh, return_T(0), [](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_generation"); + return o.getGeneration(); + }); } char const* qpdf_oh_unparse(qpdf_data qpdf, qpdf_oh oh) { - if (! qpdf_oh_valid_internal(qpdf, oh)) - { - return ""; - } - QTC::TC("qpdf", "qpdf-c called qpdf_oh_unparse"); - qpdf->tmp_string = qpdf->oh_cache[oh]->unparse(); - return qpdf->tmp_string.c_str(); + return do_with_oh( + qpdf, oh, return_T(""), [&qpdf](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_unparse"); + qpdf->tmp_string = o.unparse(); + return qpdf->tmp_string.c_str(); + }); } char const* qpdf_oh_unparse_resolved(qpdf_data qpdf, qpdf_oh oh) { - if (! qpdf_oh_valid_internal(qpdf, oh)) - { - return ""; - } - QTC::TC("qpdf", "qpdf-c called qpdf_oh_unparse_resolved"); - qpdf->tmp_string = qpdf->oh_cache[oh]->unparseResolved(); - return qpdf->tmp_string.c_str(); + return do_with_oh( + qpdf, oh, return_T(""), [&qpdf](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_unparse_resolved"); + qpdf->tmp_string = o.unparseResolved(); + return qpdf->tmp_string.c_str(); + }); } char const* qpdf_oh_unparse_binary(qpdf_data qpdf, qpdf_oh oh) { - if (! qpdf_oh_valid_internal(qpdf, oh)) - { - return ""; - } - QTC::TC("qpdf", "qpdf-c called qpdf_oh_unparse_binary"); - qpdf->tmp_string = qpdf->oh_cache[oh]->unparseBinary(); - return qpdf->tmp_string.c_str(); + return do_with_oh( + qpdf, oh, return_T(""), [&qpdf](QPDFObjectHandle& o) { + QTC::TC("qpdf", "qpdf-c called qpdf_oh_unparse_binary"); + qpdf->tmp_string = o.unparseBinary(); + return qpdf->tmp_string.c_str(); + }); } diff --git a/manual/qpdf-manual.xml b/manual/qpdf-manual.xml index fd3bd6fb..f7d224e5 100644 --- a/manual/qpdf-manual.xml +++ b/manual/qpdf-manual.xml @@ -5229,6 +5229,19 @@ print "\n"; discussion. + + + Overhaul error handling for the object handle functions in + the C API. See comments in the “Object handling” + section of include/qpdf/qpdf-c.h for + details. In particular, exceptions thrown by the underlying + C++ code when calling object accessors are caught and + converted into errors. The errors can be trapped by + registering an error handler with + qpdf_register_oh_error_handler or will + be written to stderr if no handler is registered. + + Add qpdf_get_last_string_length to the @@ -5239,9 +5252,9 @@ print "\n"; - Add qpdf_oh_is_initialized to the - C API. While you can't directly create uninitialized objects - from the C API, you still have to be able to detect them. + Add qpdf_oh_is_initialized and + qpdf_oh_new_uninitialized to the C API + to make it possible to work with uninitialized objects. diff --git a/qpdf/qpdf-ctest.c b/qpdf/qpdf-ctest.c index 4d30712e..6ee19004 100644 --- a/qpdf/qpdf-ctest.c +++ b/qpdf/qpdf-ctest.c @@ -35,28 +35,29 @@ static FILE* safe_fopen(char const* filename, char const* mode) return f; } -static void report_errors() +static void print_error(char const* label, qpdf_data qpdf, qpdf_error e) { #define POS_FMT " pos : " LL_FMT "\n" + printf("%s: %s\n", label, qpdf_get_error_full_text(qpdf, e)); + printf(" code: %d\n", qpdf_get_error_code(qpdf, e)); + printf(" file: %s\n", qpdf_get_error_filename(qpdf, e)); + printf(POS_FMT, qpdf_get_error_file_position(qpdf, e)); + printf(" text: %s\n", qpdf_get_error_message_detail(qpdf, e)); +} + +static void report_errors() +{ qpdf_error e = 0; while (qpdf_more_warnings(qpdf)) { e = qpdf_next_warning(qpdf); - printf("warning: %s\n", qpdf_get_error_full_text(qpdf, e)); - printf(" code: %d\n", qpdf_get_error_code(qpdf, e)); - printf(" file: %s\n", qpdf_get_error_filename(qpdf, e)); - printf(POS_FMT, qpdf_get_error_file_position(qpdf, e)); - printf(" text: %s\n", qpdf_get_error_message_detail(qpdf, e)); + print_error("warning", qpdf, e); } if (qpdf_has_error(qpdf)) { e = qpdf_get_error(qpdf); assert(qpdf_has_error(qpdf) == QPDF_FALSE); - printf("error: %s\n", qpdf_get_error_full_text(qpdf, e)); - printf(" code: %d\n", qpdf_get_error_code(qpdf, e)); - printf(" file: %s\n", qpdf_get_error_filename(qpdf, e)); - printf(POS_FMT, qpdf_get_error_file_position(qpdf, e)); - printf(" text: %s\n", qpdf_get_error_message_detail(qpdf, e)); + print_error("error", qpdf, e); } else { @@ -72,6 +73,16 @@ static void report_errors() } } +static void handle_oh_error(qpdf_data qpdf, qpdf_error error, void* data) +{ + char const* label = "oh error"; + if (data) + { + label = *((char const**)data); + } + print_error(label, qpdf, error); +} + static void read_file_into_memory(char const* filename, char** buf, unsigned long* size) { @@ -615,8 +626,11 @@ static void test24(char const* infile, */ qpdf_oh_replace_key(qpdf, resources, "/ProcSet", procset); - /* Release and access to exercise warnings and to show that write - * still works after releasing. + /* Release and access to exercise handling of object handle errors + * and to show that write still works after releasing. This test + * uses the default oh error handler, so messages get written to + * stderr. The warning about using the default error handler only + * appears once. */ qpdf_oh_release(qpdf, page1); contents = qpdf_oh_get_key(qpdf, page1, "/Contents"); @@ -791,6 +805,82 @@ static void test28(char const* infile, } } +static void test29(char const* infile, + char const* password, + char const* outfile, + char const* outfile2) +{ + /* Trap exceptions thrown by object accessors. Type mismatches are + * errors rather than warnings when they don't have an owning QPDF + * object. + */ + char const* label = "oh error"; + qpdf_register_oh_error_handler(qpdf, handle_oh_error, (void*)&label); + + /* get_root fails when we have no trailer */ + label = "get root"; + qpdf_oh root = qpdf_get_root(qpdf); + assert(root != 0); + assert(! qpdf_oh_is_initialized(qpdf, root)); + + label = "bad parse"; + assert(! qpdf_oh_is_initialized(qpdf, qpdf_oh_parse(qpdf, "[oops"))); + report_errors(); + + label = "type mismatch"; + assert(qpdf_oh_get_int_value_as_int( + qpdf, qpdf_oh_new_string(qpdf, "x")) == 0); + qpdf_oh int_oh = qpdf_oh_new_integer(qpdf, 12); + assert(strlen(qpdf_oh_get_string_value(qpdf, int_oh)) == 0); + + // This doesn't test every possible error flow, but it tests each + // way of handling errors in the library code. + label = "array type mismatch"; + assert(qpdf_oh_get_array_n_items(qpdf, int_oh) == 0); + assert(qpdf_oh_is_null(qpdf, qpdf_oh_get_array_item(qpdf, int_oh, 3))); + label = "append to non-array"; + qpdf_oh_append_item(qpdf, int_oh, qpdf_oh_new_null(qpdf)); + qpdf_oh array = qpdf_oh_new_array(qpdf); + label = "array bounds"; + assert(qpdf_oh_is_null(qpdf, qpdf_oh_get_array_item(qpdf, array, 3))); + + label = "dictionary iter type mismatch"; + qpdf_oh_begin_dict_key_iter(qpdf, int_oh); + assert(qpdf_oh_dict_more_keys(qpdf) == QPDF_FALSE); + label = "dictionary type mismatch"; + assert(qpdf_oh_is_null(qpdf, qpdf_oh_get_key(qpdf, int_oh, "potato"))); + assert(qpdf_oh_has_key(qpdf, int_oh, "potato") == QPDF_FALSE); + + report_errors(); +} + +static void test30(char const* infile, + char const* password, + char const* outfile, + char const* outfile2) +{ + assert(qpdf_read(qpdf, infile, password) & QPDF_ERRORS); + /* Fail to handle error */ +} + +static void test31(char const* infile, + char const* password, + char const* outfile, + char const* outfile2) +{ + /* Make sure type warnings have a specific error code. This test + * case is designed for minimal.pdf. + */ + qpdf_read(qpdf, infile, password); + qpdf_oh trailer = qpdf_get_trailer(qpdf); + assert(qpdf_oh_get_int_value(qpdf, trailer) == 0LL); + assert(! qpdf_has_error(qpdf)); + assert(qpdf_more_warnings(qpdf)); + qpdf_error e = qpdf_next_warning(qpdf); + assert(qpdf_get_error_code(qpdf, e) == qpdf_e_object); + report_errors(); +} + int main(int argc, char* argv[]) { char* p = 0; @@ -859,6 +949,9 @@ int main(int argc, char* argv[]) (n == 26) ? test26 : (n == 27) ? test27 : (n == 28) ? test28 : + (n == 29) ? test29 : + (n == 30) ? test30 : + (n == 31) ? test31 : 0); if (fn == 0) diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov index e3f6b00d..67311b01 100644 --- a/qpdf/qpdf.testcov +++ b/qpdf/qpdf.testcov @@ -602,3 +602,7 @@ QPDFObjectHandle check ownership 0 qpdf weak crypto warning 0 qpdf-c called qpdf_oh_is_initialized 0 qpdf-c registered progress reporter 0 +qpdf-c called qpdf_oh_new_uninitialized 0 +qpdf-c warn about oh error 1 +qpdf-c registered oh error handler 0 +qpdf-c cleanup warned about unhandled error 0 diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test index 21f82a22..384d9dca 100644 --- a/qpdf/qtest/qpdf.test +++ b/qpdf/qtest/qpdf.test @@ -4812,7 +4812,7 @@ foreach my $i (@c_check_types) show_ntests(); # ---------- $td->notify("--- C API Object Handle ---"); -$n_tests += 7; +$n_tests += 10; $td->runtest("C check object handles", {$td->COMMAND => "qpdf-ctest 24 minimal.pdf '' a.pdf"}, @@ -4843,6 +4843,18 @@ $td->runtest("C wrap and clone objects", {$td->COMMAND => "qpdf-ctest 28 minimal.pdf '' ''"}, {$td->STRING => "", $td->EXIT_STATUS => 0}, $td->NORMALIZE_NEWLINES); +$td->runtest("C object handle errors", + {$td->COMMAND => "qpdf-ctest 29 minimal.pdf '' ''"}, + {$td->FILE => "c-oh-errors.out", $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); +$td->runtest("C unhandled error warning", + {$td->COMMAND => "qpdf-ctest 30 bad1.pdf '' ''"}, + {$td->FILE => "c-unhandled-error.out", $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); +$td->runtest("C type mismatch warning", + {$td->COMMAND => "qpdf-ctest 31 minimal.pdf '' ''"}, + {$td->FILE => "c-type-warning.out", $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); show_ntests(); # ---------- diff --git a/qpdf/qtest/qpdf/c-object-handles.out b/qpdf/qtest/qpdf/c-object-handles.out index 675bacac..feef275e 100644 --- a/qpdf/qtest/qpdf/c-object-handles.out +++ b/qpdf/qtest/qpdf/c-object-handles.out @@ -7,18 +7,11 @@ item 0: 0 0.00 item 1: 0 0.00 item 2: 612 612.00 item 3: 792 792.00 -warning: minimal.pdf (C API object handle 6): attempted access to unknown object handle - code: 5 +minimal.pdf (C API object handle 6): attempted access to unknown object handle +minimal.pdf (C API object handle 9): attempted access to unknown object handle +minimal.pdf (C API object handle 9): attempted access to unknown object handle +warning: minimal.pdf: C API object handle accessor errors occurred, and the application did not define an error handler + code: 1 file: minimal.pdf pos : 0 - text: attempted access to unknown object handle -warning: minimal.pdf (C API object handle 9): attempted access to unknown object handle - code: 5 - file: minimal.pdf - pos : 0 - text: attempted access to unknown object handle -warning: minimal.pdf (C API object handle 9): attempted access to unknown object handle - code: 5 - file: minimal.pdf - pos : 0 - text: attempted access to unknown object handle + text: C API object handle accessor errors occurred, and the application did not define an error handler diff --git a/qpdf/qtest/qpdf/c-oh-errors.out b/qpdf/qtest/qpdf/c-oh-errors.out new file mode 100644 index 00000000..e1971759 --- /dev/null +++ b/qpdf/qtest/qpdf/c-oh-errors.out @@ -0,0 +1,55 @@ +get root: attempted to dereference an uninitialized QPDFObjectHandle + code: 1 + file: + pos : 0 + text: attempted to dereference an uninitialized QPDFObjectHandle +bad parse: parsed object (offset 1): unknown token while reading object; treating as string + code: 5 + file: parsed object + pos : 1 + text: unknown token while reading object; treating as string +type mismatch: operation for integer attempted on object of type string: returning 0 + code: 7 + file: + pos : 0 + text: operation for integer attempted on object of type string: returning 0 +type mismatch: operation for string attempted on object of type integer: returning empty string + code: 7 + file: + pos : 0 + text: operation for string attempted on object of type integer: returning empty string +array type mismatch: operation for array attempted on object of type integer: treating as empty + code: 7 + file: + pos : 0 + text: operation for array attempted on object of type integer: treating as empty +array type mismatch: operation for array attempted on object of type integer: returning null + code: 7 + file: + pos : 0 + text: operation for array attempted on object of type integer: returning null +append to non-array: operation for array attempted on object of type integer: ignoring attempt to append item + code: 7 + file: + pos : 0 + text: operation for array attempted on object of type integer: ignoring attempt to append item +array bounds: returning null for out of bounds array access + code: 7 + file: + pos : 0 + text: returning null for out of bounds array access +dictionary iter type mismatch: operation for dictionary attempted on object of type integer: treating as empty + code: 7 + file: + pos : 0 + text: operation for dictionary attempted on object of type integer: treating as empty +dictionary type mismatch: operation for dictionary attempted on object of type integer: returning null for attempted key retrieval + code: 7 + file: + pos : 0 + text: operation for dictionary attempted on object of type integer: returning null for attempted key retrieval +dictionary type mismatch: operation for dictionary attempted on object of type integer: returning false for a key containment request + code: 7 + file: + pos : 0 + text: operation for dictionary attempted on object of type integer: returning false for a key containment request diff --git a/qpdf/qtest/qpdf/c-type-warning.out b/qpdf/qtest/qpdf/c-type-warning.out new file mode 100644 index 00000000..f7829657 --- /dev/null +++ b/qpdf/qtest/qpdf/c-type-warning.out @@ -0,0 +1 @@ +WARNING: minimal.pdf, trailer at offset 715: operation for integer attempted on object of type dictionary: returning 0 diff --git a/qpdf/qtest/qpdf/c-unhandled-error.out b/qpdf/qtest/qpdf/c-unhandled-error.out new file mode 100644 index 00000000..53109397 --- /dev/null +++ b/qpdf/qtest/qpdf/c-unhandled-error.out @@ -0,0 +1,5 @@ +WARNING: bad1.pdf: can't find PDF header +WARNING: bad1.pdf: file is damaged +WARNING: bad1.pdf: can't find startxref +WARNING: bad1.pdf: Attempting to reconstruct cross-reference table +WARNING: application did not handle error: bad1.pdf: unable to find trailer dictionary while recovering damaged file