Commit 0fa8fbbcc76027a4318f1fe2f3754d0436969cd7

Authored by m-holger
1 parent f4589458

Refactor `QPDFAcroFormDocumentHelper::transformAnnotation`: replace `getKey` wit…

…h operator[], update type handling for clarity and consistency, and streamline resource merging.
libqpdf/QPDFAcroFormDocumentHelper.cc
@@ -719,7 +719,7 @@ QPDFAcroFormDocumentHelper::adjustAppearanceStream( @@ -719,7 +719,7 @@ QPDFAcroFormDocumentHelper::adjustAppearanceStream(
719 719
720 void 720 void
721 QPDFAcroFormDocumentHelper::transformAnnotations( 721 QPDFAcroFormDocumentHelper::transformAnnotations(
722 - QPDFObjectHandle old_annots, 722 + QPDFObjectHandle a_old_annots,
723 std::vector<QPDFObjectHandle>& new_annots, 723 std::vector<QPDFObjectHandle>& new_annots,
724 std::vector<QPDFObjectHandle>& new_fields, 724 std::vector<QPDFObjectHandle>& new_fields,
725 std::set<QPDFObjGen>& old_fields, 725 std::set<QPDFObjGen>& old_fields,
@@ -727,94 +727,89 @@ QPDFAcroFormDocumentHelper::transformAnnotations( @@ -727,94 +727,89 @@ QPDFAcroFormDocumentHelper::transformAnnotations(
727 QPDF* from_qpdf, 727 QPDF* from_qpdf,
728 QPDFAcroFormDocumentHelper* from_afdh) 728 QPDFAcroFormDocumentHelper* from_afdh)
729 { 729 {
730 - std::shared_ptr<QPDFAcroFormDocumentHelper> afdhph; 730 + Array old_annots = std::move(a_old_annots);
731 if (!from_qpdf) { 731 if (!from_qpdf) {
732 // Assume these are from the same QPDF. 732 // Assume these are from the same QPDF.
733 from_qpdf = &qpdf; 733 from_qpdf = &qpdf;
734 from_afdh = this; 734 from_afdh = this;
735 - } else if ((from_qpdf != &qpdf) && (!from_afdh)) {  
736 - afdhph = std::make_shared<QPDFAcroFormDocumentHelper>(*from_qpdf);  
737 - from_afdh = afdhph.get(); 735 + } else if (from_qpdf != &qpdf && !from_afdh) {
  736 + from_afdh = &QPDFAcroFormDocumentHelper::get(*from_qpdf);
738 } 737 }
739 - bool foreign = (from_qpdf != &qpdf); 738 + const bool foreign = from_qpdf != &qpdf;
740 739
741 // It's possible that we will transform annotations that don't include any form fields. This 740 // It's possible that we will transform annotations that don't include any form fields. This
742 // code takes care not to muck around with /AcroForm unless we have to. 741 // code takes care not to muck around with /AcroForm unless we have to.
743 742
744 - QPDFObjectHandle acroform = qpdf.getRoot().getKey("/AcroForm");  
745 - QPDFObjectHandle from_acroform = from_qpdf->getRoot().getKey("/AcroForm"); 743 + Dictionary acroform = qpdf.getRoot()["/AcroForm"];
  744 + Dictionary from_acroform = from_qpdf->getRoot()["/AcroForm"];
746 745
747 // /DA and /Q may be inherited from the document-level /AcroForm dictionary. If we are copying a 746 // /DA and /Q may be inherited from the document-level /AcroForm dictionary. If we are copying a
748 // foreign stream and the stream is getting one of these values from its document's /AcroForm, 747 // foreign stream and the stream is getting one of these values from its document's /AcroForm,
749 // we will need to copy the value explicitly so that it doesn't start getting its default from 748 // we will need to copy the value explicitly so that it doesn't start getting its default from
750 // the destination document. 749 // the destination document.
751 - bool override_da = false;  
752 - bool override_q = false;  
753 std::string from_default_da; 750 std::string from_default_da;
754 int from_default_q = 0; 751 int from_default_q = 0;
755 // If we copy any form fields, we will need to merge the source document's /DR into this 752 // If we copy any form fields, we will need to merge the source document's /DR into this
756 // document's /DR. 753 // document's /DR.
757 - QPDFObjectHandle from_dr = QPDFObjectHandle::newNull(); 754 + Dictionary from_dr;
  755 + std::string default_da;
  756 + int default_q = 0;
758 if (foreign) { 757 if (foreign) {
759 - std::string default_da;  
760 - int default_q = 0;  
761 - if (acroform.isDictionary()) {  
762 - if (acroform.getKey("/DA").isString()) {  
763 - default_da = acroform.getKey("/DA").getUTF8Value(); 758 + if (acroform) {
  759 + if (acroform["/DA"].isString()) {
  760 + default_da = acroform["/DA"].getUTF8Value();
764 } 761 }
765 - if (acroform.getKey("/Q").isInteger()) {  
766 - default_q = acroform.getKey("/Q").getIntValueAsInt(); 762 + if (Integer Q = acroform["/Q"]) {
  763 + default_q = Q;
767 } 764 }
768 } 765 }
769 - if (from_acroform.isDictionary()) {  
770 - if (from_acroform.getKey("/DR").isDictionary()) {  
771 - from_dr = from_acroform.getKey("/DR");  
772 - if (!from_dr.isIndirect()) { 766 + if (from_acroform) {
  767 + from_dr = from_acroform["/DR"];
  768 + if (from_dr) {
  769 + if (!from_dr.indirect()) {
773 from_dr = from_qpdf->makeIndirectObject(from_dr); 770 from_dr = from_qpdf->makeIndirectObject(from_dr);
774 } 771 }
775 from_dr = qpdf.copyForeignObject(from_dr); 772 from_dr = qpdf.copyForeignObject(from_dr);
776 } 773 }
777 - if (from_acroform.getKey("/DA").isString()) {  
778 - from_default_da = from_acroform.getKey("/DA").getUTF8Value(); 774 + if (from_acroform["/DA"].isString()) {
  775 + from_default_da = from_acroform["/DA"].getUTF8Value();
779 } 776 }
780 - if (from_acroform.getKey("/Q").isInteger()) {  
781 - from_default_q = from_acroform.getKey("/Q").getIntValueAsInt(); 777 + if (Integer Q = from_acroform["/Q"]) {
  778 + from_default_q = Q;
782 } 779 }
783 } 780 }
784 - if (from_default_da != default_da) {  
785 - override_da = true;  
786 - }  
787 - if (from_default_q != default_q) {  
788 - override_q = true;  
789 - }  
790 } 781 }
  782 + const bool override_da = from_acroform ? from_default_da != default_da : false;
  783 + const bool override_q = from_acroform ? from_default_q != default_q : false;
791 784
792 // If we have to merge /DR, we will need a mapping of conflicting keys for rewriting /DA. Set 785 // If we have to merge /DR, we will need a mapping of conflicting keys for rewriting /DA. Set
793 // this up for lazy initialization in case we encounter any form fields. 786 // this up for lazy initialization in case we encounter any form fields.
794 std::map<std::string, std::map<std::string, std::string>> dr_map; 787 std::map<std::string, std::map<std::string, std::string>> dr_map;
795 bool initialized_dr_map = false; 788 bool initialized_dr_map = false;
796 - QPDFObjectHandle dr = QPDFObjectHandle::newNull(); 789 + Dictionary dr;
  790 +
797 auto init_dr_map = [&]() { 791 auto init_dr_map = [&]() {
798 if (!initialized_dr_map) { 792 if (!initialized_dr_map) {
799 initialized_dr_map = true; 793 initialized_dr_map = true;
800 // Ensure that we have a /DR that is an indirect 794 // Ensure that we have a /DR that is an indirect
801 // dictionary object. 795 // dictionary object.
802 - if (!acroform.isDictionary()) { 796 + if (!acroform) {
803 acroform = getOrCreateAcroForm(); 797 acroform = getOrCreateAcroForm();
804 } 798 }
805 - dr = acroform.getKey("/DR");  
806 - if (!dr.isDictionary()) {  
807 - dr = QPDFObjectHandle::newDictionary(); 799 + dr = acroform["/DR"];
  800 + if (!dr) {
  801 + dr = Dictionary::empty();
808 } 802 }
809 - dr.makeResourcesIndirect(qpdf);  
810 - if (!dr.isIndirect()) {  
811 - dr = acroform.replaceKeyAndGetNew("/DR", qpdf.makeIndirectObject(dr)); 803 + QPDFObjectHandle(dr).makeResourcesIndirect(qpdf);
  804 + if (!dr.indirect()) {
  805 + acroform.replaceKey("/DR", qpdf.makeIndirectObject(dr));
  806 + dr = acroform["/DR"];
812 } 807 }
813 // Merge the other document's /DR, creating a conflict map. mergeResources checks to 808 // Merge the other document's /DR, creating a conflict map. mergeResources checks to
814 // make sure both objects are dictionaries. By this point, if this is foreign, from_dr 809 // make sure both objects are dictionaries. By this point, if this is foreign, from_dr
815 // has been copied, so we use the target qpdf as the owning qpdf. 810 // has been copied, so we use the target qpdf as the owning qpdf.
816 - from_dr.makeResourcesIndirect(qpdf);  
817 - dr.mergeResources(from_dr, &dr_map); 811 + QPDFObjectHandle(from_dr).makeResourcesIndirect(qpdf);
  812 + QPDFObjectHandle(dr).mergeResources(from_dr, &dr_map);
818 813
819 if (from_afdh->getNeedAppearances()) { 814 if (from_afdh->getNeedAppearances()) {
820 setNeedAppearances(true); 815 setNeedAppearances(true);
@@ -839,7 +834,7 @@ QPDFAcroFormDocumentHelper::transformAnnotations( @@ -839,7 +834,7 @@ QPDFAcroFormDocumentHelper::transformAnnotations(
839 // Now do the actual copies. 834 // Now do the actual copies.
840 835
841 QPDFObjGen::set added_new_fields; 836 QPDFObjGen::set added_new_fields;
842 - for (auto annot: old_annots.aitems()) { 837 + for (auto annot: old_annots) {
843 if (annot.isStream()) { 838 if (annot.isStream()) {
844 annot.warn("ignoring annotation that's a stream"); 839 annot.warn("ignoring annotation that's a stream");
845 continue; 840 continue;
@@ -904,12 +899,12 @@ QPDFAcroFormDocumentHelper::transformAnnotations( @@ -904,12 +899,12 @@ QPDFAcroFormDocumentHelper::transformAnnotations(
904 std::list<QPDFObjectHandle> queue; 899 std::list<QPDFObjectHandle> queue;
905 QPDFObjGen::set seen; 900 QPDFObjGen::set seen;
906 if (maybe_copy_object(top_field)) { 901 if (maybe_copy_object(top_field)) {
907 - queue.push_back(top_field); 902 + queue.emplace_back(top_field);
908 } 903 }
909 for (; !queue.empty(); queue.pop_front()) { 904 for (; !queue.empty(); queue.pop_front()) {
910 auto& obj = queue.front(); 905 auto& obj = queue.front();
911 if (seen.add(obj)) { 906 if (seen.add(obj)) {
912 - auto parent = obj.getKey("/Parent"); 907 + auto parent = obj["/Parent"];
913 if (parent.isIndirect()) { 908 if (parent.isIndirect()) {
914 auto parent_og = parent.getObjGen(); 909 auto parent_og = parent.getObjGen();
915 if (orig_to_copy.contains(parent_og)) { 910 if (orig_to_copy.contains(parent_og)) {
@@ -922,7 +917,7 @@ QPDFAcroFormDocumentHelper::transformAnnotations( @@ -922,7 +917,7 @@ QPDFAcroFormDocumentHelper::transformAnnotations(
922 "structure"); 917 "structure");
923 } 918 }
924 } 919 }
925 - auto kids = obj.getKey("/Kids"); 920 + auto kids = obj["/Kids"];
926 int sz = static_cast<int>(kids.size()); 921 int sz = static_cast<int>(kids.size());
927 if (sz != 1 || kids.isArray()) { 922 if (sz != 1 || kids.isArray()) {
928 for (int i = 0; i < sz; ++i) { 923 for (int i = 0; i < sz; ++i) {
@@ -952,7 +947,7 @@ QPDFAcroFormDocumentHelper::transformAnnotations( @@ -952,7 +947,7 @@ QPDFAcroFormDocumentHelper::transformAnnotations(
952 obj.replaceKey("/DR", dr); 947 obj.replaceKey("/DR", dr);
953 } 948 }
954 } 949 }
955 - if (foreign && obj.getKey("/DA").isString() && (!dr_map.empty())) { 950 + if (foreign && obj["/DA"].isString() && !dr_map.empty()) {
956 adjustDefaultAppearances(obj, dr_map); 951 adjustDefaultAppearances(obj, dr_map);
957 } 952 }
958 } 953 }
@@ -1011,7 +1006,7 @@ QPDFAcroFormDocumentHelper::transformAnnotations( @@ -1011,7 +1006,7 @@ QPDFAcroFormDocumentHelper::transformAnnotations(
1011 // Now we can safely mutate the annotation and its appearance streams. 1006 // Now we can safely mutate the annotation and its appearance streams.
1012 for (auto& stream: streams) { 1007 for (auto& stream: streams) {
1013 auto dict = stream.getDict(); 1008 auto dict = stream.getDict();
1014 - auto omatrix = dict.getKey("/Matrix"); 1009 + auto omatrix = dict["/Matrix"];
1015 QPDFMatrix apcm; 1010 QPDFMatrix apcm;
1016 if (omatrix.isArray()) { 1011 if (omatrix.isArray()) {
1017 QTC::TC("qpdf", "QPDFAcroFormDocumentHelper modify ap matrix"); 1012 QTC::TC("qpdf", "QPDFAcroFormDocumentHelper modify ap matrix");
@@ -1023,12 +1018,12 @@ QPDFAcroFormDocumentHelper::transformAnnotations( @@ -1023,12 +1018,12 @@ QPDFAcroFormDocumentHelper::transformAnnotations(
1023 if (omatrix.isArray() || (apcm != QPDFMatrix())) { 1018 if (omatrix.isArray() || (apcm != QPDFMatrix())) {
1024 dict.replaceKey("/Matrix", new_matrix); 1019 dict.replaceKey("/Matrix", new_matrix);
1025 } 1020 }
1026 - auto resources = dict.getKey("/Resources"); 1021 + auto resources = dict["/Resources"];
1027 if ((!dr_map.empty()) && resources.isDictionary()) { 1022 if ((!dr_map.empty()) && resources.isDictionary()) {
1028 adjustAppearanceStream(stream, dr_map); 1023 adjustAppearanceStream(stream, dr_map);
1029 } 1024 }
1030 } 1025 }
1031 - auto rect = cm.transformRectangle(annot.getKey("/Rect").getArrayAsRectangle()); 1026 + auto rect = cm.transformRectangle(annot["/Rect"].getArrayAsRectangle());
1032 annot.replaceKey("/Rect", QPDFObjectHandle::newFromRectangle(rect)); 1027 annot.replaceKey("/Rect", QPDFObjectHandle::newFromRectangle(rect));
1033 } 1028 }
1034 } 1029 }