Commit 00cf4ce581d08b6a59bf7d78bbb969b2ba8fe6ff

Authored by m-holger
1 parent 2fe9086a

Introduce `FormNode::AP()` method and replace manual `/AP` processing with method calls

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