mirror of
https://github.com/qpdf/qpdf.git
synced 2025-01-31 02:48:31 +00:00
Allow setting a form field's value
This commit is contained in:
parent
952a665a4e
commit
397b097c46
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 ---");
|
||||
|
BIN
qpdf/qtest/qpdf/form-no-need-appearances-filled.pdf
Normal file
BIN
qpdf/qtest/qpdf/form-no-need-appearances-filled.pdf
Normal file
Binary file not shown.
3
qpdf/qtest/qpdf/form-no-need-appearances.out
Normal file
3
qpdf/qtest/qpdf/form-no-need-appearances.out
Normal file
@ -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
|
BIN
qpdf/qtest/qpdf/form-no-need-appearances.pdf
Normal file
BIN
qpdf/qtest/qpdf/form-no-need-appearances.pdf
Normal file
Binary file not shown.
@ -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 ") +
|
||||
|
Loading…
x
Reference in New Issue
Block a user