1 // Copyright 2016 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7 #include "fpdfsdk/cpdfsdk_annotiterator.h"
8
9 #include <algorithm>
10
11 #include "core/fpdfapi/page/cpdf_page.h"
12 #include "core/fpdfapi/parser/cpdf_dictionary.h"
13 #include "core/fxcrt/containers/adapters.h"
14 #include "core/fxcrt/containers/contains.h"
15 #include "core/fxcrt/span.h"
16 #include "core/fxcrt/stl_util.h"
17 #include "fpdfsdk/cpdfsdk_annot.h"
18 #include "fpdfsdk/cpdfsdk_pageview.h"
19 #include "fpdfsdk/cpdfsdk_widget.h"
20
21 namespace {
22
GetAnnotRect(const CPDFSDK_Annot * pAnnot)23 CFX_FloatRect GetAnnotRect(const CPDFSDK_Annot* pAnnot) {
24 return pAnnot->GetPDFAnnot()->GetRect();
25 }
26
CompareByLeftAscending(const CPDFSDK_Annot * p1,const CPDFSDK_Annot * p2)27 bool CompareByLeftAscending(const CPDFSDK_Annot* p1, const CPDFSDK_Annot* p2) {
28 return GetAnnotRect(p1).left < GetAnnotRect(p2).left;
29 }
30
CompareByTopDescending(const CPDFSDK_Annot * p1,const CPDFSDK_Annot * p2)31 bool CompareByTopDescending(const CPDFSDK_Annot* p1, const CPDFSDK_Annot* p2) {
32 return GetAnnotRect(p1).top > GetAnnotRect(p2).top;
33 }
34
35 } // namespace
36
CPDFSDK_AnnotIterator(CPDFSDK_PageView * pPageView,const std::vector<CPDF_Annot::Subtype> & subtypes_to_iterate)37 CPDFSDK_AnnotIterator::CPDFSDK_AnnotIterator(
38 CPDFSDK_PageView* pPageView,
39 const std::vector<CPDF_Annot::Subtype>& subtypes_to_iterate)
40 : m_pPageView(pPageView),
41 m_subtypes(subtypes_to_iterate),
42 m_eTabOrder(GetTabOrder(pPageView)) {
43 GenerateResults();
44 }
45
46 CPDFSDK_AnnotIterator::~CPDFSDK_AnnotIterator() = default;
47
GetFirstAnnot()48 CPDFSDK_Annot* CPDFSDK_AnnotIterator::GetFirstAnnot() {
49 return m_Annots.empty() ? nullptr : m_Annots.front();
50 }
51
GetLastAnnot()52 CPDFSDK_Annot* CPDFSDK_AnnotIterator::GetLastAnnot() {
53 return m_Annots.empty() ? nullptr : m_Annots.back();
54 }
55
GetNextAnnot(CPDFSDK_Annot * pAnnot)56 CPDFSDK_Annot* CPDFSDK_AnnotIterator::GetNextAnnot(CPDFSDK_Annot* pAnnot) {
57 auto iter = std::find(m_Annots.begin(), m_Annots.end(), pAnnot);
58 if (iter == m_Annots.end())
59 return nullptr;
60 ++iter;
61 if (iter == m_Annots.end())
62 return nullptr;
63 return *iter;
64 }
65
GetPrevAnnot(CPDFSDK_Annot * pAnnot)66 CPDFSDK_Annot* CPDFSDK_AnnotIterator::GetPrevAnnot(CPDFSDK_Annot* pAnnot) {
67 auto iter = std::find(m_Annots.begin(), m_Annots.end(), pAnnot);
68 if (iter == m_Annots.begin() || iter == m_Annots.end())
69 return nullptr;
70 return *(--iter);
71 }
72
CollectAnnots(std::vector<UnownedPtr<CPDFSDK_Annot>> * pArray)73 void CPDFSDK_AnnotIterator::CollectAnnots(
74 std::vector<UnownedPtr<CPDFSDK_Annot>>* pArray) {
75 for (auto* pAnnot : m_pPageView->GetAnnotList()) {
76 if (pdfium::Contains(m_subtypes, pAnnot->GetAnnotSubtype())) {
77 CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot);
78 if (!pWidget || !pWidget->IsSignatureWidget())
79 pArray->emplace_back(pAnnot);
80 }
81 }
82 }
83
AddToAnnotsList(std::vector<UnownedPtr<CPDFSDK_Annot>> & sa,size_t idx)84 CFX_FloatRect CPDFSDK_AnnotIterator::AddToAnnotsList(
85 std::vector<UnownedPtr<CPDFSDK_Annot>>& sa,
86 size_t idx) {
87 CPDFSDK_Annot* pLeftTopAnnot = sa[idx];
88 CFX_FloatRect rcLeftTop = GetAnnotRect(pLeftTopAnnot);
89 m_Annots.emplace_back(pLeftTopAnnot);
90 sa.erase(sa.begin() + idx);
91 return rcLeftTop;
92 }
93
AddSelectedToAnnots(std::vector<UnownedPtr<CPDFSDK_Annot>> & sa,pdfium::span<const size_t> aSelect)94 void CPDFSDK_AnnotIterator::AddSelectedToAnnots(
95 std::vector<UnownedPtr<CPDFSDK_Annot>>& sa,
96 pdfium::span<const size_t> aSelect) {
97 for (size_t select_idx : aSelect) {
98 m_Annots.emplace_back(sa[select_idx]);
99 }
100
101 for (size_t select_idx : pdfium::Reversed(aSelect)) {
102 sa.erase(sa.begin() + select_idx);
103 }
104 }
105
106 // static
GetTabOrder(CPDFSDK_PageView * pPageView)107 CPDFSDK_AnnotIterator::TabOrder CPDFSDK_AnnotIterator::GetTabOrder(
108 CPDFSDK_PageView* pPageView) {
109 CPDF_Page* pPDFPage = pPageView->GetPDFPage();
110 ByteString sTabs = pPDFPage->GetDict()->GetByteStringFor("Tabs");
111 if (sTabs == "R")
112 return TabOrder::kRow;
113 if (sTabs == "C")
114 return TabOrder::kColumn;
115 return TabOrder::kStructure;
116 }
117
GenerateResults()118 void CPDFSDK_AnnotIterator::GenerateResults() {
119 switch (m_eTabOrder) {
120 case TabOrder::kStructure:
121 CollectAnnots(&m_Annots);
122 break;
123
124 case TabOrder::kRow: {
125 std::vector<UnownedPtr<CPDFSDK_Annot>> sa;
126 CollectAnnots(&sa);
127 std::sort(sa.begin(), sa.end(), CompareByLeftAscending);
128
129 while (!sa.empty()) {
130 int nLeftTopIndex = -1;
131 float fTop = 0.0f;
132 for (int i = fxcrt::CollectionSize<int>(sa) - 1; i >= 0; i--) {
133 CFX_FloatRect rcAnnot = GetAnnotRect(sa[i]);
134 if (rcAnnot.top > fTop) {
135 nLeftTopIndex = i;
136 fTop = rcAnnot.top;
137 }
138 }
139 if (nLeftTopIndex < 0)
140 continue;
141
142 CFX_FloatRect rcLeftTop = AddToAnnotsList(sa, nLeftTopIndex);
143
144 std::vector<size_t> aSelect;
145 for (size_t i = 0; i < sa.size(); ++i) {
146 CFX_FloatRect rcAnnot = GetAnnotRect(sa[i]);
147 float fCenterY = (rcAnnot.top + rcAnnot.bottom) / 2.0f;
148 if (fCenterY > rcLeftTop.bottom && fCenterY < rcLeftTop.top)
149 aSelect.push_back(i);
150 }
151 AddSelectedToAnnots(sa, aSelect);
152 }
153 break;
154 }
155
156 case TabOrder::kColumn: {
157 std::vector<UnownedPtr<CPDFSDK_Annot>> sa;
158 CollectAnnots(&sa);
159 std::sort(sa.begin(), sa.end(), CompareByTopDescending);
160
161 while (!sa.empty()) {
162 int nLeftTopIndex = -1;
163 float fLeft = -1.0f;
164 for (int i = fxcrt::CollectionSize<int>(sa) - 1; i >= 0; --i) {
165 CFX_FloatRect rcAnnot = GetAnnotRect(sa[i]);
166 if (fLeft < 0) {
167 nLeftTopIndex = 0;
168 fLeft = rcAnnot.left;
169 } else if (rcAnnot.left < fLeft) {
170 nLeftTopIndex = i;
171 fLeft = rcAnnot.left;
172 }
173 }
174 if (nLeftTopIndex < 0)
175 continue;
176
177 CFX_FloatRect rcLeftTop = AddToAnnotsList(sa, nLeftTopIndex);
178
179 std::vector<size_t> aSelect;
180 for (size_t i = 0; i < sa.size(); ++i) {
181 CFX_FloatRect rcAnnot = GetAnnotRect(sa[i]);
182 float fCenterX = (rcAnnot.left + rcAnnot.right) / 2.0f;
183 if (fCenterX > rcLeftTop.left && fCenterX < rcLeftTop.right)
184 aSelect.push_back(i);
185 }
186 AddSelectedToAnnots(sa, aSelect);
187 }
188 break;
189 }
190 }
191 }
192