Commit 83216e640c489a22bd4001c9c39affb72b8b1124
1 parent
1f35ec99
Preserve form fields when splitting pages (fixes #340)
Showing
9 changed files
with
70 additions
and
8 deletions
ChangeLog
| 1 | 1 | 2021-02-22 Jay Berkenbilt <ejb@ql.org> |
| 2 | 2 | |
| 3 | + * From qpdf CLI, --pages and --split-pages will properly preserve | |
| 4 | + interactive form functionality. Fixes #340. | |
| 5 | + | |
| 3 | 6 | * Add QPDFAcroFormDocumentHelper::copyFieldsFromForeignPage to |
| 4 | 7 | copy form fields from a foreign page into the current file. |
| 5 | 8 | ... | ... |
libqpdf/QPDFAcroFormDocumentHelper.cc
| ... | ... | @@ -676,10 +676,16 @@ QPDFAcroFormDocumentHelper::copyFieldsFromForeignPage( |
| 676 | 676 | QPDFPageObjectHelper foreign_page, |
| 677 | 677 | QPDFAcroFormDocumentHelper& foreign_afdh) |
| 678 | 678 | { |
| 679 | + std::set<QPDFObjGen> added; | |
| 679 | 680 | for (auto field: foreign_afdh.getFormFieldsForPage(foreign_page)) |
| 680 | 681 | { |
| 681 | 682 | auto new_field = this->qpdf.copyForeignObject( |
| 682 | 683 | field.getObjectHandle()); |
| 683 | - addFormField(new_field); | |
| 684 | + auto og = new_field.getObjGen(); | |
| 685 | + if (! added.count(og)) | |
| 686 | + { | |
| 687 | + addFormField(new_field); | |
| 688 | + added.insert(og); | |
| 689 | + } | |
| 684 | 690 | } |
| 685 | 691 | } | ... | ... |
qpdf/qpdf.cc
| ... | ... | @@ -5143,6 +5143,19 @@ static void get_uo_pagenos(UnderOverlay& uo, |
| 5143 | 5143 | } |
| 5144 | 5144 | } |
| 5145 | 5145 | |
| 5146 | +static QPDFAcroFormDocumentHelper* get_afdh_for_qpdf( | |
| 5147 | + std::map<unsigned long long, | |
| 5148 | + PointerHolder<QPDFAcroFormDocumentHelper>>& afdh_map, | |
| 5149 | + QPDF* q) | |
| 5150 | +{ | |
| 5151 | + auto uid = q->getUniqueId(); | |
| 5152 | + if (! afdh_map.count(uid)) | |
| 5153 | + { | |
| 5154 | + afdh_map[uid] = new QPDFAcroFormDocumentHelper(*q); | |
| 5155 | + } | |
| 5156 | + return afdh_map[uid].getPointer(); | |
| 5157 | +} | |
| 5158 | + | |
| 5146 | 5159 | static void do_under_overlay_for_page( |
| 5147 | 5160 | QPDF& pdf, |
| 5148 | 5161 | Options& o, |
| ... | ... | @@ -5164,12 +5177,7 @@ static void do_under_overlay_for_page( |
| 5164 | 5177 | PointerHolder<QPDFAcroFormDocumentHelper>> afdh; |
| 5165 | 5178 | auto make_afdh = [&](QPDFPageObjectHelper& ph) { |
| 5166 | 5179 | QPDF* q = ph.getObjectHandle().getOwningQPDF(); |
| 5167 | - auto uid = q->getUniqueId(); | |
| 5168 | - if (! afdh.count(uid)) | |
| 5169 | - { | |
| 5170 | - afdh[uid] = new QPDFAcroFormDocumentHelper(*q); | |
| 5171 | - } | |
| 5172 | - return afdh[uid].getPointer(); | |
| 5180 | + return get_afdh_for_qpdf(afdh, q); | |
| 5173 | 5181 | }; |
| 5174 | 5182 | auto dest_afdh = make_afdh(dest_page); |
| 5175 | 5183 | |
| ... | ... | @@ -5835,6 +5843,9 @@ static void handle_page_specs(QPDF& pdf, Options& o, bool& warnings) |
| 5835 | 5843 | std::vector<QPDFObjectHandle> new_labels; |
| 5836 | 5844 | bool any_page_labels = false; |
| 5837 | 5845 | int out_pageno = 0; |
| 5846 | + std::map<unsigned long long, | |
| 5847 | + PointerHolder<QPDFAcroFormDocumentHelper>> afdh_map; | |
| 5848 | + auto this_afdh = get_afdh_for_qpdf(afdh_map, &pdf); | |
| 5838 | 5849 | for (std::vector<QPDFPageData>::iterator iter = |
| 5839 | 5850 | parsed_specs.begin(); |
| 5840 | 5851 | iter != parsed_specs.end(); ++iter) |
| ... | ... | @@ -5847,6 +5858,7 @@ static void handle_page_specs(QPDF& pdf, Options& o, bool& warnings) |
| 5847 | 5858 | cis->stayOpen(true); |
| 5848 | 5859 | } |
| 5849 | 5860 | QPDFPageLabelDocumentHelper pldh(*page_data.qpdf); |
| 5861 | + auto other_afdh = get_afdh_for_qpdf(afdh_map, page_data.qpdf); | |
| 5850 | 5862 | if (pldh.hasPageLabels()) |
| 5851 | 5863 | { |
| 5852 | 5864 | any_page_labels = true; |
| ... | ... | @@ -5891,6 +5903,11 @@ static void handle_page_specs(QPDF& pdf, Options& o, bool& warnings) |
| 5891 | 5903 | // of the fact that we are using it. |
| 5892 | 5904 | selected_from_orig.insert(pageno); |
| 5893 | 5905 | } |
| 5906 | + else if (other_afdh->hasAcroForm()) | |
| 5907 | + { | |
| 5908 | + QTC::TC("qpdf", "qpdf copy form fields in pages"); | |
| 5909 | + this_afdh->copyFieldsFromForeignPage(to_copy, *other_afdh); | |
| 5910 | + } | |
| 5894 | 5911 | } |
| 5895 | 5912 | if (page_data.qpdf->anyWarnings()) |
| 5896 | 5913 | { |
| ... | ... | @@ -6269,6 +6286,7 @@ static void do_split_pages(QPDF& pdf, Options& o, bool& warnings) |
| 6269 | 6286 | dh.removeUnreferencedResources(); |
| 6270 | 6287 | } |
| 6271 | 6288 | QPDFPageLabelDocumentHelper pldh(pdf); |
| 6289 | + QPDFAcroFormDocumentHelper afdh(pdf); | |
| 6272 | 6290 | std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages(); |
| 6273 | 6291 | size_t pageno_len = QUtil::uint_to_string(pages.size()).length(); |
| 6274 | 6292 | size_t num_pages = pages.size(); |
| ... | ... | @@ -6282,6 +6300,11 @@ static void do_split_pages(QPDF& pdf, Options& o, bool& warnings) |
| 6282 | 6300 | } |
| 6283 | 6301 | QPDF outpdf; |
| 6284 | 6302 | outpdf.emptyPDF(); |
| 6303 | + PointerHolder<QPDFAcroFormDocumentHelper> out_afdh; | |
| 6304 | + if (afdh.hasAcroForm()) | |
| 6305 | + { | |
| 6306 | + out_afdh = new QPDFAcroFormDocumentHelper(outpdf); | |
| 6307 | + } | |
| 6285 | 6308 | if (o.suppress_warnings) |
| 6286 | 6309 | { |
| 6287 | 6310 | outpdf.setSuppressWarnings(true); |
| ... | ... | @@ -6290,6 +6313,12 @@ static void do_split_pages(QPDF& pdf, Options& o, bool& warnings) |
| 6290 | 6313 | { |
| 6291 | 6314 | QPDFObjectHandle page = pages.at(pageno - 1); |
| 6292 | 6315 | outpdf.addPage(page, false); |
| 6316 | + if (out_afdh.getPointer()) | |
| 6317 | + { | |
| 6318 | + QTC::TC("qpdf", "qpdf copy form fields in split_pages"); | |
| 6319 | + out_afdh->copyFieldsFromForeignPage( | |
| 6320 | + QPDFPageObjectHelper(page), afdh); | |
| 6321 | + } | |
| 6293 | 6322 | } |
| 6294 | 6323 | if (pldh.hasPageLabels()) |
| 6295 | 6324 | { | ... | ... |
qpdf/qpdf.testcov
| ... | ... | @@ -575,3 +575,5 @@ QPDFPageObjectHelper flatten inherit rotate 0 |
| 575 | 575 | QPDFAcroFormDocumentHelper copy annotation 3 |
| 576 | 576 | QPDFAcroFormDocumentHelper field with parent 3 |
| 577 | 577 | QPDFAcroFormDocumentHelper modify ap matrix 0 |
| 578 | +qpdf copy form fields in split_pages 0 | |
| 579 | +qpdf copy form fields in pages 0 | ... | ... |
qpdf/qtest/qpdf.test
| ... | ... | @@ -2414,7 +2414,7 @@ foreach my $f (qw(screen print)) |
| 2414 | 2414 | show_ntests(); |
| 2415 | 2415 | # ---------- |
| 2416 | 2416 | $td->notify("--- Copy Annotations ---"); |
| 2417 | -$n_tests += 16; | |
| 2417 | +$n_tests += 21; | |
| 2418 | 2418 | |
| 2419 | 2419 | $td->runtest("complex copy annotations", |
| 2420 | 2420 | {$td->COMMAND => |
| ... | ... | @@ -2458,6 +2458,28 @@ foreach my $d ([1, "appearances-1.pdf"], |
| 2458 | 2458 | {$td->FILE => "test80b$n.pdf"}); |
| 2459 | 2459 | } |
| 2460 | 2460 | |
| 2461 | +$td->runtest("page extraction with fields", | |
| 2462 | + {$td->COMMAND => | |
| 2463 | + "qpdf --static-id --empty" . | |
| 2464 | + " --pages fields-two-pages.pdf -- a.pdf"}, | |
| 2465 | + {$td->STRING => "", $td->EXIT_STATUS => 0}, | |
| 2466 | + $td->NORMALIZE_NEWLINES); | |
| 2467 | +$td->runtest("check output", | |
| 2468 | + {$td->FILE => "a.pdf"}, | |
| 2469 | + {$td->FILE => "fields-pages-out.pdf"}); | |
| 2470 | +$td->runtest("page splitting with fields", | |
| 2471 | + {$td->COMMAND => | |
| 2472 | + "qpdf --static-id" . | |
| 2473 | + " --split-pages fields-two-pages.pdf a.pdf"}, | |
| 2474 | + {$td->STRING => "", $td->EXIT_STATUS => 0}, | |
| 2475 | + $td->NORMALIZE_NEWLINES); | |
| 2476 | +for (my $i = 1; $i <= 2; ++$i) | |
| 2477 | +{ | |
| 2478 | + $td->runtest("check output", | |
| 2479 | + {$td->FILE => "a-$i.pdf"}, | |
| 2480 | + {$td->FILE => "fields-split-$i.pdf"}); | |
| 2481 | +} | |
| 2482 | + | |
| 2461 | 2483 | show_ntests(); |
| 2462 | 2484 | # ---------- |
| 2463 | 2485 | $td->notify("--- Page Tree Issues ---"); | ... | ... |
qpdf/qtest/qpdf/fields-pages-out.pdf
0 → 100644
No preview for this file type
qpdf/qtest/qpdf/fields-split-1.pdf
0 → 100644
No preview for this file type
qpdf/qtest/qpdf/fields-split-2.pdf
0 → 100644
No preview for this file type
qpdf/qtest/qpdf/fields-two-pages.pdf
0 → 100644
No preview for this file type