diff options
author | Jay Berkenbilt <ejb@ql.org> | 2018-06-21 21:00:00 +0200 |
---|---|---|
committer | Jay Berkenbilt <ejb@ql.org> | 2018-06-21 21:57:13 +0200 |
commit | 397b097c469db89a49e5a6c2035a0beee2e4d117 (patch) | |
tree | 5f2b5e33be51922d210146dd78a1e4a66749691d | |
parent | 952a665a4ed51400b5925e7cd69f08f0aeb374fe (diff) | |
download | qpdf-397b097c469db89a49e5a6c2035a0beee2e4d117.tar.zst |
Allow setting a form field's value
-rw-r--r-- | ChangeLog | 5 | ||||
-rw-r--r-- | include/qpdf/QPDFAcroFormDocumentHelper.hh | 17 | ||||
-rw-r--r-- | include/qpdf/QPDFFormFieldObjectHelper.hh | 23 | ||||
-rw-r--r-- | libqpdf/QPDFAcroFormDocumentHelper.cc | 35 | ||||
-rw-r--r-- | libqpdf/QPDFFormFieldObjectHelper.cc | 42 | ||||
-rw-r--r-- | qpdf/qtest/qpdf.test | 11 | ||||
-rw-r--r-- | qpdf/qtest/qpdf/form-no-need-appearances-filled.pdf | bin | 0 -> 12686 bytes | |||
-rw-r--r-- | qpdf/qtest/qpdf/form-no-need-appearances.out | 3 | ||||
-rw-r--r-- | qpdf/qtest/qpdf/form-no-need-appearances.pdf | bin | 0 -> 12572 bytes | |||
-rw-r--r-- | qpdf/test_driver.cc | 28 |
10 files changed, 163 insertions, 1 deletions
@@ -1,5 +1,10 @@ 2018-06-21 Jay Berkenbilt <ejb@ql.org> + * Added methods QPDFAcroFormDocumentHelper::setNeedAppearances and + added methods to QPDFFormFieldObjectHelper to set a field's value, + optionally updating the document to indicate that appearance + streams need to be regenerated. + * Added QPDFObject::newUnicodeString and QPDFObject::unparseBinary to allow for more convenient creation of strings that are explicitly encoded in UTF-16 BE. This is useful for creating diff --git a/include/qpdf/QPDFAcroFormDocumentHelper.hh b/include/qpdf/QPDFAcroFormDocumentHelper.hh index d786fff4..2aa0d377 100644 --- a/include/qpdf/QPDFAcroFormDocumentHelper.hh +++ b/include/qpdf/QPDFAcroFormDocumentHelper.hh @@ -135,6 +135,23 @@ class QPDFAcroFormDocumentHelper: public QPDFDocumentHelper QPDFFormFieldObjectHelper getFieldForAnnotation(QPDFAnnotationObjectHelper); + // Return the current value of /NeedAppearances. If + // /NeedAppearances is missing, return false as that is how PDF + // viewers are supposed to interpret it. + QPDF_DLL + bool getNeedAppearances(); + + // Indicate whether appearance streams must be regenerated. If you + // modify a field value, you should call setNeedAppearances(true) + // unless you also generate an appearance stream for the + // corresponding annotation at the same time. If you generate + // appearance streams for all fields, you can call + // setNeedAppearances(false). If you use + // QPDFFormFieldObjectHelper::setV, it will automatically call + // this method unless you tell it not to. + QPDF_DLL + void setNeedAppearances(bool); + private: void analyze(); void traverseField(QPDFObjectHandle field, diff --git a/include/qpdf/QPDFFormFieldObjectHelper.hh b/include/qpdf/QPDFFormFieldObjectHelper.hh index b45955a3..c22703aa 100644 --- a/include/qpdf/QPDFFormFieldObjectHelper.hh +++ b/include/qpdf/QPDFFormFieldObjectHelper.hh @@ -114,6 +114,29 @@ class QPDFFormFieldObjectHelper: public QPDFObjectHelper QPDF_DLL int getQuadding(); + // Set an attribute to the given value + QPDF_DLL + void setFieldAttribute(std::string const& key, QPDFObjectHandle value); + + // Set an attribute to the given value as a Unicode string (UTF-16 + // BE encoded). The input string should be UTF-8 encoded. + QPDF_DLL + void setFieldAttribute(std::string const& key, + std::string const& utf8_value); + + // Set /V (field value) to the given value. Optionally set + // /NeedAppearances to true. You can explicitly tell this method + // not to set /NeedAppearances if you are going to explicitly + // generate an appearance stream yourself. + QPDF_DLL + void setV(QPDFObjectHandle value, bool need_appearances = true); + + // Set /V (field value) to the given string value encoded as a + // Unicode string. The input value should be UTF-8 encoded. See + // comments above about /NeedAppearances. + QPDF_DLL + void setV(std::string const& utf8_value, bool need_appearances = true); + private: class Members { diff --git a/libqpdf/QPDFAcroFormDocumentHelper.cc b/libqpdf/QPDFAcroFormDocumentHelper.cc index 7e70fd92..46648ed9 100644 --- a/libqpdf/QPDFAcroFormDocumentHelper.cc +++ b/libqpdf/QPDFAcroFormDocumentHelper.cc @@ -250,3 +250,38 @@ QPDFAcroFormDocumentHelper::traverseField( QPDFFormFieldObjectHelper(our_field); } } + +bool +QPDFAcroFormDocumentHelper::getNeedAppearances() +{ + bool result = false; + QPDFObjectHandle acroform = this->qpdf.getRoot().getKey("/AcroForm"); + if (acroform.isDictionary() && + acroform.getKey("/NeedAppearances").isBool()) + { + result = acroform.getKey("/NeedAppearances").getBoolValue(); + } + return result; +} + +void +QPDFAcroFormDocumentHelper::setNeedAppearances(bool val) +{ + QPDFObjectHandle acroform = this->qpdf.getRoot().getKey("/AcroForm"); + if (! acroform.isDictionary()) + { + this->qpdf.getRoot().warnIfPossible( + "ignoring call to QPDFAcroFormDocumentHelper::setNeedAppearances" + " on a file that lacks an /AcroForm dictionary"); + return; + } + if (val) + { + acroform.replaceKey("/NeedAppearances", + QPDFObjectHandle::newBool(true)); + } + else + { + acroform.removeKey("/NeedAppearances"); + } +} diff --git a/libqpdf/QPDFFormFieldObjectHelper.cc b/libqpdf/QPDFFormFieldObjectHelper.cc index e6445af3..283b632d 100644 --- a/libqpdf/QPDFFormFieldObjectHelper.cc +++ b/libqpdf/QPDFFormFieldObjectHelper.cc @@ -1,5 +1,6 @@ #include <qpdf/QPDFFormFieldObjectHelper.hh> #include <qpdf/QTC.hh> +#include <qpdf/QPDFAcroFormDocumentHelper.hh> QPDFFormFieldObjectHelper::Members::~Members() { @@ -188,3 +189,44 @@ QPDFFormFieldObjectHelper::getQuadding() } return result; } + +void +QPDFFormFieldObjectHelper::setFieldAttribute( + std::string const& key, QPDFObjectHandle value) +{ + this->oh.replaceKey(key, value); +} + +void +QPDFFormFieldObjectHelper::setFieldAttribute( + std::string const& key, std::string const& utf8_value) +{ + this->oh.replaceKey(key, QPDFObjectHandle::newUnicodeString(utf8_value)); +} + +void +QPDFFormFieldObjectHelper::setV( + QPDFObjectHandle value, bool need_appearances) +{ + setFieldAttribute("/V", value); + if (need_appearances) + { + QPDF* qpdf = this->oh.getOwningQPDF(); + if (! qpdf) + { + throw std::logic_error( + "QPDFFormFieldObjectHelper::setV called with" + " need_appearances = true on an object that is" + " not associated with an owning QPDF"); + } + QPDFAcroFormDocumentHelper(*qpdf).setNeedAppearances(true); + } +} + +void +QPDFFormFieldObjectHelper::setV( + std::string const& utf8_value, bool need_appearances) +{ + setV(QPDFObjectHandle::newUnicodeString(utf8_value), + need_appearances); +} diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test index f80da1c9..5f5b9612 100644 --- a/qpdf/qtest/qpdf.test +++ b/qpdf/qtest/qpdf.test @@ -115,7 +115,7 @@ my @form_tests = ( 'form-errors', ); -$n_tests += scalar(@form_tests); +$n_tests += scalar(@form_tests) + 2; # Many of the form*.pdf files were created by converting the # LibreOffice document storage/form.odt to PDF and then manually @@ -132,6 +132,15 @@ foreach my $f (@form_tests) $td->NORMALIZE_NEWLINES); } +$td->runtest("fill fields", + {$td->COMMAND => "test_driver 44 form-no-need-appearances.pdf"}, + {$td->FILE => "form-no-need-appearances.out", + $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); +$td->runtest("compare files", + {$td->FILE => "a.pdf"}, + {$td->FILE => "form-no-need-appearances-filled.pdf"}); + show_ntests(); # ---------- $td->notify("--- Stream Replacement Tests ---"); diff --git a/qpdf/qtest/qpdf/form-no-need-appearances-filled.pdf b/qpdf/qtest/qpdf/form-no-need-appearances-filled.pdf Binary files differnew file mode 100644 index 00000000..79028179 --- /dev/null +++ b/qpdf/qtest/qpdf/form-no-need-appearances-filled.pdf diff --git a/qpdf/qtest/qpdf/form-no-need-appearances.out b/qpdf/qtest/qpdf/form-no-need-appearances.out new file mode 100644 index 00000000..00245fb6 --- /dev/null +++ b/qpdf/qtest/qpdf/form-no-need-appearances.out @@ -0,0 +1,3 @@ +Set field value: Text Box 1 -> 3.14 ÷ 0 +Set field value: Text Box 2 -> 3.14 ÷ 0 +test 44 done diff --git a/qpdf/qtest/qpdf/form-no-need-appearances.pdf b/qpdf/qtest/qpdf/form-no-need-appearances.pdf Binary files differnew file mode 100644 index 00000000..298955b0 --- /dev/null +++ b/qpdf/qtest/qpdf/form-no-need-appearances.pdf diff --git a/qpdf/test_driver.cc b/qpdf/test_driver.cc index 9f4f18fe..c167b643 100644 --- a/qpdf/test_driver.cc +++ b/qpdf/test_driver.cc @@ -1584,6 +1584,34 @@ void runtest(int n, char const* filename1, char const* arg2) } } } + else if (n == 44) + { + // Set form fields. + QPDFAcroFormDocumentHelper afdh(pdf); + std::vector<QPDFFormFieldObjectHelper> fields = afdh.getFormFields(); + for (std::vector<QPDFFormFieldObjectHelper>::iterator iter = + fields.begin(); + iter != fields.end(); ++iter) + { + QPDFFormFieldObjectHelper& field(*iter); + QPDFObjectHandle ft = field.getInheritableFieldValue("/FT"); + if (ft.isName() && (ft.getName() == "/Tx")) + { + // \xc3\xb7 is utf-8 for U+00F7 (divided by) + field.setV("3.14 \xc3\xb7 0"); + std::cout << "Set field value: " + << field.getFullyQualifiedName() + << " -> " + << field.getValueAsString() + << std::endl; + } + } + QPDFWriter w(pdf, "a.pdf"); + w.setQDFMode(true); + w.setStaticID(true); + w.setSuppressOriginalObjectIDs(true); + w.write(); + } else { throw std::runtime_error(std::string("invalid test ") + |