Commit 1f35ec9988eaf1ad3705655c434701d175c5b49f
1 parent
f02aa74b
Add methods for copying form fields
Showing
5 changed files
with
81 additions
and
14 deletions
ChangeLog
| 1 | 2021-02-22 Jay Berkenbilt <ejb@ql.org> | 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 | * Update pdf-overlay-page example to include copying of | 9 | * Update pdf-overlay-page example to include copying of |
| 4 | annotations. | 10 | annotations. |
| 5 | 11 |
include/qpdf/QPDFAcroFormDocumentHelper.hh
| @@ -140,6 +140,11 @@ class QPDFAcroFormDocumentHelper: public QPDFDocumentHelper | @@ -140,6 +140,11 @@ class QPDFAcroFormDocumentHelper: public QPDFDocumentHelper | ||
| 140 | std::vector<QPDFAnnotationObjectHelper> | 140 | std::vector<QPDFAnnotationObjectHelper> |
| 141 | getWidgetAnnotationsForPage(QPDFPageObjectHelper); | 141 | getWidgetAnnotationsForPage(QPDFPageObjectHelper); |
| 142 | 142 | ||
| 143 | + // Return form fields for a page. | ||
| 144 | + QPDF_DLL | ||
| 145 | + std::vector<QPDFFormFieldObjectHelper> | ||
| 146 | + getFormFieldsForPage(QPDFPageObjectHelper); | ||
| 147 | + | ||
| 143 | // Return the terminal field that is associated with this | 148 | // Return the terminal field that is associated with this |
| 144 | // annotation. If the annotation dictionary is merged with the | 149 | // annotation. If the annotation dictionary is merged with the |
| 145 | // field dictionary, the underlying object will be the same, but | 150 | // field dictionary, the underlying object will be the same, but |
| @@ -204,6 +209,13 @@ class QPDFAcroFormDocumentHelper: public QPDFDocumentHelper | @@ -204,6 +209,13 @@ class QPDFAcroFormDocumentHelper: public QPDFDocumentHelper | ||
| 204 | QPDF* from_qpdf = nullptr, | 209 | QPDF* from_qpdf = nullptr, |
| 205 | QPDFAcroFormDocumentHelper* from_afdh = nullptr); | 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 | private: | 219 | private: |
| 208 | void analyze(); | 220 | void analyze(); |
| 209 | void traverseField(QPDFObjectHandle field, | 221 | void traverseField(QPDFObjectHandle field, |
include/qpdf/QPDFFormFieldObjectHelper.hh
| @@ -54,6 +54,13 @@ class QPDFFormFieldObjectHelper: public QPDFObjectHelper | @@ -54,6 +54,13 @@ class QPDFFormFieldObjectHelper: public QPDFObjectHelper | ||
| 54 | QPDF_DLL | 54 | QPDF_DLL |
| 55 | QPDFFormFieldObjectHelper getParent(); | 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 | // Get a field value, possibly inheriting the value from an | 64 | // Get a field value, possibly inheriting the value from an |
| 58 | // ancestor node. | 65 | // ancestor node. |
| 59 | QPDF_DLL | 66 | QPDF_DLL |
libqpdf/QPDFAcroFormDocumentHelper.cc
| @@ -132,6 +132,23 @@ QPDFAcroFormDocumentHelper::getWidgetAnnotationsForPage(QPDFPageObjectHelper h) | @@ -132,6 +132,23 @@ QPDFAcroFormDocumentHelper::getWidgetAnnotationsForPage(QPDFPageObjectHelper h) | ||
| 132 | return h.getAnnotations("/Widget"); | 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 | QPDFFormFieldObjectHelper | 152 | QPDFFormFieldObjectHelper |
| 136 | QPDFAcroFormDocumentHelper::getFieldForAnnotation(QPDFAnnotationObjectHelper h) | 153 | QPDFAcroFormDocumentHelper::getFieldForAnnotation(QPDFAnnotationObjectHelper h) |
| 137 | { | 154 | { |
| @@ -501,19 +518,8 @@ QPDFAcroFormDocumentHelper::transformAnnotations( | @@ -501,19 +518,8 @@ QPDFAcroFormDocumentHelper::transformAnnotations( | ||
| 501 | // annotation and field separately in this case. | 518 | // annotation and field separately in this case. |
| 502 | have_field = true; | 519 | have_field = true; |
| 503 | // Find the top-level field. It may be the field itself. | 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 | if (foreign) | 523 | if (foreign) |
| 518 | { | 524 | { |
| 519 | // copyForeignObject returns the same value if called | 525 | // copyForeignObject returns the same value if called |
| @@ -537,7 +543,7 @@ QPDFAcroFormDocumentHelper::transformAnnotations( | @@ -537,7 +543,7 @@ QPDFAcroFormDocumentHelper::transformAnnotations( | ||
| 537 | { | 543 | { |
| 538 | queue.push_back(top_field); | 544 | queue.push_back(top_field); |
| 539 | } | 545 | } |
| 540 | - seen.clear(); | 546 | + std::set<QPDFObjGen> seen; |
| 541 | while (! queue.empty()) | 547 | while (! queue.empty()) |
| 542 | { | 548 | { |
| 543 | QPDFObjectHandle obj = queue.front(); | 549 | QPDFObjectHandle obj = queue.front(); |
| @@ -664,3 +670,16 @@ QPDFAcroFormDocumentHelper::transformAnnotations( | @@ -664,3 +670,16 @@ QPDFAcroFormDocumentHelper::transformAnnotations( | ||
| 664 | "/Rect", QPDFObjectHandle::newFromRectangle(rect)); | 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,6 +39,29 @@ QPDFFormFieldObjectHelper::getParent() | ||
| 39 | return this->oh.getKey("/Parent"); // may be null | 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 | QPDFObjectHandle | 65 | QPDFObjectHandle |
| 43 | QPDFFormFieldObjectHelper::getInheritableFieldValue(std::string const& name) | 66 | QPDFFormFieldObjectHelper::getInheritableFieldValue(std::string const& name) |
| 44 | { | 67 | { |