Commit 6ad83cfde47c8a523351f79d22b2ad6bcd317705

Authored by m-holger
1 parent d33e5a9c

Refactor QPDFFormFieldObjectHelper: extract logic into `impl::FormField`, clean …

…up method implementations, and simplify member handling.
include/qpdf/QPDFFormFieldObjectHelper.hh
... ... @@ -187,12 +187,6 @@ class QPDFFormFieldObjectHelper: public QPDFObjectHelper
187 187 void generateAppearance(QPDFAnnotationObjectHelper&);
188 188  
189 189 private:
190   - QPDFObjectHandle getFieldFromAcroForm(std::string const& name);
191   - void setRadioButtonValue(QPDFObjectHandle name);
192   - void setCheckBoxValue(bool value);
193   - void generateTextAppearance(QPDFAnnotationObjectHelper&);
194   - QPDFObjectHandle getFontFromResource(QPDFObjectHandle resources, std::string const& font_name);
195   -
196 190 class Members;
197 191  
198 192 std::shared_ptr<Members> m;
... ...
libqpdf/QPDFFormFieldObjectHelper.cc
1 1 #include <qpdf/QPDFFormFieldObjectHelper.hh>
2 2  
  3 +#include <qpdf/FormField.hh>
  4 +
3 5 #include <qpdf/Pl_QPDFTokenizer.hh>
4 6 #include <qpdf/QIntC.hh>
5 7 #include <qpdf/QPDFAcroFormDocumentHelper.hh>
... ... @@ -14,37 +16,62 @@
14 16  
15 17 using namespace qpdf;
16 18  
17   -class QPDFFormFieldObjectHelper::Members
  19 +using FormField = qpdf::impl::FormField;
  20 +
  21 +class QPDFFormFieldObjectHelper::Members: public qpdf::impl::FormField
18 22 {
  23 + public:
  24 + Members(QPDFObjectHandle const& oh) :
  25 + FormField(oh)
  26 + {
  27 + }
19 28 };
20 29  
21   -QPDFFormFieldObjectHelper::QPDFFormFieldObjectHelper(QPDFObjectHandle oh) :
22   - QPDFObjectHelper(oh),
23   - m(std::make_shared<Members>())
  30 +QPDFFormFieldObjectHelper::QPDFFormFieldObjectHelper(QPDFObjectHandle o) :
  31 + QPDFObjectHelper(o),
  32 + m(std::make_shared<Members>(oh()))
24 33 {
25 34 }
26 35  
27 36 QPDFFormFieldObjectHelper::QPDFFormFieldObjectHelper() :
28 37 QPDFObjectHelper(QPDFObjectHandle::newNull()),
29   - m(std::make_shared<Members>())
  38 + m(std::make_shared<Members>(oh()))
30 39 {
31 40 }
32 41  
33 42 bool
34 43 QPDFFormFieldObjectHelper::isNull()
35 44 {
  45 + return m->isNull();
  46 +}
  47 +
  48 +bool
  49 +FormField::isNull()
  50 +{
36 51 return oh().null();
37 52 }
38 53  
39 54 QPDFFormFieldObjectHelper
40 55 QPDFFormFieldObjectHelper::getParent()
41 56 {
  57 + return {m->getParent()};
  58 +}
  59 +
  60 +FormField
  61 +FormField::getParent()
  62 +{
42 63 return oh().getKey("/Parent"); // may be null
43 64 }
44 65  
45 66 QPDFFormFieldObjectHelper
46 67 QPDFFormFieldObjectHelper::getTopLevelField(bool* is_different)
47 68 {
  69 + return {m->getTopLevelField(is_different)};
  70 +}
  71 +
  72 +FormField
  73 +FormField::getTopLevelField(bool* is_different)
  74 +{
48 75 auto top_field = oh();
49 76 QPDFObjGen::set seen;
50 77 while (seen.add(top_field) && !top_field.getKeyIfDict("/Parent").null()) {
... ... @@ -57,7 +84,7 @@ QPDFFormFieldObjectHelper::getTopLevelField(bool* is_different)
57 84 }
58 85  
59 86 QPDFObjectHandle
60   -QPDFFormFieldObjectHelper::getFieldFromAcroForm(std::string const& name)
  87 +FormField::getFieldFromAcroForm(std::string const& name)
61 88 {
62 89 QPDFObjectHandle result = QPDFObjectHandle::newNull();
63 90 // Fields are supposed to be indirect, so this should work.
... ... @@ -75,6 +102,12 @@ QPDFFormFieldObjectHelper::getFieldFromAcroForm(std::string const&amp; name)
75 102 QPDFObjectHandle
76 103 QPDFFormFieldObjectHelper::getInheritableFieldValue(std::string const& name)
77 104 {
  105 + return m->getInheritableFieldValue(name);
  106 +}
  107 +
  108 +QPDFObjectHandle
  109 +FormField::getInheritableFieldValue(std::string const& name)
  110 +{
78 111 QPDFObjectHandle node = oh();
79 112 if (!node.isDictionary()) {
80 113 return QPDFObjectHandle::newNull();
... ... @@ -96,6 +129,12 @@ QPDFFormFieldObjectHelper::getInheritableFieldValue(std::string const&amp; name)
96 129 std::string
97 130 QPDFFormFieldObjectHelper::getInheritableFieldValueAsString(std::string const& name)
98 131 {
  132 + return m->getInheritableFieldValueAsString(name);
  133 +}
  134 +
  135 +std::string
  136 +FormField::getInheritableFieldValueAsString(std::string const& name)
  137 +{
99 138 auto fv = getInheritableFieldValue(name);
100 139 if (fv.isString()) {
101 140 return fv.getUTF8Value();
... ... @@ -106,6 +145,12 @@ QPDFFormFieldObjectHelper::getInheritableFieldValueAsString(std::string const&amp; n
106 145 std::string
107 146 QPDFFormFieldObjectHelper::getInheritableFieldValueAsName(std::string const& name)
108 147 {
  148 + return m->getInheritableFieldValueAsName(name);
  149 +}
  150 +
  151 +std::string
  152 +FormField::getInheritableFieldValueAsName(std::string const& name)
  153 +{
109 154 if (Name fv = getInheritableFieldValue(name)) {
110 155 return fv;
111 156 }
... ... @@ -115,12 +160,24 @@ QPDFFormFieldObjectHelper::getInheritableFieldValueAsName(std::string const&amp; nam
115 160 std::string
116 161 QPDFFormFieldObjectHelper::getFieldType()
117 162 {
  163 + return m->getFieldType();
  164 +}
  165 +
  166 +std::string
  167 +FormField::getFieldType()
  168 +{
118 169 return getInheritableFieldValueAsName("/FT");
119 170 }
120 171  
121 172 std::string
122 173 QPDFFormFieldObjectHelper::getFullyQualifiedName()
123 174 {
  175 + return m->getFullyQualifiedName();
  176 +}
  177 +
  178 +std::string
  179 +FormField::getFullyQualifiedName()
  180 +{
124 181 std::string result;
125 182 QPDFObjectHandle node = oh();
126 183 QPDFObjGen::set seen;
... ... @@ -139,6 +196,12 @@ QPDFFormFieldObjectHelper::getFullyQualifiedName()
139 196 std::string
140 197 QPDFFormFieldObjectHelper::getPartialName()
141 198 {
  199 + return m->getPartialName();
  200 +}
  201 +
  202 +std::string
  203 +FormField::getPartialName()
  204 +{
142 205 std::string result;
143 206 if (oh().getKey("/T").isString()) {
144 207 result = oh().getKey("/T").getUTF8Value();
... ... @@ -149,6 +212,12 @@ QPDFFormFieldObjectHelper::getPartialName()
149 212 std::string
150 213 QPDFFormFieldObjectHelper::getAlternativeName()
151 214 {
  215 + return m->getAlternativeName();
  216 +}
  217 +
  218 +std::string
  219 +FormField::getAlternativeName()
  220 +{
152 221 if (oh().getKey("/TU").isString()) {
153 222 QTC::TC("qpdf", "QPDFFormFieldObjectHelper TU present");
154 223 return oh().getKey("/TU").getUTF8Value();
... ... @@ -160,6 +229,12 @@ QPDFFormFieldObjectHelper::getAlternativeName()
160 229 std::string
161 230 QPDFFormFieldObjectHelper::getMappingName()
162 231 {
  232 + return m->getMappingName();
  233 +}
  234 +
  235 +std::string
  236 +FormField::getMappingName()
  237 +{
163 238 if (oh().getKey("/TM").isString()) {
164 239 QTC::TC("qpdf", "QPDFFormFieldObjectHelper TM present");
165 240 return oh().getKey("/TM").getUTF8Value();
... ... @@ -171,6 +246,12 @@ QPDFFormFieldObjectHelper::getMappingName()
171 246 QPDFObjectHandle
172 247 QPDFFormFieldObjectHelper::getValue()
173 248 {
  249 + return m->getValue();
  250 +}
  251 +
  252 +QPDFObjectHandle
  253 +FormField::getValue()
  254 +{
174 255 return getInheritableFieldValue("/V");
175 256 }
176 257  
... ... @@ -180,27 +261,57 @@ QPDFFormFieldObjectHelper::getValueAsString()
180 261 return getInheritableFieldValueAsString("/V");
181 262 }
182 263  
  264 +std::string
  265 +FormField::getValueAsString()
  266 +{
  267 + return getInheritableFieldValueAsString("/V");
  268 +}
  269 +
183 270 QPDFObjectHandle
184 271 QPDFFormFieldObjectHelper::getDefaultValue()
185 272 {
  273 + return m->getDefaultValue();
  274 +}
  275 +
  276 +QPDFObjectHandle
  277 +FormField::getDefaultValue()
  278 +{
186 279 return getInheritableFieldValue("/DV");
187 280 }
188 281  
189 282 std::string
190 283 QPDFFormFieldObjectHelper::getDefaultValueAsString()
191 284 {
  285 + return m->getDefaultValueAsString();
  286 +}
  287 +
  288 +std::string
  289 +FormField::getDefaultValueAsString()
  290 +{
192 291 return getInheritableFieldValueAsString("/DV");
193 292 }
194 293  
195 294 QPDFObjectHandle
196 295 QPDFFormFieldObjectHelper::getDefaultResources()
197 296 {
  297 + return m->getDefaultResources();
  298 +}
  299 +
  300 +QPDFObjectHandle
  301 +FormField::getDefaultResources()
  302 +{
198 303 return getFieldFromAcroForm("/DR");
199 304 }
200 305  
201 306 std::string
202 307 QPDFFormFieldObjectHelper::getDefaultAppearance()
203 308 {
  309 + return m->getDefaultAppearance();
  310 +}
  311 +
  312 +std::string
  313 +FormField::getDefaultAppearance()
  314 +{
204 315 auto value = getInheritableFieldValue("/DA");
205 316 bool looked_in_acroform = false;
206 317 if (!value.isString()) {
... ... @@ -217,6 +328,12 @@ QPDFFormFieldObjectHelper::getDefaultAppearance()
217 328 int
218 329 QPDFFormFieldObjectHelper::getQuadding()
219 330 {
  331 + return m->getQuadding();
  332 +}
  333 +
  334 +int
  335 +FormField::getQuadding()
  336 +{
220 337 QPDFObjectHandle fv = getInheritableFieldValue("/Q");
221 338 bool looked_in_acroform = false;
222 339 if (!fv.isInteger()) {
... ... @@ -233,6 +350,12 @@ QPDFFormFieldObjectHelper::getQuadding()
233 350 int
234 351 QPDFFormFieldObjectHelper::getFlags()
235 352 {
  353 + return m->getFlags();
  354 +}
  355 +
  356 +int
  357 +FormField::getFlags()
  358 +{
236 359 QPDFObjectHandle f = getInheritableFieldValue("/Ff");
237 360 return f.isInteger() ? f.getIntValueAsInt() : 0;
238 361 }
... ... @@ -240,42 +363,84 @@ QPDFFormFieldObjectHelper::getFlags()
240 363 bool
241 364 QPDFFormFieldObjectHelper::isText()
242 365 {
  366 + return m->isText();
  367 +}
  368 +
  369 +bool
  370 +FormField::isText()
  371 +{
243 372 return getFieldType() == "/Tx";
244 373 }
245 374  
246 375 bool
247 376 QPDFFormFieldObjectHelper::isCheckbox()
248 377 {
  378 + return m->isCheckbox();
  379 +}
  380 +
  381 +bool
  382 +FormField::isCheckbox()
  383 +{
249 384 return getFieldType() == "/Btn" && (getFlags() & (ff_btn_radio | ff_btn_pushbutton)) == 0;
250 385 }
251 386  
252 387 bool
253 388 QPDFFormFieldObjectHelper::isChecked()
254 389 {
  390 + return m->isChecked();
  391 +}
  392 +
  393 +bool
  394 +FormField::isChecked()
  395 +{
255 396 return isCheckbox() && Name(getValue()) != "/Off";
256 397 }
257 398  
258 399 bool
259 400 QPDFFormFieldObjectHelper::isRadioButton()
260 401 {
  402 + return m->isRadioButton();
  403 +}
  404 +
  405 +bool
  406 +FormField::isRadioButton()
  407 +{
261 408 return getFieldType() == "/Btn" && (getFlags() & ff_btn_radio) == ff_btn_radio;
262 409 }
263 410  
264 411 bool
265 412 QPDFFormFieldObjectHelper::isPushbutton()
266 413 {
  414 + return m->isPushbutton();
  415 +}
  416 +
  417 +bool
  418 +FormField::isPushbutton()
  419 +{
267 420 return getFieldType() == "/Btn" && (getFlags() & ff_btn_pushbutton) == ff_btn_pushbutton;
268 421 }
269 422  
270 423 bool
271 424 QPDFFormFieldObjectHelper::isChoice()
272 425 {
  426 + return m->isChoice();
  427 +}
  428 +
  429 +bool
  430 +FormField::isChoice()
  431 +{
273 432 return getFieldType() == "/Ch";
274 433 }
275 434  
276 435 std::vector<std::string>
277 436 QPDFFormFieldObjectHelper::getChoices()
278 437 {
  438 + return m->getChoices();
  439 +}
  440 +
  441 +std::vector<std::string>
  442 +FormField::getChoices()
  443 +{
279 444 if (!isChoice()) {
280 445 return {};
281 446 }
... ... @@ -296,18 +461,36 @@ QPDFFormFieldObjectHelper::getChoices()
296 461 void
297 462 QPDFFormFieldObjectHelper::setFieldAttribute(std::string const& key, QPDFObjectHandle value)
298 463 {
  464 + m->setFieldAttribute(key, value);
  465 +}
  466 +
  467 +void
  468 +FormField::setFieldAttribute(std::string const& key, QPDFObjectHandle value)
  469 +{
299 470 oh().replaceKey(key, value);
300 471 }
301 472  
302 473 void
303 474 QPDFFormFieldObjectHelper::setFieldAttribute(std::string const& key, std::string const& utf8_value)
304 475 {
  476 + m->setFieldAttribute(key, utf8_value);
  477 +}
  478 +
  479 +void
  480 +FormField::setFieldAttribute(std::string const& key, std::string const& utf8_value)
  481 +{
305 482 oh().replaceKey(key, QPDFObjectHandle::newUnicodeString(utf8_value));
306 483 }
307 484  
308 485 void
309 486 QPDFFormFieldObjectHelper::setV(QPDFObjectHandle value, bool need_appearances)
310 487 {
  488 + m->setV(value, need_appearances);
  489 +}
  490 +
  491 +void
  492 +FormField::setV(QPDFObjectHandle value, bool need_appearances)
  493 +{
311 494 Name name = value;
312 495 if (getFieldType() == "/Btn") {
313 496 if (isCheckbox()) {
... ... @@ -350,11 +533,17 @@ QPDFFormFieldObjectHelper::setV(QPDFObjectHandle value, bool need_appearances)
350 533 void
351 534 QPDFFormFieldObjectHelper::setV(std::string const& utf8_value, bool need_appearances)
352 535 {
  536 + m->setV(utf8_value, need_appearances);
  537 +}
  538 +
  539 +void
  540 +FormField::setV(std::string const& utf8_value, bool need_appearances)
  541 +{
353 542 setV(QPDFObjectHandle::newUnicodeString(utf8_value), need_appearances);
354 543 }
355 544  
356 545 void
357   -QPDFFormFieldObjectHelper::setRadioButtonValue(QPDFObjectHandle name)
  546 +FormField::setRadioButtonValue(QPDFObjectHandle name)
358 547 {
359 548 // Set the value of a radio button field. This has the following specific behavior:
360 549 // * If this is a radio button field that has a parent that is also a radio button field and has
... ... @@ -366,7 +555,7 @@ QPDFFormFieldObjectHelper::setRadioButtonValue(QPDFObjectHandle name)
366 555 // Note that we never turn on /NeedAppearances when setting a radio button field.
367 556 QPDFObjectHandle parent = oh().getKey("/Parent");
368 557 if (parent.isDictionary() && parent.getKey("/Parent").null()) {
369   - QPDFFormFieldObjectHelper ph(parent);
  558 + FormField ph(parent);
370 559 if (ph.isRadioButton()) {
371 560 // This is most likely one of the individual buttons. Try calling on the parent.
372 561 ph.setRadioButtonValue(name);
... ... @@ -409,7 +598,7 @@ QPDFFormFieldObjectHelper::setRadioButtonValue(QPDFObjectHandle name)
409 598 }
410 599  
411 600 void
412   -QPDFFormFieldObjectHelper::setCheckBoxValue(bool value)
  601 +FormField::setCheckBoxValue(bool value)
413 602 {
414 603 QPDFObjectHandle AP = oh().getKey("/AP");
415 604 QPDFObjectHandle annot;
... ... @@ -460,6 +649,12 @@ QPDFFormFieldObjectHelper::setCheckBoxValue(bool value)
460 649 void
461 650 QPDFFormFieldObjectHelper::generateAppearance(QPDFAnnotationObjectHelper& aoh)
462 651 {
  652 + m->generateAppearance(aoh);
  653 +}
  654 +
  655 +void
  656 +FormField::generateAppearance(QPDFAnnotationObjectHelper& aoh)
  657 +{
463 658 std::string ft = getFieldType();
464 659 // Ignore field types we don't know how to generate appearances for. Button fields don't really
465 660 // need them -- see code in QPDFAcroFormDocumentHelper::generateAppearancesIfNeeded.
... ... @@ -735,7 +930,7 @@ namespace
735 930 } // namespace
736 931  
737 932 QPDFObjectHandle
738   -QPDFFormFieldObjectHelper::getFontFromResource(QPDFObjectHandle resources, std::string const& name)
  933 +FormField::getFontFromResource(QPDFObjectHandle resources, std::string const& name)
739 934 {
740 935 QPDFObjectHandle result;
741 936 if (resources.isDictionary() && resources.getKey("/Font").isDictionary() &&
... ... @@ -746,7 +941,7 @@ QPDFFormFieldObjectHelper::getFontFromResource(QPDFObjectHandle resources, std::
746 941 }
747 942  
748 943 void
749   -QPDFFormFieldObjectHelper::generateTextAppearance(QPDFAnnotationObjectHelper& aoh)
  944 +FormField::generateTextAppearance(QPDFAnnotationObjectHelper& aoh)
750 945 {
751 946 QPDFObjectHandle AS = aoh.getAppearanceStream("/N");
752 947 if (AS.null()) {
... ...
libqpdf/qpdf/FormField.hh 0 → 100644
  1 +#ifndef FORMFIELD_HH
  2 +#define FORMFIELD_HH
  3 +
  4 +#include <qpdf/QPDFObjectHandle_private.hh>
  5 +#include <qpdf/QPDFObjectHelper.hh>
  6 +
  7 +#include <vector>
  8 +
  9 +class QPDFAnnotationObjectHelper;
  10 +
  11 +namespace qpdf::impl
  12 +{
  13 + // This object helper helps with form fields for interactive forms. Please see comments in
  14 + // QPDFAcroFormDocumentHelper.hh for additional details.
  15 + class FormField: public QPDFObjectHelper
  16 + {
  17 + public:
  18 + FormField() = delete;
  19 +
  20 + FormField(QPDFObjectHandle const& oh) :
  21 + QPDFObjectHelper(oh)
  22 + {
  23 + }
  24 +
  25 + ~FormField() override = default;
  26 +
  27 + bool isNull();
  28 +
  29 + // Return the field's parent. A form field object helper whose underlying object is null is
  30 + // returned if there is no parent. This condition may be tested by calling isNull().
  31 + FormField getParent();
  32 +
  33 + // Return the top-level field for this field. Typically this will be the field itself or its
  34 + // parent. If is_different is provided, it is set to true if the top-level field is
  35 + // different from the field itself; otherwise it is set to false.
  36 + FormField getTopLevelField(bool* is_different = nullptr);
  37 +
  38 + // Get a field value, possibly inheriting the value from an ancestor node.
  39 + QPDFObjectHandle getInheritableFieldValue(std::string const& name);
  40 +
  41 + // Get an inherited field value as a string. If it is not a string, silently return the
  42 + // empty string.
  43 + std::string getInheritableFieldValueAsString(std::string const& name);
  44 +
  45 + // Get an inherited field value of type name as a string representing the name. If it is not
  46 + // a name, silently return the empty string.
  47 + std::string getInheritableFieldValueAsName(std::string const& name);
  48 +
  49 + // Returns the value of /FT if present, otherwise returns the empty string.
  50 + std::string getFieldType();
  51 +
  52 + std::string getFullyQualifiedName();
  53 +
  54 + std::string getPartialName();
  55 +
  56 + // Return the alternative field name (/TU), which is the field name intended to be presented
  57 + // to users. If not present, fall back to the fully qualified name.
  58 + std::string getAlternativeName();
  59 +
  60 + // Return the mapping field name (/TM). If not present, fall back to the alternative name,
  61 + // then to the partial name.
  62 + std::string getMappingName();
  63 +
  64 + QPDFObjectHandle getValue();
  65 +
  66 + // Return the field's value as a string. If this is called with a field whose value is not a
  67 + std::string getValueAsString();
  68 +
  69 + QPDFObjectHandle getDefaultValue();
  70 +
  71 + // Return the field's default value as a string. If this is called with a field whose value
  72 + // is not a string, the empty string will be silently returned.
  73 + std::string getDefaultValueAsString();
  74 +
  75 + // Return the default appearance string, taking inheritance from the field tree into
  76 + // account. Returns the empty string if the default appearance string is not available
  77 + // (because it's erroneously absent or because this is not a variable text field). If not
  78 + // found in the field hierarchy, look in /AcroForm.
  79 + std::string getDefaultAppearance();
  80 +
  81 + // Return the default resource dictionary for the field. This comes not from the field but
  82 + // from the document-level /AcroForm dictionary. While several PDF generates put a /DR key
  83 + // in the form field's dictionary, experimentation suggests that many popular readers,
  84 + // including Adobe Acrobat and Acrobat Reader, ignore any /DR item on the field.
  85 + QPDFObjectHandle getDefaultResources();
  86 +
  87 + // Return the quadding value, taking inheritance from the field tree into account. Returns 0
  88 + // if quadding is not specified. Look in /AcroForm if not found in the field hierarchy.
  89 + int getQuadding();
  90 +
  91 + // Return field flags from /Ff. The value is a logical or of pdf_form_field_flag_e as
  92 + // defined in qpdf/Constants.h//
  93 + int getFlags();
  94 +
  95 + // Methods for testing for particular types of form fields
  96 +
  97 + // Returns true if field is of type /Tx
  98 + bool isText();
  99 + // Returns true if field is of type /Btn and flags do not indicate some other type of
  100 + // button.
  101 + bool isCheckbox();
  102 +
  103 + // Returns true if field is a checkbox and is checked.
  104 + bool isChecked();
  105 +
  106 + // Returns true if field is of type /Btn and flags indicate that it is a radio button
  107 + bool isRadioButton();
  108 +
  109 + // Returns true if field is of type /Btn and flags indicate that it is a pushbutton
  110 + bool isPushbutton();
  111 +
  112 + // Returns true if fields if of type /Ch
  113 + bool isChoice();
  114 +
  115 + // Returns choices display values as UTF-8 strings
  116 + std::vector<std::string> getChoices();
  117 +
  118 + // Set an attribute to the given value. If you have a QPDFAcroFormDocumentHelper and you
  119 + // want to set the name of a field, use QPDFAcroFormDocumentHelper::setFormFieldName
  120 + // instead.
  121 + void setFieldAttribute(std::string const& key, QPDFObjectHandle value);
  122 +
  123 + // Set an attribute to the given value as a Unicode string (UTF-16 BE encoded). The input
  124 + // string should be UTF-8 encoded. If you have a QPDFAcroFormDocumentHelper and you want to
  125 + // set the name of a field, use QPDFAcroFormDocumentHelper::setFormFieldName instead.
  126 + void setFieldAttribute(std::string const& key, std::string const& utf8_value);
  127 +
  128 + // Set /V (field value) to the given value. If need_appearances is true and the field type
  129 + // is either /Tx (text) or /Ch (choice), set /NeedAppearances to true. You can explicitly
  130 + // tell this method not to set /NeedAppearances if you are going to generate an appearance
  131 + // stream yourself. Starting with qpdf 8.3.0, this method handles fields of type /Btn
  132 + // (checkboxes, radio buttons, pushbuttons) specially. When setting a checkbox value, any
  133 + // value other than /Off will be treated as on, and the actual value set will be based on
  134 + // the appearance stream's /N dictionary, so the value that ends up in /V may not exactly
  135 + // match the value you pass in.
  136 + void setV(QPDFObjectHandle value, bool need_appearances = true);
  137 +
  138 + // Set /V (field value) to the given string value encoded as a Unicode string. The input
  139 + // value should be UTF-8 encoded. See comments above about /NeedAppearances.
  140 + void setV(std::string const& utf8_value, bool need_appearances = true);
  141 +
  142 + // Update the appearance stream for this field. Note that qpdf's ability to generate
  143 + // appearance streams is limited. We only generate appearance streams for streams of type
  144 + // text or choice. The appearance uses the default parameters provided in the file, and it
  145 + // only supports ASCII characters. Quadding is currently ignored. While this functionality
  146 + // is limited, it should do a decent job on properly constructed PDF files when field values
  147 + // are restricted to ASCII characters.
  148 + void generateAppearance(QPDFAnnotationObjectHelper&);
  149 +
  150 + private:
  151 + QPDFObjectHandle getFieldFromAcroForm(std::string const& name);
  152 + void setRadioButtonValue(QPDFObjectHandle name);
  153 + void setCheckBoxValue(bool value);
  154 + void generateTextAppearance(QPDFAnnotationObjectHelper&);
  155 + QPDFObjectHandle
  156 + getFontFromResource(QPDFObjectHandle resources, std::string const& font_name);
  157 + };
  158 +} // namespace qpdf::impl
  159 +
  160 +#endif // FORMFIELD_HH
... ...