Commit e848395d3778197893a10e33b1b7e78710d8c8b7
Committed by
GitHub
Merge pull request #1589 from m-holger/global
Introduce `global` namespace for managing qpdf global options and lim…
Showing
39 changed files
with
1355 additions
and
96 deletions
include/qpdf/Constants.h
| ... | ... | @@ -244,4 +244,50 @@ enum qpdf_page_label_e { |
| 244 | 244 | pl_roman_upper, |
| 245 | 245 | }; |
| 246 | 246 | |
| 247 | +/** | |
| 248 | + * @enum qpdf_result_e | |
| 249 | + * @brief Enum representing result codes for qpdf C-API functions. | |
| 250 | + * | |
| 251 | + * Results <= qpdf_r_no_warn indicate success without warnings, | |
| 252 | + * qpdf_r_no_warn < result <= qpdf_r_success indicates success with warnings, and | |
| 253 | + * qpdf_r_success < result indicates failure. | |
| 254 | + */ | |
| 255 | +enum qpdf_result_e { | |
| 256 | + /* success */ | |
| 257 | + qpdf_r_ok = 0, | |
| 258 | + qpdf_r_no_warn = 0xff, /// any result <= qpdf_no_warn indicates success without warning | |
| 259 | + qpdf_r_success = 0xffff, /// any result <= qpdf_r_success indicates success | |
| 260 | + /* failure */ | |
| 261 | + qpdf_r_bad_parameter = 0x10000, | |
| 262 | + | |
| 263 | + qpdf_r_no_warn_mask = 0x7fffff00, | |
| 264 | + qpdf_r_success_mask = 0x7fff0000, | |
| 265 | +}; | |
| 266 | + | |
| 267 | +/** | |
| 268 | + * @enum qpdf_param_e | |
| 269 | + * @brief This enumeration defines various parameters and configuration options for qpdf C-API | |
| 270 | + * functions. | |
| 271 | + * | |
| 272 | + * The enum values are grouped into sections based on their functionality, such as global | |
| 273 | + * options or global limits. For the meaning of individual parameters see `qpdf/global.cc` | |
| 274 | + */ | |
| 275 | +enum qpdf_param_e { | |
| 276 | + /* global state */ | |
| 277 | + qpdf_p_limit_errors = 0x10020, | |
| 278 | + | |
| 279 | + /* global options */ | |
| 280 | + qpdf_p_default_limits = 0x11100, | |
| 281 | + /* global limits */ | |
| 282 | + | |
| 283 | + /* object - parser limits */ | |
| 284 | + qpdf_p_parser_max_nesting = 0x13000, | |
| 285 | + qpdf_p_parser_max_errors, | |
| 286 | + qpdf_p_parser_max_container_size, | |
| 287 | + qpdf_p_parser_max_container_size_damaged, | |
| 288 | + | |
| 289 | + /* next section = 0x20000 */ | |
| 290 | + qpdf_enum_max = 0x7fffffff, | |
| 291 | +}; | |
| 292 | + | |
| 247 | 293 | #endif /* QPDFCONSTANTS_H */ | ... | ... |
include/qpdf/QPDFJob.hh
| ... | ... | @@ -306,6 +306,24 @@ class QPDFJob |
| 306 | 306 | Config* config; |
| 307 | 307 | }; |
| 308 | 308 | |
| 309 | + class GlobalConfig | |
| 310 | + { | |
| 311 | + friend class QPDFJob; | |
| 312 | + friend class Config; | |
| 313 | + | |
| 314 | + public: | |
| 315 | + QPDF_DLL | |
| 316 | + Config* endGlobal(); | |
| 317 | + | |
| 318 | +#include <qpdf/auto_job_c_global.hh> | |
| 319 | + | |
| 320 | + GlobalConfig(Config*); // for qpdf internal use only | |
| 321 | + GlobalConfig(GlobalConfig const&) = delete; | |
| 322 | + | |
| 323 | + private: | |
| 324 | + Config* config; | |
| 325 | + }; | |
| 326 | + | |
| 309 | 327 | class Config |
| 310 | 328 | { |
| 311 | 329 | friend class QPDFJob; |
| ... | ... | @@ -331,6 +349,8 @@ class QPDFJob |
| 331 | 349 | QPDF_DLL |
| 332 | 350 | std::shared_ptr<AttConfig> addAttachment(); |
| 333 | 351 | QPDF_DLL |
| 352 | + std::shared_ptr<GlobalConfig> global(); | |
| 353 | + QPDF_DLL | |
| 334 | 354 | std::shared_ptr<PagesConfig> pages(); |
| 335 | 355 | QPDF_DLL |
| 336 | 356 | std::shared_ptr<UOConfig> overlay(); | ... | ... |
include/qpdf/QUtil.hh
| ... | ... | @@ -20,8 +20,10 @@ |
| 20 | 20 | #ifndef QUTIL_HH |
| 21 | 21 | #define QUTIL_HH |
| 22 | 22 | |
| 23 | +#include <qpdf/Constants.h> | |
| 23 | 24 | #include <qpdf/DLL.h> |
| 24 | 25 | #include <qpdf/Types.h> |
| 26 | + | |
| 25 | 27 | #include <cstdio> |
| 26 | 28 | #include <cstring> |
| 27 | 29 | #include <ctime> |
| ... | ... | @@ -441,6 +443,25 @@ namespace QUtil |
| 441 | 443 | QPDF_DLL |
| 442 | 444 | bool is_number(char const*); |
| 443 | 445 | |
| 446 | + /// @brief Handles the result code from qpdf functions. | |
| 447 | + /// | |
| 448 | + /// **For qpdf internal use only - not part of the public API** | |
| 449 | + /// @par | |
| 450 | + /// Depending on the result code, either continues execution or throws an | |
| 451 | + /// exception in case of an invalid parameter. | |
| 452 | + /// | |
| 453 | + /// @param result The result code of type qpdf_result_e, indicating success or failure status. | |
| 454 | + /// @param context A string describing the context where this function is invoked, used for | |
| 455 | + /// error reporting if an exception is thrown. | |
| 456 | + /// | |
| 457 | + /// @throws std::logic_error If the result code is `qpdf_bad_parameter`, indicating an invalid | |
| 458 | + /// parameter was supplied to a function. The exception message will | |
| 459 | + /// include the provided context for easier debugging. | |
| 460 | + /// | |
| 461 | + /// @since 12.3 | |
| 462 | + QPDF_DLL | |
| 463 | + void handle_result_code(qpdf_result_e result, std::string_view context); | |
| 464 | + | |
| 444 | 465 | // This method parses the numeric range syntax used by the qpdf command-line tool. May throw |
| 445 | 466 | // std::runtime_error. A numeric range is as comma-separated list of groups. A group may be a |
| 446 | 467 | // number specification or a range of number specifications separated by a dash. A number | ... | ... |
include/qpdf/auto_job_c_global.hh
0 → 100644
| 1 | +// | |
| 2 | +// This file is automatically generated by generate_auto_job. | |
| 3 | +// Edits will be automatically overwritten if the build is | |
| 4 | +// run in maintainer mode. | |
| 5 | +// | |
| 6 | +// clang-format off | |
| 7 | +// | |
| 8 | +QPDF_DLL GlobalConfig* noDefaultLimits(); | |
| 9 | +QPDF_DLL GlobalConfig* parserMaxContainerSize(std::string const& parameter); | |
| 10 | +QPDF_DLL GlobalConfig* parserMaxContainerSizeDamaged(std::string const& parameter); | |
| 11 | +QPDF_DLL GlobalConfig* parserMaxErrors(std::string const& parameter); | |
| 12 | +QPDF_DLL GlobalConfig* parserMaxNesting(std::string const& parameter); | ... | ... |
include/qpdf/auto_job_c_limits.hh
0 → 100644
include/qpdf/global.hh
0 → 100644
| 1 | +// Copyright (c) 2005-2021 Jay Berkenbilt | |
| 2 | +// Copyright (c) 2022-2025 Jay Berkenbilt and Manfred Holger | |
| 3 | +// | |
| 4 | +// This file is part of qpdf. | |
| 5 | +// | |
| 6 | +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except | |
| 7 | +// in compliance with the License. You may obtain a copy of the License at | |
| 8 | +// | |
| 9 | +// http://www.apache.org/licenses/LICENSE-2.0 | |
| 10 | +// | |
| 11 | +// Unless required by applicable law or agreed to in writing, software distributed under the License | |
| 12 | +// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express | |
| 13 | +// or implied. See the License for the specific language governing permissions and limitations under | |
| 14 | +// the License. | |
| 15 | +// | |
| 16 | +// Versions of qpdf prior to version 7 were released under the terms of version 2.0 of the Artistic | |
| 17 | +// License. At your option, you may continue to consider qpdf to be licensed under those terms. | |
| 18 | +// Please see the manual for additional information. | |
| 19 | + | |
| 20 | +#ifndef GLOBAL_HH | |
| 21 | +#define GLOBAL_HH | |
| 22 | + | |
| 23 | +#include <qpdf/Constants.h> | |
| 24 | + | |
| 25 | +#include <qpdf/QUtil.hh> | |
| 26 | +#include <qpdf/qpdf-c.h> | |
| 27 | + | |
| 28 | +#include <cstdint> | |
| 29 | + | |
| 30 | +namespace qpdf::global | |
| 31 | +{ | |
| 32 | + /// Helper function to translate result codes into C++ exceptions - for qpdf internal use only. | |
| 33 | + inline void | |
| 34 | + handle_result(qpdf_result_e result) | |
| 35 | + { | |
| 36 | + if (result != qpdf_r_ok) { | |
| 37 | + QUtil::handle_result_code(result, "qpdf::global"); | |
| 38 | + } | |
| 39 | + } | |
| 40 | + | |
| 41 | + /// Helper function to wrap calls to qpdf_global_get_uint32 - for qpdf internal use only. | |
| 42 | + inline uint32_t | |
| 43 | + get_uint32(qpdf_param_e param) | |
| 44 | + { | |
| 45 | + uint32_t value; | |
| 46 | + handle_result(qpdf_global_get_uint32(param, &value)); | |
| 47 | + return value; | |
| 48 | + } | |
| 49 | + | |
| 50 | + /// Helper function to wrap calls to qpdf_global_set_uint32 - for qpdf internal use only. | |
| 51 | + inline void | |
| 52 | + set_uint32(qpdf_param_e param, uint32_t value) | |
| 53 | + { | |
| 54 | + handle_result(qpdf_global_set_uint32(param, value)); | |
| 55 | + } | |
| 56 | + | |
| 57 | + /// @brief Retrieves the number of limit errors. | |
| 58 | + /// | |
| 59 | + /// Returns the number a global limit was exceeded. This item is read only. | |
| 60 | + /// | |
| 61 | + /// @return The number of limit errors. | |
| 62 | + /// | |
| 63 | + /// @since 12.3 | |
| 64 | + uint32_t inline limit_errors() | |
| 65 | + { | |
| 66 | + return get_uint32(qpdf_p_limit_errors); | |
| 67 | + } | |
| 68 | + | |
| 69 | + namespace options | |
| 70 | + { | |
| 71 | + /// @brief Retrieves whether default limits are enabled. | |
| 72 | + /// | |
| 73 | + /// @return True if default limits are enabled. | |
| 74 | + /// | |
| 75 | + /// @since 12.3 | |
| 76 | + bool inline default_limits() | |
| 77 | + { | |
| 78 | + return get_uint32(qpdf_p_default_limits) != 0; | |
| 79 | + } | |
| 80 | + | |
| 81 | + /// @brief Disable all optional default limits if `false` is passed. | |
| 82 | + /// | |
| 83 | + /// This function disables all optional default limits if `false` is passed. Once default | |
| 84 | + /// values have been disabled they cannot be re-enabled. Passing `true` has no effect. This | |
| 85 | + /// function will leave any limits that have been explicitly set unchanged. Some limits, | |
| 86 | + /// such as limits imposed to avoid stack overflows, cannot be disabled but can be changed. | |
| 87 | + /// | |
| 88 | + /// @param value A boolean indicating whether to disable (false) the default limits. | |
| 89 | + /// | |
| 90 | + /// @since 12.3 | |
| 91 | + void inline default_limits(bool value) | |
| 92 | + { | |
| 93 | + set_uint32(qpdf_p_default_limits, value ? QPDF_TRUE : QPDF_FALSE); | |
| 94 | + } | |
| 95 | + | |
| 96 | + } // namespace options | |
| 97 | + | |
| 98 | + namespace limits | |
| 99 | + { | |
| 100 | + /// @brief Retrieves the maximum nesting level while parsing objects. | |
| 101 | + /// | |
| 102 | + /// @return The maximum nesting level while parsing objects. | |
| 103 | + /// | |
| 104 | + /// @note The maximum nesting level cannot be disabled by calling `default_limit(false)`. | |
| 105 | + /// | |
| 106 | + /// @since 12.3 | |
| 107 | + uint32_t inline parser_max_nesting() | |
| 108 | + { | |
| 109 | + return get_uint32(qpdf_p_parser_max_nesting); | |
| 110 | + } | |
| 111 | + | |
| 112 | + /// @brief Sets the maximum nesting level while parsing objects. | |
| 113 | + /// | |
| 114 | + /// @param value The maximum nesting level to set. | |
| 115 | + /// | |
| 116 | + /// @note The maximum nesting level cannot be disabled by calling `default_limit(false)`. | |
| 117 | + /// | |
| 118 | + /// @since 12.3 | |
| 119 | + void inline parser_max_nesting(uint32_t value) | |
| 120 | + { | |
| 121 | + set_uint32(qpdf_p_parser_max_nesting, value); | |
| 122 | + } | |
| 123 | + | |
| 124 | + /// @brief Retrieves the maximum number of errors allowed while parsing objects. | |
| 125 | + /// | |
| 126 | + /// A value of 0 means that there is no maximum imposed. | |
| 127 | + /// | |
| 128 | + /// @return The maximum number of errors allowed while parsing objects. | |
| 129 | + /// | |
| 130 | + /// @since 12.3 | |
| 131 | + uint32_t inline parser_max_errors() | |
| 132 | + { | |
| 133 | + return get_uint32(qpdf_p_parser_max_errors); | |
| 134 | + } | |
| 135 | + | |
| 136 | + /// Sets the maximum number of errors allowed while parsing objects. | |
| 137 | + /// | |
| 138 | + /// A value of 0 means that there is no maximum imposed. | |
| 139 | + /// | |
| 140 | + /// @param value The maximum number of errors allowed while parsing objects to set. | |
| 141 | + /// | |
| 142 | + /// @since 12.3 | |
| 143 | + void inline parser_max_errors(uint32_t value) | |
| 144 | + { | |
| 145 | + set_uint32(qpdf_p_parser_max_errors, value); | |
| 146 | + } | |
| 147 | + | |
| 148 | + /// @brief Retrieves the maximum number of top-level objects allowed in a container while | |
| 149 | + /// parsing. | |
| 150 | + /// | |
| 151 | + /// The limit applies when the PDF document's xref table is undamaged and the object itself | |
| 152 | + /// can be parsed without errors. The default limit is 4,294,967,295. | |
| 153 | + /// | |
| 154 | + /// @return The maximum number of top-level objects allowed in a container while parsing | |
| 155 | + /// objects. | |
| 156 | + /// | |
| 157 | + /// @since 12.3 | |
| 158 | + uint32_t inline parser_max_container_size() | |
| 159 | + { | |
| 160 | + return get_uint32(qpdf_p_parser_max_container_size); | |
| 161 | + } | |
| 162 | + | |
| 163 | + /// @brief Sets the maximum number of top-level objects allowed in a container while | |
| 164 | + /// parsing. | |
| 165 | + /// | |
| 166 | + /// The limit applies when the PDF document's xref table is undamaged and the object itself | |
| 167 | + /// can be parsed without errors. The default limit is 4,294,967,295. | |
| 168 | + /// | |
| 169 | + /// @param value The maximum number of top-level objects allowed in a container while | |
| 170 | + /// parsing objects to set. | |
| 171 | + /// | |
| 172 | + /// @since 12.3 | |
| 173 | + void inline parser_max_container_size(uint32_t value) | |
| 174 | + { | |
| 175 | + set_uint32(qpdf_p_parser_max_container_size, value); | |
| 176 | + } | |
| 177 | + | |
| 178 | + /// @brief Retrieves the maximum number of top-level objects allowed in a container while | |
| 179 | + /// parsing objects. | |
| 180 | + /// | |
| 181 | + /// The limit applies when the PDF document's xref table is damaged or the object itself is | |
| 182 | + /// damaged. The limit also applies when parsing xref streams. The default limit is 5,000. | |
| 183 | + /// | |
| 184 | + /// @return The maximum number of top-level objects allowed in a container while parsing | |
| 185 | + /// objects. | |
| 186 | + /// | |
| 187 | + /// @since 12.3 | |
| 188 | + uint32_t inline parser_max_container_size_damaged() | |
| 189 | + { | |
| 190 | + return get_uint32(qpdf_p_parser_max_container_size_damaged); | |
| 191 | + } | |
| 192 | + | |
| 193 | + /// @brief Sets the maximum number of top-level objects allowed in a container while | |
| 194 | + /// parsing. | |
| 195 | + /// | |
| 196 | + /// The limit applies when the PDF document's xref table is damaged or the object itself is | |
| 197 | + /// damaged. The limit also applies when parsing trailer dictionaries and xref streams. The | |
| 198 | + /// default limit is 5,000. | |
| 199 | + /// | |
| 200 | + /// @param value The maximum number of top-level objects allowed in a container while | |
| 201 | + /// parsing objects to set. | |
| 202 | + /// | |
| 203 | + /// @since 12.3 | |
| 204 | + void inline parser_max_container_size_damaged(uint32_t value) | |
| 205 | + { | |
| 206 | + set_uint32(qpdf_p_parser_max_container_size_damaged, value); | |
| 207 | + } | |
| 208 | + } // namespace limits | |
| 209 | + | |
| 210 | +} // namespace qpdf::global | |
| 211 | + | |
| 212 | +#endif // GLOBAL_HH | ... | ... |
include/qpdf/qpdf-c.h
| ... | ... | @@ -119,6 +119,8 @@ |
| 119 | 119 | #include <qpdf/DLL.h> |
| 120 | 120 | #include <qpdf/Types.h> |
| 121 | 121 | #include <qpdf/qpdflogger-c.h> |
| 122 | + | |
| 123 | +#include <stdint.h> | |
| 122 | 124 | #include <string.h> |
| 123 | 125 | |
| 124 | 126 | #ifdef __cplusplus |
| ... | ... | @@ -1000,6 +1002,50 @@ extern "C" { |
| 1000 | 1002 | /* removePage() */ |
| 1001 | 1003 | QPDF_DLL |
| 1002 | 1004 | QPDF_ERROR_CODE qpdf_remove_page(qpdf_data qpdf, qpdf_oh page); |
| 1005 | + | |
| 1006 | + /* GLOBAL OPTIONS AND SETTINGS */ | |
| 1007 | + | |
| 1008 | + QPDF_DLL | |
| 1009 | + /** | |
| 1010 | + * @brief Retrieves a 32-bit unsigned integer value associated with a global option or limit. | |
| 1011 | + * | |
| 1012 | + * This function allows querying of specific parameters, identified by the qpdf_param_e enum, | |
| 1013 | + * and retrieves their associated unsigned 32-bit integer values. The result will be stored in | |
| 1014 | + * the variable pointed to by `value`. For details about the available parameters and their | |
| 1015 | + * meanings see `qpdf/global.hh`. | |
| 1016 | + * | |
| 1017 | + * @param param[in] The parameter for which the value is being retrieved. This must be a valid | |
| 1018 | + * value from the qpdf_param_e enumeration. | |
| 1019 | + * @param value[out] A pointer to a uint32_t to store the retrieved value. This must be a valid, | |
| 1020 | + * non-null pointer. | |
| 1021 | + * | |
| 1022 | + * @return An enumeration of type qpdf_result_e indicating the result of the operation. Possible | |
| 1023 | + * values include success or specific error statuses related to the retrieval process. | |
| 1024 | + * | |
| 1025 | + * @since 12.3 | |
| 1026 | + */ | |
| 1027 | + enum qpdf_result_e qpdf_global_get_uint32(enum qpdf_param_e param, uint32_t* value); | |
| 1028 | + | |
| 1029 | + QPDF_DLL | |
| 1030 | + /** | |
| 1031 | + * @brief Sets a global option or limit for the qpdf library to a specified value. | |
| 1032 | + * | |
| 1033 | + * This function is used to configure global options or limits for the qpdf library based on the | |
| 1034 | + * provided parameter and value. The behavior depends on the specific `param` provided and its | |
| 1035 | + * valid range of values. For details about the available parameters and their meanings see | |
| 1036 | + * `qpdf/global.hh`. | |
| 1037 | + * | |
| 1038 | + * @param param[in] The parameter to be set. Must be one of the values defined in the | |
| 1039 | + * qpdf_param_e enumeration. | |
| 1040 | + * @param value[in] The value to assign to the specified parameter. Interpretation of this value | |
| 1041 | + * depends on the parameter being set. | |
| 1042 | + * | |
| 1043 | + * @return An enumeration of type qpdf_result_e indicating the result of the operation. Possible | |
| 1044 | + * values include success or specific error statuses related to the retrieval process. | |
| 1045 | + * | |
| 1046 | + * @since 12.3 | |
| 1047 | + */ | |
| 1048 | + enum qpdf_result_e qpdf_global_set_uint32(enum qpdf_param_e param, uint32_t value); | |
| 1003 | 1049 | #ifdef __cplusplus |
| 1004 | 1050 | } |
| 1005 | 1051 | ... | ... |
job.sums
| ... | ... | @@ -4,17 +4,18 @@ generate_auto_job 8e3175a515aa8837d8a01bba0346b04b3d777d70330ba5b7d52f691316054a |
| 4 | 4 | include/qpdf/auto_job_c_att.hh 4c2b171ea00531db54720bf49a43f8b34481586ae7fb6cbf225099ee42bc5bb4 |
| 5 | 5 | include/qpdf/auto_job_c_copy_att.hh 50609012bff14fd82f0649185940d617d05d530cdc522185c7f3920a561ccb42 |
| 6 | 6 | include/qpdf/auto_job_c_enc.hh 28446f3c32153a52afa239ea40503e6cc8ac2c026813526a349e0cd4ae17ddd5 |
| 7 | +include/qpdf/auto_job_c_global.hh f1dc365206d033a0d6b19b6e561cc244fbd5b49a8d9604b5b646a5fd92895a5a | |
| 7 | 8 | include/qpdf/auto_job_c_main.hh b865eb827356554763bb8349eadfcbc5cb260f80e025a5e229467c525007356d |
| 8 | 9 | include/qpdf/auto_job_c_pages.hh 09ca15649cc94fdaf6d9bdae28a20723f2a66616bf15aa86d83df31051d82506 |
| 9 | 10 | include/qpdf/auto_job_c_uo.hh 9c2f98a355858dd54d0bba444b73177a59c9e56833e02fa6406f429c07f39e62 |
| 10 | -job.yml e136726a6c7e43736b1f75f4de347fa50baf5f38ed1da58647ce2e980751fb29 | |
| 11 | -libqpdf/qpdf/auto_job_decl.hh 34ba07d3891c3e5cdd8712f991e508a0652c9db314c5d5bcdf4421b76e6f6e01 | |
| 12 | -libqpdf/qpdf/auto_job_help.hh d0cca031e99f10caa3f4b70ea574b36b0af63d24de333e7d6f0bf835e959f0be | |
| 13 | -libqpdf/qpdf/auto_job_init.hh 02c526c37ad4051cac956ac7c12ae1d020517264f3f3d3beabb066ae2529e4bf | |
| 14 | -libqpdf/qpdf/auto_job_json_decl.hh 04965f6321e54b8b3b1dd2ca101d763a22ab44fa81c69e4b6fc0fd6bb7f50f92 | |
| 15 | -libqpdf/qpdf/auto_job_json_init.hh b49378f00d521a9f3e0ce9086e30b082bc6ef8e43c845e2a3c99857b72448307 | |
| 16 | -libqpdf/qpdf/auto_job_schema.hh f6a3e8b663714bba50b594f5e31437bbcb96ca4609d2c150c3bbc172e3b000fa | |
| 11 | +job.yml 131922d22086d9f4710743e18229cc1e956268197bcae8e1aae30f3be42877be | |
| 12 | +libqpdf/qpdf/auto_job_decl.hh d612a02839e4f20a80e1c6a3ba09c17187fccddc3581ec7ebb1e3919ffd6801d | |
| 13 | +libqpdf/qpdf/auto_job_help.hh 00ac90c621b6c0529d7bad9ea596f57595517901c8d33f49d2812fbea52dfb41 | |
| 14 | +libqpdf/qpdf/auto_job_init.hh 889dde948e0ab53616584976d9520ab7ab3773c787d241f8a107f5e2f9f2112f | |
| 15 | +libqpdf/qpdf/auto_job_json_decl.hh 7dbb83ddadcea39bfd1faa4ca061e1e3c3134d693b8ae634b463e7e19dc8bd0a | |
| 16 | +libqpdf/qpdf/auto_job_json_init.hh 3c5f3d07a85e89dd7ecd79342c18e1f0ad580fc57758abb434aa9c9ae277c01e | |
| 17 | +libqpdf/qpdf/auto_job_schema.hh eb21c99d3a4dc40b333fd1b19d5de52f8813c74a1d4ca830ea4c3311c120d63e | |
| 17 | 18 | manual/_ext/qpdf.py 6add6321666031d55ed4aedf7c00e5662bba856dfcd66ccb526563bffefbb580 |
| 18 | -manual/cli.rst b7bd5e34495d3f9156ff6242988dba73a2e5dce33d71f75ec1415514a3843f35 | |
| 19 | -manual/qpdf.1 d5785d23e77b02a77180419d87787002dc244d82d586d56008ab603299f565fd | |
| 19 | +manual/cli.rst 08e9e7a18d2b0d05102a072f82eabf9ede6bfb1fb797be307ea680eed93ea60f | |
| 20 | +manual/qpdf.1 19a45f8de6b7c0584fe4395c4ae98b92147a2875e45dbdf729c70e644ccca295 | |
| 20 | 21 | manual/qpdf.1.in 436ecc85d45c4c9e2dbd1725fb7f0177fb627179469f114561adf3cb6cbb677b | ... | ... |
job.yml
| ... | ... | @@ -84,12 +84,24 @@ options: |
| 84 | 84 | - zopfli |
| 85 | 85 | optional_choices: |
| 86 | 86 | json-help: json_version |
| 87 | + - table: global | |
| 88 | + prefix: Global | |
| 89 | + config: c_global | |
| 90 | + positional: false | |
| 91 | + bare: | |
| 92 | + - no-default-limits | |
| 93 | + required_parameter: | |
| 94 | + parser-max-container-size: level | |
| 95 | + parser-max-container-size-damaged: level | |
| 96 | + parser-max-errors: level | |
| 97 | + parser-max-nesting: level | |
| 87 | 98 | - table: main |
| 88 | 99 | config: c_main |
| 89 | 100 | manual: |
| 90 | 101 | - add-attachment |
| 91 | 102 | - copy-attachments-from |
| 92 | 103 | - encrypt |
| 104 | + - global | |
| 93 | 105 | - overlay |
| 94 | 106 | - pages |
| 95 | 107 | - underlay |
| ... | ... | @@ -112,6 +124,7 @@ options: |
| 112 | 124 | - filtered-stream-data |
| 113 | 125 | - flatten-rotation |
| 114 | 126 | - generate-appearances |
| 127 | + - global | |
| 115 | 128 | - ignore-xref-streams |
| 116 | 129 | - is-encrypted |
| 117 | 130 | - json-input |
| ... | ... | @@ -399,6 +412,13 @@ json: |
| 399 | 412 | - null |
| 400 | 413 | json-stream-data: |
| 401 | 414 | json-stream-prefix: |
| 415 | + # global options | |
| 416 | + global: | |
| 417 | + no-default-limits: | |
| 418 | + parser-max-container-size: | |
| 419 | + parser-max-container-size-damaged: | |
| 420 | + parser-max-errors: | |
| 421 | + parser-max-nesting: | |
| 402 | 422 | # other options |
| 403 | 423 | update-from-json: |
| 404 | 424 | allow-weak-crypto: | ... | ... |
libqpdf/QPDFJob.cc
| ... | ... | @@ -25,6 +25,7 @@ |
| 25 | 25 | #include <qpdf/QTC.hh> |
| 26 | 26 | #include <qpdf/QUtil.hh> |
| 27 | 27 | #include <qpdf/Util.hh> |
| 28 | +#include <qpdf/global_private.hh> | |
| 28 | 29 | |
| 29 | 30 | #include <qpdf/auto_job_schema.hh> // JOB_SCHEMA_DATA |
| 30 | 31 | |
| ... | ... | @@ -485,6 +486,11 @@ QPDFJob::writeQPDF(QPDF& pdf) |
| 485 | 486 | *m->log->getWarn() << m->message_prefix << ": operation succeeded with warnings\n"; |
| 486 | 487 | } |
| 487 | 488 | } |
| 489 | + if (!m->d_cfg.suppress_warnings() && global::Limits::errors()) { | |
| 490 | + *m->log->getWarn() << m->message_prefix | |
| 491 | + << ": some configurable limits were exceeded; for more details " | |
| 492 | + "see https://qpdf.readthedocs.io/en/stable/cli.html#global-limits\n"; | |
| 493 | + } | |
| 488 | 494 | if (m->report_mem_usage) { |
| 489 | 495 | // Call get_max_memory_usage before generating output. When debugging, it's easier if print |
| 490 | 496 | // statements from get_max_memory_usage are not interleaved with the output. | ... | ... |
libqpdf/QPDFJob_argv.cc
| ... | ... | @@ -31,6 +31,7 @@ namespace |
| 31 | 31 | std::shared_ptr<QPDFJob::Config> c_main; |
| 32 | 32 | std::shared_ptr<QPDFJob::CopyAttConfig> c_copy_att; |
| 33 | 33 | std::shared_ptr<QPDFJob::AttConfig> c_att; |
| 34 | + std::shared_ptr<QPDFJob::GlobalConfig> c_global; | |
| 34 | 35 | std::shared_ptr<QPDFJob::PagesConfig> c_pages; |
| 35 | 36 | std::shared_ptr<QPDFJob::UOConfig> c_uo; |
| 36 | 37 | std::shared_ptr<QPDFJob::EncConfig> c_enc; |
| ... | ... | @@ -418,6 +419,21 @@ ArgParser::argEndSetPageLabels() |
| 418 | 419 | } |
| 419 | 420 | |
| 420 | 421 | void |
| 422 | +ArgParser::argGlobal() | |
| 423 | +{ | |
| 424 | + accumulated_args.clear(); | |
| 425 | + c_global = c_main->global(); | |
| 426 | + ap.selectOptionTable(O_GLOBAL); | |
| 427 | +} | |
| 428 | + | |
| 429 | +void | |
| 430 | +ArgParser::argEndGlobal() | |
| 431 | +{ | |
| 432 | + c_global->endGlobal(); | |
| 433 | + c_global = nullptr; | |
| 434 | +} | |
| 435 | + | |
| 436 | +void | |
| 421 | 437 | ArgParser::argJobJsonHelp() |
| 422 | 438 | { |
| 423 | 439 | *QPDFLogger::defaultLogger()->getInfo() | ... | ... |
libqpdf/QPDFJob_config.cc
| ... | ... | @@ -4,31 +4,53 @@ |
| 4 | 4 | #include <qpdf/QPDFUsage.hh> |
| 5 | 5 | #include <qpdf/QTC.hh> |
| 6 | 6 | #include <qpdf/QUtil.hh> |
| 7 | +#include <qpdf/Util.hh> | |
| 8 | +#include <qpdf/global_private.hh> | |
| 7 | 9 | |
| 8 | 10 | #include <concepts> |
| 9 | 11 | #include <regex> |
| 10 | 12 | |
| 11 | -static void | |
| 12 | -int_usage(std::string_view option, std::integral auto min, std::integral auto max) | |
| 13 | +[[noreturn]] static void | |
| 14 | +int_usage(std::string_view option, std::integral auto max, std::integral auto min) | |
| 13 | 15 | { |
| 16 | + qpdf_expect(min < max); | |
| 14 | 17 | throw QPDFUsage( |
| 15 | 18 | "invalid "s.append(option) + ": must be a number between " + std::to_string(min) + " and " + |
| 16 | 19 | std::to_string(max)); |
| 17 | 20 | } |
| 18 | 21 | |
| 19 | 22 | static int |
| 20 | -to_int(std::string_view option, std::string const& value, int min, int max) | |
| 23 | +to_int(std::string_view option, std::string const& value, int max, int min) | |
| 21 | 24 | { |
| 22 | - int result = 0; | |
| 25 | + qpdf_expect(min < max); | |
| 23 | 26 | try { |
| 24 | - result = std::stoi(value); | |
| 27 | + int result = std::stoi(value); | |
| 25 | 28 | if (result < min || result > max) { |
| 26 | - int_usage(option, min, max); | |
| 29 | + int_usage(option, max, min); | |
| 27 | 30 | } |
| 31 | + return result; | |
| 28 | 32 | } catch (std::exception&) { |
| 29 | - int_usage(option, min, max); | |
| 33 | + int_usage(option, max, min); | |
| 34 | + } | |
| 35 | +} | |
| 36 | + | |
| 37 | +static uint32_t | |
| 38 | +to_uint32( | |
| 39 | + std::string_view option, | |
| 40 | + std::string const& value, | |
| 41 | + uint32_t max = std::numeric_limits<uint32_t>::max(), | |
| 42 | + uint32_t min = 0) | |
| 43 | +{ | |
| 44 | + qpdf_expect(min < max); | |
| 45 | + try { | |
| 46 | + auto result = std::stoll(value); | |
| 47 | + if (std::cmp_less(result, min) || std::cmp_greater(result, max)) { | |
| 48 | + int_usage(option, max, min); | |
| 49 | + } | |
| 50 | + return static_cast<uint32_t>(result); | |
| 51 | + } catch (std::exception&) { | |
| 52 | + int_usage(option, max, min); | |
| 30 | 53 | } |
| 31 | - return result; | |
| 32 | 54 | } |
| 33 | 55 | |
| 34 | 56 | void |
| ... | ... | @@ -156,14 +178,14 @@ QPDFJob::Config::compressStreams(std::string const& parameter) |
| 156 | 178 | QPDFJob::Config* |
| 157 | 179 | QPDFJob::Config::compressionLevel(std::string const& parameter) |
| 158 | 180 | { |
| 159 | - o.m->compression_level = to_int("compression-level", parameter, 1, 9); | |
| 181 | + o.m->compression_level = to_int("compression-level", parameter, 9, 1); | |
| 160 | 182 | return this; |
| 161 | 183 | } |
| 162 | 184 | |
| 163 | 185 | QPDFJob::Config* |
| 164 | 186 | QPDFJob::Config::jpegQuality(std::string const& parameter) |
| 165 | 187 | { |
| 166 | - o.m->jpeg_quality = to_int("jpeg-quality", parameter, 0, 100); | |
| 188 | + o.m->jpeg_quality = to_int("jpeg-quality", parameter, 100, 0); | |
| 167 | 189 | return this; |
| 168 | 190 | } |
| 169 | 191 | |
| ... | ... | @@ -1140,6 +1162,60 @@ QPDFJob::Config::encrypt( |
| 1140 | 1162 | return std::shared_ptr<EncConfig>(new EncConfig(this)); |
| 1141 | 1163 | } |
| 1142 | 1164 | |
| 1165 | +QPDFJob::GlobalConfig::GlobalConfig(Config* c) : | |
| 1166 | + config(c) | |
| 1167 | +{ | |
| 1168 | +} | |
| 1169 | + | |
| 1170 | +std::shared_ptr<QPDFJob::GlobalConfig> | |
| 1171 | +QPDFJob::Config::global() | |
| 1172 | +{ | |
| 1173 | + return std::make_shared<GlobalConfig>(this); | |
| 1174 | +} | |
| 1175 | + | |
| 1176 | +QPDFJob::Config* | |
| 1177 | +QPDFJob::GlobalConfig::endGlobal() | |
| 1178 | +{ | |
| 1179 | + return config; | |
| 1180 | +} | |
| 1181 | + | |
| 1182 | +QPDFJob::GlobalConfig* | |
| 1183 | +QPDFJob::GlobalConfig::noDefaultLimits() | |
| 1184 | +{ | |
| 1185 | + global::Options::default_limits(false); | |
| 1186 | + return this; | |
| 1187 | +} | |
| 1188 | + | |
| 1189 | +QPDFJob::GlobalConfig* | |
| 1190 | +QPDFJob::GlobalConfig::parserMaxContainerSize(const std::string& parameter) | |
| 1191 | +{ | |
| 1192 | + global::Limits::parser_max_container_size( | |
| 1193 | + false, to_uint32("parser-max-container-size", parameter, 4'294'967'295)); | |
| 1194 | + return this; | |
| 1195 | +} | |
| 1196 | + | |
| 1197 | +QPDFJob::GlobalConfig* | |
| 1198 | +QPDFJob::GlobalConfig::parserMaxContainerSizeDamaged(const std::string& parameter) | |
| 1199 | +{ | |
| 1200 | + global::Limits::parser_max_container_size( | |
| 1201 | + true, to_uint32("parser-max-container-size-damaged", parameter, 4'294'967'295)); | |
| 1202 | + return this; | |
| 1203 | +} | |
| 1204 | + | |
| 1205 | +QPDFJob::GlobalConfig* | |
| 1206 | +QPDFJob::GlobalConfig::parserMaxErrors(const std::string& parameter) | |
| 1207 | +{ | |
| 1208 | + global::Limits::parser_max_errors(to_uint32("parser-max-errors", parameter)); | |
| 1209 | + return this; | |
| 1210 | +} | |
| 1211 | + | |
| 1212 | +QPDFJob::GlobalConfig* | |
| 1213 | +QPDFJob::GlobalConfig::parserMaxNesting(const std::string& parameter) | |
| 1214 | +{ | |
| 1215 | + global::Limits::parser_max_nesting(to_uint32("parser-max-nesting", parameter)); | |
| 1216 | + return this; | |
| 1217 | +} | |
| 1218 | + | |
| 1143 | 1219 | QPDFJob::Config* |
| 1144 | 1220 | QPDFJob::Config::setPageLabels(const std::vector<std::string>& specs) |
| 1145 | 1221 | { | ... | ... |
libqpdf/QPDFJob_json.cc
| ... | ... | @@ -63,6 +63,7 @@ namespace |
| 63 | 63 | std::shared_ptr<QPDFJob::Config> c_main; |
| 64 | 64 | std::shared_ptr<QPDFJob::CopyAttConfig> c_copy_att; |
| 65 | 65 | std::shared_ptr<QPDFJob::AttConfig> c_att; |
| 66 | + std::shared_ptr<QPDFJob::GlobalConfig> c_global; | |
| 66 | 67 | std::shared_ptr<QPDFJob::PagesConfig> c_pages; |
| 67 | 68 | std::shared_ptr<QPDFJob::UOConfig> c_uo; |
| 68 | 69 | std::shared_ptr<QPDFJob::EncConfig> c_enc; |
| ... | ... | @@ -620,6 +621,19 @@ Handlers::beginSetPageLabelsArray(JSON) |
| 620 | 621 | } |
| 621 | 622 | |
| 622 | 623 | void |
| 624 | +Handlers::beginGlobal(JSON) | |
| 625 | +{ | |
| 626 | + c_global = c_main->global(); | |
| 627 | +} | |
| 628 | + | |
| 629 | +void | |
| 630 | +Handlers::endGlobal() | |
| 631 | +{ | |
| 632 | + c_global->endGlobal(); | |
| 633 | + c_global = nullptr; | |
| 634 | +} | |
| 635 | + | |
| 636 | +void | |
| 623 | 637 | QPDFJob::initializeFromJson(std::string const& json, bool partial) |
| 624 | 638 | { |
| 625 | 639 | std::list<std::string> errors; | ... | ... |
libqpdf/QPDFParser.cc
| ... | ... | @@ -15,7 +15,7 @@ using namespace qpdf; |
| 15 | 15 | |
| 16 | 16 | using ObjectPtr = std::shared_ptr<QPDFObject>; |
| 17 | 17 | |
| 18 | -static uint32_t const& max_nesting{global::Limits::objects_max_nesting()}; | |
| 18 | +static uint32_t const& max_nesting{global::Limits::parser_max_nesting()}; | |
| 19 | 19 | |
| 20 | 20 | // The ParseGuard class allows QPDFParser to detect re-entrant parsing. It also provides |
| 21 | 21 | // special access to allow the parser to create unresolved objects and dangling references. |
| ... | ... | @@ -437,17 +437,16 @@ QPDFParser::parseRemainder(bool content_stream) |
| 437 | 437 | case QPDFTokenizer::tt_array_open: |
| 438 | 438 | case QPDFTokenizer::tt_dict_open: |
| 439 | 439 | if (stack.size() > max_nesting) { |
| 440 | - warn("ignoring excessively deeply nested data structure"); | |
| 441 | - return {}; | |
| 442 | - } else { | |
| 443 | - b_contents = false; | |
| 444 | - stack.emplace_back( | |
| 445 | - input, | |
| 446 | - (tokenizer.getType() == QPDFTokenizer::tt_array_open) ? st_array | |
| 447 | - : st_dictionary_key); | |
| 448 | - frame = &stack.back(); | |
| 449 | - continue; | |
| 440 | + limits_error( | |
| 441 | + "parser-max-nesting", "ignoring excessively deeply nested data structure"); | |
| 450 | 442 | } |
| 443 | + b_contents = false; | |
| 444 | + stack.emplace_back( | |
| 445 | + input, | |
| 446 | + (tokenizer.getType() == QPDFTokenizer::tt_array_open) ? st_array | |
| 447 | + : st_dictionary_key); | |
| 448 | + frame = &stack.back(); | |
| 449 | + continue; | |
| 451 | 450 | |
| 452 | 451 | case QPDFTokenizer::tt_bool: |
| 453 | 452 | addScalar<QPDF_Bool>(tokenizer.getValue() == "true"); |
| ... | ... | @@ -586,11 +585,11 @@ template <typename T, typename... Args> |
| 586 | 585 | void |
| 587 | 586 | QPDFParser::addScalar(Args&&... args) |
| 588 | 587 | { |
| 589 | - auto limit = Limits::objects_max_container_size(bad_count || sanity_checks); | |
| 590 | - if (frame->olist.size() > limit || frame->dict.size() > limit) { | |
| 588 | + auto limit = Limits::parser_max_container_size(bad_count || sanity_checks); | |
| 589 | + if (frame->olist.size() >= limit || frame->dict.size() >= limit) { | |
| 591 | 590 | // Stop adding scalars. We are going to abort when the close token or a bad token is |
| 592 | 591 | // encountered. |
| 593 | - max_bad_count = 0; | |
| 592 | + max_bad_count = 1; | |
| 594 | 593 | check_too_many_bad_tokens(); // always throws Error() |
| 595 | 594 | } |
| 596 | 595 | auto obj = QPDFObject::create<T>(std::forward<Args>(args)...); |
| ... | ... | @@ -644,25 +643,30 @@ QPDFParser::fixMissingKeys() |
| 644 | 643 | void |
| 645 | 644 | QPDFParser::check_too_many_bad_tokens() |
| 646 | 645 | { |
| 647 | - auto limit = Limits::objects_max_container_size(bad_count || sanity_checks); | |
| 648 | - if (frame->olist.size() > limit || frame->dict.size() > limit) { | |
| 646 | + auto limit = Limits::parser_max_container_size(bad_count || sanity_checks); | |
| 647 | + if (frame->olist.size() >= limit || frame->dict.size() >= limit) { | |
| 649 | 648 | if (bad_count) { |
| 650 | - warn( | |
| 649 | + limits_error( | |
| 650 | + "parser-max-container-size-damaged", | |
| 651 | 651 | "encountered errors while parsing an array or dictionary with more than " + |
| 652 | - std::to_string(limit) + " elements; giving up on reading object"); | |
| 653 | - throw Error(); | |
| 652 | + std::to_string(limit) + " elements; giving up on reading object"); | |
| 654 | 653 | } |
| 655 | - warn( | |
| 654 | + limits_error( | |
| 655 | + "parser-max-container-size", | |
| 656 | 656 | "encountered an array or dictionary with more than " + std::to_string(limit) + |
| 657 | - " elements during xref recovery; giving up on reading object"); | |
| 657 | + " elements during xref recovery; giving up on reading object"); | |
| 658 | 658 | } |
| 659 | - if (max_bad_count && --max_bad_count > 0 && good_count > 4) { | |
| 659 | + if (max_bad_count && --max_bad_count == 0) { | |
| 660 | + limits_error( | |
| 661 | + "parser-max-errors", "too many errors during parsing; treating object as null"); | |
| 662 | + } | |
| 663 | + if (good_count > 4) { | |
| 660 | 664 | good_count = 0; |
| 661 | 665 | bad_count = 1; |
| 662 | 666 | return; |
| 663 | 667 | } |
| 664 | 668 | if (++bad_count > 5 || |
| 665 | - (frame->state != st_array && QIntC::to_size(max_bad_count) < frame->olist.size())) { | |
| 669 | + (frame->state != st_array && std::cmp_less(max_bad_count, frame->olist.size()))) { | |
| 666 | 670 | // Give up after 5 errors in close proximity or if the number of missing dictionary keys |
| 667 | 671 | // exceeds the remaining number of allowable total errors. |
| 668 | 672 | warn("too many errors; giving up on reading object"); |
| ... | ... | @@ -672,6 +676,14 @@ QPDFParser::check_too_many_bad_tokens() |
| 672 | 676 | } |
| 673 | 677 | |
| 674 | 678 | void |
| 679 | +QPDFParser::limits_error(std::string const& limit, std::string const& msg) | |
| 680 | +{ | |
| 681 | + Limits::error(); | |
| 682 | + warn("limits error("s + limit + "): " + msg); | |
| 683 | + throw Error(); | |
| 684 | +} | |
| 685 | + | |
| 686 | +void | |
| 675 | 687 | QPDFParser::warn(QPDFExc const& e) const |
| 676 | 688 | { |
| 677 | 689 | // If parsing on behalf of a QPDF object and want to give a warning, we can warn through the | ... | ... |
libqpdf/QUtil.cc
| ... | ... | @@ -40,6 +40,7 @@ |
| 40 | 40 | #endif |
| 41 | 41 | |
| 42 | 42 | using namespace qpdf; |
| 43 | +using namespace std::literals; | |
| 43 | 44 | |
| 44 | 45 | // First element is 24 |
| 45 | 46 | static unsigned short pdf_doc_low_to_unicode[] = { |
| ... | ... | @@ -2068,3 +2069,15 @@ QUtil::is_hex_digit(char c) |
| 2068 | 2069 | { |
| 2069 | 2070 | return util::is_hex_digit(c); |
| 2070 | 2071 | } |
| 2072 | + | |
| 2073 | +void | |
| 2074 | +QUtil::handle_result_code(qpdf_result_e result, std::string_view context) | |
| 2075 | +{ | |
| 2076 | + if (result == qpdf_r_ok) { | |
| 2077 | + return; | |
| 2078 | + } | |
| 2079 | + qpdf::util::assertion( | |
| 2080 | + result == qpdf_r_bad_parameter, | |
| 2081 | + "unexpected result code received from function in "s.append(context)); | |
| 2082 | + throw std::logic_error("invalid parameter supplied to function in "s.append(context)); | |
| 2083 | +} | ... | ... |
libqpdf/global.cc
| 1 | 1 | #include <qpdf/global_private.hh> |
| 2 | 2 | |
| 3 | +#include <qpdf/Util.hh> | |
| 4 | + | |
| 3 | 5 | using namespace qpdf; |
| 6 | +using namespace qpdf::global; | |
| 7 | + | |
| 8 | +Limits Limits::l; | |
| 9 | +Options Options::o; | |
| 10 | + | |
| 11 | +void | |
| 12 | +Limits::parser_max_container_size(bool damaged, uint32_t value) | |
| 13 | +{ | |
| 14 | + if (damaged) { | |
| 15 | + l.parser_max_container_size_damaged_set_ = true; | |
| 16 | + l.parser_max_container_size_damaged_ = value; | |
| 17 | + } else { | |
| 18 | + l.parser_max_container_size_ = value; | |
| 19 | + } | |
| 20 | +} | |
| 21 | + | |
| 22 | +void | |
| 23 | +Limits::disable_defaults() | |
| 24 | +{ | |
| 25 | + if (!l.parser_max_errors_set_) { | |
| 26 | + l.parser_max_errors_ = 0; | |
| 27 | + } | |
| 28 | + if (!l.parser_max_container_size_damaged_set_) { | |
| 29 | + l.parser_max_container_size_damaged_ = std::numeric_limits<uint32_t>::max(); | |
| 30 | + } | |
| 31 | +} | |
| 32 | + | |
| 33 | +qpdf_result_e | |
| 34 | +qpdf_global_get_uint32(qpdf_param_e param, uint32_t* value) | |
| 35 | +{ | |
| 36 | + qpdf_expect(value); | |
| 37 | + switch (param) { | |
| 38 | + case qpdf_p_default_limits: | |
| 39 | + *value = Options::default_limits(); | |
| 40 | + return qpdf_r_ok; | |
| 41 | + case qpdf_p_limit_errors: | |
| 42 | + *value = Limits::errors(); | |
| 43 | + return qpdf_r_ok; | |
| 44 | + case qpdf_p_parser_max_nesting: | |
| 45 | + *value = Limits::parser_max_nesting(); | |
| 46 | + return qpdf_r_ok; | |
| 47 | + case qpdf_p_parser_max_errors: | |
| 48 | + *value = Limits::parser_max_errors(); | |
| 49 | + return qpdf_r_ok; | |
| 50 | + case qpdf_p_parser_max_container_size: | |
| 51 | + *value = Limits::parser_max_container_size(false); | |
| 52 | + return qpdf_r_ok; | |
| 53 | + case qpdf_p_parser_max_container_size_damaged: | |
| 54 | + *value = Limits::parser_max_container_size(true); | |
| 55 | + return qpdf_r_ok; | |
| 56 | + default: | |
| 57 | + return qpdf_r_bad_parameter; | |
| 58 | + } | |
| 59 | +} | |
| 4 | 60 | |
| 5 | -global::Limits global::Limits::l; | |
| 61 | +qpdf_result_e | |
| 62 | +qpdf_global_set_uint32(qpdf_param_e param, uint32_t value) | |
| 63 | +{ | |
| 64 | + switch (param) { | |
| 65 | + case qpdf_p_default_limits: | |
| 66 | + Options::default_limits(value); | |
| 67 | + return qpdf_r_ok; | |
| 68 | + case qpdf_p_parser_max_nesting: | |
| 69 | + Limits::parser_max_nesting(value); | |
| 70 | + return qpdf_r_ok; | |
| 71 | + case qpdf_p_parser_max_errors: | |
| 72 | + Limits::parser_max_errors(value); | |
| 73 | + return qpdf_r_ok; | |
| 74 | + case qpdf_p_parser_max_container_size: | |
| 75 | + Limits::parser_max_container_size(false, value); | |
| 76 | + return qpdf_r_ok; | |
| 77 | + case qpdf_p_parser_max_container_size_damaged: | |
| 78 | + Limits::parser_max_container_size(true, value); | |
| 79 | + return qpdf_r_ok; | |
| 80 | + default: | |
| 81 | + return qpdf_r_bad_parameter; | |
| 82 | + } | |
| 83 | +} | ... | ... |
libqpdf/qpdf/QPDFParser.hh
| ... | ... | @@ -124,6 +124,7 @@ class QPDFParser |
| 124 | 124 | void check_too_many_bad_tokens(); |
| 125 | 125 | void warnDuplicateKey(); |
| 126 | 126 | void fixMissingKeys(); |
| 127 | + [[noreturn]] void limits_error(std::string const& limit, std::string const& msg); | |
| 127 | 128 | void warn(qpdf_offset_t offset, std::string const& msg) const; |
| 128 | 129 | void warn(std::string const& msg) const; |
| 129 | 130 | void warn(QPDFExc const&) const; |
| ... | ... | @@ -149,7 +150,7 @@ class QPDFParser |
| 149 | 150 | // it only gets incremented or reset when a bad token is encountered. |
| 150 | 151 | int bad_count{0}; |
| 151 | 152 | // Number of bad tokens (remaining) before giving up. |
| 152 | - uint32_t max_bad_count{Limits::objects_max_errors()}; | |
| 153 | + uint32_t max_bad_count{Limits::parser_max_errors()}; | |
| 153 | 154 | // Number of good tokens since last bad token. Irrelevant if bad_count == 0. |
| 154 | 155 | int good_count{0}; |
| 155 | 156 | // Start offset including any leading whitespace. | ... | ... |
libqpdf/qpdf/auto_job_decl.hh
| ... | ... | @@ -5,6 +5,7 @@ |
| 5 | 5 | // |
| 6 | 6 | // clang-format off |
| 7 | 7 | // |
| 8 | +static constexpr char const* O_GLOBAL = "global"; | |
| 8 | 9 | static constexpr char const* O_PAGES = "pages"; |
| 9 | 10 | static constexpr char const* O_ENCRYPTION = "encryption"; |
| 10 | 11 | static constexpr char const* O_40_BIT_ENCRYPTION = "40-bit encryption"; |
| ... | ... | @@ -21,11 +22,13 @@ void argShowCrypto(); |
| 21 | 22 | void argJobJsonHelp(); |
| 22 | 23 | void argZopfli(); |
| 23 | 24 | void argJsonHelp(std::string const&); |
| 25 | +void argEndGlobal(); | |
| 24 | 26 | void argPositional(std::string const&); |
| 25 | 27 | void argAddAttachment(); |
| 26 | 28 | void argCopyAttachmentsFrom(); |
| 27 | 29 | void argEmpty(); |
| 28 | 30 | void argEncrypt(); |
| 31 | +void argGlobal(); | |
| 29 | 32 | void argOverlay(); |
| 30 | 33 | void argPages(); |
| 31 | 34 | void argReplaceInput(); | ... | ... |
libqpdf/qpdf/auto_job_help.hh
| ... | ... | @@ -1004,6 +1004,46 @@ Update a PDF file from a JSON file. Please see the "qpdf JSON" |
| 1004 | 1004 | chapter of the manual for information about how to use this |
| 1005 | 1005 | option. |
| 1006 | 1006 | )"); |
| 1007 | +ap.addHelpTopic("global", "options for changing the behaviour of qpdf", R"(The options below modify the overall behaviour of qpdf. This includes modifying | |
| 1008 | +implementation limits and changing modes of operation. | |
| 1009 | +)"); | |
| 1010 | +ap.addOptionHelp("--global", "global", "begin setting global options and limits", R"(--global [options] -- | |
| 1011 | + | |
| 1012 | +Begin setting global options and limits. | |
| 1013 | +)"); | |
| 1014 | +ap.addOptionHelp("--no-default-limits", "global", "disable optional default limits", R"(Disables all optional default limits. Explicitly set limits are unaffected. Some | |
| 1015 | +limits, especially limits designed to prevent stack overflow, cannot be removed | |
| 1016 | +with this option but can be modified. Where this is the case it is mentioned | |
| 1017 | +in the entry for the relevant option. | |
| 1018 | +)"); | |
| 1019 | +ap.addOptionHelp("--parser-max-nesting", "global", "set the maximum nesting level while parsing objects", R"(--parser-max-nesting=n | |
| 1020 | + | |
| 1021 | +Set the maximum nesting level while parsing objects. The maximum nesting level | |
| 1022 | +is not disabled by --no-default-limits. Defaults to 499. | |
| 1023 | +)"); | |
| 1024 | +ap.addOptionHelp("--parser-max-errors", "global", "set the maximum number of errors while parsing", R"(--parser-max-errors=n | |
| 1025 | + | |
| 1026 | +Set the maximum number of errors allowed while parsing an indirect object. | |
| 1027 | +A value of 0 means that no maximum is imposed. Defaults to 15. | |
| 1028 | +)"); | |
| 1029 | +ap.addOptionHelp("--parser-max-container-size", "global", "set the maximum container size while parsing", R"(--parser-max-container-size=n | |
| 1030 | + | |
| 1031 | +Set the maximum number of top-level objects allowed in a container while | |
| 1032 | +parsing. The limit applies when the PDF document's xref table is undamaged | |
| 1033 | +and the object itself can be parsed without errors. The default limit | |
| 1034 | +is 4,294,967,295. See also --parser-max-container-size-damaged. | |
| 1035 | +)"); | |
| 1036 | +ap.addOptionHelp("--parser-max-container-size-damaged", "global", "set the maximum container size while parsing damaged files", R"(--parser-max-container-size-damaged=n | |
| 1037 | + | |
| 1038 | +Set the maximum number of top-level objects allowed in a container while | |
| 1039 | +parsing. The limit applies when the PDF document's xref table is damaged | |
| 1040 | +or the object itself is damaged. The limit also applies when parsing | |
| 1041 | +xref streams. The default limit is 5,000. | |
| 1042 | +See also --parser-max-container-size. | |
| 1043 | +)"); | |
| 1044 | +} | |
| 1045 | +static void add_help_9(QPDFArgParser& ap) | |
| 1046 | +{ | |
| 1007 | 1047 | ap.addHelpTopic("testing", "options for testing or debugging", R"(The options below are useful when writing automated test code that |
| 1008 | 1048 | includes files created by qpdf or when testing qpdf itself. |
| 1009 | 1049 | )"); |
| ... | ... | @@ -1039,6 +1079,7 @@ static void add_help(QPDFArgParser& ap) |
| 1039 | 1079 | add_help_6(ap); |
| 1040 | 1080 | add_help_7(ap); |
| 1041 | 1081 | add_help_8(ap); |
| 1082 | + add_help_9(ap); | |
| 1042 | 1083 | ap.addHelpFooter("For detailed help, visit the qpdf manual: https://qpdf.readthedocs.io\n"); |
| 1043 | 1084 | } |
| 1044 | 1085 | ... | ... |
libqpdf/qpdf/auto_job_init.hh
| ... | ... | @@ -34,6 +34,12 @@ this->ap.addBare("show-crypto", b(&ArgParser::argShowCrypto)); |
| 34 | 34 | this->ap.addBare("job-json-help", b(&ArgParser::argJobJsonHelp)); |
| 35 | 35 | this->ap.addBare("zopfli", b(&ArgParser::argZopfli)); |
| 36 | 36 | this->ap.addChoices("json-help", p(&ArgParser::argJsonHelp), false, json_version_choices); |
| 37 | +this->ap.registerOptionTable("global", b(&ArgParser::argEndGlobal)); | |
| 38 | +this->ap.addBare("no-default-limits", [this](){c_global->noDefaultLimits();}); | |
| 39 | +this->ap.addRequiredParameter("parser-max-container-size", [this](std::string const& x){c_global->parserMaxContainerSize(x);}, "level"); | |
| 40 | +this->ap.addRequiredParameter("parser-max-container-size-damaged", [this](std::string const& x){c_global->parserMaxContainerSizeDamaged(x);}, "level"); | |
| 41 | +this->ap.addRequiredParameter("parser-max-errors", [this](std::string const& x){c_global->parserMaxErrors(x);}, "level"); | |
| 42 | +this->ap.addRequiredParameter("parser-max-nesting", [this](std::string const& x){c_global->parserMaxNesting(x);}, "level"); | |
| 37 | 43 | this->ap.selectMainOptionTable(); |
| 38 | 44 | this->ap.addPositional(p(&ArgParser::argPositional)); |
| 39 | 45 | this->ap.addBare("add-attachment", b(&ArgParser::argAddAttachment)); |
| ... | ... | @@ -50,6 +56,7 @@ this->ap.addBare("externalize-inline-images", [this](){c_main->externalizeInline |
| 50 | 56 | this->ap.addBare("filtered-stream-data", [this](){c_main->filteredStreamData();}); |
| 51 | 57 | this->ap.addBare("flatten-rotation", [this](){c_main->flattenRotation();}); |
| 52 | 58 | this->ap.addBare("generate-appearances", [this](){c_main->generateAppearances();}); |
| 59 | +this->ap.addBare("global", b(&ArgParser::argGlobal)); | |
| 53 | 60 | this->ap.addBare("ignore-xref-streams", [this](){c_main->ignoreXrefStreams();}); |
| 54 | 61 | this->ap.addBare("is-encrypted", [this](){c_main->isEncrypted();}); |
| 55 | 62 | this->ap.addBare("json-input", [this](){c_main->jsonInput();}); | ... | ... |
libqpdf/qpdf/auto_job_json_decl.hh
| ... | ... | @@ -24,6 +24,8 @@ void beginJsonKeyArray(JSON); |
| 24 | 24 | void endJsonKeyArray(); |
| 25 | 25 | void beginJsonObjectArray(JSON); |
| 26 | 26 | void endJsonObjectArray(); |
| 27 | +void beginGlobal(JSON); | |
| 28 | +void endGlobal(); | |
| 27 | 29 | void beginAddAttachmentArray(JSON); |
| 28 | 30 | void endAddAttachmentArray(); |
| 29 | 31 | void beginAddAttachment(JSON); | ... | ... |
libqpdf/qpdf/auto_job_json_init.hh
| ... | ... | @@ -272,6 +272,24 @@ popHandler(); // key: jsonStreamData |
| 272 | 272 | pushKey("jsonStreamPrefix"); |
| 273 | 273 | addParameter([this](std::string const& p) { c_main->jsonStreamPrefix(p); }); |
| 274 | 274 | popHandler(); // key: jsonStreamPrefix |
| 275 | +pushKey("global"); | |
| 276 | +beginDict(bindJSON(&Handlers::beginGlobal), bindBare(&Handlers::endGlobal)); // .global | |
| 277 | +pushKey("noDefaultLimits"); | |
| 278 | +addBare([this]() { c_global->noDefaultLimits(); }); | |
| 279 | +popHandler(); // key: noDefaultLimits | |
| 280 | +pushKey("parserMaxContainerSize"); | |
| 281 | +addParameter([this](std::string const& p) { c_global->parserMaxContainerSize(p); }); | |
| 282 | +popHandler(); // key: parserMaxContainerSize | |
| 283 | +pushKey("parserMaxContainerSizeDamaged"); | |
| 284 | +addParameter([this](std::string const& p) { c_global->parserMaxContainerSizeDamaged(p); }); | |
| 285 | +popHandler(); // key: parserMaxContainerSizeDamaged | |
| 286 | +pushKey("parserMaxErrors"); | |
| 287 | +addParameter([this](std::string const& p) { c_global->parserMaxErrors(p); }); | |
| 288 | +popHandler(); // key: parserMaxErrors | |
| 289 | +pushKey("parserMaxNesting"); | |
| 290 | +addParameter([this](std::string const& p) { c_global->parserMaxNesting(p); }); | |
| 291 | +popHandler(); // key: parserMaxNesting | |
| 292 | +popHandler(); // key: global | |
| 275 | 293 | pushKey("updateFromJson"); |
| 276 | 294 | addParameter([this](std::string const& p) { c_main->updateFromJson(p); }); |
| 277 | 295 | popHandler(); // key: updateFromJson | ... | ... |
libqpdf/qpdf/auto_job_schema.hh
| ... | ... | @@ -90,6 +90,13 @@ static constexpr char const* JOB_SCHEMA_DATA = R"({ |
| 90 | 90 | ], |
| 91 | 91 | "jsonStreamData": "how to handle streams in json output", |
| 92 | 92 | "jsonStreamPrefix": "prefix for json stream data files", |
| 93 | + "global": { | |
| 94 | + "noDefaultLimits": "disable optional default limits", | |
| 95 | + "parserMaxContainerSize": "set the maximum container size while parsing", | |
| 96 | + "parserMaxContainerSizeDamaged": "set the maximum container size while parsing damaged files", | |
| 97 | + "parserMaxErrors": "set the maximum number of errors while parsing", | |
| 98 | + "parserMaxNesting": "set the maximum nesting level while parsing objects" | |
| 99 | + }, | |
| 93 | 100 | "updateFromJson": "update a PDF from qpdf JSON", |
| 94 | 101 | "allowWeakCrypto": "allow insecure cryptographic algorithms", |
| 95 | 102 | "keepFilesOpen": "manage keeping multiple files open", | ... | ... |
libqpdf/qpdf/global_private.hh
| 1 | - | |
| 2 | 1 | #ifndef GLOBAL_PRIVATE_HH |
| 3 | 2 | #define GLOBAL_PRIVATE_HH |
| 4 | 3 | |
| 5 | -#include <qpdf/Constants.h> | |
| 4 | +#include <qpdf/global.hh> | |
| 6 | 5 | |
| 7 | -#include <cstdint> | |
| 8 | 6 | #include <limits> |
| 9 | 7 | |
| 10 | -namespace qpdf | |
| 8 | +namespace qpdf::global | |
| 11 | 9 | { |
| 12 | - namespace global | |
| 10 | + class Limits | |
| 13 | 11 | { |
| 14 | - class Limits | |
| 15 | - { | |
| 16 | - public: | |
| 17 | - Limits(Limits const&) = delete; | |
| 18 | - Limits(Limits&&) = delete; | |
| 19 | - Limits& operator=(Limits const&) = delete; | |
| 20 | - Limits& operator=(Limits&&) = delete; | |
| 21 | - | |
| 22 | - static uint32_t const& | |
| 23 | - objects_max_nesting() | |
| 24 | - { | |
| 25 | - return l.objects_max_nesting_; | |
| 26 | - } | |
| 12 | + public: | |
| 13 | + Limits(Limits const&) = delete; | |
| 14 | + Limits(Limits&&) = delete; | |
| 15 | + Limits& operator=(Limits const&) = delete; | |
| 16 | + Limits& operator=(Limits&&) = delete; | |
| 27 | 17 | |
| 28 | - static uint32_t const& | |
| 29 | - objects_max_errors() | |
| 30 | - { | |
| 31 | - return l.objects_max_errors_; | |
| 32 | - } | |
| 18 | + static uint32_t const& | |
| 19 | + parser_max_nesting() | |
| 20 | + { | |
| 21 | + return l.parser_max_nesting_; | |
| 22 | + } | |
| 23 | + | |
| 24 | + static void | |
| 25 | + parser_max_nesting(uint32_t value) | |
| 26 | + { | |
| 27 | + l.parser_max_nesting_ = value; | |
| 28 | + } | |
| 29 | + | |
| 30 | + static uint32_t const& | |
| 31 | + parser_max_errors() | |
| 32 | + { | |
| 33 | + return l.parser_max_errors_; | |
| 34 | + } | |
| 35 | + | |
| 36 | + static void | |
| 37 | + parser_max_errors(uint32_t value) | |
| 38 | + { | |
| 39 | + l.parser_max_errors_set_ = true; | |
| 40 | + l.parser_max_errors_ = value; | |
| 41 | + } | |
| 42 | + | |
| 43 | + static uint32_t const& | |
| 44 | + parser_max_container_size(bool damaged) | |
| 45 | + { | |
| 46 | + return damaged ? l.parser_max_container_size_damaged_ : l.parser_max_container_size_; | |
| 47 | + } | |
| 33 | 48 | |
| 34 | - static uint32_t const& | |
| 35 | - objects_max_container_size(bool damaged) | |
| 36 | - { | |
| 37 | - return damaged ? l.objects_max_container_size_damaged_ | |
| 38 | - : l.objects_max_container_size_; | |
| 49 | + static void parser_max_container_size(bool damaged, uint32_t value); | |
| 50 | + | |
| 51 | + /// Record a limit error. | |
| 52 | + static void | |
| 53 | + error() | |
| 54 | + { | |
| 55 | + if (l.errors_ < std::numeric_limits<uint32_t>::max()) { | |
| 56 | + ++l.errors_; | |
| 39 | 57 | } |
| 58 | + } | |
| 59 | + | |
| 60 | + static uint32_t const& | |
| 61 | + errors() | |
| 62 | + { | |
| 63 | + return l.errors_; | |
| 64 | + } | |
| 65 | + | |
| 66 | + static void disable_defaults(); | |
| 67 | + | |
| 68 | + private: | |
| 69 | + Limits() = default; | |
| 70 | + ~Limits() = default; | |
| 40 | 71 | |
| 41 | - private: | |
| 42 | - Limits() = default; | |
| 43 | - ~Limits() = default; | |
| 72 | + static Limits l; | |
| 44 | 73 | |
| 45 | - static Limits l; | |
| 74 | + uint32_t errors_{0}; | |
| 46 | 75 | |
| 47 | - uint32_t objects_max_nesting_{499}; | |
| 48 | - uint32_t objects_max_errors_{15}; | |
| 49 | - uint32_t objects_max_container_size_{std::numeric_limits<uint32_t>::max()}; | |
| 50 | - uint32_t objects_max_container_size_damaged_{5'000}; | |
| 51 | - }; | |
| 76 | + uint32_t parser_max_nesting_{499}; | |
| 77 | + uint32_t parser_max_errors_{15}; | |
| 78 | + bool parser_max_errors_set_{false}; | |
| 79 | + uint32_t parser_max_container_size_{std::numeric_limits<uint32_t>::max()}; | |
| 80 | + uint32_t parser_max_container_size_damaged_{5'000}; | |
| 81 | + bool parser_max_container_size_damaged_set_{false}; | |
| 82 | + }; | |
| 83 | + | |
| 84 | + class Options | |
| 85 | + { | |
| 86 | + public: | |
| 87 | + static bool | |
| 88 | + default_limits() | |
| 89 | + { | |
| 90 | + return static_cast<bool>(o.default_limits_); | |
| 91 | + } | |
| 92 | + | |
| 93 | + static void | |
| 94 | + default_limits(bool value) | |
| 95 | + { | |
| 96 | + if (!value) { | |
| 97 | + o.default_limits_ = false; | |
| 98 | + Limits::disable_defaults(); | |
| 99 | + } | |
| 100 | + } | |
| 52 | 101 | |
| 53 | - } // namespace global | |
| 102 | + private: | |
| 103 | + static Options o; | |
| 54 | 104 | |
| 55 | -} // namespace qpdf | |
| 105 | + bool default_limits_{true}; | |
| 106 | + }; | |
| 107 | +} // namespace qpdf::global | |
| 56 | 108 | |
| 57 | 109 | #endif // GLOBAL_PRIVATE_HH | ... | ... |
libtests/objects.cc
| ... | ... | @@ -5,8 +5,10 @@ |
| 5 | 5 | #include <qpdf/QPDF.hh> |
| 6 | 6 | |
| 7 | 7 | #include <qpdf/QIntC.hh> |
| 8 | +#include <qpdf/QPDFJob.hh> | |
| 8 | 9 | #include <qpdf/QPDFObjectHandle_private.hh> |
| 9 | 10 | #include <qpdf/QUtil.hh> |
| 11 | +#include <qpdf/global.hh> | |
| 10 | 12 | |
| 11 | 13 | #include <climits> |
| 12 | 14 | #include <cstdio> |
| ... | ... | @@ -153,6 +155,132 @@ test_1(QPDF& pdf, char const* arg2) |
| 153 | 155 | assert(QPDFObjectHandle(d).getDictAsMap().size() == 4); |
| 154 | 156 | } |
| 155 | 157 | |
| 158 | +static void | |
| 159 | +test_2(QPDF& pdf, char const* arg2) | |
| 160 | +{ | |
| 161 | + // Test global limits. | |
| 162 | + using namespace qpdf::global::options; | |
| 163 | + using namespace qpdf::global::limits; | |
| 164 | + | |
| 165 | + // Check default values | |
| 166 | + assert(parser_max_nesting() == 499); | |
| 167 | + assert(parser_max_errors() == 15); | |
| 168 | + assert(parser_max_container_size() == std::numeric_limits<uint32_t>::max()); | |
| 169 | + assert(parser_max_container_size_damaged() == 5'000); | |
| 170 | + assert(default_limits()); | |
| 171 | + | |
| 172 | + // Test disabling optional default limits | |
| 173 | + default_limits(false); | |
| 174 | + assert(parser_max_nesting() == 499); | |
| 175 | + assert(parser_max_errors() == 0); | |
| 176 | + assert(parser_max_container_size() == std::numeric_limits<uint32_t>::max()); | |
| 177 | + assert(parser_max_container_size_damaged() == std::numeric_limits<uint32_t>::max()); | |
| 178 | + assert(!default_limits()); | |
| 179 | + | |
| 180 | + // Check disabling default limits is irreversible | |
| 181 | + default_limits(true); | |
| 182 | + assert(!default_limits()); | |
| 183 | + | |
| 184 | + // Test setting limits | |
| 185 | + parser_max_nesting(11); | |
| 186 | + parser_max_errors(12); | |
| 187 | + parser_max_container_size(13); | |
| 188 | + parser_max_container_size_damaged(14); | |
| 189 | + | |
| 190 | + assert(parser_max_nesting() == 11); | |
| 191 | + assert(parser_max_errors() == 12); | |
| 192 | + assert(parser_max_container_size() == 13); | |
| 193 | + assert(parser_max_container_size_damaged() == 14); | |
| 194 | + | |
| 195 | + // Check disabling default limits does not override explicit limits | |
| 196 | + default_limits(false); | |
| 197 | + assert(parser_max_nesting() == 11); | |
| 198 | + assert(parser_max_errors() == 12); | |
| 199 | + assert(parser_max_container_size() == 13); | |
| 200 | + assert(parser_max_container_size_damaged() == 14); | |
| 201 | + | |
| 202 | + // Test parameter checking | |
| 203 | + QUtil::handle_result_code(qpdf_r_ok, ""); | |
| 204 | + bool thrown = false; | |
| 205 | + try { | |
| 206 | + qpdf::global::handle_result(qpdf_r_success_mask); | |
| 207 | + } catch (std::logic_error const&) { | |
| 208 | + thrown = true; | |
| 209 | + } | |
| 210 | + assert(thrown); | |
| 211 | + thrown = false; | |
| 212 | + try { | |
| 213 | + qpdf::global::get_uint32(qpdf_param_e(42)); | |
| 214 | + } catch (std::logic_error const&) { | |
| 215 | + thrown = true; | |
| 216 | + } | |
| 217 | + assert(thrown); | |
| 218 | + thrown = false; | |
| 219 | + try { | |
| 220 | + qpdf::global::set_uint32(qpdf_param_e(42), 42); | |
| 221 | + } catch (std::logic_error const&) { | |
| 222 | + thrown = true; | |
| 223 | + } | |
| 224 | + assert(thrown); | |
| 225 | + | |
| 226 | + /* Test limit errors */ | |
| 227 | + assert(qpdf::global::limit_errors() == 0); | |
| 228 | + QPDFObjectHandle::parse("[[[[]]]]"); | |
| 229 | + assert(qpdf::global::limit_errors() == 0); | |
| 230 | + parser_max_nesting(3); | |
| 231 | + try { | |
| 232 | + QPDFObjectHandle::parse("[[[[[]]]]]"); | |
| 233 | + } catch (std::exception&) { | |
| 234 | + } | |
| 235 | + assert(qpdf::global::limit_errors() == 1); | |
| 236 | + try { | |
| 237 | + QPDFObjectHandle::parse("[[[[[]]]]]"); | |
| 238 | + } catch (std::exception&) { | |
| 239 | + } | |
| 240 | + assert(qpdf::global::limit_errors() == 2); | |
| 241 | + | |
| 242 | + // Test global settings using the QPDFJob interface | |
| 243 | + QPDFJob j; | |
| 244 | + j.config() | |
| 245 | + ->inputFile("minimal.pdf") | |
| 246 | + ->global() | |
| 247 | + ->parserMaxNesting("111") | |
| 248 | + ->parserMaxErrors("112") | |
| 249 | + ->parserMaxContainerSize("113") | |
| 250 | + ->parserMaxContainerSizeDamaged("114") | |
| 251 | + ->noDefaultLimits() | |
| 252 | + ->endGlobal() | |
| 253 | + ->outputFile("a.pdf"); | |
| 254 | + auto qpdf = j.createQPDF(); | |
| 255 | + assert(parser_max_nesting() == 111); | |
| 256 | + assert(parser_max_errors() == 112); | |
| 257 | + assert(parser_max_container_size() == 113); | |
| 258 | + assert(parser_max_container_size_damaged() == 114); | |
| 259 | + assert(!default_limits()); | |
| 260 | + | |
| 261 | + // Test global settings using the JobJSON | |
| 262 | + QPDFJob jj; | |
| 263 | + jj.initializeFromJson(R"( | |
| 264 | + { | |
| 265 | + "inputFile": "minimal.pdf", | |
| 266 | + "global": { | |
| 267 | + "parserMaxNesting": "211", | |
| 268 | + "parserMaxErrors": "212", | |
| 269 | + "parserMaxContainerSize": "213", | |
| 270 | + "parserMaxContainerSizeDamaged": "214", | |
| 271 | + "noDefaultLimits": "" | |
| 272 | + }, | |
| 273 | + "outputFile": "a.pdf" | |
| 274 | + } | |
| 275 | + )"); | |
| 276 | + qpdf = j.createQPDF(); | |
| 277 | + assert(parser_max_nesting() == 211); | |
| 278 | + assert(parser_max_errors() == 212); | |
| 279 | + assert(parser_max_container_size() == 213); | |
| 280 | + assert(parser_max_container_size_damaged() == 214); | |
| 281 | + assert(!default_limits()); | |
| 282 | +} | |
| 283 | + | |
| 156 | 284 | void |
| 157 | 285 | runtest(int n, char const* filename1, char const* arg2) |
| 158 | 286 | { |
| ... | ... | @@ -160,9 +288,7 @@ runtest(int n, char const* filename1, char const* arg2) |
| 160 | 288 | // the test suite to see how the test is invoked to find the file |
| 161 | 289 | // that the test is supposed to operate on. |
| 162 | 290 | |
| 163 | - std::set<int> ignore_filename = { | |
| 164 | - 1, | |
| 165 | - }; | |
| 291 | + std::set<int> ignore_filename = {1, 2}; | |
| 166 | 292 | |
| 167 | 293 | QPDF pdf; |
| 168 | 294 | std::shared_ptr<char> file_buf; |
| ... | ... | @@ -176,9 +302,7 @@ runtest(int n, char const* filename1, char const* arg2) |
| 176 | 302 | } |
| 177 | 303 | |
| 178 | 304 | std::map<int, void (*)(QPDF&, char const*)> test_functions = { |
| 179 | - {0, test_0}, | |
| 180 | - {1, test_1}, | |
| 181 | - }; | |
| 305 | + {0, test_0}, {1, test_1}, {2, test_2}}; | |
| 182 | 306 | |
| 183 | 307 | auto fn = test_functions.find(n); |
| 184 | 308 | if (fn == test_functions.end()) { | ... | ... |
libtests/qtest/objects.test
| ... | ... | @@ -11,7 +11,7 @@ require TestDriver; |
| 11 | 11 | |
| 12 | 12 | my $td = new TestDriver('objects'); |
| 13 | 13 | |
| 14 | -my $n_tests = 2; | |
| 14 | +my $n_tests = 3; | |
| 15 | 15 | |
| 16 | 16 | $td->runtest("integer type checks", |
| 17 | 17 | {$td->COMMAND => "objects 0 minimal.pdf"}, |
| ... | ... | @@ -23,4 +23,9 @@ $td->runtest("dictionary checks", |
| 23 | 23 | {$td->STRING => => "test 1 done\n", $td->EXIT_STATUS => 0}, |
| 24 | 24 | $td->NORMALIZE_NEWLINES); |
| 25 | 25 | |
| 26 | +$td->runtest("global object limits", | |
| 27 | + {$td->COMMAND => "objects 2 -"}, | |
| 28 | + {$td->STRING => => "test 2 done\n", $td->EXIT_STATUS => 0}, | |
| 29 | + $td->NORMALIZE_NEWLINES); | |
| 30 | + | |
| 26 | 31 | $td->report($n_tests); | ... | ... |
manual/cli.rst
| ... | ... | @@ -3762,6 +3762,110 @@ Related Options |
| 3762 | 3762 | For a information about how to use this option, please see |
| 3763 | 3763 | :ref:`json`. |
| 3764 | 3764 | |
| 3765 | +.. _global-options: | |
| 3766 | + | |
| 3767 | +Global Options | |
| 3768 | +-------------- | |
| 3769 | + | |
| 3770 | +.. help-topic global: options for changing the behaviour of qpdf | |
| 3771 | + | |
| 3772 | + The options below modify the overall behaviour of qpdf. This includes modifying | |
| 3773 | + implementation limits and changing modes of operation. | |
| 3774 | + | |
| 3775 | +The options below modify the overall behaviour of qpdf. This includes modifying implementation | |
| 3776 | +limits and changing modes of operation. | |
| 3777 | + | |
| 3778 | +Related Options | |
| 3779 | +~~~~~~~~~~~~~~~ | |
| 3780 | + | |
| 3781 | +.. qpdf:option:: --global [options] -- | |
| 3782 | + | |
| 3783 | + .. help: begin setting global options and limits | |
| 3784 | + | |
| 3785 | + Begin setting global options and limits. | |
| 3786 | + | |
| 3787 | +Begin setting global options and limits. | |
| 3788 | + | |
| 3789 | + | |
| 3790 | +Global Limits | |
| 3791 | +~~~~~~~~~~~~~ | |
| 3792 | + | |
| 3793 | +qpdf uses a number of global limits to protect itself from damaged and specially constructed PDF | |
| 3794 | +files. Without these limits such files can cause qpdf to crash and/or to consume excessive | |
| 3795 | +processor and memory resources. Very few legitimate PDF files exceed these limits, however | |
| 3796 | +where necessary the limits can be modified or entirely removed by the following options. | |
| 3797 | + | |
| 3798 | +.. qpdf:option:: --no-default-limits | |
| 3799 | + | |
| 3800 | + .. help: disable optional default limits | |
| 3801 | + | |
| 3802 | + Disables all optional default limits. Explicitly set limits are unaffected. Some | |
| 3803 | + limits, especially limits designed to prevent stack overflow, cannot be removed | |
| 3804 | + with this option but can be modified. Where this is the case it is mentioned | |
| 3805 | + in the entry for the relevant option. | |
| 3806 | + | |
| 3807 | +Disables all optional default limits. Explicitly set limits are unaffected. Some limits, | |
| 3808 | +especially limits designed to prevent stack overflow, cannot be removed with this option | |
| 3809 | +but can be modified. Where this is the case it is mentioned in the entry for the relevant | |
| 3810 | +option. | |
| 3811 | + | |
| 3812 | +Parser Limits | |
| 3813 | +............. | |
| 3814 | + | |
| 3815 | +.. qpdf:option:: --parser-max-nesting=n | |
| 3816 | + | |
| 3817 | + .. help: set the maximum nesting level while parsing objects | |
| 3818 | + | |
| 3819 | + Set the maximum nesting level while parsing objects. The maximum nesting level | |
| 3820 | + is not disabled by --no-default-limits. Defaults to 499. | |
| 3821 | + | |
| 3822 | +Set the maximum nesting level while parsing objects. The maximum nesting level is not | |
| 3823 | +disabled by :qpdf:ref:`--no-default-limits`. Defaults to 499. | |
| 3824 | + | |
| 3825 | + | |
| 3826 | +.. qpdf:option:: --parser-max-errors=n | |
| 3827 | + | |
| 3828 | + .. help: set the maximum number of errors while parsing | |
| 3829 | + | |
| 3830 | + Set the maximum number of errors allowed while parsing an indirect object. | |
| 3831 | + A value of 0 means that no maximum is imposed. Defaults to 15. | |
| 3832 | + | |
| 3833 | +Set the maximum number of errors allowed while parsing an indirect object. | |
| 3834 | +A value of 0 means that no maximum is imposed. Defaults to 15. | |
| 3835 | + | |
| 3836 | +.. qpdf:option:: --parser-max-container-size=n | |
| 3837 | + | |
| 3838 | + .. help: set the maximum container size while parsing | |
| 3839 | + | |
| 3840 | + Set the maximum number of top-level objects allowed in a container while | |
| 3841 | + parsing. The limit applies when the PDF document's xref table is undamaged | |
| 3842 | + and the object itself can be parsed without errors. The default limit | |
| 3843 | + is 4,294,967,295. See also --parser-max-container-size-damaged. | |
| 3844 | + | |
| 3845 | + | |
| 3846 | +Set the maximum number of top-level objects allowed in a container while | |
| 3847 | +parsing. The limit applies when the PDF document's xref table is undamaged | |
| 3848 | +and the object itself can be parsed without errors. The default limit | |
| 3849 | +is 4,294,967,295. See also :qpdf:ref:`--parser-max-container-size-damaged`. | |
| 3850 | + | |
| 3851 | + | |
| 3852 | +.. qpdf:option:: --parser-max-container-size-damaged=n | |
| 3853 | + | |
| 3854 | + .. help: set the maximum container size while parsing damaged files | |
| 3855 | + | |
| 3856 | + Set the maximum number of top-level objects allowed in a container while | |
| 3857 | + parsing. The limit applies when the PDF document's xref table is damaged | |
| 3858 | + or the object itself is damaged. The limit also applies when parsing | |
| 3859 | + xref streams. The default limit is 5,000. | |
| 3860 | + See also --parser-max-container-size. | |
| 3861 | + | |
| 3862 | + | |
| 3863 | +Set the maximum number of top-level objects allowed in a container while | |
| 3864 | +parsing. The limit applies when the PDF document's xref table is damaged | |
| 3865 | +or the object itself is damaged. The limit also applies when parsing | |
| 3866 | +xref streams. The default limit is 5,000. | |
| 3867 | +See also :qpdf:ref:`--parser-max-container-size`. | |
| 3868 | + | |
| 3765 | 3869 | .. _test-options: |
| 3766 | 3870 | |
| 3767 | 3871 | Options for Testing or Debugging | ... | ... |
manual/qpdf.1
| ... | ... | @@ -1191,6 +1191,51 @@ how to use this option. |
| 1191 | 1191 | Update a PDF file from a JSON file. Please see the "qpdf JSON" |
| 1192 | 1192 | chapter of the manual for information about how to use this |
| 1193 | 1193 | option. |
| 1194 | +.SH GLOBAL (options for changing the behaviour of qpdf) | |
| 1195 | +The options below modify the overall behaviour of qpdf. This includes modifying | |
| 1196 | +implementation limits and changing modes of operation. | |
| 1197 | +.PP | |
| 1198 | +Related Options: | |
| 1199 | +.TP | |
| 1200 | +.B --global \-\- begin setting global options and limits | |
| 1201 | +--global [options] -- | |
| 1202 | + | |
| 1203 | +Begin setting global options and limits. | |
| 1204 | +.TP | |
| 1205 | +.B --no-default-limits \-\- disable optional default limits | |
| 1206 | +Disables all optional default limits. Explicitly set limits are unaffected. Some | |
| 1207 | +limits, especially limits designed to prevent stack overflow, cannot be removed | |
| 1208 | +with this option but can be modified. Where this is the case it is mentioned | |
| 1209 | +in the entry for the relevant option. | |
| 1210 | +.TP | |
| 1211 | +.B --parser-max-nesting \-\- set the maximum nesting level while parsing objects | |
| 1212 | +--parser-max-nesting=n | |
| 1213 | + | |
| 1214 | +Set the maximum nesting level while parsing objects. The maximum nesting level | |
| 1215 | +is not disabled by --no-default-limits. Defaults to 499. | |
| 1216 | +.TP | |
| 1217 | +.B --parser-max-errors \-\- set the maximum number of errors while parsing | |
| 1218 | +--parser-max-errors=n | |
| 1219 | + | |
| 1220 | +Set the maximum number of errors allowed while parsing an indirect object. | |
| 1221 | +A value of 0 means that no maximum is imposed. Defaults to 15. | |
| 1222 | +.TP | |
| 1223 | +.B --parser-max-container-size \-\- set the maximum container size while parsing | |
| 1224 | +--parser-max-container-size=n | |
| 1225 | + | |
| 1226 | +Set the maximum number of top-level objects allowed in a container while | |
| 1227 | +parsing. The limit applies when the PDF document's xref table is undamaged | |
| 1228 | +and the object itself can be parsed without errors. The default limit | |
| 1229 | +is 4,294,967,295. See also --parser-max-container-size-damaged. | |
| 1230 | +.TP | |
| 1231 | +.B --parser-max-container-size-damaged \-\- set the maximum container size while parsing damaged files | |
| 1232 | +--parser-max-container-size-damaged=n | |
| 1233 | + | |
| 1234 | +Set the maximum number of top-level objects allowed in a container while | |
| 1235 | +parsing. The limit applies when the PDF document's xref table is damaged | |
| 1236 | +or the object itself is damaged. The limit also applies when parsing | |
| 1237 | +xref streams. The default limit is 5,000. | |
| 1238 | +See also --parser-max-container-size. | |
| 1194 | 1239 | .SH TESTING (options for testing or debugging) |
| 1195 | 1240 | The options below are useful when writing automated test code that |
| 1196 | 1241 | includes files created by qpdf or when testing qpdf itself. | ... | ... |
manual/release-notes.rst
| ... | ... | @@ -33,7 +33,7 @@ more detail. |
| 33 | 33 | |
| 34 | 34 | - Bug fixes |
| 35 | 35 | |
| 36 | - - Set `is_different` flag in `QPDFFormFieldObjectHelper::getTopLevelField` to | |
| 36 | + - Set ``is_different`` flag in ``QPDFFormFieldObjectHelper::getTopLevelField`` to | |
| 37 | 37 | false if the field is a top-level field. Previously the flag was only set |
| 38 | 38 | if the field is a top-level field. |
| 39 | 39 | |
| ... | ... | @@ -58,7 +58,15 @@ more detail. |
| 58 | 58 | - Add new ``Buffer`` methods ``move``, ``view``, ``data``, ``size`` and |
| 59 | 59 | ``empty``. The new methods present the ``Buffer`` as a ``char`` (rather |
| 60 | 60 | than ``unsigned char``) container and facilitate the efficient moving |
| 61 | - of its content into a `std::string``. | |
| 61 | + of its content into a ``std::string``. | |
| 62 | + | |
| 63 | + - Add various new functions in the ``qpdf::`global`` namespace to access | |
| 64 | + and set/modify global settings and limits. See :ref:`global-options` | |
| 65 | + and header file ``qpdf/global.hh`` for further detail. | |
| 66 | + | |
| 67 | + - Add new C-API functions ``qpdf_global_get_uint32`` and | |
| 68 | + ``qpdf_global_set_uint32`` to access and set/modify various global | |
| 69 | + settings and limits. | |
| 62 | 70 | |
| 63 | 71 | - Build fixes |
| 64 | 72 | |
| ... | ... | @@ -74,6 +82,9 @@ more detail. |
| 74 | 82 | - Option :qpdf:ref:`--check` now includes additional basic checks of the |
| 75 | 83 | AcroForm, Dests, Outlines, and PageLabels structures. |
| 76 | 84 | |
| 85 | + - Add new option :qpdf:ref:`--global` to set or modify various global | |
| 86 | + options and limits. See :ref:`global-options` for further detail. | |
| 87 | + | |
| 77 | 88 | - Fix completion scripts and handling to avoid leaking arguments |
| 78 | 89 | into the environment during completion and to correctly handle |
| 79 | 90 | ``bashcompinit`` for zsh users. | ... | ... |
qpdf/qtest/arg-parsing.test
| ... | ... | @@ -15,7 +15,7 @@ cleanup(); |
| 15 | 15 | |
| 16 | 16 | my $td = new TestDriver('arg-parsing'); |
| 17 | 17 | |
| 18 | -my $n_tests = 32; | |
| 18 | +my $n_tests = 33; | |
| 19 | 19 | |
| 20 | 20 | $td->runtest("required argument", |
| 21 | 21 | {$td->COMMAND => "qpdf --password minimal.pdf"}, |
| ... | ... | @@ -187,5 +187,12 @@ $td->runtest("bad jpeg-quality", |
| 187 | 187 | $td->EXIT_STATUS => 2}, |
| 188 | 188 | $td->NORMALIZE_NEWLINES); |
| 189 | 189 | |
| 190 | +$td->runtest("bad objects-container-max-container-size", | |
| 191 | + {$td->COMMAND => "qpdf --global --parser-max-container-size=-1 -- minimal.pdf"}, | |
| 192 | + {$td->REGEXP => | |
| 193 | + "invalid parser-max-container-size: must be a number between 0 and 4294967295", | |
| 194 | + $td->EXIT_STATUS => 2}, | |
| 195 | + $td->NORMALIZE_NEWLINES); | |
| 196 | + | |
| 190 | 197 | cleanup(); |
| 191 | 198 | $td->report($n_tests); | ... | ... |
qpdf/qtest/global.test
0 → 100644
| 1 | +#!/usr/bin/env perl | |
| 2 | +require 5.008; | |
| 3 | +use warnings; | |
| 4 | +use strict; | |
| 5 | +use File::Copy; | |
| 6 | + | |
| 7 | +unshift(@INC, '.'); | |
| 8 | +require qpdf_test_helpers; | |
| 9 | + | |
| 10 | +chdir("qpdf") or die "chdir testdir failed: $!\n"; | |
| 11 | + | |
| 12 | +require TestDriver; | |
| 13 | + | |
| 14 | +cleanup(); | |
| 15 | + | |
| 16 | +my $td = new TestDriver('global'); | |
| 17 | + | |
| 18 | +my $n_tests = 4; | |
| 19 | + | |
| 20 | +$td->runtest("parser-max-nesting", | |
| 21 | + {$td->COMMAND => "qpdf --global --parser-max-nesting=4 -- global.pdf a.pdf"}, | |
| 22 | + {$td->FILE => "global1.out", | |
| 23 | + $td->EXIT_STATUS => 3}, | |
| 24 | + $td->NORMALIZE_NEWLINES); | |
| 25 | + | |
| 26 | +$td->runtest("parser-max-errors", | |
| 27 | + {$td->COMMAND => "qpdf --global --parser-max-errors=2 -- global_damaged.pdf a.pdf"}, | |
| 28 | + {$td->FILE => "global2.out", | |
| 29 | + $td->EXIT_STATUS => 3}, | |
| 30 | + $td->NORMALIZE_NEWLINES); | |
| 31 | + | |
| 32 | +$td->runtest("parser-max-container-size", | |
| 33 | + {$td->COMMAND => "qpdf --global --parser-max-container-size=3 -- global.pdf a.pdf"}, | |
| 34 | + {$td->FILE => "global3.out", | |
| 35 | + $td->EXIT_STATUS => 3}, | |
| 36 | + $td->NORMALIZE_NEWLINES); | |
| 37 | + | |
| 38 | +$td->runtest("parser-max-container-size-damaged", | |
| 39 | + {$td->COMMAND => "qpdf --global --parser-max-container-size-damaged=9 -- global_damaged.pdf a.pdf"}, | |
| 40 | + {$td->FILE => "global4.out", | |
| 41 | + $td->EXIT_STATUS => 3}, | |
| 42 | + $td->NORMALIZE_NEWLINES); | |
| 43 | + | |
| 44 | +cleanup(); | |
| 45 | +$td->report($n_tests); | ... | ... |
qpdf/qtest/qpdf/global.pdf
0 → 100644
| 1 | +%PDF-1.3 | |
| 2 | +1 0 obj | |
| 3 | +<< | |
| 4 | + /Type /Catalog | |
| 5 | + /Pages 2 0 R | |
| 6 | +>> | |
| 7 | +endobj | |
| 8 | + | |
| 9 | +2 0 obj | |
| 10 | +<< | |
| 11 | + /Type /Pages | |
| 12 | + /Kids [ | |
| 13 | + 3 0 R | |
| 14 | + ] | |
| 15 | + /Count 1 | |
| 16 | +>> | |
| 17 | +endobj | |
| 18 | + | |
| 19 | +3 0 obj | |
| 20 | +<< | |
| 21 | + /Type /Page | |
| 22 | + /Parent 2 0 R | |
| 23 | + /MediaBox [0 0 612 792] | |
| 24 | + /Contents 4 0 R | |
| 25 | + /Resources << | |
| 26 | + /ProcSet 5 0 R | |
| 27 | + /Font << | |
| 28 | + /F1 6 0 R | |
| 29 | + >> | |
| 30 | + >> | |
| 31 | +>> | |
| 32 | +endobj | |
| 33 | + | |
| 34 | +4 0 obj | |
| 35 | +<< | |
| 36 | + /Length 44 | |
| 37 | +>> | |
| 38 | +stream | |
| 39 | +BT | |
| 40 | + /F1 24 Tf | |
| 41 | + 72 720 Td | |
| 42 | + (Potato) Tj | |
| 43 | +ET | |
| 44 | +endstream | |
| 45 | +endobj | |
| 46 | + | |
| 47 | +5 0 obj | |
| 48 | +[ | |
| 49 | ||
| 50 | + /Text | |
| 51 | +] | |
| 52 | +endobj | |
| 53 | + | |
| 54 | +6 0 obj | |
| 55 | +<< | |
| 56 | + /Type /Font | |
| 57 | + /Subtype /Type1 | |
| 58 | + /Name /F1 | |
| 59 | + /BaseFont /Helvetica | |
| 60 | + /Encoding /WinAnsiEncoding | |
| 61 | +>> | |
| 62 | +endobj | |
| 63 | + | |
| 64 | +xref | |
| 65 | +0 7 | |
| 66 | +0000000000 65535 f | |
| 67 | +0000000009 00000 n | |
| 68 | +0000000063 00000 n | |
| 69 | +0000000135 00000 n | |
| 70 | +0000000307 00000 n | |
| 71 | +0000000403 00000 n | |
| 72 | +0000000438 00000 n | |
| 73 | +trailer << | |
| 74 | + /Size 7 | |
| 75 | + /Root 1 0 R | |
| 76 | + /Nesting [ [ [ [ [ /1 /2 /3 /4 /5 /6 /7 /8 /9 /10 ] ] ] ] ] | |
| 77 | +>> | |
| 78 | +startxref | |
| 79 | +556 | |
| 80 | +%%EOF | ... | ... |
qpdf/qtest/qpdf/global1.out
0 → 100644
| 1 | +WARNING: global.pdf (trailer, offset 759): limits error(parser-max-nesting): ignoring excessively deeply nested data structure | |
| 2 | +WARNING: global.pdf: file is damaged | |
| 3 | +WARNING: global.pdf (offset 712): expected trailer dictionary | |
| 4 | +WARNING: global.pdf: Attempting to reconstruct cross-reference table | |
| 5 | +WARNING: global.pdf (trailer, offset 759): limits error(parser-max-nesting): ignoring excessively deeply nested data structure | |
| 6 | +WARNING: global.pdf: unable to find trailer dictionary while recovering damaged file | |
| 7 | +qpdf: operation succeeded with warnings; resulting file may have some problems | |
| 8 | +qpdf: some configurable limits were exceeded; for more details see https://qpdf.readthedocs.io/en/stable/cli.html#global-limits | ... | ... |
qpdf/qtest/qpdf/global2.out
0 → 100644
| 1 | +WARNING: global_damaged.pdf: file is damaged | |
| 2 | +WARNING: global_damaged.pdf (offset 55): xref not found | |
| 3 | +WARNING: global_damaged.pdf: Attempting to reconstruct cross-reference table | |
| 4 | +WARNING: global_damaged.pdf (trailer, offset 764): unknown token while reading object; treating as null | |
| 5 | +WARNING: global_damaged.pdf (trailer, offset 767): unknown token while reading object; treating as null | |
| 6 | +WARNING: global_damaged.pdf (trailer, offset 767): limits error(parser-max-errors): too many errors during parsing; treating object as null | |
| 7 | +WARNING: global_damaged.pdf: unable to find trailer dictionary while recovering damaged file | |
| 8 | +qpdf: operation succeeded with warnings; resulting file may have some problems | |
| 9 | +qpdf: some configurable limits were exceeded; for more details see https://qpdf.readthedocs.io/en/stable/cli.html#global-limits | ... | ... |
qpdf/qtest/qpdf/global3.out
0 → 100644
| 1 | +WARNING: global.pdf (trailer, offset 770): limits error(parser-max-container-size): encountered an array or dictionary with more than 3 elements during xref recovery; giving up on reading object | |
| 2 | +WARNING: global.pdf: file is damaged | |
| 3 | +WARNING: global.pdf (offset 712): expected trailer dictionary | |
| 4 | +WARNING: global.pdf: Attempting to reconstruct cross-reference table | |
| 5 | +qpdf: operation succeeded with warnings; resulting file may have some problems | |
| 6 | +qpdf: some configurable limits were exceeded; for more details see https://qpdf.readthedocs.io/en/stable/cli.html#global-limits | ... | ... |
qpdf/qtest/qpdf/global4.out
0 → 100644
| 1 | +WARNING: global_damaged.pdf: file is damaged | |
| 2 | +WARNING: global_damaged.pdf (offset 55): xref not found | |
| 3 | +WARNING: global_damaged.pdf: Attempting to reconstruct cross-reference table | |
| 4 | +WARNING: global_damaged.pdf (trailer, offset 764): unknown token while reading object; treating as null | |
| 5 | +WARNING: global_damaged.pdf (trailer, offset 767): unknown token while reading object; treating as null | |
| 6 | +WARNING: global_damaged.pdf (trailer, offset 770): unknown token while reading object; treating as null | |
| 7 | +WARNING: global_damaged.pdf (trailer, offset 788): limits error(parser-max-container-size-damaged): encountered errors while parsing an array or dictionary with more than 9 elements; giving up on reading object | |
| 8 | +WARNING: global_damaged.pdf: unable to find trailer dictionary while recovering damaged file | |
| 9 | +qpdf: operation succeeded with warnings; resulting file may have some problems | |
| 10 | +qpdf: some configurable limits were exceeded; for more details see https://qpdf.readthedocs.io/en/stable/cli.html#global-limits | ... | ... |
qpdf/qtest/qpdf/global_damaged.pdf
0 → 100644
| 1 | +%PDF-1.3 | |
| 2 | +1 0 obj | |
| 3 | +<< | |
| 4 | + /Type /Catalog | |
| 5 | + /Pages 2 0 R | |
| 6 | +>> | |
| 7 | +endobj | |
| 8 | + | |
| 9 | +2 0 obj | |
| 10 | +<< | |
| 11 | + /Type /Pages | |
| 12 | + /Kids [ | |
| 13 | + 3 0 R | |
| 14 | + ] | |
| 15 | + /Count 1 | |
| 16 | +>> | |
| 17 | +endobj | |
| 18 | + | |
| 19 | +3 0 obj | |
| 20 | +<< | |
| 21 | + /Type /Page | |
| 22 | + /Parent 2 0 R | |
| 23 | + /MediaBox [0 0 612 792] | |
| 24 | + /Contents 4 0 R | |
| 25 | + /Resources << | |
| 26 | + /ProcSet 5 0 R | |
| 27 | + /Font << | |
| 28 | + /F1 6 0 R | |
| 29 | + >> | |
| 30 | + >> | |
| 31 | +>> | |
| 32 | +endobj | |
| 33 | + | |
| 34 | +4 0 obj | |
| 35 | +<< | |
| 36 | + /Length 44 | |
| 37 | +>> | |
| 38 | +stream | |
| 39 | +BT | |
| 40 | + /F1 24 Tf | |
| 41 | + 72 720 Td | |
| 42 | + (Potato) Tj | |
| 43 | +ET | |
| 44 | +endstream | |
| 45 | +endobj | |
| 46 | + | |
| 47 | +5 0 obj | |
| 48 | +[ | |
| 49 | ||
| 50 | + /Text | |
| 51 | +] | |
| 52 | +endobj | |
| 53 | + | |
| 54 | +6 0 obj | |
| 55 | +<< | |
| 56 | + /Type /Font | |
| 57 | + /Subtype /Type1 | |
| 58 | + /Name /F1 | |
| 59 | + /BaseFont /Helvetica | |
| 60 | + /Encoding /WinAnsiEncoding | |
| 61 | +>> | |
| 62 | +endobj | |
| 63 | + | |
| 64 | +xref | |
| 65 | +0 7 | |
| 66 | +0000000000 65535 f | |
| 67 | +0000000009 00000 n | |
| 68 | +0000000063 00000 n | |
| 69 | +0000000135 00000 n | |
| 70 | +0000000307 00000 n | |
| 71 | +0000000403 00000 n | |
| 72 | +0000000438 00000 n | |
| 73 | +trailer << | |
| 74 | + /Size 7 | |
| 75 | + /Root 1 0 R | |
| 76 | + /Nesting [ [ [ [ [ /1 !2 !3 !4 /5 /6 /7 /8 /9 /10 ] ] ] ] ] | |
| 77 | +>> | |
| 78 | +startxref | |
| 79 | +55 | |
| 80 | +%%EOF | ... | ... |
qpdf/qtest/qpdf/issue-146.out
| 1 | 1 | WARNING: issue-146.pdf: file is damaged |
| 2 | 2 | WARNING: issue-146.pdf: can't find startxref |
| 3 | 3 | WARNING: issue-146.pdf: Attempting to reconstruct cross-reference table |
| 4 | -WARNING: issue-146.pdf (trailer, offset 695): ignoring excessively deeply nested data structure | |
| 4 | +WARNING: issue-146.pdf (trailer, offset 695): limits error(parser-max-nesting): ignoring excessively deeply nested data structure | |
| 5 | 5 | WARNING: issue-146.pdf (object 1 0, offset 92): expected endobj |
| 6 | 6 | WARNING: issue-146.pdf (object 7 0, offset 146): unknown token while reading object; treating as null |
| 7 | 7 | WARNING: issue-146.pdf (object 7 0, offset 168): expected endobj | ... | ... |
qpdf/qtest/qpdf/issue-202.out
| 1 | -WARNING: issue-202.pdf (trailer, offset 55770): ignoring excessively deeply nested data structure | |
| 1 | +WARNING: issue-202.pdf (trailer, offset 55770): limits error(parser-max-nesting): ignoring excessively deeply nested data structure | |
| 2 | 2 | WARNING: issue-202.pdf: file is damaged |
| 3 | 3 | WARNING: issue-202.pdf (offset 54769): expected trailer dictionary |
| 4 | 4 | WARNING: issue-202.pdf: Attempting to reconstruct cross-reference table |
| 5 | -WARNING: issue-202.pdf (trailer, offset 55770): ignoring excessively deeply nested data structure | |
| 5 | +WARNING: issue-202.pdf (trailer, offset 55770): limits error(parser-max-nesting): ignoring excessively deeply nested data structure | |
| 6 | 6 | WARNING: issue-202.pdf (object 222 0, offset 50101): dictionary has duplicated key /Creator; last occurrence overrides earlier ones |
| 7 | 7 | WARNING: issue-202.pdf (object 222 0, offset 50101): dictionary has duplicated key /Producer; last occurrence overrides earlier ones |
| 8 | 8 | WARNING: issue-202.pdf: unable to find trailer dictionary while recovering damaged file |
| 9 | 9 | qpdf: operation succeeded with warnings; resulting file may have some problems |
| 10 | +qpdf: some configurable limits were exceeded; for more details see https://qpdf.readthedocs.io/en/stable/cli.html#global-limits | ... | ... |