Add methods for copying form fields

This commit is contained in:
Jay Berkenbilt 2021-02-22 17:44:13 -05:00
parent f02aa74bf5
commit 1f35ec9988
5 changed files with 81 additions and 14 deletions

View File

@ -1,5 +1,11 @@
2021-02-22 Jay Berkenbilt <ejb@ql.org>
* Add QPDFAcroFormDocumentHelper::copyFieldsFromForeignPage to
copy form fields from a foreign page into the current file.
* Add QPDFFormFieldObjectHelper::getTopLevelField to get the
top-level field for a given form field.
* Update pdf-overlay-page example to include copying of
annotations.

View File

@ -140,6 +140,11 @@ class QPDFAcroFormDocumentHelper: public QPDFDocumentHelper
std::vector<QPDFAnnotationObjectHelper>
getWidgetAnnotationsForPage(QPDFPageObjectHelper);
// Return form fields for a page.
QPDF_DLL
std::vector<QPDFFormFieldObjectHelper>
getFormFieldsForPage(QPDFPageObjectHelper);
// Return the terminal field that is associated with this
// annotation. If the annotation dictionary is merged with the
// field dictionary, the underlying object will be the same, but
@ -204,6 +209,13 @@ class QPDFAcroFormDocumentHelper: public QPDFDocumentHelper
QPDF* from_qpdf = nullptr,
QPDFAcroFormDocumentHelper* from_afdh = nullptr);
// Copy form fields from a page in a different QPDF object to this
// QPDF.
QPDF_DLL
void copyFieldsFromForeignPage(
QPDFPageObjectHelper foreign_page,
QPDFAcroFormDocumentHelper& foreign_afdh);
private:
void analyze();
void traverseField(QPDFObjectHandle field,

View File

@ -54,6 +54,13 @@ class QPDFFormFieldObjectHelper: public QPDFObjectHelper
QPDF_DLL
QPDFFormFieldObjectHelper getParent();
// Return the top-level field for this field. Typically this will
// be the field itself or its parent. If is_different is provided,
// it is set to true if the top-level field is different from the
// field itself; otherwise it is set to false.
QPDF_DLL
QPDFFormFieldObjectHelper getTopLevelField(bool* is_different = nullptr);
// Get a field value, possibly inheriting the value from an
// ancestor node.
QPDF_DLL

View File

@ -132,6 +132,23 @@ QPDFAcroFormDocumentHelper::getWidgetAnnotationsForPage(QPDFPageObjectHelper h)
return h.getAnnotations("/Widget");
}
std::vector<QPDFFormFieldObjectHelper>
QPDFAcroFormDocumentHelper::getFormFieldsForPage(QPDFPageObjectHelper ph)
{
std::vector<QPDFFormFieldObjectHelper> result;
auto widget_annotations = getWidgetAnnotationsForPage(ph);
for (auto annot: widget_annotations)
{
auto field = getFieldForAnnotation(annot);
field = field.getTopLevelField();
if (field.getObjectHandle().isDictionary())
{
result.push_back(field);
}
}
return result;
}
QPDFFormFieldObjectHelper
QPDFAcroFormDocumentHelper::getFieldForAnnotation(QPDFAnnotationObjectHelper h)
{
@ -501,19 +518,8 @@ QPDFAcroFormDocumentHelper::transformAnnotations(
// annotation and field separately in this case.
have_field = true;
// Find the top-level field. It may be the field itself.
top_field = ffield_oh;
std::set<QPDFObjGen> seen;
while (! top_field.getKey("/Parent").isNull())
{
top_field = top_field.getKey("/Parent");
have_parent = true;
auto og = top_field.getObjGen();
if (seen.count(og))
{
break;
}
seen.insert(og);
}
top_field = ffield.getTopLevelField(
&have_parent).getObjectHandle();
if (foreign)
{
// copyForeignObject returns the same value if called
@ -537,7 +543,7 @@ QPDFAcroFormDocumentHelper::transformAnnotations(
{
queue.push_back(top_field);
}
seen.clear();
std::set<QPDFObjGen> seen;
while (! queue.empty())
{
QPDFObjectHandle obj = queue.front();
@ -664,3 +670,16 @@ QPDFAcroFormDocumentHelper::transformAnnotations(
"/Rect", QPDFObjectHandle::newFromRectangle(rect));
}
}
void
QPDFAcroFormDocumentHelper::copyFieldsFromForeignPage(
QPDFPageObjectHelper foreign_page,
QPDFAcroFormDocumentHelper& foreign_afdh)
{
for (auto field: foreign_afdh.getFormFieldsForPage(foreign_page))
{
auto new_field = this->qpdf.copyForeignObject(
field.getObjectHandle());
addFormField(new_field);
}
}

View File

@ -39,6 +39,29 @@ QPDFFormFieldObjectHelper::getParent()
return this->oh.getKey("/Parent"); // may be null
}
QPDFFormFieldObjectHelper
QPDFFormFieldObjectHelper::getTopLevelField(bool* is_different)
{
auto top_field = this->oh;
std::set<QPDFObjGen> seen;
while (top_field.isDictionary() &&
(! top_field.getKey("/Parent").isNull()))
{
top_field = top_field.getKey("/Parent");
if (is_different)
{
*is_different = true;
}
auto og = top_field.getObjGen();
if (seen.count(og))
{
break;
}
seen.insert(og);
}
return QPDFFormFieldObjectHelper(top_field);
}
QPDFObjectHandle
QPDFFormFieldObjectHelper::getInheritableFieldValue(std::string const& name)
{