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 76 QPDFObjectHelper.cc
77 77 QPDFOutlineDocumentHelper.cc
78 78 QPDFOutlineObjectHelper.cc
79   - QPDFPageDocumentHelper.cc
80 79 QPDFPageLabelDocumentHelper.cc
81 80 QPDFPageObjectHelper.cc
82 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 2 #include <qpdf/QPDF_private.hh>
2 3  
  4 +#include <qpdf/QPDFAcroFormDocumentHelper.hh>
3 5 #include <qpdf/QPDFExc.hh>
4 6 #include <qpdf/QPDFObjectHandle_private.hh>
5 7 #include <qpdf/QTC.hh>
... ... @@ -565,3 +567,169 @@ Pages::find(QPDFObjGen og)
565 567 }
566 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 +}
... ...