aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog3
-rw-r--r--libqpdf/QPDFAcroFormDocumentHelper.cc8
-rw-r--r--qpdf/qpdf.cc41
-rw-r--r--qpdf/qpdf.testcov2
-rw-r--r--qpdf/qtest/qpdf.test24
-rw-r--r--qpdf/qtest/qpdf/fields-pages-out.pdfbin0 -> 42519 bytes
-rw-r--r--qpdf/qtest/qpdf/fields-split-1.pdfbin0 -> 7215 bytes
-rw-r--r--qpdf/qtest/qpdf/fields-split-2.pdfbin0 -> 35666 bytes
-rw-r--r--qpdf/qtest/qpdf/fields-two-pages.pdfbin0 -> 42592 bytes
9 files changed, 70 insertions, 8 deletions
diff --git a/ChangeLog b/ChangeLog
index 8693e5d4..ab4bb571 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,8 @@
2021-02-22 Jay Berkenbilt <ejb@ql.org>
+ * From qpdf CLI, --pages and --split-pages will properly preserve
+ interactive form functionality. Fixes #340.
+
* Add QPDFAcroFormDocumentHelper::copyFieldsFromForeignPage to
copy form fields from a foreign page into the current file.
diff --git a/libqpdf/QPDFAcroFormDocumentHelper.cc b/libqpdf/QPDFAcroFormDocumentHelper.cc
index dce413bd..611d469a 100644
--- a/libqpdf/QPDFAcroFormDocumentHelper.cc
+++ b/libqpdf/QPDFAcroFormDocumentHelper.cc
@@ -676,10 +676,16 @@ QPDFAcroFormDocumentHelper::copyFieldsFromForeignPage(
QPDFPageObjectHelper foreign_page,
QPDFAcroFormDocumentHelper& foreign_afdh)
{
+ std::set<QPDFObjGen> added;
for (auto field: foreign_afdh.getFormFieldsForPage(foreign_page))
{
auto new_field = this->qpdf.copyForeignObject(
field.getObjectHandle());
- addFormField(new_field);
+ auto og = new_field.getObjGen();
+ if (! added.count(og))
+ {
+ addFormField(new_field);
+ added.insert(og);
+ }
}
}
diff --git a/qpdf/qpdf.cc b/qpdf/qpdf.cc
index 0935e4f8..2080a44a 100644
--- a/qpdf/qpdf.cc
+++ b/qpdf/qpdf.cc
@@ -5143,6 +5143,19 @@ static void get_uo_pagenos(UnderOverlay& uo,
}
}
+static QPDFAcroFormDocumentHelper* get_afdh_for_qpdf(
+ std::map<unsigned long long,
+ PointerHolder<QPDFAcroFormDocumentHelper>>& afdh_map,
+ QPDF* q)
+{
+ auto uid = q->getUniqueId();
+ if (! afdh_map.count(uid))
+ {
+ afdh_map[uid] = new QPDFAcroFormDocumentHelper(*q);
+ }
+ return afdh_map[uid].getPointer();
+}
+
static void do_under_overlay_for_page(
QPDF& pdf,
Options& o,
@@ -5164,12 +5177,7 @@ static void do_under_overlay_for_page(
PointerHolder<QPDFAcroFormDocumentHelper>> afdh;
auto make_afdh = [&](QPDFPageObjectHelper& ph) {
QPDF* q = ph.getObjectHandle().getOwningQPDF();
- auto uid = q->getUniqueId();
- if (! afdh.count(uid))
- {
- afdh[uid] = new QPDFAcroFormDocumentHelper(*q);
- }
- return afdh[uid].getPointer();
+ return get_afdh_for_qpdf(afdh, q);
};
auto dest_afdh = make_afdh(dest_page);
@@ -5835,6 +5843,9 @@ static void handle_page_specs(QPDF& pdf, Options& o, bool& warnings)
std::vector<QPDFObjectHandle> new_labels;
bool any_page_labels = false;
int out_pageno = 0;
+ std::map<unsigned long long,
+ PointerHolder<QPDFAcroFormDocumentHelper>> afdh_map;
+ auto this_afdh = get_afdh_for_qpdf(afdh_map, &pdf);
for (std::vector<QPDFPageData>::iterator iter =
parsed_specs.begin();
iter != parsed_specs.end(); ++iter)
@@ -5847,6 +5858,7 @@ static void handle_page_specs(QPDF& pdf, Options& o, bool& warnings)
cis->stayOpen(true);
}
QPDFPageLabelDocumentHelper pldh(*page_data.qpdf);
+ auto other_afdh = get_afdh_for_qpdf(afdh_map, page_data.qpdf);
if (pldh.hasPageLabels())
{
any_page_labels = true;
@@ -5891,6 +5903,11 @@ static void handle_page_specs(QPDF& pdf, Options& o, bool& warnings)
// of the fact that we are using it.
selected_from_orig.insert(pageno);
}
+ else if (other_afdh->hasAcroForm())
+ {
+ QTC::TC("qpdf", "qpdf copy form fields in pages");
+ this_afdh->copyFieldsFromForeignPage(to_copy, *other_afdh);
+ }
}
if (page_data.qpdf->anyWarnings())
{
@@ -6269,6 +6286,7 @@ static void do_split_pages(QPDF& pdf, Options& o, bool& warnings)
dh.removeUnreferencedResources();
}
QPDFPageLabelDocumentHelper pldh(pdf);
+ QPDFAcroFormDocumentHelper afdh(pdf);
std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages();
size_t pageno_len = QUtil::uint_to_string(pages.size()).length();
size_t num_pages = pages.size();
@@ -6282,6 +6300,11 @@ static void do_split_pages(QPDF& pdf, Options& o, bool& warnings)
}
QPDF outpdf;
outpdf.emptyPDF();
+ PointerHolder<QPDFAcroFormDocumentHelper> out_afdh;
+ if (afdh.hasAcroForm())
+ {
+ out_afdh = new QPDFAcroFormDocumentHelper(outpdf);
+ }
if (o.suppress_warnings)
{
outpdf.setSuppressWarnings(true);
@@ -6290,6 +6313,12 @@ static void do_split_pages(QPDF& pdf, Options& o, bool& warnings)
{
QPDFObjectHandle page = pages.at(pageno - 1);
outpdf.addPage(page, false);
+ if (out_afdh.getPointer())
+ {
+ QTC::TC("qpdf", "qpdf copy form fields in split_pages");
+ out_afdh->copyFieldsFromForeignPage(
+ QPDFPageObjectHelper(page), afdh);
+ }
}
if (pldh.hasPageLabels())
{
diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov
index bbc23dfc..a68e88df 100644
--- a/qpdf/qpdf.testcov
+++ b/qpdf/qpdf.testcov
@@ -575,3 +575,5 @@ QPDFPageObjectHelper flatten inherit rotate 0
QPDFAcroFormDocumentHelper copy annotation 3
QPDFAcroFormDocumentHelper field with parent 3
QPDFAcroFormDocumentHelper modify ap matrix 0
+qpdf copy form fields in split_pages 0
+qpdf copy form fields in pages 0
diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test
index 09df4258..ff660118 100644
--- a/qpdf/qtest/qpdf.test
+++ b/qpdf/qtest/qpdf.test
@@ -2414,7 +2414,7 @@ foreach my $f (qw(screen print))
show_ntests();
# ----------
$td->notify("--- Copy Annotations ---");
-$n_tests += 16;
+$n_tests += 21;
$td->runtest("complex copy annotations",
{$td->COMMAND =>
@@ -2458,6 +2458,28 @@ foreach my $d ([1, "appearances-1.pdf"],
{$td->FILE => "test80b$n.pdf"});
}
+$td->runtest("page extraction with fields",
+ {$td->COMMAND =>
+ "qpdf --static-id --empty" .
+ " --pages fields-two-pages.pdf -- a.pdf"},
+ {$td->STRING => "", $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+$td->runtest("check output",
+ {$td->FILE => "a.pdf"},
+ {$td->FILE => "fields-pages-out.pdf"});
+$td->runtest("page splitting with fields",
+ {$td->COMMAND =>
+ "qpdf --static-id" .
+ " --split-pages fields-two-pages.pdf a.pdf"},
+ {$td->STRING => "", $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+for (my $i = 1; $i <= 2; ++$i)
+{
+ $td->runtest("check output",
+ {$td->FILE => "a-$i.pdf"},
+ {$td->FILE => "fields-split-$i.pdf"});
+}
+
show_ntests();
# ----------
$td->notify("--- Page Tree Issues ---");
diff --git a/qpdf/qtest/qpdf/fields-pages-out.pdf b/qpdf/qtest/qpdf/fields-pages-out.pdf
new file mode 100644
index 00000000..8b3eaa83
--- /dev/null
+++ b/qpdf/qtest/qpdf/fields-pages-out.pdf
Binary files differ
diff --git a/qpdf/qtest/qpdf/fields-split-1.pdf b/qpdf/qtest/qpdf/fields-split-1.pdf
new file mode 100644
index 00000000..9f8a7870
--- /dev/null
+++ b/qpdf/qtest/qpdf/fields-split-1.pdf
Binary files differ
diff --git a/qpdf/qtest/qpdf/fields-split-2.pdf b/qpdf/qtest/qpdf/fields-split-2.pdf
new file mode 100644
index 00000000..909d0ac3
--- /dev/null
+++ b/qpdf/qtest/qpdf/fields-split-2.pdf
Binary files differ
diff --git a/qpdf/qtest/qpdf/fields-two-pages.pdf b/qpdf/qtest/qpdf/fields-two-pages.pdf
new file mode 100644
index 00000000..d8f3095f
--- /dev/null
+++ b/qpdf/qtest/qpdf/fields-two-pages.pdf
Binary files differ