mirror of
https://github.com/qpdf/qpdf.git
synced 2025-01-02 22:50:20 +00:00
Preserve form fields when splitting pages (fixes #340)
This commit is contained in:
parent
1f35ec9988
commit
83216e640c
@ -1,5 +1,8 @@
|
|||||||
2021-02-22 Jay Berkenbilt <ejb@ql.org>
|
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
|
* Add QPDFAcroFormDocumentHelper::copyFieldsFromForeignPage to
|
||||||
copy form fields from a foreign page into the current file.
|
copy form fields from a foreign page into the current file.
|
||||||
|
|
||||||
|
@ -676,10 +676,16 @@ QPDFAcroFormDocumentHelper::copyFieldsFromForeignPage(
|
|||||||
QPDFPageObjectHelper foreign_page,
|
QPDFPageObjectHelper foreign_page,
|
||||||
QPDFAcroFormDocumentHelper& foreign_afdh)
|
QPDFAcroFormDocumentHelper& foreign_afdh)
|
||||||
{
|
{
|
||||||
|
std::set<QPDFObjGen> added;
|
||||||
for (auto field: foreign_afdh.getFormFieldsForPage(foreign_page))
|
for (auto field: foreign_afdh.getFormFieldsForPage(foreign_page))
|
||||||
{
|
{
|
||||||
auto new_field = this->qpdf.copyForeignObject(
|
auto new_field = this->qpdf.copyForeignObject(
|
||||||
field.getObjectHandle());
|
field.getObjectHandle());
|
||||||
addFormField(new_field);
|
auto og = new_field.getObjGen();
|
||||||
|
if (! added.count(og))
|
||||||
|
{
|
||||||
|
addFormField(new_field);
|
||||||
|
added.insert(og);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
41
qpdf/qpdf.cc
41
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(
|
static void do_under_overlay_for_page(
|
||||||
QPDF& pdf,
|
QPDF& pdf,
|
||||||
Options& o,
|
Options& o,
|
||||||
@ -5164,12 +5177,7 @@ static void do_under_overlay_for_page(
|
|||||||
PointerHolder<QPDFAcroFormDocumentHelper>> afdh;
|
PointerHolder<QPDFAcroFormDocumentHelper>> afdh;
|
||||||
auto make_afdh = [&](QPDFPageObjectHelper& ph) {
|
auto make_afdh = [&](QPDFPageObjectHelper& ph) {
|
||||||
QPDF* q = ph.getObjectHandle().getOwningQPDF();
|
QPDF* q = ph.getObjectHandle().getOwningQPDF();
|
||||||
auto uid = q->getUniqueId();
|
return get_afdh_for_qpdf(afdh, q);
|
||||||
if (! afdh.count(uid))
|
|
||||||
{
|
|
||||||
afdh[uid] = new QPDFAcroFormDocumentHelper(*q);
|
|
||||||
}
|
|
||||||
return afdh[uid].getPointer();
|
|
||||||
};
|
};
|
||||||
auto dest_afdh = make_afdh(dest_page);
|
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;
|
std::vector<QPDFObjectHandle> new_labels;
|
||||||
bool any_page_labels = false;
|
bool any_page_labels = false;
|
||||||
int out_pageno = 0;
|
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 =
|
for (std::vector<QPDFPageData>::iterator iter =
|
||||||
parsed_specs.begin();
|
parsed_specs.begin();
|
||||||
iter != parsed_specs.end(); ++iter)
|
iter != parsed_specs.end(); ++iter)
|
||||||
@ -5847,6 +5858,7 @@ static void handle_page_specs(QPDF& pdf, Options& o, bool& warnings)
|
|||||||
cis->stayOpen(true);
|
cis->stayOpen(true);
|
||||||
}
|
}
|
||||||
QPDFPageLabelDocumentHelper pldh(*page_data.qpdf);
|
QPDFPageLabelDocumentHelper pldh(*page_data.qpdf);
|
||||||
|
auto other_afdh = get_afdh_for_qpdf(afdh_map, page_data.qpdf);
|
||||||
if (pldh.hasPageLabels())
|
if (pldh.hasPageLabels())
|
||||||
{
|
{
|
||||||
any_page_labels = true;
|
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.
|
// of the fact that we are using it.
|
||||||
selected_from_orig.insert(pageno);
|
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())
|
if (page_data.qpdf->anyWarnings())
|
||||||
{
|
{
|
||||||
@ -6269,6 +6286,7 @@ static void do_split_pages(QPDF& pdf, Options& o, bool& warnings)
|
|||||||
dh.removeUnreferencedResources();
|
dh.removeUnreferencedResources();
|
||||||
}
|
}
|
||||||
QPDFPageLabelDocumentHelper pldh(pdf);
|
QPDFPageLabelDocumentHelper pldh(pdf);
|
||||||
|
QPDFAcroFormDocumentHelper afdh(pdf);
|
||||||
std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages();
|
std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages();
|
||||||
size_t pageno_len = QUtil::uint_to_string(pages.size()).length();
|
size_t pageno_len = QUtil::uint_to_string(pages.size()).length();
|
||||||
size_t num_pages = pages.size();
|
size_t num_pages = pages.size();
|
||||||
@ -6282,6 +6300,11 @@ static void do_split_pages(QPDF& pdf, Options& o, bool& warnings)
|
|||||||
}
|
}
|
||||||
QPDF outpdf;
|
QPDF outpdf;
|
||||||
outpdf.emptyPDF();
|
outpdf.emptyPDF();
|
||||||
|
PointerHolder<QPDFAcroFormDocumentHelper> out_afdh;
|
||||||
|
if (afdh.hasAcroForm())
|
||||||
|
{
|
||||||
|
out_afdh = new QPDFAcroFormDocumentHelper(outpdf);
|
||||||
|
}
|
||||||
if (o.suppress_warnings)
|
if (o.suppress_warnings)
|
||||||
{
|
{
|
||||||
outpdf.setSuppressWarnings(true);
|
outpdf.setSuppressWarnings(true);
|
||||||
@ -6290,6 +6313,12 @@ static void do_split_pages(QPDF& pdf, Options& o, bool& warnings)
|
|||||||
{
|
{
|
||||||
QPDFObjectHandle page = pages.at(pageno - 1);
|
QPDFObjectHandle page = pages.at(pageno - 1);
|
||||||
outpdf.addPage(page, false);
|
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())
|
if (pldh.hasPageLabels())
|
||||||
{
|
{
|
||||||
|
@ -575,3 +575,5 @@ QPDFPageObjectHelper flatten inherit rotate 0
|
|||||||
QPDFAcroFormDocumentHelper copy annotation 3
|
QPDFAcroFormDocumentHelper copy annotation 3
|
||||||
QPDFAcroFormDocumentHelper field with parent 3
|
QPDFAcroFormDocumentHelper field with parent 3
|
||||||
QPDFAcroFormDocumentHelper modify ap matrix 0
|
QPDFAcroFormDocumentHelper modify ap matrix 0
|
||||||
|
qpdf copy form fields in split_pages 0
|
||||||
|
qpdf copy form fields in pages 0
|
||||||
|
@ -2414,7 +2414,7 @@ foreach my $f (qw(screen print))
|
|||||||
show_ntests();
|
show_ntests();
|
||||||
# ----------
|
# ----------
|
||||||
$td->notify("--- Copy Annotations ---");
|
$td->notify("--- Copy Annotations ---");
|
||||||
$n_tests += 16;
|
$n_tests += 21;
|
||||||
|
|
||||||
$td->runtest("complex copy annotations",
|
$td->runtest("complex copy annotations",
|
||||||
{$td->COMMAND =>
|
{$td->COMMAND =>
|
||||||
@ -2458,6 +2458,28 @@ foreach my $d ([1, "appearances-1.pdf"],
|
|||||||
{$td->FILE => "test80b$n.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();
|
show_ntests();
|
||||||
# ----------
|
# ----------
|
||||||
$td->notify("--- Page Tree Issues ---");
|
$td->notify("--- Page Tree Issues ---");
|
||||||
|
BIN
qpdf/qtest/qpdf/fields-pages-out.pdf
Normal file
BIN
qpdf/qtest/qpdf/fields-pages-out.pdf
Normal file
Binary file not shown.
BIN
qpdf/qtest/qpdf/fields-split-1.pdf
Normal file
BIN
qpdf/qtest/qpdf/fields-split-1.pdf
Normal file
Binary file not shown.
BIN
qpdf/qtest/qpdf/fields-split-2.pdf
Normal file
BIN
qpdf/qtest/qpdf/fields-split-2.pdf
Normal file
Binary file not shown.
BIN
qpdf/qtest/qpdf/fields-two-pages.pdf
Normal file
BIN
qpdf/qtest/qpdf/fields-two-pages.pdf
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user