Commit 0421e2ae26a8ca0655a09c5bb36199a52bfe53fe
1 parent
4489c4bc
Refactor `QPDFAcroFormDocumentHelper::transformAnnotations`: extract `traverse_f…
…ield` function to increase code clarity.
Showing
1 changed file
with
53 additions
and
49 deletions
libqpdf/QPDFAcroFormDocumentHelper.cc
| @@ -834,6 +834,58 @@ QPDFAcroFormDocumentHelper::transformAnnotations( | @@ -834,6 +834,58 @@ QPDFAcroFormDocumentHelper::transformAnnotations( | ||
| 834 | } | 834 | } |
| 835 | }; | 835 | }; |
| 836 | 836 | ||
| 837 | + auto traverse_field = [&](QPDFObjectHandle& top_field) -> void { | ||
| 838 | + std::list<Dictionary> queue; | ||
| 839 | + QPDFObjGen::set seen; | ||
| 840 | + queue.emplace_back(top_field); | ||
| 841 | + for (; !queue.empty(); queue.pop_front()) { | ||
| 842 | + auto& obj = queue.front(); | ||
| 843 | + if (seen.add(obj)) { | ||
| 844 | + auto parent = obj["/Parent"]; | ||
| 845 | + if (parent.isIndirect()) { | ||
| 846 | + auto parent_og = parent.id_gen(); | ||
| 847 | + if (orig_to_copy.contains(parent_og)) { | ||
| 848 | + obj.replaceKey("/Parent", orig_to_copy[parent_og]); | ||
| 849 | + } else { | ||
| 850 | + parent.warn( | ||
| 851 | + "while traversing field " + obj.id_gen().unparse(',') + | ||
| 852 | + ", found parent (" + parent_og.unparse(',') + | ||
| 853 | + ") that had not been seen, indicating likely invalid field " | ||
| 854 | + "structure"); | ||
| 855 | + } | ||
| 856 | + } | ||
| 857 | + size_t i = 0; | ||
| 858 | + Array Kids = obj["/Kids"]; | ||
| 859 | + for (auto& kid: Kids) { | ||
| 860 | + if (maybe_copy_object(kid)) { | ||
| 861 | + Kids.set(i, kid); | ||
| 862 | + queue.emplace_back(kid); | ||
| 863 | + } | ||
| 864 | + ++i; | ||
| 865 | + } | ||
| 866 | + adjustInheritedFields( | ||
| 867 | + obj, override_da, from_default_da, override_q, from_default_q); | ||
| 868 | + if (foreign) { | ||
| 869 | + // Lazily initialize our /DR and the conflict map. | ||
| 870 | + init_dr_map(); | ||
| 871 | + // The spec doesn't say anything about /DR on the field, but lots of writers | ||
| 872 | + // put one there, and it is frequently the same as the document-level /DR. | ||
| 873 | + // To avoid having the field's /DR point to information that we are not | ||
| 874 | + // maintaining, just reset it to that if it exists. Empirical evidence | ||
| 875 | + // suggests that many readers, including Acrobat, Adobe Acrobat Reader, | ||
| 876 | + // chrome, firefox, the mac Preview application, and several of the free | ||
| 877 | + // readers on Linux all ignore /DR at the field level. | ||
| 878 | + if (obj.contains("/DR")) { | ||
| 879 | + obj.replaceKey("/DR", dr); | ||
| 880 | + } | ||
| 881 | + if (obj["/DA"].isString() && !dr_map.empty()) { | ||
| 882 | + adjustDefaultAppearances(obj, dr_map); | ||
| 883 | + } | ||
| 884 | + } | ||
| 885 | + } | ||
| 886 | + } | ||
| 887 | + }; | ||
| 888 | + | ||
| 837 | auto transform_annotation = | 889 | auto transform_annotation = |
| 838 | [&](QPDFObjectHandle& annot) -> std::tuple<QPDFObjectHandle, bool, bool> { | 890 | [&](QPDFObjectHandle& annot) -> std::tuple<QPDFObjectHandle, bool, bool> { |
| 839 | // Make copies of annotations and fields down to the appearance streams, preserving all | 891 | // Make copies of annotations and fields down to the appearance streams, preserving all |
| @@ -896,55 +948,7 @@ QPDFAcroFormDocumentHelper::transformAnnotations( | @@ -896,55 +948,7 @@ QPDFAcroFormDocumentHelper::transformAnnotations( | ||
| 896 | 948 | ||
| 897 | // Traverse the field, copying kids, and preserving integrity. | 949 | // Traverse the field, copying kids, and preserving integrity. |
| 898 | if (maybe_copy_object(top_field)) { | 950 | if (maybe_copy_object(top_field)) { |
| 899 | - std::list<Dictionary> queue; | ||
| 900 | - QPDFObjGen::set seen; | ||
| 901 | - queue.emplace_back(top_field); | ||
| 902 | - for (; !queue.empty(); queue.pop_front()) { | ||
| 903 | - auto& obj = queue.front(); | ||
| 904 | - if (seen.add(obj)) { | ||
| 905 | - auto parent = obj["/Parent"]; | ||
| 906 | - if (parent.isIndirect()) { | ||
| 907 | - auto parent_og = parent.id_gen(); | ||
| 908 | - if (orig_to_copy.contains(parent_og)) { | ||
| 909 | - obj.replaceKey("/Parent", orig_to_copy[parent_og]); | ||
| 910 | - } else { | ||
| 911 | - parent.warn( | ||
| 912 | - "while traversing field " + obj.id_gen().unparse(',') + | ||
| 913 | - ", found parent (" + parent_og.unparse(',') + | ||
| 914 | - ") that had not been seen, indicating likely invalid field " | ||
| 915 | - "structure"); | ||
| 916 | - } | ||
| 917 | - } | ||
| 918 | - size_t i = 0; | ||
| 919 | - Array Kids = obj["/Kids"]; | ||
| 920 | - for (auto& kid: Kids) { | ||
| 921 | - if (maybe_copy_object(kid)) { | ||
| 922 | - Kids.set(i, kid); | ||
| 923 | - queue.emplace_back(kid); | ||
| 924 | - } | ||
| 925 | - ++i; | ||
| 926 | - } | ||
| 927 | - adjustInheritedFields( | ||
| 928 | - obj, override_da, from_default_da, override_q, from_default_q); | ||
| 929 | - if (foreign) { | ||
| 930 | - // Lazily initialize our /DR and the conflict map. | ||
| 931 | - init_dr_map(); | ||
| 932 | - // The spec doesn't say anything about /DR on the field, but lots of writers | ||
| 933 | - // put one there, and it is frequently the same as the document-level /DR. | ||
| 934 | - // To avoid having the field's /DR point to information that we are not | ||
| 935 | - // maintaining, just reset it to that if it exists. Empirical evidence | ||
| 936 | - // suggests that many readers, including Acrobat, Adobe Acrobat Reader, | ||
| 937 | - // chrome, firefox, the mac Preview application, and several of the free | ||
| 938 | - // readers on Linux all ignore /DR at the field level. | ||
| 939 | - if (obj.contains("/DR")) { | ||
| 940 | - obj.replaceKey("/DR", dr); | ||
| 941 | - } | ||
| 942 | - if (obj["/DA"].isString() && !dr_map.empty()) { | ||
| 943 | - adjustDefaultAppearances(obj, dr_map); | ||
| 944 | - } | ||
| 945 | - } | ||
| 946 | - } | ||
| 947 | - } | 951 | + traverse_field(top_field); |
| 948 | } | 952 | } |
| 949 | 953 | ||
| 950 | // Now switch to copies. We already switched for top_field | 954 | // Now switch to copies. We already switched for top_field |