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_baannot.h"
8
9 #include <vector>
10
11 #include "constants/annotation_common.h"
12 #include "constants/annotation_flags.h"
13 #include "constants/form_fields.h"
14 #include "core/fpdfapi/parser/cpdf_array.h"
15 #include "core/fpdfapi/parser/cpdf_dictionary.h"
16 #include "core/fpdfapi/parser/cpdf_name.h"
17 #include "core/fpdfapi/parser/cpdf_number.h"
18 #include "core/fpdfapi/parser/cpdf_reference.h"
19 #include "core/fpdfapi/parser/cpdf_stream.h"
20 #include "core/fpdfapi/parser/cpdf_string.h"
21 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
22 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
23 #include "core/fxge/cfx_drawutils.h"
24 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
25 #include "fpdfsdk/cpdfsdk_pageview.h"
26 #include "third_party/abseil-cpp/absl/types/optional.h"
27 #include "third_party/base/check.h"
28 #include "third_party/base/containers/contains.h"
29
CPDFSDK_BAAnnot(CPDF_Annot * pAnnot,CPDFSDK_PageView * pPageView)30 CPDFSDK_BAAnnot::CPDFSDK_BAAnnot(CPDF_Annot* pAnnot,
31 CPDFSDK_PageView* pPageView)
32 : CPDFSDK_Annot(pPageView), m_pAnnot(pAnnot) {}
33
34 CPDFSDK_BAAnnot::~CPDFSDK_BAAnnot() = default;
35
AsBAAnnot()36 CPDFSDK_BAAnnot* CPDFSDK_BAAnnot::AsBAAnnot() {
37 return this;
38 }
39
GetUnsafeInputHandlers()40 CPDFSDK_Annot::UnsafeInputHandlers* CPDFSDK_BAAnnot::GetUnsafeInputHandlers() {
41 return this;
42 }
43
GetPDFAnnot() const44 CPDF_Annot* CPDFSDK_BAAnnot::GetPDFAnnot() const {
45 return m_pAnnot;
46 }
47
GetAnnotDict() const48 const CPDF_Dictionary* CPDFSDK_BAAnnot::GetAnnotDict() const {
49 return m_pAnnot->GetAnnotDict();
50 }
51
GetMutableAnnotDict()52 RetainPtr<CPDF_Dictionary> CPDFSDK_BAAnnot::GetMutableAnnotDict() {
53 return m_pAnnot->GetMutableAnnotDict();
54 }
55
GetAPDict()56 RetainPtr<CPDF_Dictionary> CPDFSDK_BAAnnot::GetAPDict() {
57 return GetMutableAnnotDict()->GetOrCreateDictFor(pdfium::annotation::kAP);
58 }
59
ClearCachedAnnotAP()60 void CPDFSDK_BAAnnot::ClearCachedAnnotAP() {
61 m_pAnnot->ClearCachedAP();
62 }
63
IsFocusableAnnot(const CPDF_Annot::Subtype & annot_type) const64 bool CPDFSDK_BAAnnot::IsFocusableAnnot(
65 const CPDF_Annot::Subtype& annot_type) const {
66 return pdfium::Contains(
67 GetPageView()->GetFormFillEnv()->GetFocusableAnnotSubtypes(), annot_type);
68 }
69
GetRect() const70 CFX_FloatRect CPDFSDK_BAAnnot::GetRect() const {
71 return m_pAnnot->GetRect();
72 }
73
GetAnnotSubtype() const74 CPDF_Annot::Subtype CPDFSDK_BAAnnot::GetAnnotSubtype() const {
75 return m_pAnnot->GetSubtype();
76 }
77
DrawAppearance(CFX_RenderDevice * pDevice,const CFX_Matrix & mtUser2Device,CPDF_Annot::AppearanceMode mode)78 void CPDFSDK_BAAnnot::DrawAppearance(CFX_RenderDevice* pDevice,
79 const CFX_Matrix& mtUser2Device,
80 CPDF_Annot::AppearanceMode mode) {
81 m_pAnnot->DrawAppearance(GetPageView()->GetPDFPage(), pDevice, mtUser2Device,
82 mode);
83 }
84
IsAppearanceValid()85 bool CPDFSDK_BAAnnot::IsAppearanceValid() {
86 return !!GetAnnotDict()->GetDictFor(pdfium::annotation::kAP);
87 }
88
SetAnnotName(const WideString & sName)89 void CPDFSDK_BAAnnot::SetAnnotName(const WideString& sName) {
90 RetainPtr<CPDF_Dictionary> pDict = GetMutableAnnotDict();
91 if (sName.IsEmpty()) {
92 pDict->RemoveFor(pdfium::annotation::kNM);
93 return;
94 }
95 pDict->SetNewFor<CPDF_String>(pdfium::annotation::kNM, sName.AsStringView());
96 }
97
GetAnnotName() const98 WideString CPDFSDK_BAAnnot::GetAnnotName() const {
99 return GetAnnotDict()->GetUnicodeTextFor(pdfium::annotation::kNM);
100 }
101
SetFlags(uint32_t nFlags)102 void CPDFSDK_BAAnnot::SetFlags(uint32_t nFlags) {
103 GetMutableAnnotDict()->SetNewFor<CPDF_Number>(pdfium::annotation::kF,
104 static_cast<int>(nFlags));
105 }
106
GetFlags() const107 uint32_t CPDFSDK_BAAnnot::GetFlags() const {
108 return GetAnnotDict()->GetIntegerFor(pdfium::annotation::kF);
109 }
110
SetAppStateOff()111 void CPDFSDK_BAAnnot::SetAppStateOff() {
112 RetainPtr<CPDF_Dictionary> pDict = GetMutableAnnotDict();
113 pDict->SetNewFor<CPDF_String>(pdfium::annotation::kAS, "Off", false);
114 }
115
GetAppState() const116 ByteString CPDFSDK_BAAnnot::GetAppState() const {
117 return GetAnnotDict()->GetByteStringFor(pdfium::annotation::kAS);
118 }
119
SetBorderWidth(int nWidth)120 void CPDFSDK_BAAnnot::SetBorderWidth(int nWidth) {
121 RetainPtr<CPDF_Dictionary> pAnnotDict = GetMutableAnnotDict();
122 RetainPtr<CPDF_Array> pBorder =
123 pAnnotDict->GetMutableArrayFor(pdfium::annotation::kBorder);
124 if (pBorder) {
125 pBorder->SetNewAt<CPDF_Number>(2, nWidth);
126 return;
127 }
128 pAnnotDict->GetOrCreateDictFor("BS")->SetNewFor<CPDF_Number>("W", nWidth);
129 }
130
GetBorderWidth() const131 int CPDFSDK_BAAnnot::GetBorderWidth() const {
132 RetainPtr<const CPDF_Array> pBorder =
133 GetAnnotDict()->GetArrayFor(pdfium::annotation::kBorder);
134 if (pBorder)
135 return pBorder->GetIntegerAt(2);
136
137 RetainPtr<const CPDF_Dictionary> pBSDict = GetAnnotDict()->GetDictFor("BS");
138 if (pBSDict)
139 return pBSDict->GetIntegerFor("W", 1);
140
141 return 1;
142 }
143
SetBorderStyle(BorderStyle nStyle)144 void CPDFSDK_BAAnnot::SetBorderStyle(BorderStyle nStyle) {
145 RetainPtr<CPDF_Dictionary> pBSDict =
146 GetMutableAnnotDict()->GetOrCreateDictFor("BS");
147 const char* name = nullptr;
148 switch (nStyle) {
149 case BorderStyle::kSolid:
150 name = "S";
151 break;
152 case BorderStyle::kDash:
153 name = "D";
154 break;
155 case BorderStyle::kBeveled:
156 name = "B";
157 break;
158 case BorderStyle::kInset:
159 name = "I";
160 break;
161 case BorderStyle::kUnderline:
162 name = "U";
163 break;
164 }
165 pBSDict->SetNewFor<CPDF_Name>("S", name);
166 }
167
GetBorderStyle() const168 BorderStyle CPDFSDK_BAAnnot::GetBorderStyle() const {
169 RetainPtr<const CPDF_Dictionary> pBSDict = GetAnnotDict()->GetDictFor("BS");
170 if (pBSDict) {
171 ByteString sBorderStyle = pBSDict->GetByteStringFor("S", "S");
172 if (sBorderStyle == "S")
173 return BorderStyle::kSolid;
174 if (sBorderStyle == "D")
175 return BorderStyle::kDash;
176 if (sBorderStyle == "B")
177 return BorderStyle::kBeveled;
178 if (sBorderStyle == "I")
179 return BorderStyle::kInset;
180 if (sBorderStyle == "U")
181 return BorderStyle::kUnderline;
182 }
183
184 RetainPtr<const CPDF_Array> pBorder =
185 GetAnnotDict()->GetArrayFor(pdfium::annotation::kBorder);
186 if (pBorder) {
187 if (pBorder->size() >= 4) {
188 RetainPtr<const CPDF_Array> pDP = pBorder->GetArrayAt(3);
189 if (pDP && pDP->size() > 0)
190 return BorderStyle::kDash;
191 }
192 }
193
194 return BorderStyle::kSolid;
195 }
196
IsVisible() const197 bool CPDFSDK_BAAnnot::IsVisible() const {
198 uint32_t nFlags = GetFlags();
199 return !((nFlags & pdfium::annotation_flags::kInvisible) ||
200 (nFlags & pdfium::annotation_flags::kHidden) ||
201 (nFlags & pdfium::annotation_flags::kNoView));
202 }
203
GetAction() const204 CPDF_Action CPDFSDK_BAAnnot::GetAction() const {
205 return CPDF_Action(GetAnnotDict()->GetDictFor("A"));
206 }
207
GetAAction() const208 CPDF_AAction CPDFSDK_BAAnnot::GetAAction() const {
209 return CPDF_AAction(GetAnnotDict()->GetDictFor(pdfium::form_fields::kAA));
210 }
211
GetAAction(CPDF_AAction::AActionType eAAT)212 CPDF_Action CPDFSDK_BAAnnot::GetAAction(CPDF_AAction::AActionType eAAT) {
213 CPDF_AAction AAction = GetAAction();
214 if (AAction.ActionExist(eAAT))
215 return AAction.GetAction(eAAT);
216
217 if (eAAT == CPDF_AAction::kButtonUp || eAAT == CPDF_AAction::kKeyStroke)
218 return GetAction();
219
220 return CPDF_Action(nullptr);
221 }
222
SetOpenState(bool bOpenState)223 void CPDFSDK_BAAnnot::SetOpenState(bool bOpenState) {
224 m_pAnnot->SetPopupAnnotOpenState(bOpenState);
225 }
226
UpdateAnnotRects()227 void CPDFSDK_BAAnnot::UpdateAnnotRects() {
228 std::vector<CFX_FloatRect> rects;
229 rects.push_back(GetRect());
230
231 absl::optional<CFX_FloatRect> annot_rect = m_pAnnot->GetPopupAnnotRect();
232 if (annot_rect.has_value())
233 rects.push_back(annot_rect.value());
234
235 // Make the rects round up to avoid https://crbug.com/662804
236 for (CFX_FloatRect& rect : rects)
237 rect.Inflate(1, 1);
238
239 GetPageView()->UpdateRects(rects);
240 }
241
InvalidateRect()242 void CPDFSDK_BAAnnot::InvalidateRect() {
243 CFX_FloatRect view_bounding_box = GetViewBBox();
244 if (view_bounding_box.IsEmpty())
245 return;
246
247 view_bounding_box.Inflate(1, 1);
248 view_bounding_box.Normalize();
249 FX_RECT rect = view_bounding_box.GetOuterRect();
250 GetPageView()->GetFormFillEnv()->Invalidate(GetPage(), rect);
251 }
252
GetLayoutOrder() const253 int CPDFSDK_BAAnnot::GetLayoutOrder() const {
254 if (m_pAnnot->GetSubtype() == CPDF_Annot::Subtype::POPUP)
255 return 1;
256
257 return CPDFSDK_Annot::GetLayoutOrder();
258 }
259
OnDraw(CFX_RenderDevice * pDevice,const CFX_Matrix & mtUser2Device,bool bDrawAnnots)260 void CPDFSDK_BAAnnot::OnDraw(CFX_RenderDevice* pDevice,
261 const CFX_Matrix& mtUser2Device,
262 bool bDrawAnnots) {
263 if (!IsVisible())
264 return;
265
266 const CPDF_Annot::Subtype annot_type = GetAnnotSubtype();
267 if (bDrawAnnots && annot_type == CPDF_Annot::Subtype::POPUP) {
268 DrawAppearance(pDevice, mtUser2Device, CPDF_Annot::AppearanceMode::kNormal);
269 return;
270 }
271
272 if (!is_focused_ || !IsFocusableAnnot(annot_type) ||
273 this != GetPageView()->GetFormFillEnv()->GetFocusAnnot()) {
274 return;
275 }
276
277 CFX_FloatRect view_bounding_box = GetViewBBox();
278 if (view_bounding_box.IsEmpty())
279 return;
280
281 view_bounding_box.Normalize();
282 CFX_DrawUtils::DrawFocusRect(pDevice, mtUser2Device, view_bounding_box);
283 }
284
DoHitTest(const CFX_PointF & point)285 bool CPDFSDK_BAAnnot::DoHitTest(const CFX_PointF& point) {
286 return false;
287 }
288
GetViewBBox()289 CFX_FloatRect CPDFSDK_BAAnnot::GetViewBBox() {
290 return GetRect();
291 }
292
OnMouseEnter(Mask<FWL_EVENTFLAG> nFlags)293 void CPDFSDK_BAAnnot::OnMouseEnter(Mask<FWL_EVENTFLAG> nFlags) {
294 SetOpenState(true);
295 UpdateAnnotRects();
296 }
297
OnMouseExit(Mask<FWL_EVENTFLAG> nFlags)298 void CPDFSDK_BAAnnot::OnMouseExit(Mask<FWL_EVENTFLAG> nFlags) {
299 SetOpenState(false);
300 UpdateAnnotRects();
301 }
302
OnLButtonDown(Mask<FWL_EVENTFLAG> nFlags,const CFX_PointF & point)303 bool CPDFSDK_BAAnnot::OnLButtonDown(Mask<FWL_EVENTFLAG> nFlags,
304 const CFX_PointF& point) {
305 return false;
306 }
307
OnLButtonUp(Mask<FWL_EVENTFLAG> nFlags,const CFX_PointF & point)308 bool CPDFSDK_BAAnnot::OnLButtonUp(Mask<FWL_EVENTFLAG> nFlags,
309 const CFX_PointF& point) {
310 return false;
311 }
312
OnLButtonDblClk(Mask<FWL_EVENTFLAG> nFlags,const CFX_PointF & point)313 bool CPDFSDK_BAAnnot::OnLButtonDblClk(Mask<FWL_EVENTFLAG> nFlags,
314 const CFX_PointF& point) {
315 return false;
316 }
317
OnMouseMove(Mask<FWL_EVENTFLAG> nFlags,const CFX_PointF & point)318 bool CPDFSDK_BAAnnot::OnMouseMove(Mask<FWL_EVENTFLAG> nFlags,
319 const CFX_PointF& point) {
320 return false;
321 }
322
OnMouseWheel(Mask<FWL_EVENTFLAG> nFlags,const CFX_PointF & point,const CFX_Vector & delta)323 bool CPDFSDK_BAAnnot::OnMouseWheel(Mask<FWL_EVENTFLAG> nFlags,
324 const CFX_PointF& point,
325 const CFX_Vector& delta) {
326 return false;
327 }
328
OnRButtonDown(Mask<FWL_EVENTFLAG> nFlags,const CFX_PointF & point)329 bool CPDFSDK_BAAnnot::OnRButtonDown(Mask<FWL_EVENTFLAG> nFlags,
330 const CFX_PointF& point) {
331 return false;
332 }
333
OnRButtonUp(Mask<FWL_EVENTFLAG> nFlags,const CFX_PointF & point)334 bool CPDFSDK_BAAnnot::OnRButtonUp(Mask<FWL_EVENTFLAG> nFlags,
335 const CFX_PointF& point) {
336 return false;
337 }
338
OnChar(uint32_t nChar,Mask<FWL_EVENTFLAG> nFlags)339 bool CPDFSDK_BAAnnot::OnChar(uint32_t nChar, Mask<FWL_EVENTFLAG> nFlags) {
340 return false;
341 }
342
OnKeyDown(FWL_VKEYCODE nKeyCode,Mask<FWL_EVENTFLAG> nFlags)343 bool CPDFSDK_BAAnnot::OnKeyDown(FWL_VKEYCODE nKeyCode,
344 Mask<FWL_EVENTFLAG> nFlags) {
345 // OnKeyDown() is implemented only for link annotations for now. As
346 // OnKeyDown() is implemented for other subtypes, following check should be
347 // modified.
348 if (nKeyCode != FWL_VKEY_Return ||
349 GetAnnotSubtype() != CPDF_Annot::Subtype::LINK) {
350 return false;
351 }
352
353 CPDF_Action action = GetAAction(CPDF_AAction::kKeyStroke);
354 CPDFSDK_FormFillEnvironment* env = GetPageView()->GetFormFillEnv();
355 if (action.HasDict()) {
356 return env->DoActionLink(action, CPDF_AAction::kKeyStroke, nFlags);
357 }
358
359 return env->DoActionDestination(GetDestination());
360 }
361
OnSetFocus(Mask<FWL_EVENTFLAG> nFlags)362 bool CPDFSDK_BAAnnot::OnSetFocus(Mask<FWL_EVENTFLAG> nFlags) {
363 if (!IsFocusableAnnot(GetAnnotSubtype()))
364 return false;
365
366 is_focused_ = true;
367 InvalidateRect();
368 return true;
369 }
370
OnKillFocus(Mask<FWL_EVENTFLAG> nFlags)371 bool CPDFSDK_BAAnnot::OnKillFocus(Mask<FWL_EVENTFLAG> nFlags) {
372 if (!IsFocusableAnnot(GetAnnotSubtype()))
373 return false;
374
375 is_focused_ = false;
376 InvalidateRect();
377 return true;
378 }
379
CanUndo()380 bool CPDFSDK_BAAnnot::CanUndo() {
381 return false;
382 }
383
CanRedo()384 bool CPDFSDK_BAAnnot::CanRedo() {
385 return false;
386 }
387
Undo()388 bool CPDFSDK_BAAnnot::Undo() {
389 return false;
390 }
391
Redo()392 bool CPDFSDK_BAAnnot::Redo() {
393 return false;
394 }
395
GetText()396 WideString CPDFSDK_BAAnnot::GetText() {
397 return WideString();
398 }
399
GetSelectedText()400 WideString CPDFSDK_BAAnnot::GetSelectedText() {
401 return WideString();
402 }
403
ReplaceAndKeepSelection(const WideString & text)404 void CPDFSDK_BAAnnot::ReplaceAndKeepSelection(const WideString& text) {}
405
ReplaceSelection(const WideString & text)406 void CPDFSDK_BAAnnot::ReplaceSelection(const WideString& text) {}
407
SelectAllText()408 bool CPDFSDK_BAAnnot::SelectAllText() {
409 return false;
410 }
411
SetIndexSelected(int index,bool selected)412 bool CPDFSDK_BAAnnot::SetIndexSelected(int index, bool selected) {
413 return false;
414 }
415
IsIndexSelected(int index)416 bool CPDFSDK_BAAnnot::IsIndexSelected(int index) {
417 return false;
418 }
419
GetDestination() const420 CPDF_Dest CPDFSDK_BAAnnot::GetDestination() const {
421 if (m_pAnnot->GetSubtype() != CPDF_Annot::Subtype::LINK)
422 return CPDF_Dest(nullptr);
423
424 // Link annotations can have "Dest" entry defined as an explicit array.
425 // See ISO 32000-1:2008 spec, section 12.3.2.1.
426 return CPDF_Dest::Create(GetPageView()->GetPDFDocument(),
427 GetAnnotDict()->GetDirectObjectFor("Dest"));
428 }
429