1 // Copyright 2016 PDFium Authors. All rights reserved.
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 "core/fpdfdoc/cpdf_annot.h"
8
9 #include <utility>
10
11 #include "core/fpdfapi/page/cpdf_form.h"
12 #include "core/fpdfapi/page/cpdf_page.h"
13 #include "core/fpdfapi/parser/cpdf_array.h"
14 #include "core/fpdfapi/parser/cpdf_boolean.h"
15 #include "core/fpdfapi/parser/cpdf_document.h"
16 #include "core/fpdfapi/render/cpdf_rendercontext.h"
17 #include "core/fpdfapi/render/cpdf_renderoptions.h"
18 #include "core/fpdfdoc/cpvt_generateap.h"
19 #include "core/fxcrt/fx_memory.h"
20 #include "core/fxge/cfx_graphstatedata.h"
21 #include "core/fxge/cfx_pathdata.h"
22 #include "core/fxge/cfx_renderdevice.h"
23 #include "third_party/base/ptr_util.h"
24
25 namespace {
26
27 char kPDFiumKey_HasGeneratedAP[] = "PDFIUM_HasGeneratedAP";
28
IsTextMarkupAnnotation(CPDF_Annot::Subtype type)29 bool IsTextMarkupAnnotation(CPDF_Annot::Subtype type) {
30 return type == CPDF_Annot::Subtype::HIGHLIGHT ||
31 type == CPDF_Annot::Subtype::SQUIGGLY ||
32 type == CPDF_Annot::Subtype::STRIKEOUT ||
33 type == CPDF_Annot::Subtype::UNDERLINE;
34 }
35
ShouldGenerateAPForAnnotation(CPDF_Dictionary * pAnnotDict)36 bool ShouldGenerateAPForAnnotation(CPDF_Dictionary* pAnnotDict) {
37 // If AP dictionary exists and defines an appearance for normal mode, we use
38 // the appearance defined in the existing AP dictionary.
39 CPDF_Dictionary* pAP = pAnnotDict->GetDictFor("AP");
40 if (pAP && pAP->GetDictFor("N"))
41 return false;
42
43 return !CPDF_Annot::IsAnnotationHidden(pAnnotDict);
44 }
45
AnnotGetMatrix(const CPDF_Page * pPage,CPDF_Annot * pAnnot,CPDF_Annot::AppearanceMode mode,const CFX_Matrix * pUser2Device,CFX_Matrix * matrix)46 CPDF_Form* AnnotGetMatrix(const CPDF_Page* pPage,
47 CPDF_Annot* pAnnot,
48 CPDF_Annot::AppearanceMode mode,
49 const CFX_Matrix* pUser2Device,
50 CFX_Matrix* matrix) {
51 CPDF_Form* pForm = pAnnot->GetAPForm(pPage, mode);
52 if (!pForm)
53 return nullptr;
54
55 CFX_Matrix form_matrix = pForm->m_pFormDict->GetMatrixFor("Matrix");
56 CFX_FloatRect form_bbox =
57 form_matrix.TransformRect(pForm->m_pFormDict->GetRectFor("BBox"));
58 matrix->MatchRect(pAnnot->GetRect(), form_bbox);
59 matrix->Concat(*pUser2Device);
60 return pForm;
61 }
62
FPDFDOC_GetAnnotAPInternal(const CPDF_Dictionary * pAnnotDict,CPDF_Annot::AppearanceMode eMode,bool bFallbackToNormal)63 CPDF_Stream* FPDFDOC_GetAnnotAPInternal(const CPDF_Dictionary* pAnnotDict,
64 CPDF_Annot::AppearanceMode eMode,
65 bool bFallbackToNormal) {
66 CPDF_Dictionary* pAP = pAnnotDict->GetDictFor("AP");
67 if (!pAP)
68 return nullptr;
69
70 const char* ap_entry = "N";
71 if (eMode == CPDF_Annot::Down)
72 ap_entry = "D";
73 else if (eMode == CPDF_Annot::Rollover)
74 ap_entry = "R";
75 if (bFallbackToNormal && !pAP->KeyExist(ap_entry))
76 ap_entry = "N";
77
78 CPDF_Object* psub = pAP->GetDirectObjectFor(ap_entry);
79 if (!psub)
80 return nullptr;
81 if (CPDF_Stream* pStream = psub->AsStream())
82 return pStream;
83
84 CPDF_Dictionary* pDict = psub->AsDictionary();
85 if (!pDict)
86 return nullptr;
87
88 ByteString as = pAnnotDict->GetStringFor("AS");
89 if (as.IsEmpty()) {
90 ByteString value = pAnnotDict->GetStringFor("V");
91 if (value.IsEmpty()) {
92 CPDF_Dictionary* pParentDict = pAnnotDict->GetDictFor("Parent");
93 value = pParentDict ? pParentDict->GetStringFor("V") : ByteString();
94 }
95 as = (!value.IsEmpty() && pDict->KeyExist(value)) ? value : "Off";
96 }
97 return pDict->GetStreamFor(as);
98 }
99
100 } // namespace
101
CPDF_Annot(std::unique_ptr<CPDF_Dictionary> pDict,CPDF_Document * pDocument)102 CPDF_Annot::CPDF_Annot(std::unique_ptr<CPDF_Dictionary> pDict,
103 CPDF_Document* pDocument)
104 : m_pAnnotDict(std::move(pDict)), m_pDocument(pDocument) {
105 Init();
106 }
107
CPDF_Annot(CPDF_Dictionary * pDict,CPDF_Document * pDocument)108 CPDF_Annot::CPDF_Annot(CPDF_Dictionary* pDict, CPDF_Document* pDocument)
109 : m_pAnnotDict(pDict), m_pDocument(pDocument) {
110 Init();
111 }
112
~CPDF_Annot()113 CPDF_Annot::~CPDF_Annot() {
114 ClearCachedAP();
115 }
116
Init()117 void CPDF_Annot::Init() {
118 m_nSubtype = StringToAnnotSubtype(m_pAnnotDict->GetStringFor("Subtype"));
119 m_bIsTextMarkupAnnotation = IsTextMarkupAnnotation(m_nSubtype);
120 m_bHasGeneratedAP = m_pAnnotDict->GetBooleanFor(kPDFiumKey_HasGeneratedAP);
121 GenerateAPIfNeeded();
122 }
123
GenerateAPIfNeeded()124 void CPDF_Annot::GenerateAPIfNeeded() {
125 if (!ShouldGenerateAPForAnnotation(m_pAnnotDict.Get()))
126 return;
127 if (!CPVT_GenerateAP::GenerateAnnotAP(m_nSubtype, m_pDocument.Get(),
128 m_pAnnotDict.Get())) {
129 return;
130 }
131
132 m_pAnnotDict->SetNewFor<CPDF_Boolean>(kPDFiumKey_HasGeneratedAP, true);
133 m_bHasGeneratedAP = true;
134 }
135
ShouldDrawAnnotation()136 bool CPDF_Annot::ShouldDrawAnnotation() {
137 if (IsAnnotationHidden(m_pAnnotDict.Get()))
138 return false;
139
140 if (m_nSubtype == CPDF_Annot::Subtype::POPUP && !m_bOpenState)
141 return false;
142
143 return true;
144 }
145
ClearCachedAP()146 void CPDF_Annot::ClearCachedAP() {
147 m_APMap.clear();
148 }
149
GetSubtype() const150 CPDF_Annot::Subtype CPDF_Annot::GetSubtype() const {
151 return m_nSubtype;
152 }
153
RectForDrawing() const154 CFX_FloatRect CPDF_Annot::RectForDrawing() const {
155 if (!m_pAnnotDict)
156 return CFX_FloatRect();
157
158 bool bShouldUseQuadPointsCoords =
159 m_bIsTextMarkupAnnotation && m_bHasGeneratedAP;
160 if (bShouldUseQuadPointsCoords)
161 return RectFromQuadPoints(m_pAnnotDict.Get());
162
163 return m_pAnnotDict->GetRectFor("Rect");
164 }
165
GetRect() const166 CFX_FloatRect CPDF_Annot::GetRect() const {
167 if (!m_pAnnotDict)
168 return CFX_FloatRect();
169
170 CFX_FloatRect rect = RectForDrawing();
171 rect.Normalize();
172 return rect;
173 }
174
GetFlags() const175 uint32_t CPDF_Annot::GetFlags() const {
176 return m_pAnnotDict->GetIntegerFor("F");
177 }
178
FPDFDOC_GetAnnotAP(const CPDF_Dictionary * pAnnotDict,CPDF_Annot::AppearanceMode eMode)179 CPDF_Stream* FPDFDOC_GetAnnotAP(const CPDF_Dictionary* pAnnotDict,
180 CPDF_Annot::AppearanceMode eMode) {
181 return FPDFDOC_GetAnnotAPInternal(pAnnotDict, eMode, true);
182 }
183
FPDFDOC_GetAnnotAPNoFallback(const CPDF_Dictionary * pAnnotDict,CPDF_Annot::AppearanceMode eMode)184 CPDF_Stream* FPDFDOC_GetAnnotAPNoFallback(const CPDF_Dictionary* pAnnotDict,
185 CPDF_Annot::AppearanceMode eMode) {
186 return FPDFDOC_GetAnnotAPInternal(pAnnotDict, eMode, false);
187 }
188
GetAPForm(const CPDF_Page * pPage,AppearanceMode mode)189 CPDF_Form* CPDF_Annot::GetAPForm(const CPDF_Page* pPage, AppearanceMode mode) {
190 CPDF_Stream* pStream = FPDFDOC_GetAnnotAP(m_pAnnotDict.Get(), mode);
191 if (!pStream)
192 return nullptr;
193
194 auto it = m_APMap.find(pStream);
195 if (it != m_APMap.end())
196 return it->second.get();
197
198 auto pNewForm = pdfium::MakeUnique<CPDF_Form>(
199 m_pDocument.Get(), pPage->m_pResources.Get(), pStream);
200 pNewForm->ParseContent();
201
202 CPDF_Form* pResult = pNewForm.get();
203 m_APMap[pStream] = std::move(pNewForm);
204 return pResult;
205 }
206
207 // Static.
RectFromQuadPoints(CPDF_Dictionary * pAnnotDict)208 CFX_FloatRect CPDF_Annot::RectFromQuadPoints(CPDF_Dictionary* pAnnotDict) {
209 CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints");
210 if (!pArray)
211 return CFX_FloatRect();
212
213 // QuadPoints are defined with 4 pairs of numbers
214 // ([ pair0, pair1, pair2, pair3 ]), where
215 // pair0 = top_left
216 // pair1 = top_right
217 // pair2 = bottom_left
218 // pair3 = bottom_right
219 //
220 // On the other hand, /Rect is define as 2 pairs [pair0, pair1] where:
221 // pair0 = bottom_left
222 // pair1 = top_right.
223 return CFX_FloatRect(pArray->GetNumberAt(4), pArray->GetNumberAt(5),
224 pArray->GetNumberAt(2), pArray->GetNumberAt(3));
225 }
226
227 // Static.
IsAnnotationHidden(CPDF_Dictionary * pAnnotDict)228 bool CPDF_Annot::IsAnnotationHidden(CPDF_Dictionary* pAnnotDict) {
229 return !!(pAnnotDict->GetIntegerFor("F") & ANNOTFLAG_HIDDEN);
230 }
231
232 // Static.
StringToAnnotSubtype(const ByteString & sSubtype)233 CPDF_Annot::Subtype CPDF_Annot::StringToAnnotSubtype(
234 const ByteString& sSubtype) {
235 if (sSubtype == "Text")
236 return CPDF_Annot::Subtype::TEXT;
237 if (sSubtype == "Link")
238 return CPDF_Annot::Subtype::LINK;
239 if (sSubtype == "FreeText")
240 return CPDF_Annot::Subtype::FREETEXT;
241 if (sSubtype == "Line")
242 return CPDF_Annot::Subtype::LINE;
243 if (sSubtype == "Square")
244 return CPDF_Annot::Subtype::SQUARE;
245 if (sSubtype == "Circle")
246 return CPDF_Annot::Subtype::CIRCLE;
247 if (sSubtype == "Polygon")
248 return CPDF_Annot::Subtype::POLYGON;
249 if (sSubtype == "PolyLine")
250 return CPDF_Annot::Subtype::POLYLINE;
251 if (sSubtype == "Highlight")
252 return CPDF_Annot::Subtype::HIGHLIGHT;
253 if (sSubtype == "Underline")
254 return CPDF_Annot::Subtype::UNDERLINE;
255 if (sSubtype == "Squiggly")
256 return CPDF_Annot::Subtype::SQUIGGLY;
257 if (sSubtype == "StrikeOut")
258 return CPDF_Annot::Subtype::STRIKEOUT;
259 if (sSubtype == "Stamp")
260 return CPDF_Annot::Subtype::STAMP;
261 if (sSubtype == "Caret")
262 return CPDF_Annot::Subtype::CARET;
263 if (sSubtype == "Ink")
264 return CPDF_Annot::Subtype::INK;
265 if (sSubtype == "Popup")
266 return CPDF_Annot::Subtype::POPUP;
267 if (sSubtype == "FileAttachment")
268 return CPDF_Annot::Subtype::FILEATTACHMENT;
269 if (sSubtype == "Sound")
270 return CPDF_Annot::Subtype::SOUND;
271 if (sSubtype == "Movie")
272 return CPDF_Annot::Subtype::MOVIE;
273 if (sSubtype == "Widget")
274 return CPDF_Annot::Subtype::WIDGET;
275 if (sSubtype == "Screen")
276 return CPDF_Annot::Subtype::SCREEN;
277 if (sSubtype == "PrinterMark")
278 return CPDF_Annot::Subtype::PRINTERMARK;
279 if (sSubtype == "TrapNet")
280 return CPDF_Annot::Subtype::TRAPNET;
281 if (sSubtype == "Watermark")
282 return CPDF_Annot::Subtype::WATERMARK;
283 if (sSubtype == "3D")
284 return CPDF_Annot::Subtype::THREED;
285 if (sSubtype == "RichMedia")
286 return CPDF_Annot::Subtype::RICHMEDIA;
287 if (sSubtype == "XFAWidget")
288 return CPDF_Annot::Subtype::XFAWIDGET;
289 return CPDF_Annot::Subtype::UNKNOWN;
290 }
291
292 // Static.
AnnotSubtypeToString(CPDF_Annot::Subtype nSubtype)293 ByteString CPDF_Annot::AnnotSubtypeToString(CPDF_Annot::Subtype nSubtype) {
294 if (nSubtype == CPDF_Annot::Subtype::TEXT)
295 return "Text";
296 if (nSubtype == CPDF_Annot::Subtype::LINK)
297 return "Link";
298 if (nSubtype == CPDF_Annot::Subtype::FREETEXT)
299 return "FreeText";
300 if (nSubtype == CPDF_Annot::Subtype::LINE)
301 return "Line";
302 if (nSubtype == CPDF_Annot::Subtype::SQUARE)
303 return "Square";
304 if (nSubtype == CPDF_Annot::Subtype::CIRCLE)
305 return "Circle";
306 if (nSubtype == CPDF_Annot::Subtype::POLYGON)
307 return "Polygon";
308 if (nSubtype == CPDF_Annot::Subtype::POLYLINE)
309 return "PolyLine";
310 if (nSubtype == CPDF_Annot::Subtype::HIGHLIGHT)
311 return "Highlight";
312 if (nSubtype == CPDF_Annot::Subtype::UNDERLINE)
313 return "Underline";
314 if (nSubtype == CPDF_Annot::Subtype::SQUIGGLY)
315 return "Squiggly";
316 if (nSubtype == CPDF_Annot::Subtype::STRIKEOUT)
317 return "StrikeOut";
318 if (nSubtype == CPDF_Annot::Subtype::STAMP)
319 return "Stamp";
320 if (nSubtype == CPDF_Annot::Subtype::CARET)
321 return "Caret";
322 if (nSubtype == CPDF_Annot::Subtype::INK)
323 return "Ink";
324 if (nSubtype == CPDF_Annot::Subtype::POPUP)
325 return "Popup";
326 if (nSubtype == CPDF_Annot::Subtype::FILEATTACHMENT)
327 return "FileAttachment";
328 if (nSubtype == CPDF_Annot::Subtype::SOUND)
329 return "Sound";
330 if (nSubtype == CPDF_Annot::Subtype::MOVIE)
331 return "Movie";
332 if (nSubtype == CPDF_Annot::Subtype::WIDGET)
333 return "Widget";
334 if (nSubtype == CPDF_Annot::Subtype::SCREEN)
335 return "Screen";
336 if (nSubtype == CPDF_Annot::Subtype::PRINTERMARK)
337 return "PrinterMark";
338 if (nSubtype == CPDF_Annot::Subtype::TRAPNET)
339 return "TrapNet";
340 if (nSubtype == CPDF_Annot::Subtype::WATERMARK)
341 return "Watermark";
342 if (nSubtype == CPDF_Annot::Subtype::THREED)
343 return "3D";
344 if (nSubtype == CPDF_Annot::Subtype::RICHMEDIA)
345 return "RichMedia";
346 if (nSubtype == CPDF_Annot::Subtype::XFAWIDGET)
347 return "XFAWidget";
348 return "";
349 }
350
DrawAppearance(CPDF_Page * pPage,CFX_RenderDevice * pDevice,const CFX_Matrix & mtUser2Device,AppearanceMode mode,const CPDF_RenderOptions * pOptions)351 bool CPDF_Annot::DrawAppearance(CPDF_Page* pPage,
352 CFX_RenderDevice* pDevice,
353 const CFX_Matrix& mtUser2Device,
354 AppearanceMode mode,
355 const CPDF_RenderOptions* pOptions) {
356 if (!ShouldDrawAnnotation())
357 return false;
358
359 // It might happen that by the time this annotation instance was created,
360 // it was flagged as "hidden" (e.g. /F 2), and hence CPVT_GenerateAP decided
361 // to not "generate" its AP.
362 // If for a reason the object is no longer hidden, but still does not have
363 // its "AP" generated, generate it now.
364 GenerateAPIfNeeded();
365
366 CFX_Matrix matrix;
367 CPDF_Form* pForm = AnnotGetMatrix(pPage, this, mode, &mtUser2Device, &matrix);
368 if (!pForm)
369 return false;
370
371 CPDF_RenderContext context(pPage);
372 context.AppendLayer(pForm, &matrix);
373 context.Render(pDevice, pOptions, nullptr);
374 return true;
375 }
376
DrawInContext(const CPDF_Page * pPage,CPDF_RenderContext * pContext,const CFX_Matrix * pUser2Device,AppearanceMode mode)377 bool CPDF_Annot::DrawInContext(const CPDF_Page* pPage,
378 CPDF_RenderContext* pContext,
379 const CFX_Matrix* pUser2Device,
380 AppearanceMode mode) {
381 if (!ShouldDrawAnnotation())
382 return false;
383
384 // It might happen that by the time this annotation instance was created,
385 // it was flagged as "hidden" (e.g. /F 2), and hence CPVT_GenerateAP decided
386 // to not "generate" its AP.
387 // If for a reason the object is no longer hidden, but still does not have
388 // its "AP" generated, generate it now.
389 GenerateAPIfNeeded();
390
391 CFX_Matrix matrix;
392 CPDF_Form* pForm = AnnotGetMatrix(pPage, this, mode, pUser2Device, &matrix);
393 if (!pForm)
394 return false;
395
396 pContext->AppendLayer(pForm, &matrix);
397 return true;
398 }
399
DrawBorder(CFX_RenderDevice * pDevice,const CFX_Matrix * pUser2Device,const CPDF_RenderOptions * pOptions)400 void CPDF_Annot::DrawBorder(CFX_RenderDevice* pDevice,
401 const CFX_Matrix* pUser2Device,
402 const CPDF_RenderOptions* pOptions) {
403 if (GetSubtype() == CPDF_Annot::Subtype::POPUP)
404 return;
405
406 uint32_t annot_flags = GetFlags();
407 if (annot_flags & ANNOTFLAG_HIDDEN) {
408 return;
409 }
410 bool bPrinting = pDevice->GetDeviceClass() == FXDC_PRINTER ||
411 (pOptions && (pOptions->HasFlag(RENDER_PRINTPREVIEW)));
412 if (bPrinting && (annot_flags & ANNOTFLAG_PRINT) == 0) {
413 return;
414 }
415 if (!bPrinting && (annot_flags & ANNOTFLAG_NOVIEW)) {
416 return;
417 }
418 CPDF_Dictionary* pBS = m_pAnnotDict->GetDictFor("BS");
419 char style_char;
420 float width;
421 CPDF_Array* pDashArray = nullptr;
422 if (!pBS) {
423 CPDF_Array* pBorderArray = m_pAnnotDict->GetArrayFor("Border");
424 style_char = 'S';
425 if (pBorderArray) {
426 width = pBorderArray->GetNumberAt(2);
427 if (pBorderArray->GetCount() == 4) {
428 pDashArray = pBorderArray->GetArrayAt(3);
429 if (!pDashArray) {
430 return;
431 }
432 size_t nLen = pDashArray->GetCount();
433 size_t i = 0;
434 for (; i < nLen; ++i) {
435 CPDF_Object* pObj = pDashArray->GetDirectObjectAt(i);
436 if (pObj && pObj->GetInteger()) {
437 break;
438 }
439 }
440 if (i == nLen) {
441 return;
442 }
443 style_char = 'D';
444 }
445 } else {
446 width = 1;
447 }
448 } else {
449 ByteString style = pBS->GetStringFor("S");
450 pDashArray = pBS->GetArrayFor("D");
451 style_char = style[1];
452 width = pBS->GetNumberFor("W");
453 }
454 if (width <= 0) {
455 return;
456 }
457 CPDF_Array* pColor = m_pAnnotDict->GetArrayFor("C");
458 uint32_t argb = 0xff000000;
459 if (pColor) {
460 int R = (int32_t)(pColor->GetNumberAt(0) * 255);
461 int G = (int32_t)(pColor->GetNumberAt(1) * 255);
462 int B = (int32_t)(pColor->GetNumberAt(2) * 255);
463 argb = ArgbEncode(0xff, R, G, B);
464 }
465 CFX_GraphStateData graph_state;
466 graph_state.m_LineWidth = width;
467 if (style_char == 'D') {
468 if (pDashArray) {
469 size_t dash_count = pDashArray->GetCount();
470 if (dash_count % 2) {
471 dash_count++;
472 }
473 graph_state.m_DashArray = FX_Alloc(float, dash_count);
474 graph_state.m_DashCount = dash_count;
475 size_t i;
476 for (i = 0; i < pDashArray->GetCount(); ++i) {
477 graph_state.m_DashArray[i] = pDashArray->GetNumberAt(i);
478 }
479 if (i < dash_count) {
480 graph_state.m_DashArray[i] = graph_state.m_DashArray[i - 1];
481 }
482 } else {
483 graph_state.m_DashArray = FX_Alloc(float, 2);
484 graph_state.m_DashCount = 2;
485 graph_state.m_DashArray[0] = graph_state.m_DashArray[1] = 3 * 1.0f;
486 }
487 }
488 CFX_FloatRect rect = GetRect();
489 CFX_PathData path;
490 width /= 2;
491 path.AppendRect(rect.left + width, rect.bottom + width, rect.right - width,
492 rect.top - width);
493 int fill_type = 0;
494 if (pOptions && (pOptions->HasFlag(RENDER_NOPATHSMOOTH)))
495 fill_type |= FXFILL_NOPATHSMOOTH;
496
497 pDevice->DrawPath(&path, pUser2Device, &graph_state, argb, argb, fill_type);
498 }
499