Commit 72c10d8617c799432e28dabf1679b1a6f5245c02
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
Showing
11 changed files
with
672 additions
and
320 deletions
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 | 19 | 2021-12-02 Jay Berkenbilt <ejb@ql.org> |
| 2 | 20 | |
| 3 | 21 | * C API: Add qpdf_oh_is_initialized. | ... | ... |
include/qpdf/qpdf-c.h
| ... | ... | @@ -62,22 +62,27 @@ |
| 62 | 62 | * string was just returned. |
| 63 | 63 | * |
| 64 | 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 | 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 | 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 | 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 | 87 | * Most functions defined here have obvious counterparts that are |
| 83 | 88 | * methods to either QPDF or QPDFWriter. Please see comments in |
| ... | ... | @@ -550,13 +555,51 @@ extern "C" { |
| 550 | 555 | * handle, the object is safely part of the dictionary or array. |
| 551 | 556 | * Similarly, any other object handle refering to the object remains |
| 552 | 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 | 586 | /* For examples of using this API, see examples/pdf-c-objects.c */ |
| 557 | 587 | |
| 558 | 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 | 603 | /* Releasing objects -- see comments above. These functions have no |
| 561 | 604 | * equivalent in the C++ API. |
| 562 | 605 | */ |
| ... | ... | @@ -659,7 +702,7 @@ extern "C" { |
| 659 | 702 | /* The memory returned by qpdf_oh_dict_next_key is owned by |
| 660 | 703 | * qpdf_data. It is good until the next call to |
| 661 | 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 | 706 | * previous return values. |
| 664 | 707 | */ |
| 665 | 708 | QPDF_DLL |
| ... | ... | @@ -677,6 +720,8 @@ extern "C" { |
| 677 | 720 | qpdf_data data, qpdf_oh oh, char const* key); |
| 678 | 721 | |
| 679 | 722 | QPDF_DLL |
| 723 | + qpdf_oh qpdf_oh_new_uninitialized(qpdf_data qpdf); | |
| 724 | + QPDF_DLL | |
| 680 | 725 | qpdf_oh qpdf_oh_new_null(qpdf_data data); |
| 681 | 726 | QPDF_DLL |
| 682 | 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 | 41 | PointerHolder<Buffer> output_buffer; |
| 42 | 42 | |
| 43 | 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 | 47 | std::map<qpdf_oh, PointerHolder<QPDFObjectHandle>> oh_cache; |
| 45 | 48 | qpdf_oh next_oh; |
| 46 | 49 | std::set<std::string> cur_iter_dict_keys; |
| ... | ... | @@ -48,8 +51,32 @@ struct _qpdf_data |
| 48 | 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 | 75 | _qpdf_data::_qpdf_data() : |
| 52 | 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 | 80 | next_oh(0) |
| 54 | 81 | { |
| 55 | 82 | } |
| ... | ... | @@ -170,6 +197,13 @@ void qpdf_cleanup(qpdf_data* qpdf) |
| 170 | 197 | { |
| 171 | 198 | QTC::TC("qpdf", "qpdf-c called qpdf_cleanup"); |
| 172 | 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 | 207 | delete *qpdf; |
| 174 | 208 | *qpdf = 0; |
| 175 | 209 | } |
| ... | ... | @@ -841,6 +875,38 @@ QPDF_ERROR_CODE qpdf_write(qpdf_data qpdf) |
| 841 | 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 | 910 | static qpdf_oh |
| 845 | 911 | new_object(qpdf_data qpdf, QPDFObjectHandle const& qoh) |
| 846 | 912 | { |
| ... | ... | @@ -867,310 +933,367 @@ void qpdf_oh_release_all(qpdf_data qpdf) |
| 867 | 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 | 957 | qpdf_oh qpdf_get_trailer(qpdf_data qpdf) |
| 871 | 958 | { |
| 872 | 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 | 966 | qpdf_oh qpdf_get_root(qpdf_data qpdf) |
| 877 | 967 | { |
| 878 | 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 | 1011 | QPDF_BOOL qpdf_oh_is_initialized(qpdf_data qpdf, qpdf_oh oh) |
| 903 | 1012 | { |
| 904 | 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 | 1020 | QPDF_BOOL qpdf_oh_is_bool(qpdf_data qpdf, qpdf_oh oh) |
| 910 | 1021 | { |
| 911 | 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 | 1029 | QPDF_BOOL qpdf_oh_is_null(qpdf_data qpdf, qpdf_oh oh) |
| 917 | 1030 | { |
| 918 | 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 | 1038 | QPDF_BOOL qpdf_oh_is_integer(qpdf_data qpdf, qpdf_oh oh) |
| 924 | 1039 | { |
| 925 | 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 | 1047 | QPDF_BOOL qpdf_oh_is_real(qpdf_data qpdf, qpdf_oh oh) |
| 931 | 1048 | { |
| 932 | 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 | 1056 | QPDF_BOOL qpdf_oh_is_name(qpdf_data qpdf, qpdf_oh oh) |
| 938 | 1057 | { |
| 939 | 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 | 1065 | QPDF_BOOL qpdf_oh_is_string(qpdf_data qpdf, qpdf_oh oh) |
| 945 | 1066 | { |
| 946 | 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 | 1074 | QPDF_BOOL qpdf_oh_is_operator(qpdf_data qpdf, qpdf_oh oh) |
| 952 | 1075 | { |
| 953 | 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 | 1083 | QPDF_BOOL qpdf_oh_is_inline_image(qpdf_data qpdf, qpdf_oh oh) |
| 959 | 1084 | { |
| 960 | 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 | 1092 | QPDF_BOOL qpdf_oh_is_array(qpdf_data qpdf, qpdf_oh oh) |
| 966 | 1093 | { |
| 967 | 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 | 1101 | QPDF_BOOL qpdf_oh_is_dictionary(qpdf_data qpdf, qpdf_oh oh) |
| 973 | 1102 | { |
| 974 | 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 | 1110 | QPDF_BOOL qpdf_oh_is_stream(qpdf_data qpdf, qpdf_oh oh) |
| 980 | 1111 | { |
| 981 | 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 | 1119 | QPDF_BOOL qpdf_oh_is_indirect(qpdf_data qpdf, qpdf_oh oh) |
| 987 | 1120 | { |
| 988 | 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 | 1128 | QPDF_BOOL qpdf_oh_is_scalar(qpdf_data qpdf, qpdf_oh oh) |
| 994 | 1129 | { |
| 995 | 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 | 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 | 1167 | qpdf_oh qpdf_oh_parse(qpdf_data qpdf, char const* object_str) |
| 1023 | 1168 | { |
| 1024 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 1320 | |
| 1198 | 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 | 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 | 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 | 1354 | qpdf_oh qpdf_oh_new_null(qpdf_data qpdf) |
| ... | ... | @@ -1288,156 +1414,143 @@ qpdf_oh qpdf_oh_new_dictionary(qpdf_data qpdf) |
| 1288 | 1414 | |
| 1289 | 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 | 1424 | static QPDFObjectHandle |
| 1299 | 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 | 1435 | void qpdf_oh_set_array_item(qpdf_data qpdf, qpdf_oh oh, |
| 1312 | 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 | 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 | 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 | 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 | 1472 | void qpdf_oh_replace_key(qpdf_data qpdf, qpdf_oh oh, |
| 1352 | 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 | 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 | 1491 | void qpdf_oh_replace_or_remove_key(qpdf_data qpdf, qpdf_oh oh, |
| 1372 | 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 | 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 | 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 | 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 | 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 | 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 | 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 "\n"; |
| 5231 | 5231 | </listitem> |
| 5232 | 5232 | <listitem> |
| 5233 | 5233 | <para> |
| 5234 | + Overhaul error handling for the object handle functions in | |
| 5235 | + the C API. See comments in the “Object handling” | |
| 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 | 5247 | Add <function>qpdf_get_last_string_length</function> to the |
| 5235 | 5248 | C API to get the length of the last string that was |
| 5236 | 5249 | returned. This is needed to handle strings that contain |
| ... | ... | @@ -5239,9 +5252,9 @@ print "\n"; |
| 5239 | 5252 | </listitem> |
| 5240 | 5253 | <listitem> |
| 5241 | 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 | 5258 | </para> |
| 5246 | 5259 | </listitem> |
| 5247 | 5260 | <listitem> | ... | ... |
qpdf/qpdf-ctest.c
| ... | ... | @@ -35,28 +35,29 @@ static FILE* safe_fopen(char const* filename, char const* mode) |
| 35 | 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 | 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 | 50 | qpdf_error e = 0; |
| 42 | 51 | while (qpdf_more_warnings(qpdf)) |
| 43 | 52 | { |
| 44 | 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 | 56 | if (qpdf_has_error(qpdf)) |
| 52 | 57 | { |
| 53 | 58 | e = qpdf_get_error(qpdf); |
| 54 | 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 | 62 | else |
| 62 | 63 | { |
| ... | ... | @@ -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 | 86 | static void read_file_into_memory(char const* filename, |
| 76 | 87 | char** buf, unsigned long* size) |
| 77 | 88 | { |
| ... | ... | @@ -615,8 +626,11 @@ static void test24(char const* infile, |
| 615 | 626 | */ |
| 616 | 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 | 635 | qpdf_oh_release(qpdf, page1); |
| 622 | 636 | contents = qpdf_oh_get_key(qpdf, page1, "/Contents"); |
| ... | ... | @@ -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 | 884 | int main(int argc, char* argv[]) |
| 795 | 885 | { |
| 796 | 886 | char* p = 0; |
| ... | ... | @@ -859,6 +949,9 @@ int main(int argc, char* argv[]) |
| 859 | 949 | (n == 26) ? test26 : |
| 860 | 950 | (n == 27) ? test27 : |
| 861 | 951 | (n == 28) ? test28 : |
| 952 | + (n == 29) ? test29 : | |
| 953 | + (n == 30) ? test30 : | |
| 954 | + (n == 31) ? test31 : | |
| 862 | 955 | 0); |
| 863 | 956 | |
| 864 | 957 | if (fn == 0) | ... | ... |
qpdf/qpdf.testcov
| ... | ... | @@ -602,3 +602,7 @@ QPDFObjectHandle check ownership 0 |
| 602 | 602 | qpdf weak crypto warning 0 |
| 603 | 603 | qpdf-c called qpdf_oh_is_initialized 0 |
| 604 | 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 | 4812 | show_ntests(); |
| 4813 | 4813 | # ---------- |
| 4814 | 4814 | $td->notify("--- C API Object Handle ---"); |
| 4815 | -$n_tests += 7; | |
| 4815 | +$n_tests += 10; | |
| 4816 | 4816 | |
| 4817 | 4817 | $td->runtest("C check object handles", |
| 4818 | 4818 | {$td->COMMAND => "qpdf-ctest 24 minimal.pdf '' a.pdf"}, |
| ... | ... | @@ -4843,6 +4843,18 @@ $td->runtest("C wrap and clone objects", |
| 4843 | 4843 | {$td->COMMAND => "qpdf-ctest 28 minimal.pdf '' ''"}, |
| 4844 | 4844 | {$td->STRING => "", $td->EXIT_STATUS => 0}, |
| 4845 | 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 | 4859 | show_ntests(); |
| 4848 | 4860 | # ---------- | ... | ... |
qpdf/qtest/qpdf/c-object-handles.out
| ... | ... | @@ -7,18 +7,11 @@ item 0: 0 0.00 |
| 7 | 7 | item 1: 0 0.00 |
| 8 | 8 | item 2: 612 612.00 |
| 9 | 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 | 15 | file: minimal.pdf |
| 13 | 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 | ... | ... |