Commit 00cf4ce581d08b6a59bf7d78bbb969b2ba8fe6ff
1 parent
2fe9086a
Introduce `FormNode::AP()` method and replace manual `/AP` processing with method calls
Showing
3 changed files
with
57 additions
and
42 deletions
libqpdf/QPDFFormFieldObjectHelper.cc
| @@ -415,7 +415,13 @@ QPDFFormFieldObjectHelper::setFieldAttribute(std::string const& key, QPDFObjectH | @@ -415,7 +415,13 @@ QPDFFormFieldObjectHelper::setFieldAttribute(std::string const& key, QPDFObjectH | ||
| 415 | void | 415 | void |
| 416 | FormNode::setFieldAttribute(std::string const& key, QPDFObjectHandle value) | 416 | FormNode::setFieldAttribute(std::string const& key, QPDFObjectHandle value) |
| 417 | { | 417 | { |
| 418 | - oh().replaceKey(key, value); | 418 | + replace(key, value); |
| 419 | +} | ||
| 420 | + | ||
| 421 | +void | ||
| 422 | +FormNode::setFieldAttribute(std::string const& key, Name const& value) | ||
| 423 | +{ | ||
| 424 | + replace(key, value); | ||
| 419 | } | 425 | } |
| 420 | 426 | ||
| 421 | void | 427 | void |
| @@ -427,7 +433,7 @@ QPDFFormFieldObjectHelper::setFieldAttribute(std::string const& key, std::string | @@ -427,7 +433,7 @@ QPDFFormFieldObjectHelper::setFieldAttribute(std::string const& key, std::string | ||
| 427 | void | 433 | void |
| 428 | FormNode::setFieldAttribute(std::string const& key, std::string const& utf8_value) | 434 | FormNode::setFieldAttribute(std::string const& key, std::string const& utf8_value) |
| 429 | { | 435 | { |
| 430 | - oh().replaceKey(key, QPDFObjectHandle::newUnicodeString(utf8_value)); | 436 | + replace(key, String::utf16(utf8_value)); |
| 431 | } | 437 | } |
| 432 | 438 | ||
| 433 | void | 439 | void |
| @@ -517,13 +523,13 @@ FormNode::setRadioButtonValue(QPDFObjectHandle name) | @@ -517,13 +523,13 @@ FormNode::setRadioButtonValue(QPDFObjectHandle name) | ||
| 517 | } | 523 | } |
| 518 | setFieldAttribute("/V", name); | 524 | setFieldAttribute("/V", name); |
| 519 | for (FormNode kid: kids) { | 525 | for (FormNode kid: kids) { |
| 520 | - QPDFObjectHandle AP = kid["/AP"]; | 526 | + auto ap = kid.AP(); |
| 521 | QPDFObjectHandle annot; | 527 | QPDFObjectHandle annot; |
| 522 | - if (AP.null()) { | 528 | + if (!ap) { |
| 523 | // The widget may be below. If there is more than one, just find the first one. | 529 | // The widget may be below. If there is more than one, just find the first one. |
| 524 | - for (auto const& grandkid: kid.Kids()) { | ||
| 525 | - AP = grandkid.getKey("/AP"); | ||
| 526 | - if (!AP.null()) { | 530 | + for (FormNode grandkid: kid.Kids()) { |
| 531 | + ap = grandkid.AP(); | ||
| 532 | + if (ap) { | ||
| 527 | annot = grandkid; | 533 | annot = grandkid; |
| 528 | break; | 534 | break; |
| 529 | } | 535 | } |
| @@ -535,11 +541,10 @@ FormNode::setRadioButtonValue(QPDFObjectHandle name) | @@ -535,11 +541,10 @@ FormNode::setRadioButtonValue(QPDFObjectHandle name) | ||
| 535 | warn("unable to set the value of this radio button"); | 541 | warn("unable to set the value of this radio button"); |
| 536 | continue; | 542 | continue; |
| 537 | } | 543 | } |
| 538 | - if (AP.isDictionary() && AP.getKey("/N").isDictionary() && | ||
| 539 | - AP.getKey("/N").hasKey(name.getName())) { | ||
| 540 | - annot.replaceKey("/AS", name); | 544 | + if (ap["/N"].contains(name.getName())) { |
| 545 | + annot.replace("/AS", name); | ||
| 541 | } else { | 546 | } else { |
| 542 | - annot.replaceKey("/AS", QPDFObjectHandle::newName("/Off")); | 547 | + annot.replace("/AS", Name("/Off")); |
| 543 | } | 548 | } |
| 544 | } | 549 | } |
| 545 | } | 550 | } |
| @@ -547,27 +552,26 @@ FormNode::setRadioButtonValue(QPDFObjectHandle name) | @@ -547,27 +552,26 @@ FormNode::setRadioButtonValue(QPDFObjectHandle name) | ||
| 547 | void | 552 | void |
| 548 | FormNode::setCheckBoxValue(bool value) | 553 | FormNode::setCheckBoxValue(bool value) |
| 549 | { | 554 | { |
| 550 | - QPDFObjectHandle AP = oh().getKey("/AP"); | 555 | + auto ap = AP(); |
| 551 | QPDFObjectHandle annot; | 556 | QPDFObjectHandle annot; |
| 552 | - if (AP.null()) { | ||
| 553 | - // The widget may be below. If there is more than one, just | ||
| 554 | - // find the first one. | ||
| 555 | - for (auto const& kid: Kids()) { | ||
| 556 | - AP = kid.getKey("/AP"); | ||
| 557 | - if (!AP.null()) { | 557 | + if (ap) { |
| 558 | + annot = oh(); | ||
| 559 | + } else { | ||
| 560 | + // The widget may be below. If there is more than one, just find the first one. | ||
| 561 | + for (FormNode kid: Kids()) { | ||
| 562 | + ap = kid.AP(); | ||
| 563 | + if (ap) { | ||
| 558 | annot = kid; | 564 | annot = kid; |
| 559 | break; | 565 | break; |
| 560 | } | 566 | } |
| 561 | } | 567 | } |
| 562 | - } else { | ||
| 563 | - annot = oh(); | ||
| 564 | } | 568 | } |
| 565 | std::string on_value; | 569 | std::string on_value; |
| 566 | if (value) { | 570 | if (value) { |
| 567 | // Set the "on" value to the first value in the appearance stream's normal state dictionary | 571 | // Set the "on" value to the first value in the appearance stream's normal state dictionary |
| 568 | // that isn't /Off. If not found, fall back to /Yes. | 572 | // that isn't /Off. If not found, fall back to /Yes. |
| 569 | - if (AP.isDictionary()) { | ||
| 570 | - for (auto const& item: AP.getKey("/N").as_dictionary()) { | 573 | + if (ap) { |
| 574 | + for (auto const& item: Dictionary(ap["/N"])) { | ||
| 571 | if (item.first != "/Off") { | 575 | if (item.first != "/Off") { |
| 572 | on_value = item.first; | 576 | on_value = item.first; |
| 573 | break; | 577 | break; |
| @@ -580,15 +584,13 @@ FormNode::setCheckBoxValue(bool value) | @@ -580,15 +584,13 @@ FormNode::setCheckBoxValue(bool value) | ||
| 580 | } | 584 | } |
| 581 | 585 | ||
| 582 | // Set /AS to the on value or /Off in addition to setting /V. | 586 | // Set /AS to the on value or /Off in addition to setting /V. |
| 583 | - QPDFObjectHandle name = QPDFObjectHandle::newName(value ? on_value : "/Off"); | 587 | + auto name = Name(value ? on_value : "/Off"); |
| 584 | setFieldAttribute("/V", name); | 588 | setFieldAttribute("/V", name); |
| 585 | if (!annot) { | 589 | if (!annot) { |
| 586 | - QTC::TC("qpdf", "QPDFObjectHandle broken checkbox"); | ||
| 587 | warn("unable to set the value of this checkbox"); | 590 | warn("unable to set the value of this checkbox"); |
| 588 | return; | 591 | return; |
| 589 | } | 592 | } |
| 590 | - QTC::TC("qpdf", "QPDFFormFieldObjectHelper set checkbox AS"); | ||
| 591 | - annot.replaceKey("/AS", name); | 593 | + annot.replace("/AS", name); |
| 592 | } | 594 | } |
| 593 | 595 | ||
| 594 | void | 596 | void |
| @@ -899,12 +901,11 @@ FormNode::generateTextAppearance(QPDFAnnotationObjectHelper& aoh) | @@ -899,12 +901,11 @@ FormNode::generateTextAppearance(QPDFAnnotationObjectHelper& aoh) | ||
| 899 | {"/Subtype", Name("/Form")}}); | 901 | {"/Subtype", Name("/Form")}}); |
| 900 | AS = QPDFObjectHandle::newStream(oh().getOwningQPDF(), "/Tx BMC\nEMC\n"); | 902 | AS = QPDFObjectHandle::newStream(oh().getOwningQPDF(), "/Tx BMC\nEMC\n"); |
| 901 | AS.replaceDict(dict); | 903 | AS.replaceDict(dict); |
| 902 | - Dictionary AP = aoh.getAppearanceDictionary(); | ||
| 903 | - if (!AP) { | ||
| 904 | - aoh.getObjectHandle().replaceKey("/AP", Dictionary::empty()); | ||
| 905 | - AP = aoh.getAppearanceDictionary(); | 904 | + if (auto ap = AP()) { |
| 905 | + ap.replace("/N", AS); | ||
| 906 | + } else { | ||
| 907 | + aoh.replace("/AP", Dictionary({{"/N", AS}})); | ||
| 906 | } | 908 | } |
| 907 | - AP.replace("/N", AS); | ||
| 908 | } | 909 | } |
| 909 | if (!AS.isStream()) { | 910 | if (!AS.isStream()) { |
| 910 | aoh.warn("unable to get normal appearance stream for update"); | 911 | aoh.warn("unable to get normal appearance stream for update"); |
libqpdf/qpdf/AcroForm.hh
| @@ -356,18 +356,19 @@ namespace qpdf::impl | @@ -356,18 +356,19 @@ namespace qpdf::impl | ||
| 356 | { | 356 | { |
| 357 | } | 357 | } |
| 358 | 358 | ||
| 359 | - /// @brief Retrieves the /Kids array. | 359 | + // Widget and annotation attributes |
| 360 | + | ||
| 361 | + /// @brief Retrieves the /AP attribute of the form node as a Dictionary. | ||
| 360 | /// | 362 | /// |
| 361 | - /// This method returns the /Kids entry, which is an array of the immediate descendants of | ||
| 362 | - /// this node. It is only present if the node is a form field rather than a pure widget | ||
| 363 | - /// annotation. | 363 | + /// The /AP attribute, short for "appearance dictionary," defines how an annotation is |
| 364 | + /// presented visually on a page. See section 12.5.5 of the PDF specification for more | ||
| 365 | + /// details. | ||
| 364 | /// | 366 | /// |
| 365 | - /// @return An `Array` object containing the /Kids elements. If the /Kids entry | ||
| 366 | - /// does not exist or is not a valid array, the returned `Array` will be invalid. | ||
| 367 | - Array | ||
| 368 | - Kids() const | 367 | + /// @return A Dictionary containing the /AP attribute of the form node. |
| 368 | + Dictionary | ||
| 369 | + AP() const | ||
| 369 | { | 370 | { |
| 370 | - return {get("/Kids")}; | 371 | + return {get("/AP")}; |
| 371 | } | 372 | } |
| 372 | 373 | ||
| 373 | /// Retrieves the /Parent form field of the current field. | 374 | /// Retrieves the /Parent form field of the current field. |
| @@ -384,6 +385,20 @@ namespace qpdf::impl | @@ -384,6 +385,20 @@ namespace qpdf::impl | ||
| 384 | return {get("/Parent")}; | 385 | return {get("/Parent")}; |
| 385 | } | 386 | } |
| 386 | 387 | ||
| 388 | + /// @brief Retrieves the /Kids array. | ||
| 389 | + /// | ||
| 390 | + /// This method returns the /Kids entry, which is an array of the immediate descendants of | ||
| 391 | + /// this node. It is only present if the node is a form field rather than a pure widget | ||
| 392 | + /// annotation. | ||
| 393 | + /// | ||
| 394 | + /// @return An `Array` object containing the /Kids elements. If the /Kids entry | ||
| 395 | + /// does not exist or is not a valid array, the returned `Array` will be invalid. | ||
| 396 | + Array | ||
| 397 | + Kids() const | ||
| 398 | + { | ||
| 399 | + return {get("/Kids")}; | ||
| 400 | + } | ||
| 401 | + | ||
| 387 | /// @brief Returns the top-level field associated with the current field. | 402 | /// @brief Returns the top-level field associated with the current field. |
| 388 | /// | 403 | /// |
| 389 | /// The function traverses the hierarchy of parent fields to identify the highest-level | 404 | /// The function traverses the hierarchy of parent fields to identify the highest-level |
| @@ -670,6 +685,7 @@ namespace qpdf::impl | @@ -670,6 +685,7 @@ namespace qpdf::impl | ||
| 670 | // want to set the name of a field, use QPDFAcroFormDocumentHelper::setFormFieldName | 685 | // want to set the name of a field, use QPDFAcroFormDocumentHelper::setFormFieldName |
| 671 | // instead. | 686 | // instead. |
| 672 | void setFieldAttribute(std::string const& key, QPDFObjectHandle value); | 687 | void setFieldAttribute(std::string const& key, QPDFObjectHandle value); |
| 688 | + void setFieldAttribute(std::string const& key, Name const& value); | ||
| 673 | 689 | ||
| 674 | // Set an attribute to the given value as a Unicode string (UTF-16 BE encoded). The input | 690 | // Set an attribute to the given value as a Unicode string (UTF-16 BE encoded). The input |
| 675 | // string should be UTF-8 encoded. If you have a QPDFAcroFormDocumentHelper and you want to | 691 | // string should be UTF-8 encoded. If you have a QPDFAcroFormDocumentHelper and you want to |
qpdf/qpdf.testcov
| @@ -193,8 +193,6 @@ QPDFPageDocumentHelper non-widget annotation 0 | @@ -193,8 +193,6 @@ QPDFPageDocumentHelper non-widget annotation 0 | ||
| 193 | QPDFObjectHandle replace with copy 0 | 193 | QPDFObjectHandle replace with copy 0 |
| 194 | QPDFAnnotationObjectHelper forbidden flags 0 | 194 | QPDFAnnotationObjectHelper forbidden flags 0 |
| 195 | QPDFAnnotationObjectHelper missing required flags 0 | 195 | QPDFAnnotationObjectHelper missing required flags 0 |
| 196 | -QPDFFormFieldObjectHelper set checkbox AS 0 | ||
| 197 | -QPDFObjectHandle broken checkbox 0 | ||
| 198 | QPDFFormFieldObjectHelper list not found 0 | 196 | QPDFFormFieldObjectHelper list not found 0 |
| 199 | QPDFFormFieldObjectHelper list found 0 | 197 | QPDFFormFieldObjectHelper list found 0 |
| 200 | QPDFFormFieldObjectHelper list first too low 0 | 198 | QPDFFormFieldObjectHelper list first too low 0 |