aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog5
-rw-r--r--include/qpdf/QPDFAcroFormDocumentHelper.hh17
-rw-r--r--include/qpdf/QPDFFormFieldObjectHelper.hh23
-rw-r--r--libqpdf/QPDFAcroFormDocumentHelper.cc35
-rw-r--r--libqpdf/QPDFFormFieldObjectHelper.cc42
-rw-r--r--qpdf/qtest/qpdf.test11
-rw-r--r--qpdf/qtest/qpdf/form-no-need-appearances-filled.pdfbin0 -> 12686 bytes
-rw-r--r--qpdf/qtest/qpdf/form-no-need-appearances.out3
-rw-r--r--qpdf/qtest/qpdf/form-no-need-appearances.pdfbin0 -> 12572 bytes
-rw-r--r--qpdf/test_driver.cc28
10 files changed, 163 insertions, 1 deletions
diff --git a/ChangeLog b/ChangeLog
index e27b680d..3d593dff 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
new file mode 100644
index 00000000..79028179
--- /dev/null
+++ b/qpdf/qtest/qpdf/form-no-need-appearances-filled.pdf
Binary files differ
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
new file mode 100644
index 00000000..298955b0
--- /dev/null
+++ b/qpdf/qtest/qpdf/form-no-need-appearances.pdf
Binary files differ
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 ") +