Commit 5e7b37e15cb366f75dea16a2fc8c1db4d71da0a3
Committed by
GitHub
Merge pull request #1530 from m-holger/afdh
Cache QPDFAcroFormDocumentHelpers
Showing
8 changed files
with
118 additions
and
102 deletions
include/qpdf/QPDF.hh
| ... | ... | @@ -57,6 +57,7 @@ class BitWriter; |
| 57 | 57 | class BufferInputSource; |
| 58 | 58 | class QPDFLogger; |
| 59 | 59 | class QPDFParser; |
| 60 | +class QPDFAcroFormDocumentHelper; | |
| 60 | 61 | |
| 61 | 62 | class QPDF |
| 62 | 63 | { |
| ... | ... | @@ -792,6 +793,7 @@ class QPDF |
| 792 | 793 | class JobSetter; |
| 793 | 794 | |
| 794 | 795 | inline bool reconstructed_xref() const; |
| 796 | + inline QPDFAcroFormDocumentHelper& acroform(); | |
| 795 | 797 | |
| 796 | 798 | // For testing only -- do not add to DLL |
| 797 | 799 | static bool test_json_validators(); | ... | ... |
include/qpdf/QPDFAcroFormDocumentHelper.hh
| ... | ... | @@ -68,6 +68,21 @@ |
| 68 | 68 | class QPDFAcroFormDocumentHelper: public QPDFDocumentHelper |
| 69 | 69 | { |
| 70 | 70 | public: |
| 71 | + // Get a shared document helper for a given QPDF object. | |
| 72 | + // | |
| 73 | + // Retrieving a document helper for a QPDF object rather than creating a new one avoids repeated | |
| 74 | + // validation of the Acroform structure, which can be expensive. | |
| 75 | + QPDF_DLL | |
| 76 | + static QPDFAcroFormDocumentHelper& get(QPDF& qpdf); | |
| 77 | + | |
| 78 | + // Re-validate the AcroForm structure. This is useful if you have modified the structure of the | |
| 79 | + // AcroForm dictionary in a way that would invalidate the cache. | |
| 80 | + // | |
| 81 | + // If repair is true, the document will be repaired if possible if the validation encounters | |
| 82 | + // errors. | |
| 83 | + QPDF_DLL | |
| 84 | + void validate(bool repair = true); | |
| 85 | + | |
| 71 | 86 | QPDF_DLL |
| 72 | 87 | QPDFAcroFormDocumentHelper(QPDF&); |
| 73 | 88 | |
| ... | ... | @@ -226,23 +241,7 @@ class QPDFAcroFormDocumentHelper: public QPDFDocumentHelper |
| 226 | 241 | void adjustAppearanceStream( |
| 227 | 242 | QPDFObjectHandle stream, std::map<std::string, std::map<std::string, std::string>> dr_map); |
| 228 | 243 | |
| 229 | - class Members | |
| 230 | - { | |
| 231 | - friend class QPDFAcroFormDocumentHelper; | |
| 232 | - | |
| 233 | - public: | |
| 234 | - ~Members() = default; | |
| 235 | - | |
| 236 | - private: | |
| 237 | - Members() = default; | |
| 238 | - Members(Members const&) = delete; | |
| 239 | - | |
| 240 | - bool cache_valid{false}; | |
| 241 | - std::map<QPDFObjGen, std::vector<QPDFAnnotationObjectHelper>> field_to_annotations; | |
| 242 | - std::map<QPDFObjGen, QPDFFormFieldObjectHelper> annotation_to_field; | |
| 243 | - std::map<QPDFObjGen, std::string> field_to_name; | |
| 244 | - std::map<std::string, std::set<QPDFObjGen>> name_to_fields; | |
| 245 | - }; | |
| 244 | + class Members; | |
| 246 | 245 | |
| 247 | 246 | std::shared_ptr<Members> m; |
| 248 | 247 | }; | ... | ... |
libqpdf/QPDFAcroFormDocumentHelper.cc
| ... | ... | @@ -3,6 +3,7 @@ |
| 3 | 3 | #include <qpdf/Pl_Buffer.hh> |
| 4 | 4 | #include <qpdf/QPDFObjectHandle_private.hh> |
| 5 | 5 | #include <qpdf/QPDFPageDocumentHelper.hh> |
| 6 | +#include <qpdf/QPDF_private.hh> | |
| 6 | 7 | #include <qpdf/QTC.hh> |
| 7 | 8 | #include <qpdf/QUtil.hh> |
| 8 | 9 | #include <qpdf/ResourceFinder.hh> |
| ... | ... | @@ -12,15 +13,42 @@ |
| 12 | 13 | using namespace qpdf; |
| 13 | 14 | using namespace std::literals; |
| 14 | 15 | |
| 16 | +class QPDFAcroFormDocumentHelper::Members | |
| 17 | +{ | |
| 18 | + public: | |
| 19 | + Members() = default; | |
| 20 | + Members(Members const&) = delete; | |
| 21 | + ~Members() = default; | |
| 22 | + | |
| 23 | + bool cache_valid{false}; | |
| 24 | + std::map<QPDFObjGen, std::vector<QPDFAnnotationObjectHelper>> field_to_annotations; | |
| 25 | + std::map<QPDFObjGen, QPDFFormFieldObjectHelper> annotation_to_field; | |
| 26 | + std::map<QPDFObjGen, std::string> field_to_name; | |
| 27 | + std::map<std::string, std::set<QPDFObjGen>> name_to_fields; | |
| 28 | +}; | |
| 29 | + | |
| 15 | 30 | QPDFAcroFormDocumentHelper::QPDFAcroFormDocumentHelper(QPDF& qpdf) : |
| 16 | 31 | QPDFDocumentHelper(qpdf), |
| 17 | - m(new Members()) | |
| 32 | + m(std::make_shared<Members>()) | |
| 18 | 33 | { |
| 19 | 34 | // We have to analyze up front. Otherwise, when we are adding annotations and fields, we are in |
| 20 | 35 | // a temporarily unstable configuration where some widget annotations are not reachable. |
| 21 | 36 | analyze(); |
| 22 | 37 | } |
| 23 | 38 | |
| 39 | +QPDFAcroFormDocumentHelper& | |
| 40 | +QPDFAcroFormDocumentHelper::get(QPDF& qpdf) | |
| 41 | +{ | |
| 42 | + return qpdf.acroform(); | |
| 43 | +} | |
| 44 | + | |
| 45 | +void | |
| 46 | +QPDFAcroFormDocumentHelper::validate(bool repair) | |
| 47 | +{ | |
| 48 | + invalidateCache(); | |
| 49 | + analyze(); | |
| 50 | +} | |
| 51 | + | |
| 24 | 52 | void |
| 25 | 53 | QPDFAcroFormDocumentHelper::invalidateCache() |
| 26 | 54 | { | ... | ... |
libqpdf/QPDFFormFieldObjectHelper.cc
| ... | ... | @@ -5,6 +5,7 @@ |
| 5 | 5 | #include <qpdf/QPDFAcroFormDocumentHelper.hh> |
| 6 | 6 | #include <qpdf/QPDFAnnotationObjectHelper.hh> |
| 7 | 7 | #include <qpdf/QPDFObjectHandle_private.hh> |
| 8 | +#include <qpdf/QPDF_private.hh> | |
| 8 | 9 | #include <qpdf/QTC.hh> |
| 9 | 10 | #include <qpdf/QUtil.hh> |
| 10 | 11 | #include <cstdlib> |
| ... | ... | @@ -88,23 +89,21 @@ QPDFFormFieldObjectHelper::getInheritableFieldValue(std::string const& name) |
| 88 | 89 | std::string |
| 89 | 90 | QPDFFormFieldObjectHelper::getInheritableFieldValueAsString(std::string const& name) |
| 90 | 91 | { |
| 91 | - QPDFObjectHandle fv = getInheritableFieldValue(name); | |
| 92 | - std::string result; | |
| 92 | + auto fv = getInheritableFieldValue(name); | |
| 93 | 93 | if (fv.isString()) { |
| 94 | - result = fv.getUTF8Value(); | |
| 94 | + return fv.getUTF8Value(); | |
| 95 | 95 | } |
| 96 | - return result; | |
| 96 | + return {}; | |
| 97 | 97 | } |
| 98 | 98 | |
| 99 | 99 | std::string |
| 100 | 100 | QPDFFormFieldObjectHelper::getInheritableFieldValueAsName(std::string const& name) |
| 101 | 101 | { |
| 102 | - QPDFObjectHandle fv = getInheritableFieldValue(name); | |
| 103 | - std::string result; | |
| 102 | + auto fv = getInheritableFieldValue(name); | |
| 104 | 103 | if (fv.isName()) { |
| 105 | - result = fv.getName(); | |
| 104 | + return fv.getName(); | |
| 106 | 105 | } |
| 107 | - return result; | |
| 106 | + return {}; | |
| 108 | 107 | } |
| 109 | 108 | |
| 110 | 109 | std::string |
| ... | ... | @@ -203,12 +202,11 @@ QPDFFormFieldObjectHelper::getDefaultAppearance() |
| 203 | 202 | value = getFieldFromAcroForm("/DA"); |
| 204 | 203 | looked_in_acroform = true; |
| 205 | 204 | } |
| 206 | - std::string result; | |
| 207 | 205 | if (value.isString()) { |
| 208 | 206 | QTC::TC("qpdf", "QPDFFormFieldObjectHelper DA present", looked_in_acroform ? 0 : 1); |
| 209 | - result = value.getUTF8Value(); | |
| 207 | + return value.getUTF8Value(); | |
| 210 | 208 | } |
| 211 | - return result; | |
| 209 | + return {}; | |
| 212 | 210 | } |
| 213 | 211 | |
| 214 | 212 | int |
| ... | ... | @@ -220,12 +218,11 @@ QPDFFormFieldObjectHelper::getQuadding() |
| 220 | 218 | fv = getFieldFromAcroForm("/Q"); |
| 221 | 219 | looked_in_acroform = true; |
| 222 | 220 | } |
| 223 | - int result = 0; | |
| 224 | 221 | if (fv.isInteger()) { |
| 225 | 222 | QTC::TC("qpdf", "QPDFFormFieldObjectHelper Q present", looked_in_acroform ? 0 : 1); |
| 226 | - result = QIntC::to_int(fv.getIntValue()); | |
| 223 | + return QIntC::to_int(fv.getIntValue()); | |
| 227 | 224 | } |
| 228 | - return result; | |
| 225 | + return 0; | |
| 229 | 226 | } |
| 230 | 227 | |
| 231 | 228 | int |
| ... | ... | @@ -238,46 +235,46 @@ QPDFFormFieldObjectHelper::getFlags() |
| 238 | 235 | bool |
| 239 | 236 | QPDFFormFieldObjectHelper::isText() |
| 240 | 237 | { |
| 241 | - return (getFieldType() == "/Tx"); | |
| 238 | + return getFieldType() == "/Tx"; | |
| 242 | 239 | } |
| 243 | 240 | |
| 244 | 241 | bool |
| 245 | 242 | QPDFFormFieldObjectHelper::isCheckbox() |
| 246 | 243 | { |
| 247 | - return ((getFieldType() == "/Btn") && ((getFlags() & (ff_btn_radio | ff_btn_pushbutton)) == 0)); | |
| 244 | + return getFieldType() == "/Btn" && (getFlags() & (ff_btn_radio | ff_btn_pushbutton)) == 0; | |
| 248 | 245 | } |
| 249 | 246 | |
| 250 | 247 | bool |
| 251 | 248 | QPDFFormFieldObjectHelper::isChecked() |
| 252 | 249 | { |
| 253 | - return isCheckbox() && getValue().isName() && (getValue().getName() != "/Off"); | |
| 250 | + return isCheckbox() && getValue().isName() && getValue().getName() != "/Off"; | |
| 254 | 251 | } |
| 255 | 252 | |
| 256 | 253 | bool |
| 257 | 254 | QPDFFormFieldObjectHelper::isRadioButton() |
| 258 | 255 | { |
| 259 | - return ((getFieldType() == "/Btn") && ((getFlags() & ff_btn_radio) == ff_btn_radio)); | |
| 256 | + return getFieldType() == "/Btn" && (getFlags() & ff_btn_radio) == ff_btn_radio; | |
| 260 | 257 | } |
| 261 | 258 | |
| 262 | 259 | bool |
| 263 | 260 | QPDFFormFieldObjectHelper::isPushbutton() |
| 264 | 261 | { |
| 265 | - return ((getFieldType() == "/Btn") && ((getFlags() & ff_btn_pushbutton) == ff_btn_pushbutton)); | |
| 262 | + return getFieldType() == "/Btn" && (getFlags() & ff_btn_pushbutton) == ff_btn_pushbutton; | |
| 266 | 263 | } |
| 267 | 264 | |
| 268 | 265 | bool |
| 269 | 266 | QPDFFormFieldObjectHelper::isChoice() |
| 270 | 267 | { |
| 271 | - return (getFieldType() == "/Ch"); | |
| 268 | + return getFieldType() == "/Ch"; | |
| 272 | 269 | } |
| 273 | 270 | |
| 274 | 271 | std::vector<std::string> |
| 275 | 272 | QPDFFormFieldObjectHelper::getChoices() |
| 276 | 273 | { |
| 277 | - std::vector<std::string> result; | |
| 278 | 274 | if (!isChoice()) { |
| 279 | - return result; | |
| 275 | + return {}; | |
| 280 | 276 | } |
| 277 | + std::vector<std::string> result; | |
| 281 | 278 | for (auto const& item: getInheritableFieldValue("/Opt").as_array()) { |
| 282 | 279 | if (item.isString()) { |
| 283 | 280 | result.emplace_back(item.getUTF8Value()); |
| ... | ... | @@ -308,25 +305,26 @@ QPDFFormFieldObjectHelper::setV(QPDFObjectHandle value, bool need_appearances) |
| 308 | 305 | { |
| 309 | 306 | if (getFieldType() == "/Btn") { |
| 310 | 307 | if (isCheckbox()) { |
| 311 | - bool okay = false; | |
| 312 | - if (value.isName()) { | |
| 313 | - std::string name = value.getName(); | |
| 314 | - okay = true; | |
| 315 | - // Accept any value other than /Off to mean checked. Files have been seen that use | |
| 316 | - // /1 or other values. | |
| 317 | - setCheckBoxValue((name != "/Off")); | |
| 318 | - } | |
| 319 | - if (!okay) { | |
| 308 | + if (!value.isName()) { | |
| 320 | 309 | warn("ignoring attempt to set a checkbox field to a value whose type is not name"); |
| 310 | + return; | |
| 321 | 311 | } |
| 322 | - } else if (isRadioButton()) { | |
| 323 | - if (value.isName()) { | |
| 324 | - setRadioButtonValue(value); | |
| 325 | - } else { | |
| 312 | + std::string name = value.getName(); | |
| 313 | + // Accept any value other than /Off to mean checked. Files have been seen that use | |
| 314 | + // /1 or other values. | |
| 315 | + setCheckBoxValue(name != "/Off"); | |
| 316 | + return; | |
| 317 | + } | |
| 318 | + if (isRadioButton()) { | |
| 319 | + if (!value.isName()) { | |
| 326 | 320 | warn( |
| 327 | 321 | "ignoring attempt to set a radio button field to an object that is not a name"); |
| 322 | + return; | |
| 328 | 323 | } |
| 329 | - } else if (isPushbutton()) { | |
| 324 | + setRadioButtonValue(value); | |
| 325 | + return; | |
| 326 | + } | |
| 327 | + if (isPushbutton()) { | |
| 330 | 328 | warn("ignoring attempt set the value of a pushbutton field"); |
| 331 | 329 | } |
| 332 | 330 | return; |
| ... | ... | @@ -340,7 +338,7 @@ QPDFFormFieldObjectHelper::setV(QPDFObjectHandle value, bool need_appearances) |
| 340 | 338 | QPDF& qpdf = oh().getQPDF( |
| 341 | 339 | "QPDFFormFieldObjectHelper::setV called with need_appearances = " |
| 342 | 340 | "true on an object that is not associated with an owning QPDF"); |
| 343 | - QPDFAcroFormDocumentHelper(qpdf).setNeedAppearances(true); | |
| 341 | + qpdf.acroform().setNeedAppearances(true); | |
| 344 | 342 | } |
| 345 | 343 | } |
| 346 | 344 | ... | ... |
libqpdf/QPDFJob.cc
| ... | ... | @@ -1180,7 +1180,7 @@ void |
| 1180 | 1180 | QPDFJob::doJSONAcroform(Pipeline* p, bool& first, QPDF& pdf) |
| 1181 | 1181 | { |
| 1182 | 1182 | JSON j_acroform = JSON::makeDictionary(); |
| 1183 | - QPDFAcroFormDocumentHelper afdh(pdf); | |
| 1183 | + auto& afdh = pdf.acroform(); | |
| 1184 | 1184 | j_acroform.addDictionaryMember("hasacroform", JSON::makeBool(afdh.hasAcroForm())); |
| 1185 | 1185 | j_acroform.addDictionaryMember("needappearances", JSON::makeBool(afdh.getNeedAppearances())); |
| 1186 | 1186 | JSON j_fields = j_acroform.addDictionaryMember("fields", JSON::makeArray()); |
| ... | ... | @@ -1888,17 +1888,6 @@ QPDFJob::validateUnderOverlay(QPDF& pdf, UnderOverlay* uo) |
| 1888 | 1888 | } |
| 1889 | 1889 | } |
| 1890 | 1890 | |
| 1891 | -static QPDFAcroFormDocumentHelper* | |
| 1892 | -get_afdh_for_qpdf( | |
| 1893 | - std::map<unsigned long long, std::shared_ptr<QPDFAcroFormDocumentHelper>>& afdh_map, QPDF* q) | |
| 1894 | -{ | |
| 1895 | - auto uid = q->getUniqueId(); | |
| 1896 | - if (!afdh_map.contains(uid)) { | |
| 1897 | - afdh_map[uid] = std::make_shared<QPDFAcroFormDocumentHelper>(*q); | |
| 1898 | - } | |
| 1899 | - return afdh_map[uid].get(); | |
| 1900 | -} | |
| 1901 | - | |
| 1902 | 1891 | std::string |
| 1903 | 1892 | QPDFJob::doUnderOverlayForPage( |
| 1904 | 1893 | QPDF& pdf, |
| ... | ... | @@ -1914,13 +1903,7 @@ QPDFJob::doUnderOverlayForPage( |
| 1914 | 1903 | if (!(pagenos.contains(pageno) && pagenos[pageno].contains(uo_idx))) { |
| 1915 | 1904 | return ""; |
| 1916 | 1905 | } |
| 1917 | - | |
| 1918 | - std::map<unsigned long long, std::shared_ptr<QPDFAcroFormDocumentHelper>> afdh; | |
| 1919 | - auto make_afdh = [&](QPDFPageObjectHelper& ph) { | |
| 1920 | - QPDF& q = ph.getObjectHandle().getQPDF(); | |
| 1921 | - return get_afdh_for_qpdf(afdh, &q); | |
| 1922 | - }; | |
| 1923 | - auto dest_afdh = make_afdh(dest_page); | |
| 1906 | + auto& dest_afdh = dest_page.qpdf()->acroform(); | |
| 1924 | 1907 | |
| 1925 | 1908 | std::string content; |
| 1926 | 1909 | int min_suffix = 1; |
| ... | ... | @@ -1940,7 +1923,7 @@ QPDFJob::doUnderOverlayForPage( |
| 1940 | 1923 | QPDFMatrix cm; |
| 1941 | 1924 | std::string new_content = dest_page.placeFormXObject( |
| 1942 | 1925 | fo[from_pageno][uo_idx], name, dest_page.getTrimBox().getArrayAsRectangle(), cm); |
| 1943 | - dest_page.copyAnnotations(from_page, cm, dest_afdh, make_afdh(from_page)); | |
| 1926 | + dest_page.copyAnnotations(from_page, cm, &dest_afdh, &from_page.qpdf()->acroform()); | |
| 1944 | 1927 | if (!new_content.empty()) { |
| 1945 | 1928 | resources.mergeResources("<< /XObject << >> >>"_qpdf); |
| 1946 | 1929 | auto xobject = resources.getKey("/XObject"); |
| ... | ... | @@ -2182,15 +2165,15 @@ void |
| 2182 | 2165 | QPDFJob::handleTransformations(QPDF& pdf) |
| 2183 | 2166 | { |
| 2184 | 2167 | QPDFPageDocumentHelper dh(pdf); |
| 2185 | - std::shared_ptr<QPDFAcroFormDocumentHelper> afdh; | |
| 2186 | - auto make_afdh = [&]() { | |
| 2187 | - if (!afdh.get()) { | |
| 2188 | - afdh = std::make_shared<QPDFAcroFormDocumentHelper>(pdf); | |
| 2168 | + QPDFAcroFormDocumentHelper* afdh_ptr = nullptr; | |
| 2169 | + auto afdh = [&]() -> QPDFAcroFormDocumentHelper& { | |
| 2170 | + if (!afdh_ptr) { | |
| 2171 | + afdh_ptr = &pdf.acroform(); | |
| 2189 | 2172 | } |
| 2173 | + return *afdh_ptr; | |
| 2190 | 2174 | }; |
| 2191 | 2175 | if (m->remove_restrictions) { |
| 2192 | - make_afdh(); | |
| 2193 | - afdh->disableDigitalSignatures(); | |
| 2176 | + afdh().disableDigitalSignatures(); | |
| 2194 | 2177 | } |
| 2195 | 2178 | if (m->externalize_inline_images || (m->optimize_images && (!m->keep_inline_images))) { |
| 2196 | 2179 | for (auto& ph: dh.getAllPages()) { |
| ... | ... | @@ -2225,8 +2208,7 @@ QPDFJob::handleTransformations(QPDF& pdf) |
| 2225 | 2208 | } |
| 2226 | 2209 | } |
| 2227 | 2210 | if (m->generate_appearances) { |
| 2228 | - make_afdh(); | |
| 2229 | - afdh->generateAppearancesIfNeeded(); | |
| 2211 | + afdh().generateAppearancesIfNeeded(); | |
| 2230 | 2212 | } |
| 2231 | 2213 | if (m->flatten_annotations) { |
| 2232 | 2214 | dh.flattenAnnotations(m->flatten_annotations_required, m->flatten_annotations_forbidden); |
| ... | ... | @@ -2237,9 +2219,8 @@ QPDFJob::handleTransformations(QPDF& pdf) |
| 2237 | 2219 | } |
| 2238 | 2220 | } |
| 2239 | 2221 | if (m->flatten_rotation) { |
| 2240 | - make_afdh(); | |
| 2241 | 2222 | for (auto& page: dh.getAllPages()) { |
| 2242 | - page.flattenRotation(afdh.get()); | |
| 2223 | + page.flattenRotation(&afdh()); | |
| 2243 | 2224 | } |
| 2244 | 2225 | } |
| 2245 | 2226 | if (m->remove_page_labels) { |
| ... | ... | @@ -2559,8 +2540,7 @@ QPDFJob::handlePageSpecs(QPDF& pdf, std::vector<std::unique_ptr<QPDF>>& page_hea |
| 2559 | 2540 | std::vector<QPDFObjectHandle> new_labels; |
| 2560 | 2541 | bool any_page_labels = false; |
| 2561 | 2542 | int out_pageno = 0; |
| 2562 | - std::map<unsigned long long, std::shared_ptr<QPDFAcroFormDocumentHelper>> afdh_map; | |
| 2563 | - auto this_afdh = get_afdh_for_qpdf(afdh_map, &pdf); | |
| 2543 | + auto& this_afdh = pdf.acroform(); | |
| 2564 | 2544 | std::set<QPDFObjGen> referenced_fields; |
| 2565 | 2545 | for (auto& page_data: parsed_specs) { |
| 2566 | 2546 | ClosedFileInputSource* cis = nullptr; |
| ... | ... | @@ -2569,7 +2549,7 @@ QPDFJob::handlePageSpecs(QPDF& pdf, std::vector<std::unique_ptr<QPDF>>& page_hea |
| 2569 | 2549 | cis->stayOpen(true); |
| 2570 | 2550 | } |
| 2571 | 2551 | QPDFPageLabelDocumentHelper pldh(*page_data.qpdf); |
| 2572 | - auto other_afdh = get_afdh_for_qpdf(afdh_map, page_data.qpdf); | |
| 2552 | + auto& other_afdh = page_data.qpdf->acroform(); | |
| 2573 | 2553 | if (pldh.hasPageLabels()) { |
| 2574 | 2554 | any_page_labels = true; |
| 2575 | 2555 | } |
| ... | ... | @@ -2611,15 +2591,15 @@ QPDFJob::handlePageSpecs(QPDF& pdf, std::vector<std::unique_ptr<QPDF>>& page_hea |
| 2611 | 2591 | // the original file until all copy operations are completed, any foreign pages that |
| 2612 | 2592 | // conflict with original pages will be adjusted. If we copy any page from the original |
| 2613 | 2593 | // file more than once, that page would be in conflict with the previous copy of itself. |
| 2614 | - if ((!this_file && other_afdh->hasAcroForm()) || !first_copy_from_orig) { | |
| 2594 | + if ((!this_file && other_afdh.hasAcroForm()) || !first_copy_from_orig) { | |
| 2615 | 2595 | if (!this_file) { |
| 2616 | 2596 | QTC::TC("qpdf", "QPDFJob copy fields not this file"); |
| 2617 | 2597 | } else if (!first_copy_from_orig) { |
| 2618 | 2598 | QTC::TC("qpdf", "QPDFJob copy fields non-first from orig"); |
| 2619 | 2599 | } |
| 2620 | 2600 | try { |
| 2621 | - this_afdh->fixCopiedAnnotations( | |
| 2622 | - new_page, to_copy.getObjectHandle(), *other_afdh, &referenced_fields); | |
| 2601 | + this_afdh.fixCopiedAnnotations( | |
| 2602 | + new_page, to_copy.getObjectHandle(), other_afdh, &referenced_fields); | |
| 2623 | 2603 | } catch (std::exception& e) { |
| 2624 | 2604 | pdf.warn( |
| 2625 | 2605 | qpdf_e_damaged_pdf, |
| ... | ... | @@ -2647,7 +2627,7 @@ QPDFJob::handlePageSpecs(QPDF& pdf, std::vector<std::unique_ptr<QPDF>>& page_hea |
| 2647 | 2627 | for (size_t pageno = 0; pageno < orig_pages.size(); ++pageno) { |
| 2648 | 2628 | auto page = orig_pages.at(pageno); |
| 2649 | 2629 | if (selected_from_orig.contains(QIntC::to_int(pageno))) { |
| 2650 | - for (auto field: this_afdh->getFormFieldsForPage(page)) { | |
| 2630 | + for (auto field: this_afdh.getFormFieldsForPage(page)) { | |
| 2651 | 2631 | QTC::TC("qpdf", "QPDFJob pages keeping field from original"); |
| 2652 | 2632 | referenced_fields.insert(field.getObjectHandle().getObjGen()); |
| 2653 | 2633 | } |
| ... | ... | @@ -2656,7 +2636,7 @@ QPDFJob::handlePageSpecs(QPDF& pdf, std::vector<std::unique_ptr<QPDF>>& page_hea |
| 2656 | 2636 | } |
| 2657 | 2637 | } |
| 2658 | 2638 | // Remove unreferenced form fields |
| 2659 | - if (this_afdh->hasAcroForm()) { | |
| 2639 | + if (this_afdh.hasAcroForm()) { | |
| 2660 | 2640 | auto acroform = pdf.getRoot().getKey("/AcroForm"); |
| 2661 | 2641 | auto fields = acroform.getKey("/Fields"); |
| 2662 | 2642 | if (fields.isArray()) { |
| ... | ... | @@ -3013,7 +2993,7 @@ QPDFJob::doSplitPages(QPDF& pdf) |
| 3013 | 2993 | dh.removeUnreferencedResources(); |
| 3014 | 2994 | } |
| 3015 | 2995 | QPDFPageLabelDocumentHelper pldh(pdf); |
| 3016 | - QPDFAcroFormDocumentHelper afdh(pdf); | |
| 2996 | + auto& afdh = pdf.acroform(); | |
| 3017 | 2997 | std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages(); |
| 3018 | 2998 | size_t pageno_len = std::to_string(pages.size()).length(); |
| 3019 | 2999 | size_t num_pages = pages.size(); |
| ... | ... | @@ -3025,10 +3005,7 @@ QPDFJob::doSplitPages(QPDF& pdf) |
| 3025 | 3005 | } |
| 3026 | 3006 | QPDF outpdf; |
| 3027 | 3007 | outpdf.emptyPDF(); |
| 3028 | - std::shared_ptr<QPDFAcroFormDocumentHelper> out_afdh; | |
| 3029 | - if (afdh.hasAcroForm()) { | |
| 3030 | - out_afdh = std::make_shared<QPDFAcroFormDocumentHelper>(outpdf); | |
| 3031 | - } | |
| 3008 | + QPDFAcroFormDocumentHelper* out_afdh = afdh.hasAcroForm() ? &outpdf.acroform() : nullptr; | |
| 3032 | 3009 | if (m->suppress_warnings) { |
| 3033 | 3010 | outpdf.setSuppressWarnings(true); |
| 3034 | 3011 | } |
| ... | ... | @@ -3036,8 +3013,7 @@ QPDFJob::doSplitPages(QPDF& pdf) |
| 3036 | 3013 | QPDFObjectHandle page = pages.at(pageno - 1); |
| 3037 | 3014 | outpdf.addPage(page, false); |
| 3038 | 3015 | auto new_page = added_page(outpdf, page); |
| 3039 | - if (out_afdh.get()) { | |
| 3040 | - QTC::TC("qpdf", "QPDFJob copy form fields in split_pages"); | |
| 3016 | + if (out_afdh) { | |
| 3041 | 3017 | try { |
| 3042 | 3018 | out_afdh->fixCopiedAnnotations(new_page, page, afdh); |
| 3043 | 3019 | } catch (std::exception& e) { | ... | ... |
libqpdf/QPDFPageDocumentHelper.cc
| 1 | 1 | #include <qpdf/QPDFPageDocumentHelper.hh> |
| 2 | 2 | |
| 3 | 3 | #include <qpdf/QPDFAcroFormDocumentHelper.hh> |
| 4 | +#include <qpdf/QPDF_private.hh> | |
| 4 | 5 | #include <qpdf/QTC.hh> |
| 5 | 6 | #include <qpdf/QUtil.hh> |
| 6 | 7 | |
| ... | ... | @@ -55,7 +56,7 @@ QPDFPageDocumentHelper::removePage(QPDFPageObjectHelper page) |
| 55 | 56 | void |
| 56 | 57 | QPDFPageDocumentHelper::flattenAnnotations(int required_flags, int forbidden_flags) |
| 57 | 58 | { |
| 58 | - QPDFAcroFormDocumentHelper afdh(qpdf); | |
| 59 | + auto& afdh = qpdf.acroform(); | |
| 59 | 60 | if (afdh.getNeedAppearances()) { |
| 60 | 61 | qpdf.getRoot() |
| 61 | 62 | .getKey("/AcroForm") | ... | ... |
libqpdf/qpdf/QPDF_private.hh
| ... | ... | @@ -3,6 +3,7 @@ |
| 3 | 3 | |
| 4 | 4 | #include <qpdf/QPDF.hh> |
| 5 | 5 | |
| 6 | +#include <qpdf/QPDFAcroFormDocumentHelper.hh> | |
| 6 | 7 | #include <qpdf/QPDFObject_private.hh> |
| 7 | 8 | #include <qpdf/QPDFTokenizer_private.hh> |
| 8 | 9 | |
| ... | ... | @@ -547,6 +548,9 @@ class QPDF::Members |
| 547 | 548 | // Optimization data |
| 548 | 549 | std::map<ObjUser, std::set<QPDFObjGen>> obj_user_to_objects; |
| 549 | 550 | std::map<QPDFObjGen, std::set<ObjUser>> object_to_obj_users; |
| 551 | + | |
| 552 | + // Document Helpers; | |
| 553 | + std::unique_ptr<QPDFAcroFormDocumentHelper> acroform; | |
| 550 | 554 | }; |
| 551 | 555 | |
| 552 | 556 | // JobSetter class is restricted to QPDFJob. |
| ... | ... | @@ -569,4 +573,13 @@ QPDF::reconstructed_xref() const |
| 569 | 573 | return m->reconstructed_xref; |
| 570 | 574 | } |
| 571 | 575 | |
| 576 | +inline QPDFAcroFormDocumentHelper& | |
| 577 | +QPDF::acroform() | |
| 578 | +{ | |
| 579 | + if (!m->acroform) { | |
| 580 | + m->acroform = std::make_unique<QPDFAcroFormDocumentHelper>(*this); | |
| 581 | + } | |
| 582 | + return *m->acroform; | |
| 583 | +} | |
| 584 | + | |
| 572 | 585 | #endif // QPDF_PRIVATE_HH | ... | ... |
qpdf/qpdf.testcov
| ... | ... | @@ -519,7 +519,6 @@ QPDFPageObjectHelper flatten inherit rotate 0 |
| 519 | 519 | QPDFAcroFormDocumentHelper copy annotation 3 |
| 520 | 520 | QPDFAcroFormDocumentHelper field with parent 3 |
| 521 | 521 | QPDFAcroFormDocumentHelper modify ap matrix 0 |
| 522 | -QPDFJob copy form fields in split_pages 0 | |
| 523 | 522 | QPDFJob pages keeping field from original 0 |
| 524 | 523 | QPDFObjectHandle merge reuse 0 |
| 525 | 524 | QPDFObjectHandle merge generate 0 | ... | ... |