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