Commit 1f35ec9988eaf1ad3705655c434701d175c5b49f
1 parent
f02aa74b
Add methods for copying form fields
Showing
5 changed files
with
81 additions
and
14 deletions
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 | { | ... | ... |