Commit 4f23d70b9a489f961429119275173afa862acc89

Authored by m-holger
1 parent 361f147f

Refactor `QPDFPageDocumentHelper`: merge implementation into `QPDF_pages`, updat…

…e includes, and streamline page-related logic.
libqpdf/CMakeLists.txt
@@ -76,7 +76,6 @@ set(libqpdf_SOURCES @@ -76,7 +76,6 @@ set(libqpdf_SOURCES
76 QPDFObjectHelper.cc 76 QPDFObjectHelper.cc
77 QPDFOutlineDocumentHelper.cc 77 QPDFOutlineDocumentHelper.cc
78 QPDFOutlineObjectHelper.cc 78 QPDFOutlineObjectHelper.cc
79 - QPDFPageDocumentHelper.cc  
80 QPDFPageLabelDocumentHelper.cc 79 QPDFPageLabelDocumentHelper.cc
81 QPDFPageObjectHelper.cc 80 QPDFPageObjectHelper.cc
82 QPDFParser.cc 81 QPDFParser.cc
libqpdf/QPDFPageDocumentHelper.cc deleted
1 -#include <qpdf/QPDFPageDocumentHelper.hh>  
2 -  
3 -#include <qpdf/QPDFAcroFormDocumentHelper.hh>  
4 -#include <qpdf/QPDFObjectHandle_private.hh>  
5 -#include <qpdf/QPDF_private.hh>  
6 -#include <qpdf/QTC.hh>  
7 -#include <qpdf/QUtil.hh>  
8 -  
9 -class QPDFPageDocumentHelper::Members  
10 -{  
11 -};  
12 -  
13 -QPDFPageDocumentHelper::QPDFPageDocumentHelper(QPDF& qpdf) :  
14 - QPDFDocumentHelper(qpdf)  
15 -{  
16 -}  
17 -  
18 -QPDFPageDocumentHelper&  
19 -QPDFPageDocumentHelper::get(QPDF& qpdf)  
20 -{  
21 - return qpdf.doc().page_dh();  
22 -}  
23 -  
24 -void  
25 -QPDFPageDocumentHelper::validate(bool repair)  
26 -{  
27 -}  
28 -  
29 -std::vector<QPDFPageObjectHelper>  
30 -QPDFPageDocumentHelper::getAllPages()  
31 -{  
32 - auto& pp = qpdf.doc().pages();  
33 - return {pp.begin(), pp.end()};  
34 -}  
35 -  
36 -void  
37 -QPDFPageDocumentHelper::pushInheritedAttributesToPage()  
38 -{  
39 - qpdf.pushInheritedAttributesToPage();  
40 -}  
41 -  
42 -void  
43 -QPDFPageDocumentHelper::removeUnreferencedResources()  
44 -{  
45 - for (auto& ph: getAllPages()) {  
46 - ph.removeUnreferencedResources();  
47 - }  
48 -}  
49 -  
50 -void  
51 -QPDFPageDocumentHelper::addPage(QPDFPageObjectHelper newpage, bool first)  
52 -{  
53 - qpdf.addPage(newpage.getObjectHandle(), first);  
54 -}  
55 -  
56 -void  
57 -QPDFPageDocumentHelper::addPageAt(  
58 - QPDFPageObjectHelper newpage, bool before, QPDFPageObjectHelper refpage)  
59 -{  
60 - qpdf.addPageAt(newpage.getObjectHandle(), before, refpage.getObjectHandle());  
61 -}  
62 -  
63 -void  
64 -QPDFPageDocumentHelper::removePage(QPDFPageObjectHelper page)  
65 -{  
66 - qpdf.removePage(page.getObjectHandle());  
67 -}  
68 -  
69 -void  
70 -QPDFPageDocumentHelper::flattenAnnotations(int required_flags, int forbidden_flags)  
71 -{  
72 - auto& afdh = qpdf.doc().acroform();  
73 - if (afdh.getNeedAppearances()) {  
74 - qpdf.getRoot()  
75 - .getKey("/AcroForm")  
76 - .warn(  
77 - "document does not have updated appearance streams, so form fields "  
78 - "will not be flattened");  
79 - }  
80 - for (auto& ph: getAllPages()) {  
81 - QPDFObjectHandle resources = ph.getAttribute("/Resources", true);  
82 - if (!resources.isDictionary()) {  
83 - // As of #1521, this should be impossible unless a user inserted an invalid page.  
84 - resources = ph.getObjectHandle().replaceKeyAndGetNew(  
85 - "/Resources", QPDFObjectHandle::newDictionary());  
86 - }  
87 - flattenAnnotationsForPage(ph, resources, afdh, required_flags, forbidden_flags);  
88 - }  
89 - if (!afdh.getNeedAppearances()) {  
90 - qpdf.getRoot().removeKey("/AcroForm");  
91 - }  
92 -}  
93 -  
94 -void  
95 -QPDFPageDocumentHelper::flattenAnnotationsForPage(  
96 - QPDFPageObjectHelper& page,  
97 - QPDFObjectHandle& resources,  
98 - QPDFAcroFormDocumentHelper& afdh,  
99 - int required_flags,  
100 - int forbidden_flags)  
101 -{  
102 - bool need_appearances = afdh.getNeedAppearances();  
103 - std::vector<QPDFAnnotationObjectHelper> annots = page.getAnnotations();  
104 - std::vector<QPDFObjectHandle> new_annots;  
105 - std::string new_content;  
106 - int rotate = 0;  
107 - QPDFObjectHandle rotate_obj = page.getObjectHandle().getKey("/Rotate");  
108 - if (rotate_obj.isInteger() && rotate_obj.getIntValue()) {  
109 - rotate = rotate_obj.getIntValueAsInt();  
110 - }  
111 - int next_fx = 1;  
112 - for (auto& aoh: annots) {  
113 - QPDFObjectHandle as = aoh.getAppearanceStream("/N");  
114 - bool is_widget = (aoh.getSubtype() == "/Widget");  
115 - bool process = true;  
116 - if (need_appearances && is_widget) {  
117 - QTC::TC("qpdf", "QPDFPageDocumentHelper skip widget need appearances");  
118 - process = false;  
119 - }  
120 - if (process && as.isStream()) {  
121 - if (is_widget) {  
122 - QTC::TC("qpdf", "QPDFPageDocumentHelper merge DR");  
123 - QPDFFormFieldObjectHelper ff = afdh.getFieldForAnnotation(aoh);  
124 - QPDFObjectHandle as_resources = as.getDict().getKey("/Resources");  
125 - if (as_resources.isIndirect()) {  
126 - QTC::TC("qpdf", "QPDFPageDocumentHelper indirect as resources");  
127 - as.getDict().replaceKey("/Resources", as_resources.shallowCopy());  
128 - as_resources = as.getDict().getKey("/Resources");  
129 - }  
130 - as_resources.mergeResources(ff.getDefaultResources());  
131 - } else {  
132 - QTC::TC("qpdf", "QPDFPageDocumentHelper non-widget annotation");  
133 - }  
134 - std::string name = resources.getUniqueResourceName("/Fxo", next_fx);  
135 - std::string content =  
136 - aoh.getPageContentForAppearance(name, rotate, required_flags, forbidden_flags);  
137 - if (!content.empty()) {  
138 - resources.mergeResources("<< /XObject << >> >>"_qpdf);  
139 - resources.getKey("/XObject").replaceKey(name, as);  
140 - ++next_fx;  
141 - }  
142 - new_content += content;  
143 - } else if (process && !aoh.getAppearanceDictionary().null()) {  
144 - // If an annotation has no selected appearance stream, just drop the annotation when  
145 - // flattening. This can happen for unchecked checkboxes and radio buttons, popup windows  
146 - // associated with comments that aren't visible, and other types of annotations that  
147 - // aren't visible. Annotations that have no appearance streams at all, such as Link,  
148 - // Popup, and Projection, should be preserved.  
149 - QTC::TC("qpdf", "QPDFPageDocumentHelper ignore annotation with no appearance");  
150 - } else {  
151 - new_annots.push_back(aoh.getObjectHandle());  
152 - }  
153 - }  
154 - if (new_annots.size() != annots.size()) {  
155 - QPDFObjectHandle page_oh = page.getObjectHandle();  
156 - if (new_annots.empty()) {  
157 - QTC::TC("qpdf", "QPDFPageDocumentHelper remove annots");  
158 - page_oh.removeKey("/Annots");  
159 - } else {  
160 - QPDFObjectHandle old_annots = page_oh.getKey("/Annots");  
161 - QPDFObjectHandle new_annots_oh = QPDFObjectHandle::newArray(new_annots);  
162 - if (old_annots.isIndirect()) {  
163 - QTC::TC("qpdf", "QPDFPageDocumentHelper replace indirect annots");  
164 - qpdf.replaceObject(old_annots.getObjGen(), new_annots_oh);  
165 - } else {  
166 - QTC::TC("qpdf", "QPDFPageDocumentHelper replace direct annots");  
167 - page_oh.replaceKey("/Annots", new_annots_oh);  
168 - }  
169 - }  
170 - page.addPageContents(qpdf.newStream("q\n"), true);  
171 - page.addPageContents(qpdf.newStream("\nQ\n" + new_content), false);  
172 - }  
173 -}  
libqpdf/QPDF_pages.cc
  1 +#include <qpdf/QPDFPageDocumentHelper.hh>
1 #include <qpdf/QPDF_private.hh> 2 #include <qpdf/QPDF_private.hh>
2 3
  4 +#include <qpdf/QPDFAcroFormDocumentHelper.hh>
3 #include <qpdf/QPDFExc.hh> 5 #include <qpdf/QPDFExc.hh>
4 #include <qpdf/QPDFObjectHandle_private.hh> 6 #include <qpdf/QPDFObjectHandle_private.hh>
5 #include <qpdf/QTC.hh> 7 #include <qpdf/QTC.hh>
@@ -565,3 +567,169 @@ Pages::find(QPDFObjGen og) @@ -565,3 +567,169 @@ Pages::find(QPDFObjGen og)
565 } 567 }
566 return (*it).second; 568 return (*it).second;
567 } 569 }
  570 +
  571 +class QPDFPageDocumentHelper::Members
  572 +{
  573 +};
  574 +
  575 +QPDFPageDocumentHelper::QPDFPageDocumentHelper(QPDF& qpdf) :
  576 + QPDFDocumentHelper(qpdf)
  577 +{
  578 +}
  579 +
  580 +QPDFPageDocumentHelper&
  581 +QPDFPageDocumentHelper::get(QPDF& qpdf)
  582 +{
  583 + return qpdf.doc().page_dh();
  584 +}
  585 +
  586 +void
  587 +QPDFPageDocumentHelper::validate(bool repair)
  588 +{
  589 +}
  590 +
  591 +std::vector<QPDFPageObjectHelper>
  592 +QPDFPageDocumentHelper::getAllPages()
  593 +{
  594 + auto& pp = qpdf.doc().pages();
  595 + return {pp.begin(), pp.end()};
  596 +}
  597 +
  598 +void
  599 +QPDFPageDocumentHelper::pushInheritedAttributesToPage()
  600 +{
  601 + qpdf.pushInheritedAttributesToPage();
  602 +}
  603 +
  604 +void
  605 +QPDFPageDocumentHelper::removeUnreferencedResources()
  606 +{
  607 + for (auto& ph: getAllPages()) {
  608 + ph.removeUnreferencedResources();
  609 + }
  610 +}
  611 +
  612 +void
  613 +QPDFPageDocumentHelper::addPage(QPDFPageObjectHelper newpage, bool first)
  614 +{
  615 + qpdf.addPage(newpage.getObjectHandle(), first);
  616 +}
  617 +
  618 +void
  619 +QPDFPageDocumentHelper::addPageAt(
  620 + QPDFPageObjectHelper newpage, bool before, QPDFPageObjectHelper refpage)
  621 +{
  622 + qpdf.addPageAt(newpage.getObjectHandle(), before, refpage.getObjectHandle());
  623 +}
  624 +
  625 +void
  626 +QPDFPageDocumentHelper::removePage(QPDFPageObjectHelper page)
  627 +{
  628 + qpdf.removePage(page.getObjectHandle());
  629 +}
  630 +
  631 +void
  632 +QPDFPageDocumentHelper::flattenAnnotations(int required_flags, int forbidden_flags)
  633 +{
  634 + auto& afdh = qpdf.doc().acroform();
  635 + if (afdh.getNeedAppearances()) {
  636 + qpdf.getRoot()
  637 + .getKey("/AcroForm")
  638 + .warn(
  639 + "document does not have updated appearance streams, so form fields "
  640 + "will not be flattened");
  641 + }
  642 + for (auto& ph: getAllPages()) {
  643 + QPDFObjectHandle resources = ph.getAttribute("/Resources", true);
  644 + if (!resources.isDictionary()) {
  645 + // As of #1521, this should be impossible unless a user inserted an invalid page.
  646 + resources = ph.getObjectHandle().replaceKeyAndGetNew(
  647 + "/Resources", QPDFObjectHandle::newDictionary());
  648 + }
  649 + flattenAnnotationsForPage(ph, resources, afdh, required_flags, forbidden_flags);
  650 + }
  651 + if (!afdh.getNeedAppearances()) {
  652 + qpdf.getRoot().removeKey("/AcroForm");
  653 + }
  654 +}
  655 +
  656 +void
  657 +QPDFPageDocumentHelper::flattenAnnotationsForPage(
  658 + QPDFPageObjectHelper& page,
  659 + QPDFObjectHandle& resources,
  660 + QPDFAcroFormDocumentHelper& afdh,
  661 + int required_flags,
  662 + int forbidden_flags)
  663 +{
  664 + bool need_appearances = afdh.getNeedAppearances();
  665 + std::vector<QPDFAnnotationObjectHelper> annots = page.getAnnotations();
  666 + std::vector<QPDFObjectHandle> new_annots;
  667 + std::string new_content;
  668 + int rotate = 0;
  669 + QPDFObjectHandle rotate_obj = page.getObjectHandle().getKey("/Rotate");
  670 + if (rotate_obj.isInteger() && rotate_obj.getIntValue()) {
  671 + rotate = rotate_obj.getIntValueAsInt();
  672 + }
  673 + int next_fx = 1;
  674 + for (auto& aoh: annots) {
  675 + QPDFObjectHandle as = aoh.getAppearanceStream("/N");
  676 + bool is_widget = (aoh.getSubtype() == "/Widget");
  677 + bool process = true;
  678 + if (need_appearances && is_widget) {
  679 + QTC::TC("qpdf", "QPDFPageDocumentHelper skip widget need appearances");
  680 + process = false;
  681 + }
  682 + if (process && as.isStream()) {
  683 + if (is_widget) {
  684 + QTC::TC("qpdf", "QPDFPageDocumentHelper merge DR");
  685 + QPDFFormFieldObjectHelper ff = afdh.getFieldForAnnotation(aoh);
  686 + QPDFObjectHandle as_resources = as.getDict().getKey("/Resources");
  687 + if (as_resources.isIndirect()) {
  688 + QTC::TC("qpdf", "QPDFPageDocumentHelper indirect as resources");
  689 + as.getDict().replaceKey("/Resources", as_resources.shallowCopy());
  690 + as_resources = as.getDict().getKey("/Resources");
  691 + }
  692 + as_resources.mergeResources(ff.getDefaultResources());
  693 + } else {
  694 + QTC::TC("qpdf", "QPDFPageDocumentHelper non-widget annotation");
  695 + }
  696 + std::string name = resources.getUniqueResourceName("/Fxo", next_fx);
  697 + std::string content =
  698 + aoh.getPageContentForAppearance(name, rotate, required_flags, forbidden_flags);
  699 + if (!content.empty()) {
  700 + resources.mergeResources("<< /XObject << >> >>"_qpdf);
  701 + resources.getKey("/XObject").replaceKey(name, as);
  702 + ++next_fx;
  703 + }
  704 + new_content += content;
  705 + } else if (process && !aoh.getAppearanceDictionary().null()) {
  706 + // If an annotation has no selected appearance stream, just drop the annotation when
  707 + // flattening. This can happen for unchecked checkboxes and radio buttons, popup windows
  708 + // associated with comments that aren't visible, and other types of annotations that
  709 + // aren't visible. Annotations that have no appearance streams at all, such as Link,
  710 + // Popup, and Projection, should be preserved.
  711 + QTC::TC("qpdf", "QPDFPageDocumentHelper ignore annotation with no appearance");
  712 + } else {
  713 + new_annots.push_back(aoh.getObjectHandle());
  714 + }
  715 + }
  716 + if (new_annots.size() != annots.size()) {
  717 + QPDFObjectHandle page_oh = page.getObjectHandle();
  718 + if (new_annots.empty()) {
  719 + QTC::TC("qpdf", "QPDFPageDocumentHelper remove annots");
  720 + page_oh.removeKey("/Annots");
  721 + } else {
  722 + QPDFObjectHandle old_annots = page_oh.getKey("/Annots");
  723 + QPDFObjectHandle new_annots_oh = QPDFObjectHandle::newArray(new_annots);
  724 + if (old_annots.isIndirect()) {
  725 + QTC::TC("qpdf", "QPDFPageDocumentHelper replace indirect annots");
  726 + qpdf.replaceObject(old_annots.getObjGen(), new_annots_oh);
  727 + } else {
  728 + QTC::TC("qpdf", "QPDFPageDocumentHelper replace direct annots");
  729 + page_oh.replaceKey("/Annots", new_annots_oh);
  730 + }
  731 + }
  732 + page.addPageContents(qpdf.newStream("q\n"), true);
  733 + page.addPageContents(qpdf.newStream("\nQ\n" + new_content), false);
  734 + }
  735 +}