Commit 1f35ec9988eaf1ad3705655c434701d175c5b49f

Authored by Jay Berkenbilt
1 parent f02aa74b

Add methods for copying form fields

ChangeLog
1 1 2021-02-22 Jay Berkenbilt <ejb@ql.org>
2 2  
  3 + * Add QPDFAcroFormDocumentHelper::copyFieldsFromForeignPage to
  4 + copy form fields from a foreign page into the current file.
  5 +
  6 + * Add QPDFFormFieldObjectHelper::getTopLevelField to get the
  7 + top-level field for a given form field.
  8 +
3 9 * Update pdf-overlay-page example to include copying of
4 10 annotations.
5 11  
... ...
include/qpdf/QPDFAcroFormDocumentHelper.hh
... ... @@ -140,6 +140,11 @@ class QPDFAcroFormDocumentHelper: public QPDFDocumentHelper
140 140 std::vector<QPDFAnnotationObjectHelper>
141 141 getWidgetAnnotationsForPage(QPDFPageObjectHelper);
142 142  
  143 + // Return form fields for a page.
  144 + QPDF_DLL
  145 + std::vector<QPDFFormFieldObjectHelper>
  146 + getFormFieldsForPage(QPDFPageObjectHelper);
  147 +
143 148 // Return the terminal field that is associated with this
144 149 // annotation. If the annotation dictionary is merged with the
145 150 // field dictionary, the underlying object will be the same, but
... ... @@ -204,6 +209,13 @@ class QPDFAcroFormDocumentHelper: public QPDFDocumentHelper
204 209 QPDF* from_qpdf = nullptr,
205 210 QPDFAcroFormDocumentHelper* from_afdh = nullptr);
206 211  
  212 + // Copy form fields from a page in a different QPDF object to this
  213 + // QPDF.
  214 + QPDF_DLL
  215 + void copyFieldsFromForeignPage(
  216 + QPDFPageObjectHelper foreign_page,
  217 + QPDFAcroFormDocumentHelper& foreign_afdh);
  218 +
207 219 private:
208 220 void analyze();
209 221 void traverseField(QPDFObjectHandle field,
... ...
include/qpdf/QPDFFormFieldObjectHelper.hh
... ... @@ -54,6 +54,13 @@ class QPDFFormFieldObjectHelper: public QPDFObjectHelper
54 54 QPDF_DLL
55 55 QPDFFormFieldObjectHelper getParent();
56 56  
  57 + // Return the top-level field for this field. Typically this will
  58 + // be the field itself or its parent. If is_different is provided,
  59 + // it is set to true if the top-level field is different from the
  60 + // field itself; otherwise it is set to false.
  61 + QPDF_DLL
  62 + QPDFFormFieldObjectHelper getTopLevelField(bool* is_different = nullptr);
  63 +
57 64 // Get a field value, possibly inheriting the value from an
58 65 // ancestor node.
59 66 QPDF_DLL
... ...
libqpdf/QPDFAcroFormDocumentHelper.cc
... ... @@ -132,6 +132,23 @@ QPDFAcroFormDocumentHelper::getWidgetAnnotationsForPage(QPDFPageObjectHelper h)
132 132 return h.getAnnotations("/Widget");
133 133 }
134 134  
  135 +std::vector<QPDFFormFieldObjectHelper>
  136 +QPDFAcroFormDocumentHelper::getFormFieldsForPage(QPDFPageObjectHelper ph)
  137 +{
  138 + std::vector<QPDFFormFieldObjectHelper> result;
  139 + auto widget_annotations = getWidgetAnnotationsForPage(ph);
  140 + for (auto annot: widget_annotations)
  141 + {
  142 + auto field = getFieldForAnnotation(annot);
  143 + field = field.getTopLevelField();
  144 + if (field.getObjectHandle().isDictionary())
  145 + {
  146 + result.push_back(field);
  147 + }
  148 + }
  149 + return result;
  150 +}
  151 +
135 152 QPDFFormFieldObjectHelper
136 153 QPDFAcroFormDocumentHelper::getFieldForAnnotation(QPDFAnnotationObjectHelper h)
137 154 {
... ... @@ -501,19 +518,8 @@ QPDFAcroFormDocumentHelper::transformAnnotations(
501 518 // annotation and field separately in this case.
502 519 have_field = true;
503 520 // Find the top-level field. It may be the field itself.
504   - top_field = ffield_oh;
505   - std::set<QPDFObjGen> seen;
506   - while (! top_field.getKey("/Parent").isNull())
507   - {
508   - top_field = top_field.getKey("/Parent");
509   - have_parent = true;
510   - auto og = top_field.getObjGen();
511   - if (seen.count(og))
512   - {
513   - break;
514   - }
515   - seen.insert(og);
516   - }
  521 + top_field = ffield.getTopLevelField(
  522 + &have_parent).getObjectHandle();
517 523 if (foreign)
518 524 {
519 525 // copyForeignObject returns the same value if called
... ... @@ -537,7 +543,7 @@ QPDFAcroFormDocumentHelper::transformAnnotations(
537 543 {
538 544 queue.push_back(top_field);
539 545 }
540   - seen.clear();
  546 + std::set<QPDFObjGen> seen;
541 547 while (! queue.empty())
542 548 {
543 549 QPDFObjectHandle obj = queue.front();
... ... @@ -664,3 +670,16 @@ QPDFAcroFormDocumentHelper::transformAnnotations(
664 670 "/Rect", QPDFObjectHandle::newFromRectangle(rect));
665 671 }
666 672 }
  673 +
  674 +void
  675 +QPDFAcroFormDocumentHelper::copyFieldsFromForeignPage(
  676 + QPDFPageObjectHelper foreign_page,
  677 + QPDFAcroFormDocumentHelper& foreign_afdh)
  678 +{
  679 + for (auto field: foreign_afdh.getFormFieldsForPage(foreign_page))
  680 + {
  681 + auto new_field = this->qpdf.copyForeignObject(
  682 + field.getObjectHandle());
  683 + addFormField(new_field);
  684 + }
  685 +}
... ...
libqpdf/QPDFFormFieldObjectHelper.cc
... ... @@ -39,6 +39,29 @@ QPDFFormFieldObjectHelper::getParent()
39 39 return this->oh.getKey("/Parent"); // may be null
40 40 }
41 41  
  42 +QPDFFormFieldObjectHelper
  43 +QPDFFormFieldObjectHelper::getTopLevelField(bool* is_different)
  44 +{
  45 + auto top_field = this->oh;
  46 + std::set<QPDFObjGen> seen;
  47 + while (top_field.isDictionary() &&
  48 + (! top_field.getKey("/Parent").isNull()))
  49 + {
  50 + top_field = top_field.getKey("/Parent");
  51 + if (is_different)
  52 + {
  53 + *is_different = true;
  54 + }
  55 + auto og = top_field.getObjGen();
  56 + if (seen.count(og))
  57 + {
  58 + break;
  59 + }
  60 + seen.insert(og);
  61 + }
  62 + return QPDFFormFieldObjectHelper(top_field);
  63 +}
  64 +
42 65 QPDFObjectHandle
43 66 QPDFFormFieldObjectHelper::getInheritableFieldValue(std::string const& name)
44 67 {
... ...