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