Preserve form fields when splitting pages (fixes #340)

This commit is contained in:
Jay Berkenbilt 2021-02-22 18:36:05 -05:00
parent 1f35ec9988
commit 83216e640c
9 changed files with 70 additions and 8 deletions

View File

@ -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.

View File

@ -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);
}
}
}

View File

@ -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())
{

View File

@ -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

View File

@ -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 ---");

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.