Commit 72c10d8617c799432e28dabf1679b1a6f5245c02

Authored by Jay Berkenbilt
1 parent 3340dbe9

C API: overhaul error handling

* Handle error conditions that occur when using the object handle
  interfaces. In the past, some exceptions were not correctly
  converted to errors or warnings.
* Add more detailed information to qpdf-c.h
* Make it possible to work more explicitly with uninitialized objects
ChangeLog
  1 +2021-12-10 Jay Berkenbilt <ejb@ql.org>
  2 +
  3 + * C API: Overhaul how errors are handle the C API's object handle
  4 + interfaces. Clarify documentation regarding object accessors and
  5 + how type errors and warnings are handled. Many cases that used to
  6 + crash code that used the C API can now be trapped and will be
  7 + written stderr if not trapped. The new method
  8 + qpdf_register_oh_error_handler can be used to specifically handle
  9 + errors that occur when accessing object handles. See qpdf-c.h for
  10 + details.
  11 +
  12 + * C API: Add qpdf_oh_new_uninitialized to explicitly create
  13 + uninitialized object handles.
  14 +
  15 + * Add new error code qpdf_e_object that is used for exceptions
  16 + (including warnings) that are caused by using QPDFObjectHandle
  17 + methods on object handles of the wrong type.
  18 +
1 2021-12-02 Jay Berkenbilt <ejb@ql.org> 19 2021-12-02 Jay Berkenbilt <ejb@ql.org>
2 20
3 * C API: Add qpdf_oh_is_initialized. 21 * C API: Add qpdf_oh_is_initialized.
include/qpdf/qpdf-c.h
@@ -62,22 +62,27 @@ @@ -62,22 +62,27 @@
62 * string was just returned. 62 * string was just returned.
63 * 63 *
64 * Many functions defined here merely set parameters and therefore 64 * Many functions defined here merely set parameters and therefore
65 - * never return error conditions. Functions that may cause PDF  
66 - * files to be read or written may return error conditions. Such  
67 - * functions return an error code. If there were no errors or  
68 - * warnings, they return QPDF_SUCCESS. If there were warnings,  
69 - * the return value has the QPDF_WARNINGS bit set. If there  
70 - * errors, the QPDF_ERRORS bit is set. In other words, if there  
71 - * are both warnings and errors, then the return status will be  
72 - * QPDF_WARNINGS | QPDF_ERRORS. You may also call the 65 + * never return error conditions. Functions that access or return
  66 + * qpdf_oh object handles may generate warnings but have no way to
  67 + * return errors, but the errors may be checked afterwards or
  68 + * handled using a registered handler. This is discussed in more
  69 + * detail in the section on object handling. Functions that may
  70 + * cause PDF files to be read or written may return error
  71 + * conditions. Such functions return an error code. If there were
  72 + * no errors or warnings, they return QPDF_SUCCESS. If there were
  73 + * warnings, the return value has the QPDF_WARNINGS bit set. If
  74 + * there were errors, the QPDF_ERRORS bit is set. In other words,
  75 + * if there are both warnings and errors, then the return status
  76 + * will be QPDF_WARNINGS | QPDF_ERRORS. You may also call the
73 * qpdf_more_warnings and qpdf_more_errors functions to test 77 * qpdf_more_warnings and qpdf_more_errors functions to test
74 - * whether there are unseen warning or error conditions. By 78 + * whether there are unseen warning or error conditions. By
75 * default, warnings are written to stderr when detected, but this 79 * default, warnings are written to stderr when detected, but this
76 - * behavior can be suppressed. In all cases, errors and warnings 80 + * behavior can be suppressed. In all cases, errors and warnings
77 * may be retrieved by calling qpdf_next_warning and 81 * may be retrieved by calling qpdf_next_warning and
78 - * qpdf_next_error. All exceptions thrown by the C++ interface  
79 - * are caught and converted into error messages by the C  
80 - * interface. 82 + * qpdf_get_error. All exceptions thrown by the C++ interface are
  83 + * caught and converted into error messages by the C interface.
  84 + * Any exceptions to this are qpdf bugs and should be reported at
  85 + * https://github.com/qpdf/qpdf/issues/new.
81 * 86 *
82 * Most functions defined here have obvious counterparts that are 87 * Most functions defined here have obvious counterparts that are
83 * methods to either QPDF or QPDFWriter. Please see comments in 88 * methods to either QPDF or QPDFWriter. Please see comments in
@@ -550,13 +555,51 @@ extern &quot;C&quot; { @@ -550,13 +555,51 @@ extern &quot;C&quot; {
550 * handle, the object is safely part of the dictionary or array. 555 * handle, the object is safely part of the dictionary or array.
551 * Similarly, any other object handle refering to the object remains 556 * Similarly, any other object handle refering to the object remains
552 * valid. Explicitly releasing an object handle is essentially the 557 * valid. Explicitly releasing an object handle is essentially the
553 - * same as letting a QPDFObjectHandle go out of scope in the C++ API. 558 + * same as letting a QPDFObjectHandle go out of scope in the C++
  559 + * API.
  560 + *
  561 + * Important note about error handling:
  562 + *
  563 + * While many of the functions that operate on the QPDF object
  564 + * return error codes, the qpdf_oh functions return values such as
  565 + * object handles or data. They have no way to return error codes.
  566 + * If they generate warnings, the warnings are handled using the
  567 + * error/warning handling functions described above. If the
  568 + * underlying C++ call throws an exception, the error handler
  569 + * registered with qpdf_register_oh_error_handler() will be
  570 + * called. If no handler is registered, the exception is written
  571 + * to STDERR. In either case, a sensible fallback value is
  572 + * returned (0 for numbers, QPDF_FALSE for booleans, "" for
  573 + * strings, or a null object). It is sensible for a C program to
  574 + * use setjmp and longjmp with this error handler since the C++
  575 + * code has raised an exception, but you can also just set a flag
  576 + * and check it after each call.
  577 + *
  578 + * All conditions under which exceptions would be thrown by object
  579 + * accessors are caused by programmer error or major problems such
  580 + * as running out of memory or not being able to read the input
  581 + * file. If they are ever caused by invalid data in the PDF file,
  582 + * it is a bug in qpdf, which should be reported at
  583 + * https://github.com/qpdf/qpdf/issues/new.
554 */ 584 */
555 585
556 /* For examples of using this API, see examples/pdf-c-objects.c */ 586 /* For examples of using this API, see examples/pdf-c-objects.c */
557 587
558 typedef unsigned int qpdf_oh; 588 typedef unsigned int qpdf_oh;
559 589
  590 + /* If an exception is thrown by the C++ code when any of the
  591 + * qpdf_oh functions are called, the registered handle_error
  592 + * function will be called. The value passed to data will be
  593 + * passed along to the error handler function. If any errors occur
  594 + * and no error handler is accessed, a single warning will be
  595 + * issued, and the error will be written to stderr.
  596 + */
  597 + QPDF_DLL
  598 + void qpdf_register_oh_error_handler(
  599 + qpdf_data qpdf,
  600 + void (*handle_error)(qpdf_data qpdf, qpdf_error error, void* data),
  601 + void* data);
  602 +
560 /* Releasing objects -- see comments above. These functions have no 603 /* Releasing objects -- see comments above. These functions have no
561 * equivalent in the C++ API. 604 * equivalent in the C++ API.
562 */ 605 */
@@ -659,7 +702,7 @@ extern &quot;C&quot; { @@ -659,7 +702,7 @@ extern &quot;C&quot; {
659 /* The memory returned by qpdf_oh_dict_next_key is owned by 702 /* The memory returned by qpdf_oh_dict_next_key is owned by
660 * qpdf_data. It is good until the next call to 703 * qpdf_data. It is good until the next call to
661 * qpdf_oh_dict_next_key with the same qpdf_data object. Calling 704 * qpdf_oh_dict_next_key with the same qpdf_data object. Calling
662 - * the method again, even with a different dict, invalidates 705 + * the function again, even with a different dict, invalidates
663 * previous return values. 706 * previous return values.
664 */ 707 */
665 QPDF_DLL 708 QPDF_DLL
@@ -677,6 +720,8 @@ extern &quot;C&quot; { @@ -677,6 +720,8 @@ extern &quot;C&quot; {
677 qpdf_data data, qpdf_oh oh, char const* key); 720 qpdf_data data, qpdf_oh oh, char const* key);
678 721
679 QPDF_DLL 722 QPDF_DLL
  723 + qpdf_oh qpdf_oh_new_uninitialized(qpdf_data qpdf);
  724 + QPDF_DLL
680 qpdf_oh qpdf_oh_new_null(qpdf_data data); 725 qpdf_oh qpdf_oh_new_null(qpdf_data data);
681 QPDF_DLL 726 QPDF_DLL
682 qpdf_oh qpdf_oh_new_bool(qpdf_data data, QPDF_BOOL value); 727 qpdf_oh qpdf_oh_new_bool(qpdf_data data, QPDF_BOOL value);
libqpdf/qpdf-c.cc
@@ -41,6 +41,9 @@ struct _qpdf_data @@ -41,6 +41,9 @@ struct _qpdf_data
41 PointerHolder<Buffer> output_buffer; 41 PointerHolder<Buffer> output_buffer;
42 42
43 // QPDFObjectHandle support 43 // QPDFObjectHandle support
  44 + void (*oh_error_handler)(qpdf_data, qpdf_error, void*);
  45 + void* oh_error_handler_data;
  46 + bool default_oh_error_handler_called;
44 std::map<qpdf_oh, PointerHolder<QPDFObjectHandle>> oh_cache; 47 std::map<qpdf_oh, PointerHolder<QPDFObjectHandle>> oh_cache;
45 qpdf_oh next_oh; 48 qpdf_oh next_oh;
46 std::set<std::string> cur_iter_dict_keys; 49 std::set<std::string> cur_iter_dict_keys;
@@ -48,8 +51,32 @@ struct _qpdf_data @@ -48,8 +51,32 @@ struct _qpdf_data
48 std::string cur_dict_key; 51 std::string cur_dict_key;
49 }; 52 };
50 53
  54 +static void default_oh_error_handler(qpdf_data qpdf, qpdf_error e, void* data)
  55 +{
  56 + bool* called = reinterpret_cast<bool*>(data);
  57 + if (called != nullptr)
  58 + {
  59 + QTC::TC("qpdf", "qpdf-c warn about oh error", *called ? 0 : 1);
  60 + if (! *called)
  61 + {
  62 + qpdf->warnings.push_back(
  63 + QPDFExc(
  64 + qpdf_e_internal,
  65 + qpdf->qpdf->getFilename(),
  66 + "", 0,
  67 + "C API object handle accessor errors occurred,"
  68 + " and the application did not define an error handler"));
  69 + *called = true;
  70 + }
  71 + }
  72 + std::cerr << e->exc->what() << std::endl;
  73 +}
  74 +
51 _qpdf_data::_qpdf_data() : 75 _qpdf_data::_qpdf_data() :
52 write_memory(false), 76 write_memory(false),
  77 + oh_error_handler(default_oh_error_handler),
  78 + oh_error_handler_data(&this->default_oh_error_handler_called),
  79 + default_oh_error_handler_called(false),
53 next_oh(0) 80 next_oh(0)
54 { 81 {
55 } 82 }
@@ -170,6 +197,13 @@ void qpdf_cleanup(qpdf_data* qpdf) @@ -170,6 +197,13 @@ void qpdf_cleanup(qpdf_data* qpdf)
170 { 197 {
171 QTC::TC("qpdf", "qpdf-c called qpdf_cleanup"); 198 QTC::TC("qpdf", "qpdf-c called qpdf_cleanup");
172 qpdf_oh_release_all(*qpdf); 199 qpdf_oh_release_all(*qpdf);
  200 + if ((*qpdf)->error.getPointer())
  201 + {
  202 + QTC::TC("qpdf", "qpdf-c cleanup warned about unhandled error");
  203 + std::cerr << "WARNING: application did not handle error: "
  204 + << (*qpdf)->error->what() << std::endl;
  205 +
  206 + }
173 delete *qpdf; 207 delete *qpdf;
174 *qpdf = 0; 208 *qpdf = 0;
175 } 209 }
@@ -841,6 +875,38 @@ QPDF_ERROR_CODE qpdf_write(qpdf_data qpdf) @@ -841,6 +875,38 @@ QPDF_ERROR_CODE qpdf_write(qpdf_data qpdf)
841 return status; 875 return status;
842 } 876 }
843 877
  878 +void qpdf_register_oh_error_handler(
  879 + qpdf_data qpdf,
  880 + void (*handle_error)(qpdf_data qpdf, qpdf_error error, void* data),
  881 + void* data)
  882 +{
  883 + QTC::TC("qpdf", "qpdf-c registered oh error handler");
  884 + qpdf->oh_error_handler = handle_error;
  885 + qpdf->oh_error_handler_data = data;
  886 +}
  887 +
  888 +template<class RET>
  889 +static RET trap_oh_errors(
  890 + qpdf_data qpdf,
  891 + std::function<RET()> fallback,
  892 + std::function<RET(qpdf_data)> fn)
  893 +{
  894 + // Note: fallback is a function so we don't have to evaluate it
  895 + // unless needed. This is important because sometimes the fallback
  896 + // creates an object.
  897 + RET ret;
  898 + QPDF_ERROR_CODE status = trap_errors(qpdf, [&ret, &fn] (qpdf_data q) {
  899 + ret = fn(q);
  900 + });
  901 + if (status & QPDF_ERRORS)
  902 + {
  903 + (*qpdf->oh_error_handler)(
  904 + qpdf, qpdf_get_error(qpdf), qpdf->oh_error_handler_data);
  905 + return fallback();
  906 + }
  907 + return ret;
  908 +}
  909 +
844 static qpdf_oh 910 static qpdf_oh
845 new_object(qpdf_data qpdf, QPDFObjectHandle const& qoh) 911 new_object(qpdf_data qpdf, QPDFObjectHandle const& qoh)
846 { 912 {
@@ -867,310 +933,367 @@ void qpdf_oh_release_all(qpdf_data qpdf) @@ -867,310 +933,367 @@ void qpdf_oh_release_all(qpdf_data qpdf)
867 qpdf->oh_cache.clear(); 933 qpdf->oh_cache.clear();
868 } 934 }
869 935
  936 +template <class T>
  937 +static std::function<T()> return_T(T const& r)
  938 +{
  939 + return [&r]() { return r; };
  940 +}
  941 +
  942 +static QPDF_BOOL return_false()
  943 +{
  944 + return QPDF_FALSE;
  945 +}
  946 +
  947 +static std::function<qpdf_oh()> return_uninitialized(qpdf_data qpdf)
  948 +{
  949 + return [qpdf]() { return qpdf_oh_new_uninitialized(qpdf); };
  950 +}
  951 +
  952 +static std::function<qpdf_oh()> return_null(qpdf_data qpdf)
  953 +{
  954 + return [qpdf]() { return qpdf_oh_new_null(qpdf); };
  955 +}
  956 +
870 qpdf_oh qpdf_get_trailer(qpdf_data qpdf) 957 qpdf_oh qpdf_get_trailer(qpdf_data qpdf)
871 { 958 {
872 QTC::TC("qpdf", "qpdf-c called qpdf_get_trailer"); 959 QTC::TC("qpdf", "qpdf-c called qpdf_get_trailer");
873 - return new_object(qpdf, qpdf->qpdf->getTrailer()); 960 + return trap_oh_errors<qpdf_oh>(
  961 + qpdf, return_uninitialized(qpdf), [] (qpdf_data q) {
  962 + return new_object(q, q->qpdf->getTrailer());
  963 + });
874 } 964 }
875 965
876 qpdf_oh qpdf_get_root(qpdf_data qpdf) 966 qpdf_oh qpdf_get_root(qpdf_data qpdf)
877 { 967 {
878 QTC::TC("qpdf", "qpdf-c called qpdf_get_root"); 968 QTC::TC("qpdf", "qpdf-c called qpdf_get_root");
879 - return new_object(qpdf, qpdf->qpdf->getRoot());  
880 -}  
881 -  
882 -static bool  
883 -qpdf_oh_valid_internal(qpdf_data qpdf, qpdf_oh oh)  
884 -{  
885 - auto i = qpdf->oh_cache.find(oh);  
886 - bool result = ((i != qpdf->oh_cache.end()) &&  
887 - (i->second).getPointer());  
888 - if (! result)  
889 - {  
890 - QTC::TC("qpdf", "qpdf-c invalid object handle");  
891 - qpdf->warnings.push_back(  
892 - QPDFExc(  
893 - qpdf_e_damaged_pdf,  
894 - qpdf->qpdf->getFilename(),  
895 - std::string("C API object handle ") +  
896 - QUtil::uint_to_string(oh),  
897 - 0, "attempted access to unknown object handle"));  
898 - }  
899 - return result; 969 + return trap_oh_errors<qpdf_oh>(
  970 + qpdf, return_uninitialized(qpdf), [] (qpdf_data q) {
  971 + return new_object(q, q->qpdf->getRoot());
  972 + });
  973 +}
  974 +
  975 +template<class RET>
  976 +static RET do_with_oh(
  977 + qpdf_data qpdf, qpdf_oh oh,
  978 + std::function<RET()> fallback,
  979 + std::function<RET(QPDFObjectHandle&)> fn)
  980 +{
  981 + return trap_oh_errors<RET>(
  982 + qpdf, fallback, [&fn, &oh](qpdf_data q) {
  983 + auto i = q->oh_cache.find(oh);
  984 + bool result = ((i != q->oh_cache.end()) &&
  985 + (i->second).getPointer());
  986 + if (! result)
  987 + {
  988 + QTC::TC("qpdf", "qpdf-c invalid object handle");
  989 + throw QPDFExc(
  990 + qpdf_e_internal,
  991 + q->qpdf->getFilename(),
  992 + std::string("C API object handle ") +
  993 + QUtil::uint_to_string(oh),
  994 + 0, "attempted access to unknown object handle");
  995 + }
  996 + return fn(*(q->oh_cache[oh]));
  997 + });
  998 +}
  999 +
  1000 +static void do_with_oh_void(
  1001 + qpdf_data qpdf, qpdf_oh oh,
  1002 + std::function<void(QPDFObjectHandle&)> fn)
  1003 +{
  1004 + do_with_oh<bool>(
  1005 + qpdf, oh, return_T<bool>(false), [&fn](QPDFObjectHandle& o) {
  1006 + fn(o);
  1007 + return true; // unused
  1008 + });
900 } 1009 }
901 1010
902 QPDF_BOOL qpdf_oh_is_initialized(qpdf_data qpdf, qpdf_oh oh) 1011 QPDF_BOOL qpdf_oh_is_initialized(qpdf_data qpdf, qpdf_oh oh)
903 { 1012 {
904 QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_initialized"); 1013 QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_initialized");
905 - return (qpdf_oh_valid_internal(qpdf, oh) &&  
906 - qpdf->oh_cache[oh]->isInitialized()); 1014 + return do_with_oh<QPDF_BOOL>(
  1015 + qpdf, oh, return_false, [](QPDFObjectHandle& o) {
  1016 + return o.isInitialized();
  1017 + });
907 } 1018 }
908 1019
909 QPDF_BOOL qpdf_oh_is_bool(qpdf_data qpdf, qpdf_oh oh) 1020 QPDF_BOOL qpdf_oh_is_bool(qpdf_data qpdf, qpdf_oh oh)
910 { 1021 {
911 QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_bool"); 1022 QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_bool");
912 - return (qpdf_oh_valid_internal(qpdf, oh) &&  
913 - qpdf->oh_cache[oh]->isBool()); 1023 + return do_with_oh<QPDF_BOOL>(
  1024 + qpdf, oh, return_false, [](QPDFObjectHandle& o) {
  1025 + return o.isBool();
  1026 + });
914 } 1027 }
915 1028
916 QPDF_BOOL qpdf_oh_is_null(qpdf_data qpdf, qpdf_oh oh) 1029 QPDF_BOOL qpdf_oh_is_null(qpdf_data qpdf, qpdf_oh oh)
917 { 1030 {
918 QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_null"); 1031 QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_null");
919 - return (qpdf_oh_valid_internal(qpdf, oh) &&  
920 - qpdf->oh_cache[oh]->isNull()); 1032 + return do_with_oh<QPDF_BOOL>(
  1033 + qpdf, oh, return_false, [](QPDFObjectHandle& o) {
  1034 + return o.isNull();
  1035 + });
921 } 1036 }
922 1037
923 QPDF_BOOL qpdf_oh_is_integer(qpdf_data qpdf, qpdf_oh oh) 1038 QPDF_BOOL qpdf_oh_is_integer(qpdf_data qpdf, qpdf_oh oh)
924 { 1039 {
925 QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_integer"); 1040 QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_integer");
926 - return (qpdf_oh_valid_internal(qpdf, oh) &&  
927 - qpdf->oh_cache[oh]->isInteger()); 1041 + return do_with_oh<QPDF_BOOL>(
  1042 + qpdf, oh, return_false, [](QPDFObjectHandle& o) {
  1043 + return o.isInteger();
  1044 + });
928 } 1045 }
929 1046
930 QPDF_BOOL qpdf_oh_is_real(qpdf_data qpdf, qpdf_oh oh) 1047 QPDF_BOOL qpdf_oh_is_real(qpdf_data qpdf, qpdf_oh oh)
931 { 1048 {
932 QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_real"); 1049 QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_real");
933 - return (qpdf_oh_valid_internal(qpdf, oh) &&  
934 - qpdf->oh_cache[oh]->isReal()); 1050 + return do_with_oh<QPDF_BOOL>(
  1051 + qpdf, oh, return_false, [](QPDFObjectHandle& o) {
  1052 + return o.isReal();
  1053 + });
935 } 1054 }
936 1055
937 QPDF_BOOL qpdf_oh_is_name(qpdf_data qpdf, qpdf_oh oh) 1056 QPDF_BOOL qpdf_oh_is_name(qpdf_data qpdf, qpdf_oh oh)
938 { 1057 {
939 QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_name"); 1058 QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_name");
940 - return (qpdf_oh_valid_internal(qpdf, oh) &&  
941 - qpdf->oh_cache[oh]->isName()); 1059 + return do_with_oh<QPDF_BOOL>(
  1060 + qpdf, oh, return_false, [](QPDFObjectHandle& o) {
  1061 + return o.isName();
  1062 + });
942 } 1063 }
943 1064
944 QPDF_BOOL qpdf_oh_is_string(qpdf_data qpdf, qpdf_oh oh) 1065 QPDF_BOOL qpdf_oh_is_string(qpdf_data qpdf, qpdf_oh oh)
945 { 1066 {
946 QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_string"); 1067 QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_string");
947 - return (qpdf_oh_valid_internal(qpdf, oh) &&  
948 - qpdf->oh_cache[oh]->isString()); 1068 + return do_with_oh<QPDF_BOOL>(
  1069 + qpdf, oh, return_false, [](QPDFObjectHandle& o) {
  1070 + return o.isString();
  1071 + });
949 } 1072 }
950 1073
951 QPDF_BOOL qpdf_oh_is_operator(qpdf_data qpdf, qpdf_oh oh) 1074 QPDF_BOOL qpdf_oh_is_operator(qpdf_data qpdf, qpdf_oh oh)
952 { 1075 {
953 QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_operator"); 1076 QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_operator");
954 - return (qpdf_oh_valid_internal(qpdf, oh) &&  
955 - qpdf->oh_cache[oh]->isOperator()); 1077 + return do_with_oh<QPDF_BOOL>(
  1078 + qpdf, oh, return_false, [](QPDFObjectHandle& o) {
  1079 + return o.isOperator();
  1080 + });
956 } 1081 }
957 1082
958 QPDF_BOOL qpdf_oh_is_inline_image(qpdf_data qpdf, qpdf_oh oh) 1083 QPDF_BOOL qpdf_oh_is_inline_image(qpdf_data qpdf, qpdf_oh oh)
959 { 1084 {
960 QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_inline_image"); 1085 QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_inline_image");
961 - return (qpdf_oh_valid_internal(qpdf, oh) &&  
962 - qpdf->oh_cache[oh]->isInlineImage()); 1086 + return do_with_oh<QPDF_BOOL>(
  1087 + qpdf, oh, return_false, [](QPDFObjectHandle& o) {
  1088 + return o.isInlineImage();
  1089 + });
963 } 1090 }
964 1091
965 QPDF_BOOL qpdf_oh_is_array(qpdf_data qpdf, qpdf_oh oh) 1092 QPDF_BOOL qpdf_oh_is_array(qpdf_data qpdf, qpdf_oh oh)
966 { 1093 {
967 QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_array"); 1094 QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_array");
968 - return (qpdf_oh_valid_internal(qpdf, oh) &&  
969 - qpdf->oh_cache[oh]->isArray()); 1095 + return do_with_oh<QPDF_BOOL>(
  1096 + qpdf, oh, return_false, [](QPDFObjectHandle& o) {
  1097 + return o.isArray();
  1098 + });
970 } 1099 }
971 1100
972 QPDF_BOOL qpdf_oh_is_dictionary(qpdf_data qpdf, qpdf_oh oh) 1101 QPDF_BOOL qpdf_oh_is_dictionary(qpdf_data qpdf, qpdf_oh oh)
973 { 1102 {
974 QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_dictionary"); 1103 QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_dictionary");
975 - return (qpdf_oh_valid_internal(qpdf, oh) &&  
976 - qpdf->oh_cache[oh]->isDictionary()); 1104 + return do_with_oh<QPDF_BOOL>(
  1105 + qpdf, oh, return_false, [](QPDFObjectHandle& o) {
  1106 + return o.isDictionary();
  1107 + });
977 } 1108 }
978 1109
979 QPDF_BOOL qpdf_oh_is_stream(qpdf_data qpdf, qpdf_oh oh) 1110 QPDF_BOOL qpdf_oh_is_stream(qpdf_data qpdf, qpdf_oh oh)
980 { 1111 {
981 QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_stream"); 1112 QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_stream");
982 - return (qpdf_oh_valid_internal(qpdf, oh) &&  
983 - qpdf->oh_cache[oh]->isStream()); 1113 + return do_with_oh<QPDF_BOOL>(
  1114 + qpdf, oh, return_false, [](QPDFObjectHandle& o) {
  1115 + return o.isStream();
  1116 + });
984 } 1117 }
985 1118
986 QPDF_BOOL qpdf_oh_is_indirect(qpdf_data qpdf, qpdf_oh oh) 1119 QPDF_BOOL qpdf_oh_is_indirect(qpdf_data qpdf, qpdf_oh oh)
987 { 1120 {
988 QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_indirect"); 1121 QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_indirect");
989 - return (qpdf_oh_valid_internal(qpdf, oh) &&  
990 - qpdf->oh_cache[oh]->isIndirect()); 1122 + return do_with_oh<QPDF_BOOL>(
  1123 + qpdf, oh, return_false, [](QPDFObjectHandle& o) {
  1124 + return o.isIndirect();
  1125 + });
991 } 1126 }
992 1127
993 QPDF_BOOL qpdf_oh_is_scalar(qpdf_data qpdf, qpdf_oh oh) 1128 QPDF_BOOL qpdf_oh_is_scalar(qpdf_data qpdf, qpdf_oh oh)
994 { 1129 {
995 QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_scalar"); 1130 QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_scalar");
996 - return (qpdf_oh_valid_internal(qpdf, oh) &&  
997 - qpdf->oh_cache[oh]->isScalar()); 1131 + return do_with_oh<QPDF_BOOL>(
  1132 + qpdf, oh, return_false, [](QPDFObjectHandle& o) {
  1133 + return o.isScalar();
  1134 + });
  1135 +}
  1136 +
  1137 +QPDF_BOOL qpdf_oh_is_number(qpdf_data qpdf, qpdf_oh oh)
  1138 +{
  1139 + return do_with_oh<QPDF_BOOL>(
  1140 + qpdf, oh, return_false, [](QPDFObjectHandle& o) {
  1141 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_number");
  1142 + return o.isNumber();
  1143 + });
998 } 1144 }
999 1145
1000 qpdf_oh qpdf_oh_wrap_in_array(qpdf_data qpdf, qpdf_oh oh) 1146 qpdf_oh qpdf_oh_wrap_in_array(qpdf_data qpdf, qpdf_oh oh)
1001 { 1147 {
1002 - if (! qpdf_oh_valid_internal(qpdf, oh))  
1003 - {  
1004 - return qpdf_oh_new_array(qpdf);  
1005 - }  
1006 - auto qoh = qpdf->oh_cache[oh];  
1007 - if (qoh->isArray())  
1008 - {  
1009 - QTC::TC("qpdf", "qpdf-c array to wrap_in_array");  
1010 - return new_object(qpdf, *qoh);  
1011 - }  
1012 - else  
1013 - {  
1014 - QTC::TC("qpdf", "qpdf-c non-array to wrap_in_array");  
1015 - return new_object(qpdf,  
1016 - QPDFObjectHandle::newArray(  
1017 - std::vector<QPDFObjectHandle>{  
1018 - *qpdf->oh_cache[oh]}));  
1019 - } 1148 + return do_with_oh<qpdf_oh>(
  1149 + qpdf, oh,
  1150 + [&qpdf](){ return qpdf_oh_new_array(qpdf); },
  1151 + [&qpdf](QPDFObjectHandle& qoh) {
  1152 + if (qoh.isArray())
  1153 + {
  1154 + QTC::TC("qpdf", "qpdf-c array to wrap_in_array");
  1155 + return new_object(qpdf, qoh);
  1156 + }
  1157 + else
  1158 + {
  1159 + QTC::TC("qpdf", "qpdf-c non-array to wrap_in_array");
  1160 + return new_object(qpdf,
  1161 + QPDFObjectHandle::newArray(
  1162 + std::vector<QPDFObjectHandle>{qoh}));
  1163 + }
  1164 + });
1020 } 1165 }
1021 1166
1022 qpdf_oh qpdf_oh_parse(qpdf_data qpdf, char const* object_str) 1167 qpdf_oh qpdf_oh_parse(qpdf_data qpdf, char const* object_str)
1023 { 1168 {
1024 QTC::TC("qpdf", "qpdf-c called qpdf_oh_parse"); 1169 QTC::TC("qpdf", "qpdf-c called qpdf_oh_parse");
1025 - return new_object(qpdf, QPDFObjectHandle::parse(object_str)); 1170 + return trap_oh_errors<qpdf_oh>(
  1171 + qpdf, return_uninitialized(qpdf), [&object_str] (qpdf_data q) {
  1172 + return new_object(q, QPDFObjectHandle::parse(object_str));
  1173 + });
1026 } 1174 }
1027 1175
1028 QPDF_BOOL qpdf_oh_get_bool_value(qpdf_data qpdf, qpdf_oh oh) 1176 QPDF_BOOL qpdf_oh_get_bool_value(qpdf_data qpdf, qpdf_oh oh)
1029 { 1177 {
1030 - if (! qpdf_oh_valid_internal(qpdf, oh))  
1031 - {  
1032 - return QPDF_FALSE;  
1033 - }  
1034 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_bool_value");  
1035 - return qpdf->oh_cache[oh]->getBoolValue(); 1178 + return do_with_oh<QPDF_BOOL>(
  1179 + qpdf, oh, return_false, [](QPDFObjectHandle& o) {
  1180 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_bool_value");
  1181 + return o.getBoolValue();
  1182 + });
1036 } 1183 }
1037 1184
1038 long long qpdf_oh_get_int_value(qpdf_data qpdf, qpdf_oh oh) 1185 long long qpdf_oh_get_int_value(qpdf_data qpdf, qpdf_oh oh)
1039 { 1186 {
1040 - if (! qpdf_oh_valid_internal(qpdf, oh))  
1041 - {  
1042 - return 0LL;  
1043 - }  
1044 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_int_value");  
1045 - return qpdf->oh_cache[oh]->getIntValue(); 1187 + return do_with_oh<long long>(
  1188 + qpdf, oh, return_T<long long>(0LL), [](QPDFObjectHandle& o) {
  1189 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_int_value");
  1190 + return o.getIntValue();
  1191 + });
1046 } 1192 }
1047 1193
1048 int qpdf_oh_get_int_value_as_int(qpdf_data qpdf, qpdf_oh oh) 1194 int qpdf_oh_get_int_value_as_int(qpdf_data qpdf, qpdf_oh oh)
1049 { 1195 {
1050 - if (! qpdf_oh_valid_internal(qpdf, oh))  
1051 - {  
1052 - return 0;  
1053 - }  
1054 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_int_value_as_int");  
1055 - return qpdf->oh_cache[oh]->getIntValueAsInt(); 1196 + return do_with_oh<int>(
  1197 + qpdf, oh, return_T<int>(0), [](QPDFObjectHandle& o) {
  1198 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_int_value_as_int");
  1199 + return o.getIntValueAsInt();
  1200 + });
1056 } 1201 }
1057 1202
1058 unsigned long long qpdf_oh_get_uint_value(qpdf_data qpdf, qpdf_oh oh) 1203 unsigned long long qpdf_oh_get_uint_value(qpdf_data qpdf, qpdf_oh oh)
1059 { 1204 {
1060 - if (! qpdf_oh_valid_internal(qpdf, oh))  
1061 - {  
1062 - return 0ULL;  
1063 - }  
1064 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_uint_value");  
1065 - return qpdf->oh_cache[oh]->getUIntValue(); 1205 + return do_with_oh<unsigned long long>(
  1206 + qpdf, oh, return_T<unsigned long long>(0ULL), [](QPDFObjectHandle& o) {
  1207 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_uint_value");
  1208 + return o.getUIntValue();
  1209 + });
1066 } 1210 }
1067 1211
1068 unsigned int qpdf_oh_get_uint_value_as_uint(qpdf_data qpdf, qpdf_oh oh) 1212 unsigned int qpdf_oh_get_uint_value_as_uint(qpdf_data qpdf, qpdf_oh oh)
1069 { 1213 {
1070 - if (! qpdf_oh_valid_internal(qpdf, oh))  
1071 - {  
1072 - return 0U;  
1073 - }  
1074 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_uint_value_as_uint");  
1075 - return qpdf->oh_cache[oh]->getUIntValueAsUInt(); 1214 + return do_with_oh<unsigned int>(
  1215 + qpdf, oh, return_T<unsigned int>(0U), [](QPDFObjectHandle& o) {
  1216 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_uint_value_as_uint");
  1217 + return o.getUIntValueAsUInt();
  1218 + });
1076 } 1219 }
1077 1220
1078 char const* qpdf_oh_get_real_value(qpdf_data qpdf, qpdf_oh oh) 1221 char const* qpdf_oh_get_real_value(qpdf_data qpdf, qpdf_oh oh)
1079 { 1222 {
1080 - if (! qpdf_oh_valid_internal(qpdf, oh))  
1081 - {  
1082 - return "";  
1083 - }  
1084 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_real_value");  
1085 - qpdf->tmp_string = qpdf->oh_cache[oh]->getRealValue();  
1086 - return qpdf->tmp_string.c_str();  
1087 -}  
1088 -  
1089 -QPDF_BOOL qpdf_oh_is_number(qpdf_data qpdf, qpdf_oh oh)  
1090 -{  
1091 - if (! qpdf_oh_valid_internal(qpdf, oh))  
1092 - {  
1093 - return QPDF_FALSE;  
1094 - }  
1095 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_number");  
1096 - return qpdf->oh_cache[oh]->isNumber(); 1223 + return do_with_oh<char const*>(
  1224 + qpdf, oh, return_T<char const*>(""), [&qpdf](QPDFObjectHandle& o) {
  1225 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_real_value");
  1226 + qpdf->tmp_string = o.getRealValue();
  1227 + return qpdf->tmp_string.c_str();
  1228 + });
1097 } 1229 }
1098 1230
1099 double qpdf_oh_get_numeric_value(qpdf_data qpdf, qpdf_oh oh) 1231 double qpdf_oh_get_numeric_value(qpdf_data qpdf, qpdf_oh oh)
1100 { 1232 {
1101 - if (! qpdf_oh_valid_internal(qpdf, oh))  
1102 - {  
1103 - return 0.0;  
1104 - }  
1105 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_numeric_value");  
1106 - return qpdf->oh_cache[oh]->getNumericValue(); 1233 + return do_with_oh<double>(
  1234 + qpdf, oh, return_T<double>(0.0), [](QPDFObjectHandle& o) {
  1235 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_numeric_value");
  1236 + return o.getNumericValue();
  1237 + });
1107 } 1238 }
1108 1239
1109 char const* qpdf_oh_get_name(qpdf_data qpdf, qpdf_oh oh) 1240 char const* qpdf_oh_get_name(qpdf_data qpdf, qpdf_oh oh)
1110 { 1241 {
1111 - if (! qpdf_oh_valid_internal(qpdf, oh))  
1112 - {  
1113 - return "";  
1114 - }  
1115 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_name");  
1116 - qpdf->tmp_string = qpdf->oh_cache[oh]->getName();  
1117 - return qpdf->tmp_string.c_str(); 1242 + return do_with_oh<char const*>(
  1243 + qpdf, oh, return_T<char const*>(""), [&qpdf](QPDFObjectHandle& o) {
  1244 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_name");
  1245 + qpdf->tmp_string = o.getName();
  1246 + return qpdf->tmp_string.c_str();
  1247 + });
1118 } 1248 }
1119 1249
1120 char const* qpdf_oh_get_string_value(qpdf_data qpdf, qpdf_oh oh) 1250 char const* qpdf_oh_get_string_value(qpdf_data qpdf, qpdf_oh oh)
1121 { 1251 {
1122 - if (! qpdf_oh_valid_internal(qpdf, oh))  
1123 - {  
1124 - return "";  
1125 - }  
1126 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_string_value");  
1127 - qpdf->tmp_string = qpdf->oh_cache[oh]->getStringValue();  
1128 - return qpdf->tmp_string.c_str(); 1252 + return do_with_oh<char const*>(
  1253 + qpdf, oh, return_T<char const*>(""), [&qpdf](QPDFObjectHandle& o) {
  1254 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_string_value");
  1255 + qpdf->tmp_string = o.getStringValue();
  1256 + return qpdf->tmp_string.c_str();
  1257 + });
1129 } 1258 }
1130 1259
1131 char const* qpdf_oh_get_utf8_value(qpdf_data qpdf, qpdf_oh oh) 1260 char const* qpdf_oh_get_utf8_value(qpdf_data qpdf, qpdf_oh oh)
1132 { 1261 {
1133 - if (! qpdf_oh_valid_internal(qpdf, oh))  
1134 - {  
1135 - return "";  
1136 - }  
1137 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_utf8_value");  
1138 - qpdf->tmp_string = qpdf->oh_cache[oh]->getUTF8Value();  
1139 - return qpdf->tmp_string.c_str(); 1262 + return do_with_oh<char const*>(
  1263 + qpdf, oh, return_T<char const*>(""), [&qpdf](QPDFObjectHandle& o) {
  1264 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_utf8_value");
  1265 + qpdf->tmp_string = o.getUTF8Value();
  1266 + return qpdf->tmp_string.c_str();
  1267 + });
1140 } 1268 }
1141 1269
1142 int qpdf_oh_get_array_n_items(qpdf_data qpdf, qpdf_oh oh) 1270 int qpdf_oh_get_array_n_items(qpdf_data qpdf, qpdf_oh oh)
1143 { 1271 {
1144 - if (! qpdf_oh_valid_internal(qpdf, oh))  
1145 - {  
1146 - return 0;  
1147 - }  
1148 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_array_n_items");  
1149 - return qpdf->oh_cache[oh]->getArrayNItems(); 1272 + return do_with_oh<int>(
  1273 + qpdf, oh, return_T<int>(0), [](QPDFObjectHandle& o) {
  1274 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_array_n_items");
  1275 + return o.getArrayNItems();
  1276 + });
1150 } 1277 }
1151 1278
1152 qpdf_oh qpdf_oh_get_array_item(qpdf_data qpdf, qpdf_oh oh, int n) 1279 qpdf_oh qpdf_oh_get_array_item(qpdf_data qpdf, qpdf_oh oh, int n)
1153 { 1280 {
1154 - if (! qpdf_oh_valid_internal(qpdf, oh))  
1155 - {  
1156 - return qpdf_oh_new_null(qpdf);  
1157 - }  
1158 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_array_item");  
1159 - return new_object(qpdf, qpdf->oh_cache[oh]->getArrayItem(n)); 1281 + return do_with_oh<qpdf_oh>(
  1282 + qpdf, oh, return_null(qpdf), [&qpdf, &n](QPDFObjectHandle& o) {
  1283 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_array_item");
  1284 + return new_object(qpdf, o.getArrayItem(n));
  1285 + });
1160 } 1286 }
1161 1287
1162 void qpdf_oh_begin_dict_key_iter(qpdf_data qpdf, qpdf_oh oh) 1288 void qpdf_oh_begin_dict_key_iter(qpdf_data qpdf, qpdf_oh oh)
1163 { 1289 {
1164 - if (qpdf_oh_valid_internal(qpdf, oh) &&  
1165 - qpdf_oh_is_dictionary(qpdf, oh))  
1166 - {  
1167 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_begin_dict_key_iter");  
1168 - qpdf->cur_iter_dict_keys = qpdf->oh_cache[oh]->getKeys();  
1169 - }  
1170 - else  
1171 - {  
1172 - qpdf->cur_iter_dict_keys = {};  
1173 - } 1290 + qpdf->cur_iter_dict_keys = do_with_oh<std::set<std::string>>(
  1291 + qpdf, oh,
  1292 + [](){ return std::set<std::string>(); },
  1293 + [](QPDFObjectHandle& o) {
  1294 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_begin_dict_key_iter");
  1295 + return o.getKeys();
  1296 + });
1174 qpdf->dict_iter = qpdf->cur_iter_dict_keys.begin(); 1297 qpdf->dict_iter = qpdf->cur_iter_dict_keys.begin();
1175 } 1298 }
1176 1299
@@ -1197,32 +1320,35 @@ char const* qpdf_oh_dict_next_key(qpdf_data qpdf) @@ -1197,32 +1320,35 @@ char const* qpdf_oh_dict_next_key(qpdf_data qpdf)
1197 1320
1198 QPDF_BOOL qpdf_oh_has_key(qpdf_data qpdf, qpdf_oh oh, char const* key) 1321 QPDF_BOOL qpdf_oh_has_key(qpdf_data qpdf, qpdf_oh oh, char const* key)
1199 { 1322 {
1200 - if (! qpdf_oh_valid_internal(qpdf, oh))  
1201 - {  
1202 - return QPDF_FALSE;  
1203 - }  
1204 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_has_key");  
1205 - return qpdf->oh_cache[oh]->hasKey(key); 1323 + return do_with_oh<QPDF_BOOL>(
  1324 + qpdf, oh, return_false, [&key](QPDFObjectHandle& o) {
  1325 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_has_key");
  1326 + return o.hasKey(key);
  1327 + });
1206 } 1328 }
1207 1329
1208 qpdf_oh qpdf_oh_get_key(qpdf_data qpdf, qpdf_oh oh, char const* key) 1330 qpdf_oh qpdf_oh_get_key(qpdf_data qpdf, qpdf_oh oh, char const* key)
1209 { 1331 {
1210 - if (! qpdf_oh_valid_internal(qpdf, oh))  
1211 - {  
1212 - return qpdf_oh_new_null(qpdf);  
1213 - }  
1214 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_key");  
1215 - return new_object(qpdf, qpdf->oh_cache[oh]->getKey(key)); 1332 + return do_with_oh<qpdf_oh>(
  1333 + qpdf, oh, return_null(qpdf), [&qpdf, &key](QPDFObjectHandle& o) {
  1334 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_key");
  1335 + return new_object(qpdf, o.getKey(key));
  1336 + });
1216 } 1337 }
1217 1338
1218 QPDF_BOOL qpdf_oh_is_or_has_name(qpdf_data qpdf, qpdf_oh oh, char const* key) 1339 QPDF_BOOL qpdf_oh_is_or_has_name(qpdf_data qpdf, qpdf_oh oh, char const* key)
1219 { 1340 {
1220 - if (! qpdf_oh_valid_internal(qpdf, oh))  
1221 - {  
1222 - return QPDF_FALSE;  
1223 - }  
1224 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_or_has_name");  
1225 - return qpdf->oh_cache[oh]->isOrHasName(key); 1341 + return do_with_oh<QPDF_BOOL>(
  1342 + qpdf, oh, return_false, [&key](QPDFObjectHandle& o) {
  1343 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_is_or_has_name");
  1344 + return o.isOrHasName(key);
  1345 + });
  1346 +}
  1347 +
  1348 +qpdf_oh qpdf_oh_new_uninitialized(qpdf_data qpdf)
  1349 +{
  1350 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_new_uninitialized");
  1351 + return new_object(qpdf, QPDFObjectHandle());
1226 } 1352 }
1227 1353
1228 qpdf_oh qpdf_oh_new_null(qpdf_data qpdf) 1354 qpdf_oh qpdf_oh_new_null(qpdf_data qpdf)
@@ -1288,156 +1414,143 @@ qpdf_oh qpdf_oh_new_dictionary(qpdf_data qpdf) @@ -1288,156 +1414,143 @@ qpdf_oh qpdf_oh_new_dictionary(qpdf_data qpdf)
1288 1414
1289 void qpdf_oh_make_direct(qpdf_data qpdf, qpdf_oh oh) 1415 void qpdf_oh_make_direct(qpdf_data qpdf, qpdf_oh oh)
1290 { 1416 {
1291 - if (qpdf_oh_valid_internal(qpdf, oh))  
1292 - {  
1293 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_make_direct");  
1294 - qpdf->oh_cache[oh]->makeDirect();  
1295 - } 1417 + do_with_oh_void(
  1418 + qpdf, oh, [](QPDFObjectHandle& o) {
  1419 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_make_direct");
  1420 + o.makeDirect();
  1421 + });
1296 } 1422 }
1297 1423
1298 static QPDFObjectHandle 1424 static QPDFObjectHandle
1299 qpdf_oh_item_internal(qpdf_data qpdf, qpdf_oh item) 1425 qpdf_oh_item_internal(qpdf_data qpdf, qpdf_oh item)
1300 { 1426 {
1301 - if (qpdf_oh_valid_internal(qpdf, item))  
1302 - {  
1303 - return *(qpdf->oh_cache[item]);  
1304 - }  
1305 - else  
1306 - {  
1307 - return QPDFObjectHandle::newNull();  
1308 - } 1427 + return do_with_oh<QPDFObjectHandle>(
  1428 + qpdf, item,
  1429 + [](){return QPDFObjectHandle::newNull();},
  1430 + [](QPDFObjectHandle& o) {
  1431 + return o;
  1432 + });
1309 } 1433 }
1310 1434
1311 void qpdf_oh_set_array_item(qpdf_data qpdf, qpdf_oh oh, 1435 void qpdf_oh_set_array_item(qpdf_data qpdf, qpdf_oh oh,
1312 int at, qpdf_oh item) 1436 int at, qpdf_oh item)
1313 { 1437 {
1314 - if (qpdf_oh_is_array(qpdf, oh))  
1315 - {  
1316 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_set_array_item");  
1317 - qpdf->oh_cache[oh]->setArrayItem(  
1318 - at, qpdf_oh_item_internal(qpdf, item));  
1319 - } 1438 + do_with_oh_void(
  1439 + qpdf, oh, [&qpdf, &at, &item](QPDFObjectHandle& o) {
  1440 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_set_array_item");
  1441 + o.setArrayItem(at, qpdf_oh_item_internal(qpdf, item));
  1442 + });
1320 } 1443 }
1321 1444
1322 void qpdf_oh_insert_item(qpdf_data qpdf, qpdf_oh oh, int at, qpdf_oh item) 1445 void qpdf_oh_insert_item(qpdf_data qpdf, qpdf_oh oh, int at, qpdf_oh item)
1323 { 1446 {
1324 - if (qpdf_oh_is_array(qpdf, oh))  
1325 - {  
1326 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_insert_item");  
1327 - qpdf->oh_cache[oh]->insertItem(  
1328 - at, qpdf_oh_item_internal(qpdf, item));  
1329 - } 1447 + do_with_oh_void(
  1448 + qpdf, oh, [&qpdf, &at, &item](QPDFObjectHandle& o) {
  1449 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_insert_item");
  1450 + o.insertItem(at, qpdf_oh_item_internal(qpdf, item));
  1451 + });
1330 } 1452 }
1331 1453
1332 void qpdf_oh_append_item(qpdf_data qpdf, qpdf_oh oh, qpdf_oh item) 1454 void qpdf_oh_append_item(qpdf_data qpdf, qpdf_oh oh, qpdf_oh item)
1333 { 1455 {
1334 - if (qpdf_oh_is_array(qpdf, oh))  
1335 - {  
1336 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_append_item");  
1337 - qpdf->oh_cache[oh]->appendItem(  
1338 - qpdf_oh_item_internal(qpdf, item));  
1339 - } 1456 + do_with_oh_void(
  1457 + qpdf, oh, [&qpdf, &item](QPDFObjectHandle& o) {
  1458 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_append_item");
  1459 + o.appendItem(qpdf_oh_item_internal(qpdf, item));
  1460 + });
1340 } 1461 }
1341 1462
1342 void qpdf_oh_erase_item(qpdf_data qpdf, qpdf_oh oh, int at) 1463 void qpdf_oh_erase_item(qpdf_data qpdf, qpdf_oh oh, int at)
1343 { 1464 {
1344 - if (qpdf_oh_is_array(qpdf, oh))  
1345 - {  
1346 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_erase_item");  
1347 - qpdf->oh_cache[oh]->eraseItem(at);  
1348 - } 1465 + do_with_oh_void(
  1466 + qpdf, oh, [&at](QPDFObjectHandle& o) {
  1467 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_erase_item");
  1468 + o.eraseItem(at);
  1469 + });
1349 } 1470 }
1350 1471
1351 void qpdf_oh_replace_key(qpdf_data qpdf, qpdf_oh oh, 1472 void qpdf_oh_replace_key(qpdf_data qpdf, qpdf_oh oh,
1352 char const* key, qpdf_oh item) 1473 char const* key, qpdf_oh item)
1353 { 1474 {
1354 - if (qpdf_oh_is_dictionary(qpdf, oh))  
1355 - {  
1356 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_replace_key");  
1357 - qpdf->oh_cache[oh]->replaceKey(  
1358 - key, qpdf_oh_item_internal(qpdf, item));  
1359 - } 1475 + do_with_oh_void(
  1476 + qpdf, oh, [&qpdf, &key, &item](QPDFObjectHandle& o) {
  1477 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_replace_key");
  1478 + o.replaceKey(key, qpdf_oh_item_internal(qpdf, item));
  1479 + });
1360 } 1480 }
1361 1481
1362 void qpdf_oh_remove_key(qpdf_data qpdf, qpdf_oh oh, char const* key) 1482 void qpdf_oh_remove_key(qpdf_data qpdf, qpdf_oh oh, char const* key)
1363 { 1483 {
1364 - if (qpdf_oh_is_dictionary(qpdf, oh))  
1365 - {  
1366 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_remove_key");  
1367 - qpdf->oh_cache[oh]->removeKey(key);  
1368 - } 1484 + do_with_oh_void(
  1485 + qpdf, oh, [&key](QPDFObjectHandle& o) {
  1486 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_remove_key");
  1487 + o.removeKey(key);
  1488 + });
1369 } 1489 }
1370 1490
1371 void qpdf_oh_replace_or_remove_key(qpdf_data qpdf, qpdf_oh oh, 1491 void qpdf_oh_replace_or_remove_key(qpdf_data qpdf, qpdf_oh oh,
1372 char const* key, qpdf_oh item) 1492 char const* key, qpdf_oh item)
1373 { 1493 {
1374 - if (qpdf_oh_is_dictionary(qpdf, oh))  
1375 - {  
1376 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_replace_or_remove_key");  
1377 - qpdf->oh_cache[oh]->replaceOrRemoveKey(  
1378 - key, qpdf_oh_item_internal(qpdf, item));  
1379 - } 1494 + do_with_oh_void(
  1495 + qpdf, oh, [&qpdf, &key, &item](QPDFObjectHandle& o) {
  1496 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_replace_or_remove_key");
  1497 + o.replaceOrRemoveKey(key, qpdf_oh_item_internal(qpdf, item));
  1498 + });
1380 } 1499 }
1381 1500
1382 qpdf_oh qpdf_oh_get_dict(qpdf_data qpdf, qpdf_oh oh) 1501 qpdf_oh qpdf_oh_get_dict(qpdf_data qpdf, qpdf_oh oh)
1383 { 1502 {
1384 - if (! qpdf_oh_valid_internal(qpdf, oh))  
1385 - {  
1386 - return qpdf_oh_new_null(qpdf);  
1387 - }  
1388 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_dict");  
1389 - return new_object(qpdf, qpdf->oh_cache[oh]->getDict()); 1503 + return do_with_oh<qpdf_oh>(
  1504 + qpdf, oh, return_null(qpdf), [&qpdf](QPDFObjectHandle& o) {
  1505 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_dict");
  1506 + return new_object(qpdf, o.getDict());
  1507 + });
1390 } 1508 }
1391 1509
1392 int qpdf_oh_get_object_id(qpdf_data qpdf, qpdf_oh oh) 1510 int qpdf_oh_get_object_id(qpdf_data qpdf, qpdf_oh oh)
1393 { 1511 {
1394 - if (! qpdf_oh_valid_internal(qpdf, oh))  
1395 - {  
1396 - return 0;  
1397 - }  
1398 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_object_id");  
1399 - return qpdf->oh_cache[oh]->getObjectID(); 1512 + return do_with_oh<int>(
  1513 + qpdf, oh, return_T<int>(0), [](QPDFObjectHandle& o) {
  1514 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_object_id");
  1515 + return o.getObjectID();
  1516 + });
1400 } 1517 }
1401 1518
1402 int qpdf_oh_get_generation(qpdf_data qpdf, qpdf_oh oh) 1519 int qpdf_oh_get_generation(qpdf_data qpdf, qpdf_oh oh)
1403 { 1520 {
1404 - if (! qpdf_oh_valid_internal(qpdf, oh))  
1405 - {  
1406 - return 0;  
1407 - }  
1408 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_generation");  
1409 - return qpdf->oh_cache[oh]->getGeneration(); 1521 + return do_with_oh<int>(
  1522 + qpdf, oh, return_T<int>(0), [](QPDFObjectHandle& o) {
  1523 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_get_generation");
  1524 + return o.getGeneration();
  1525 + });
1410 } 1526 }
1411 1527
1412 char const* qpdf_oh_unparse(qpdf_data qpdf, qpdf_oh oh) 1528 char const* qpdf_oh_unparse(qpdf_data qpdf, qpdf_oh oh)
1413 { 1529 {
1414 - if (! qpdf_oh_valid_internal(qpdf, oh))  
1415 - {  
1416 - return "";  
1417 - }  
1418 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_unparse");  
1419 - qpdf->tmp_string = qpdf->oh_cache[oh]->unparse();  
1420 - return qpdf->tmp_string.c_str(); 1530 + return do_with_oh<char const*>(
  1531 + qpdf, oh, return_T<char const*>(""), [&qpdf](QPDFObjectHandle& o) {
  1532 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_unparse");
  1533 + qpdf->tmp_string = o.unparse();
  1534 + return qpdf->tmp_string.c_str();
  1535 + });
1421 } 1536 }
1422 1537
1423 char const* qpdf_oh_unparse_resolved(qpdf_data qpdf, qpdf_oh oh) 1538 char const* qpdf_oh_unparse_resolved(qpdf_data qpdf, qpdf_oh oh)
1424 { 1539 {
1425 - if (! qpdf_oh_valid_internal(qpdf, oh))  
1426 - {  
1427 - return "";  
1428 - }  
1429 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_unparse_resolved");  
1430 - qpdf->tmp_string = qpdf->oh_cache[oh]->unparseResolved();  
1431 - return qpdf->tmp_string.c_str(); 1540 + return do_with_oh<char const*>(
  1541 + qpdf, oh, return_T<char const*>(""), [&qpdf](QPDFObjectHandle& o) {
  1542 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_unparse_resolved");
  1543 + qpdf->tmp_string = o.unparseResolved();
  1544 + return qpdf->tmp_string.c_str();
  1545 + });
1432 } 1546 }
1433 1547
1434 char const* qpdf_oh_unparse_binary(qpdf_data qpdf, qpdf_oh oh) 1548 char const* qpdf_oh_unparse_binary(qpdf_data qpdf, qpdf_oh oh)
1435 { 1549 {
1436 - if (! qpdf_oh_valid_internal(qpdf, oh))  
1437 - {  
1438 - return "";  
1439 - }  
1440 - QTC::TC("qpdf", "qpdf-c called qpdf_oh_unparse_binary");  
1441 - qpdf->tmp_string = qpdf->oh_cache[oh]->unparseBinary();  
1442 - return qpdf->tmp_string.c_str(); 1550 + return do_with_oh<char const*>(
  1551 + qpdf, oh, return_T<char const*>(""), [&qpdf](QPDFObjectHandle& o) {
  1552 + QTC::TC("qpdf", "qpdf-c called qpdf_oh_unparse_binary");
  1553 + qpdf->tmp_string = o.unparseBinary();
  1554 + return qpdf->tmp_string.c_str();
  1555 + });
1443 } 1556 }
manual/qpdf-manual.xml
@@ -5231,6 +5231,19 @@ print &quot;\n&quot;; @@ -5231,6 +5231,19 @@ print &quot;\n&quot;;
5231 </listitem> 5231 </listitem>
5232 <listitem> 5232 <listitem>
5233 <para> 5233 <para>
  5234 + Overhaul error handling for the object handle functions in
  5235 + the C API. See comments in the &ldquo;Object handling&rdquo;
  5236 + section of <filename>include/qpdf/qpdf-c.h</filename> for
  5237 + details. In particular, exceptions thrown by the underlying
  5238 + C++ code when calling object accessors are caught and
  5239 + converted into errors. The errors can be trapped by
  5240 + registering an error handler with
  5241 + <function>qpdf_register_oh_error_handler</function> or will
  5242 + be written to stderr if no handler is registered.
  5243 + </para>
  5244 + </listitem>
  5245 + <listitem>
  5246 + <para>
5234 Add <function>qpdf_get_last_string_length</function> to the 5247 Add <function>qpdf_get_last_string_length</function> to the
5235 C API to get the length of the last string that was 5248 C API to get the length of the last string that was
5236 returned. This is needed to handle strings that contain 5249 returned. This is needed to handle strings that contain
@@ -5239,9 +5252,9 @@ print &quot;\n&quot;; @@ -5239,9 +5252,9 @@ print &quot;\n&quot;;
5239 </listitem> 5252 </listitem>
5240 <listitem> 5253 <listitem>
5241 <para> 5254 <para>
5242 - Add <function>qpdf_oh_is_initialized</function> to the  
5243 - C API. While you can't directly create uninitialized objects  
5244 - from the C API, you still have to be able to detect them. 5255 + Add <function>qpdf_oh_is_initialized</function> and
  5256 + <function>qpdf_oh_new_uninitialized</function> to the C API
  5257 + to make it possible to work with uninitialized objects.
5245 </para> 5258 </para>
5246 </listitem> 5259 </listitem>
5247 <listitem> 5260 <listitem>
qpdf/qpdf-ctest.c
@@ -35,28 +35,29 @@ static FILE* safe_fopen(char const* filename, char const* mode) @@ -35,28 +35,29 @@ static FILE* safe_fopen(char const* filename, char const* mode)
35 return f; 35 return f;
36 } 36 }
37 37
38 -static void report_errors() 38 +static void print_error(char const* label, qpdf_data qpdf, qpdf_error e)
39 { 39 {
40 #define POS_FMT " pos : " LL_FMT "\n" 40 #define POS_FMT " pos : " LL_FMT "\n"
  41 + printf("%s: %s\n", label, qpdf_get_error_full_text(qpdf, e));
  42 + printf(" code: %d\n", qpdf_get_error_code(qpdf, e));
  43 + printf(" file: %s\n", qpdf_get_error_filename(qpdf, e));
  44 + printf(POS_FMT, qpdf_get_error_file_position(qpdf, e));
  45 + printf(" text: %s\n", qpdf_get_error_message_detail(qpdf, e));
  46 +}
  47 +
  48 +static void report_errors()
  49 +{
41 qpdf_error e = 0; 50 qpdf_error e = 0;
42 while (qpdf_more_warnings(qpdf)) 51 while (qpdf_more_warnings(qpdf))
43 { 52 {
44 e = qpdf_next_warning(qpdf); 53 e = qpdf_next_warning(qpdf);
45 - printf("warning: %s\n", qpdf_get_error_full_text(qpdf, e));  
46 - printf(" code: %d\n", qpdf_get_error_code(qpdf, e));  
47 - printf(" file: %s\n", qpdf_get_error_filename(qpdf, e));  
48 - printf(POS_FMT, qpdf_get_error_file_position(qpdf, e));  
49 - printf(" text: %s\n", qpdf_get_error_message_detail(qpdf, e)); 54 + print_error("warning", qpdf, e);
50 } 55 }
51 if (qpdf_has_error(qpdf)) 56 if (qpdf_has_error(qpdf))
52 { 57 {
53 e = qpdf_get_error(qpdf); 58 e = qpdf_get_error(qpdf);
54 assert(qpdf_has_error(qpdf) == QPDF_FALSE); 59 assert(qpdf_has_error(qpdf) == QPDF_FALSE);
55 - printf("error: %s\n", qpdf_get_error_full_text(qpdf, e));  
56 - printf(" code: %d\n", qpdf_get_error_code(qpdf, e));  
57 - printf(" file: %s\n", qpdf_get_error_filename(qpdf, e));  
58 - printf(POS_FMT, qpdf_get_error_file_position(qpdf, e));  
59 - printf(" text: %s\n", qpdf_get_error_message_detail(qpdf, e)); 60 + print_error("error", qpdf, e);
60 } 61 }
61 else 62 else
62 { 63 {
@@ -72,6 +73,16 @@ static void report_errors() @@ -72,6 +73,16 @@ static void report_errors()
72 } 73 }
73 } 74 }
74 75
  76 +static void handle_oh_error(qpdf_data qpdf, qpdf_error error, void* data)
  77 +{
  78 + char const* label = "oh error";
  79 + if (data)
  80 + {
  81 + label = *((char const**)data);
  82 + }
  83 + print_error(label, qpdf, error);
  84 +}
  85 +
75 static void read_file_into_memory(char const* filename, 86 static void read_file_into_memory(char const* filename,
76 char** buf, unsigned long* size) 87 char** buf, unsigned long* size)
77 { 88 {
@@ -615,8 +626,11 @@ static void test24(char const* infile, @@ -615,8 +626,11 @@ static void test24(char const* infile,
615 */ 626 */
616 qpdf_oh_replace_key(qpdf, resources, "/ProcSet", procset); 627 qpdf_oh_replace_key(qpdf, resources, "/ProcSet", procset);
617 628
618 - /* Release and access to exercise warnings and to show that write  
619 - * still works after releasing. 629 + /* Release and access to exercise handling of object handle errors
  630 + * and to show that write still works after releasing. This test
  631 + * uses the default oh error handler, so messages get written to
  632 + * stderr. The warning about using the default error handler only
  633 + * appears once.
620 */ 634 */
621 qpdf_oh_release(qpdf, page1); 635 qpdf_oh_release(qpdf, page1);
622 contents = qpdf_oh_get_key(qpdf, page1, "/Contents"); 636 contents = qpdf_oh_get_key(qpdf, page1, "/Contents");
@@ -791,6 +805,82 @@ static void test28(char const* infile, @@ -791,6 +805,82 @@ static void test28(char const* infile,
791 } 805 }
792 } 806 }
793 807
  808 +static void test29(char const* infile,
  809 + char const* password,
  810 + char const* outfile,
  811 + char const* outfile2)
  812 +{
  813 + /* Trap exceptions thrown by object accessors. Type mismatches are
  814 + * errors rather than warnings when they don't have an owning QPDF
  815 + * object.
  816 + */
  817 + char const* label = "oh error";
  818 + qpdf_register_oh_error_handler(qpdf, handle_oh_error, (void*)&label);
  819 +
  820 + /* get_root fails when we have no trailer */
  821 + label = "get root";
  822 + qpdf_oh root = qpdf_get_root(qpdf);
  823 + assert(root != 0);
  824 + assert(! qpdf_oh_is_initialized(qpdf, root));
  825 +
  826 + label = "bad parse";
  827 + assert(! qpdf_oh_is_initialized(qpdf, qpdf_oh_parse(qpdf, "[oops")));
  828 + report_errors();
  829 +
  830 + label = "type mismatch";
  831 + assert(qpdf_oh_get_int_value_as_int(
  832 + qpdf, qpdf_oh_new_string(qpdf, "x")) == 0);
  833 + qpdf_oh int_oh = qpdf_oh_new_integer(qpdf, 12);
  834 + assert(strlen(qpdf_oh_get_string_value(qpdf, int_oh)) == 0);
  835 +
  836 + // This doesn't test every possible error flow, but it tests each
  837 + // way of handling errors in the library code.
  838 + label = "array type mismatch";
  839 + assert(qpdf_oh_get_array_n_items(qpdf, int_oh) == 0);
  840 + assert(qpdf_oh_is_null(qpdf, qpdf_oh_get_array_item(qpdf, int_oh, 3)));
  841 + label = "append to non-array";
  842 + qpdf_oh_append_item(qpdf, int_oh, qpdf_oh_new_null(qpdf));
  843 + qpdf_oh array = qpdf_oh_new_array(qpdf);
  844 + label = "array bounds";
  845 + assert(qpdf_oh_is_null(qpdf, qpdf_oh_get_array_item(qpdf, array, 3)));
  846 +
  847 + label = "dictionary iter type mismatch";
  848 + qpdf_oh_begin_dict_key_iter(qpdf, int_oh);
  849 + assert(qpdf_oh_dict_more_keys(qpdf) == QPDF_FALSE);
  850 + label = "dictionary type mismatch";
  851 + assert(qpdf_oh_is_null(qpdf, qpdf_oh_get_key(qpdf, int_oh, "potato")));
  852 + assert(qpdf_oh_has_key(qpdf, int_oh, "potato") == QPDF_FALSE);
  853 +
  854 + report_errors();
  855 +}
  856 +
  857 +static void test30(char const* infile,
  858 + char const* password,
  859 + char const* outfile,
  860 + char const* outfile2)
  861 +{
  862 + assert(qpdf_read(qpdf, infile, password) & QPDF_ERRORS);
  863 + /* Fail to handle error */
  864 +}
  865 +
  866 +static void test31(char const* infile,
  867 + char const* password,
  868 + char const* outfile,
  869 + char const* outfile2)
  870 +{
  871 + /* Make sure type warnings have a specific error code. This test
  872 + * case is designed for minimal.pdf.
  873 + */
  874 + qpdf_read(qpdf, infile, password);
  875 + qpdf_oh trailer = qpdf_get_trailer(qpdf);
  876 + assert(qpdf_oh_get_int_value(qpdf, trailer) == 0LL);
  877 + assert(! qpdf_has_error(qpdf));
  878 + assert(qpdf_more_warnings(qpdf));
  879 + qpdf_error e = qpdf_next_warning(qpdf);
  880 + assert(qpdf_get_error_code(qpdf, e) == qpdf_e_object);
  881 + report_errors();
  882 +}
  883 +
794 int main(int argc, char* argv[]) 884 int main(int argc, char* argv[])
795 { 885 {
796 char* p = 0; 886 char* p = 0;
@@ -859,6 +949,9 @@ int main(int argc, char* argv[]) @@ -859,6 +949,9 @@ int main(int argc, char* argv[])
859 (n == 26) ? test26 : 949 (n == 26) ? test26 :
860 (n == 27) ? test27 : 950 (n == 27) ? test27 :
861 (n == 28) ? test28 : 951 (n == 28) ? test28 :
  952 + (n == 29) ? test29 :
  953 + (n == 30) ? test30 :
  954 + (n == 31) ? test31 :
862 0); 955 0);
863 956
864 if (fn == 0) 957 if (fn == 0)
qpdf/qpdf.testcov
@@ -602,3 +602,7 @@ QPDFObjectHandle check ownership 0 @@ -602,3 +602,7 @@ QPDFObjectHandle check ownership 0
602 qpdf weak crypto warning 0 602 qpdf weak crypto warning 0
603 qpdf-c called qpdf_oh_is_initialized 0 603 qpdf-c called qpdf_oh_is_initialized 0
604 qpdf-c registered progress reporter 0 604 qpdf-c registered progress reporter 0
  605 +qpdf-c called qpdf_oh_new_uninitialized 0
  606 +qpdf-c warn about oh error 1
  607 +qpdf-c registered oh error handler 0
  608 +qpdf-c cleanup warned about unhandled error 0
qpdf/qtest/qpdf.test
@@ -4812,7 +4812,7 @@ foreach my $i (@c_check_types) @@ -4812,7 +4812,7 @@ foreach my $i (@c_check_types)
4812 show_ntests(); 4812 show_ntests();
4813 # ---------- 4813 # ----------
4814 $td->notify("--- C API Object Handle ---"); 4814 $td->notify("--- C API Object Handle ---");
4815 -$n_tests += 7; 4815 +$n_tests += 10;
4816 4816
4817 $td->runtest("C check object handles", 4817 $td->runtest("C check object handles",
4818 {$td->COMMAND => "qpdf-ctest 24 minimal.pdf '' a.pdf"}, 4818 {$td->COMMAND => "qpdf-ctest 24 minimal.pdf '' a.pdf"},
@@ -4843,6 +4843,18 @@ $td-&gt;runtest(&quot;C wrap and clone objects&quot;, @@ -4843,6 +4843,18 @@ $td-&gt;runtest(&quot;C wrap and clone objects&quot;,
4843 {$td->COMMAND => "qpdf-ctest 28 minimal.pdf '' ''"}, 4843 {$td->COMMAND => "qpdf-ctest 28 minimal.pdf '' ''"},
4844 {$td->STRING => "", $td->EXIT_STATUS => 0}, 4844 {$td->STRING => "", $td->EXIT_STATUS => 0},
4845 $td->NORMALIZE_NEWLINES); 4845 $td->NORMALIZE_NEWLINES);
  4846 +$td->runtest("C object handle errors",
  4847 + {$td->COMMAND => "qpdf-ctest 29 minimal.pdf '' ''"},
  4848 + {$td->FILE => "c-oh-errors.out", $td->EXIT_STATUS => 0},
  4849 + $td->NORMALIZE_NEWLINES);
  4850 +$td->runtest("C unhandled error warning",
  4851 + {$td->COMMAND => "qpdf-ctest 30 bad1.pdf '' ''"},
  4852 + {$td->FILE => "c-unhandled-error.out", $td->EXIT_STATUS => 0},
  4853 + $td->NORMALIZE_NEWLINES);
  4854 +$td->runtest("C type mismatch warning",
  4855 + {$td->COMMAND => "qpdf-ctest 31 minimal.pdf '' ''"},
  4856 + {$td->FILE => "c-type-warning.out", $td->EXIT_STATUS => 0},
  4857 + $td->NORMALIZE_NEWLINES);
4846 4858
4847 show_ntests(); 4859 show_ntests();
4848 # ---------- 4860 # ----------
qpdf/qtest/qpdf/c-object-handles.out
@@ -7,18 +7,11 @@ item 0: 0 0.00 @@ -7,18 +7,11 @@ item 0: 0 0.00
7 item 1: 0 0.00 7 item 1: 0 0.00
8 item 2: 612 612.00 8 item 2: 612 612.00
9 item 3: 792 792.00 9 item 3: 792 792.00
10 -warning: minimal.pdf (C API object handle 6): attempted access to unknown object handle  
11 - code: 5 10 +minimal.pdf (C API object handle 6): attempted access to unknown object handle
  11 +minimal.pdf (C API object handle 9): attempted access to unknown object handle
  12 +minimal.pdf (C API object handle 9): attempted access to unknown object handle
  13 +warning: minimal.pdf: C API object handle accessor errors occurred, and the application did not define an error handler
  14 + code: 1
12 file: minimal.pdf 15 file: minimal.pdf
13 pos : 0 16 pos : 0
14 - text: attempted access to unknown object handle  
15 -warning: minimal.pdf (C API object handle 9): attempted access to unknown object handle  
16 - code: 5  
17 - file: minimal.pdf  
18 - pos : 0  
19 - text: attempted access to unknown object handle  
20 -warning: minimal.pdf (C API object handle 9): attempted access to unknown object handle  
21 - code: 5  
22 - file: minimal.pdf  
23 - pos : 0  
24 - text: attempted access to unknown object handle 17 + text: C API object handle accessor errors occurred, and the application did not define an error handler
qpdf/qtest/qpdf/c-oh-errors.out 0 โ†’ 100644
  1 +get root: attempted to dereference an uninitialized QPDFObjectHandle
  2 + code: 1
  3 + file:
  4 + pos : 0
  5 + text: attempted to dereference an uninitialized QPDFObjectHandle
  6 +bad parse: parsed object (offset 1): unknown token while reading object; treating as string
  7 + code: 5
  8 + file: parsed object
  9 + pos : 1
  10 + text: unknown token while reading object; treating as string
  11 +type mismatch: operation for integer attempted on object of type string: returning 0
  12 + code: 7
  13 + file:
  14 + pos : 0
  15 + text: operation for integer attempted on object of type string: returning 0
  16 +type mismatch: operation for string attempted on object of type integer: returning empty string
  17 + code: 7
  18 + file:
  19 + pos : 0
  20 + text: operation for string attempted on object of type integer: returning empty string
  21 +array type mismatch: operation for array attempted on object of type integer: treating as empty
  22 + code: 7
  23 + file:
  24 + pos : 0
  25 + text: operation for array attempted on object of type integer: treating as empty
  26 +array type mismatch: operation for array attempted on object of type integer: returning null
  27 + code: 7
  28 + file:
  29 + pos : 0
  30 + text: operation for array attempted on object of type integer: returning null
  31 +append to non-array: operation for array attempted on object of type integer: ignoring attempt to append item
  32 + code: 7
  33 + file:
  34 + pos : 0
  35 + text: operation for array attempted on object of type integer: ignoring attempt to append item
  36 +array bounds: returning null for out of bounds array access
  37 + code: 7
  38 + file:
  39 + pos : 0
  40 + text: returning null for out of bounds array access
  41 +dictionary iter type mismatch: operation for dictionary attempted on object of type integer: treating as empty
  42 + code: 7
  43 + file:
  44 + pos : 0
  45 + text: operation for dictionary attempted on object of type integer: treating as empty
  46 +dictionary type mismatch: operation for dictionary attempted on object of type integer: returning null for attempted key retrieval
  47 + code: 7
  48 + file:
  49 + pos : 0
  50 + text: operation for dictionary attempted on object of type integer: returning null for attempted key retrieval
  51 +dictionary type mismatch: operation for dictionary attempted on object of type integer: returning false for a key containment request
  52 + code: 7
  53 + file:
  54 + pos : 0
  55 + text: operation for dictionary attempted on object of type integer: returning false for a key containment request
qpdf/qtest/qpdf/c-type-warning.out 0 โ†’ 100644
  1 +WARNING: minimal.pdf, trailer at offset 715: operation for integer attempted on object of type dictionary: returning 0
qpdf/qtest/qpdf/c-unhandled-error.out 0 โ†’ 100644
  1 +WARNING: bad1.pdf: can't find PDF header
  2 +WARNING: bad1.pdf: file is damaged
  3 +WARNING: bad1.pdf: can't find startxref
  4 +WARNING: bad1.pdf: Attempting to reconstruct cross-reference table
  5 +WARNING: application did not handle error: bad1.pdf: unable to find trailer dictionary while recovering damaged file