• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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