1 // Copyright 2014 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/include/fpdfapi/fpdf_pageobj.h"
8 #include "core/include/fpdfdoc/fpdf_doc.h"
9 
CPDF_AnnotList(CPDF_Page * pPage)10 CPDF_AnnotList::CPDF_AnnotList(CPDF_Page* pPage)
11     : m_pDocument(pPage->m_pDocument) {
12   if (!pPage->m_pFormDict)
13     return;
14 
15   CPDF_Array* pAnnots = pPage->m_pFormDict->GetArray("Annots");
16   if (!pAnnots)
17     return;
18 
19   CPDF_Dictionary* pRoot = m_pDocument->GetRoot();
20   CPDF_Dictionary* pAcroForm = pRoot->GetDict("AcroForm");
21   FX_BOOL bRegenerateAP = pAcroForm && pAcroForm->GetBoolean("NeedAppearances");
22   for (FX_DWORD i = 0; i < pAnnots->GetCount(); ++i) {
23     CPDF_Dictionary* pDict = ToDictionary(pAnnots->GetElementValue(i));
24     if (!pDict)
25       continue;
26 
27     FX_DWORD dwObjNum = pDict->GetObjNum();
28     if (dwObjNum == 0) {
29       dwObjNum = m_pDocument->AddIndirectObject(pDict);
30       CPDF_Reference* pAction = new CPDF_Reference(m_pDocument, dwObjNum);
31       pAnnots->InsertAt(i, pAction);
32       pAnnots->RemoveAt(i + 1);
33       pDict = pAnnots->GetDict(i);
34     }
35     m_AnnotList.push_back(new CPDF_Annot(pDict, this));
36     if (bRegenerateAP && pDict->GetConstString("Subtype") == "Widget" &&
37         CPDF_InterForm::UpdatingAPEnabled()) {
38       FPDF_GenerateAP(m_pDocument, pDict);
39     }
40   }
41 }
42 
~CPDF_AnnotList()43 CPDF_AnnotList::~CPDF_AnnotList() {
44   for (CPDF_Annot* annot : m_AnnotList)
45     delete annot;
46 }
47 
DisplayPass(CPDF_Page * pPage,CFX_RenderDevice * pDevice,CPDF_RenderContext * pContext,FX_BOOL bPrinting,CFX_Matrix * pMatrix,FX_BOOL bWidgetPass,CPDF_RenderOptions * pOptions,FX_RECT * clip_rect)48 void CPDF_AnnotList::DisplayPass(CPDF_Page* pPage,
49                                  CFX_RenderDevice* pDevice,
50                                  CPDF_RenderContext* pContext,
51                                  FX_BOOL bPrinting,
52                                  CFX_Matrix* pMatrix,
53                                  FX_BOOL bWidgetPass,
54                                  CPDF_RenderOptions* pOptions,
55                                  FX_RECT* clip_rect) {
56   for (CPDF_Annot* pAnnot : m_AnnotList) {
57     FX_BOOL bWidget = pAnnot->GetSubType() == "Widget";
58     if ((bWidgetPass && !bWidget) || (!bWidgetPass && bWidget))
59       continue;
60 
61     FX_DWORD annot_flags = pAnnot->GetFlags();
62     if (annot_flags & ANNOTFLAG_HIDDEN)
63       continue;
64 
65     if (bPrinting && (annot_flags & ANNOTFLAG_PRINT) == 0)
66       continue;
67 
68     if (!bPrinting && (annot_flags & ANNOTFLAG_NOVIEW))
69       continue;
70 
71     if (pOptions) {
72       IPDF_OCContext* pOCContext = pOptions->m_pOCContext;
73       CPDF_Dictionary* pAnnotDict = pAnnot->GetAnnotDict();
74       if (pOCContext && pAnnotDict &&
75           !pOCContext->CheckOCGVisible(pAnnotDict->GetDict("OC"))) {
76         continue;
77       }
78     }
79     CPDF_Rect annot_rect_f;
80     pAnnot->GetRect(annot_rect_f);
81     CFX_Matrix matrix = *pMatrix;
82     if (clip_rect) {
83       annot_rect_f.Transform(&matrix);
84       FX_RECT annot_rect = annot_rect_f.GetOutterRect();
85       annot_rect.Intersect(*clip_rect);
86       if (annot_rect.IsEmpty()) {
87         continue;
88       }
89     }
90     if (pContext) {
91       pAnnot->DrawInContext(pPage, pContext, &matrix, CPDF_Annot::Normal);
92     } else if (!pAnnot->DrawAppearance(pPage, pDevice, &matrix,
93                                        CPDF_Annot::Normal, pOptions)) {
94       pAnnot->DrawBorder(pDevice, &matrix, pOptions);
95     }
96   }
97 }
98 
DisplayAnnots(CPDF_Page * pPage,CFX_RenderDevice * pDevice,CPDF_RenderContext * pContext,FX_BOOL bPrinting,CFX_Matrix * pUser2Device,FX_DWORD dwAnnotFlags,CPDF_RenderOptions * pOptions,FX_RECT * pClipRect)99 void CPDF_AnnotList::DisplayAnnots(CPDF_Page* pPage,
100                                    CFX_RenderDevice* pDevice,
101                                    CPDF_RenderContext* pContext,
102                                    FX_BOOL bPrinting,
103                                    CFX_Matrix* pUser2Device,
104                                    FX_DWORD dwAnnotFlags,
105                                    CPDF_RenderOptions* pOptions,
106                                    FX_RECT* pClipRect) {
107   if (dwAnnotFlags & 0x01) {
108     DisplayPass(pPage, pDevice, pContext, bPrinting, pUser2Device, FALSE,
109                 pOptions, pClipRect);
110   }
111   if (dwAnnotFlags & 0x02) {
112     DisplayPass(pPage, pDevice, pContext, bPrinting, pUser2Device, TRUE,
113                 pOptions, pClipRect);
114   }
115 }
116 
CPDF_Annot(CPDF_Dictionary * pDict,CPDF_AnnotList * pList)117 CPDF_Annot::CPDF_Annot(CPDF_Dictionary* pDict, CPDF_AnnotList* pList)
118     : m_pAnnotDict(pDict),
119       m_pList(pList),
120       m_sSubtype(m_pAnnotDict->GetConstString("Subtype")) {}
~CPDF_Annot()121 CPDF_Annot::~CPDF_Annot() {
122   ClearCachedAP();
123 }
ClearCachedAP()124 void CPDF_Annot::ClearCachedAP() {
125   for (const auto& pair : m_APMap) {
126     delete pair.second;
127   }
128   m_APMap.clear();
129 }
GetSubType() const130 CFX_ByteString CPDF_Annot::GetSubType() const {
131   return m_sSubtype;
132 }
133 
GetRect(CPDF_Rect & rect) const134 void CPDF_Annot::GetRect(CPDF_Rect& rect) const {
135   if (!m_pAnnotDict) {
136     return;
137   }
138   rect = m_pAnnotDict->GetRect("Rect");
139   rect.Normalize();
140 }
141 
GetFlags() const142 FX_DWORD CPDF_Annot::GetFlags() const {
143   return m_pAnnotDict->GetInteger("F");
144 }
145 
FPDFDOC_GetAnnotAP(CPDF_Dictionary * pAnnotDict,CPDF_Annot::AppearanceMode mode)146 CPDF_Stream* FPDFDOC_GetAnnotAP(CPDF_Dictionary* pAnnotDict,
147                                 CPDF_Annot::AppearanceMode mode) {
148   CPDF_Dictionary* pAP = pAnnotDict->GetDict("AP");
149   if (!pAP) {
150     return NULL;
151   }
152   const FX_CHAR* ap_entry = "N";
153   if (mode == CPDF_Annot::Down)
154     ap_entry = "D";
155   else if (mode == CPDF_Annot::Rollover)
156     ap_entry = "R";
157   if (!pAP->KeyExist(ap_entry))
158     ap_entry = "N";
159 
160   CPDF_Object* psub = pAP->GetElementValue(ap_entry);
161   if (!psub)
162     return nullptr;
163   if (CPDF_Stream* pStream = psub->AsStream())
164     return pStream;
165 
166   if (CPDF_Dictionary* pDict = psub->AsDictionary()) {
167     CFX_ByteString as = pAnnotDict->GetString("AS");
168     if (as.IsEmpty()) {
169       CFX_ByteString value = pAnnotDict->GetString("V");
170       if (value.IsEmpty()) {
171         CPDF_Dictionary* pDict = pAnnotDict->GetDict("Parent");
172         value = pDict ? pDict->GetString("V") : CFX_ByteString();
173       }
174       if (value.IsEmpty() || !pDict->KeyExist(value))
175         as = "Off";
176       else
177         as = value;
178     }
179     return pDict->GetStream(as);
180   }
181   return nullptr;
182 }
183 
GetAPForm(const CPDF_Page * pPage,AppearanceMode mode)184 CPDF_Form* CPDF_Annot::GetAPForm(const CPDF_Page* pPage, AppearanceMode mode) {
185   CPDF_Stream* pStream = FPDFDOC_GetAnnotAP(m_pAnnotDict, mode);
186   if (!pStream)
187     return nullptr;
188 
189   auto it = m_APMap.find(pStream);
190   if (it != m_APMap.end())
191     return it->second;
192 
193   CPDF_Form* pNewForm =
194       new CPDF_Form(m_pList->GetDocument(), pPage->m_pResources, pStream);
195   pNewForm->ParseContent(nullptr, nullptr, nullptr, nullptr);
196   m_APMap[pStream] = pNewForm;
197   return pNewForm;
198 }
199 
FPDFDOC_Annot_GetMatrix(const CPDF_Page * pPage,CPDF_Annot * pAnnot,CPDF_Annot::AppearanceMode mode,const CFX_Matrix * pUser2Device,CFX_Matrix & matrix)200 static CPDF_Form* FPDFDOC_Annot_GetMatrix(const CPDF_Page* pPage,
201                                           CPDF_Annot* pAnnot,
202                                           CPDF_Annot::AppearanceMode mode,
203                                           const CFX_Matrix* pUser2Device,
204                                           CFX_Matrix& matrix) {
205   CPDF_Form* pForm = pAnnot->GetAPForm(pPage, mode);
206   if (!pForm) {
207     return NULL;
208   }
209   CFX_FloatRect form_bbox = pForm->m_pFormDict->GetRect("BBox");
210   CFX_Matrix form_matrix = pForm->m_pFormDict->GetMatrix("Matrix");
211   form_matrix.TransformRect(form_bbox);
212   CPDF_Rect arect;
213   pAnnot->GetRect(arect);
214   matrix.MatchRect(arect, form_bbox);
215   matrix.Concat(*pUser2Device);
216   return pForm;
217 }
DrawAppearance(CPDF_Page * pPage,CFX_RenderDevice * pDevice,const CFX_Matrix * pUser2Device,AppearanceMode mode,const CPDF_RenderOptions * pOptions)218 FX_BOOL CPDF_Annot::DrawAppearance(CPDF_Page* pPage,
219                                    CFX_RenderDevice* pDevice,
220                                    const CFX_Matrix* pUser2Device,
221                                    AppearanceMode mode,
222                                    const CPDF_RenderOptions* pOptions) {
223   CFX_Matrix matrix;
224   CPDF_Form* pForm =
225       FPDFDOC_Annot_GetMatrix(pPage, this, mode, pUser2Device, matrix);
226   if (!pForm) {
227     return FALSE;
228   }
229   CPDF_RenderContext context(pPage);
230   context.DrawObjectList(pDevice, pForm, &matrix, pOptions);
231   return TRUE;
232 }
DrawInContext(const CPDF_Page * pPage,CPDF_RenderContext * pContext,const CFX_Matrix * pUser2Device,AppearanceMode mode)233 FX_BOOL CPDF_Annot::DrawInContext(const CPDF_Page* pPage,
234                                   CPDF_RenderContext* pContext,
235                                   const CFX_Matrix* pUser2Device,
236                                   AppearanceMode mode) {
237   CFX_Matrix matrix;
238   CPDF_Form* pForm =
239       FPDFDOC_Annot_GetMatrix(pPage, this, mode, pUser2Device, matrix);
240   if (!pForm) {
241     return FALSE;
242   }
243   pContext->AppendObjectList(pForm, &matrix);
244   return TRUE;
245 }
DrawBorder(CFX_RenderDevice * pDevice,const CFX_Matrix * pUser2Device,const CPDF_RenderOptions * pOptions)246 void CPDF_Annot::DrawBorder(CFX_RenderDevice* pDevice,
247                             const CFX_Matrix* pUser2Device,
248                             const CPDF_RenderOptions* pOptions) {
249   if (GetSubType() == "Popup") {
250     return;
251   }
252   FX_DWORD annot_flags = GetFlags();
253   if (annot_flags & ANNOTFLAG_HIDDEN) {
254     return;
255   }
256   FX_BOOL bPrinting = pDevice->GetDeviceClass() == FXDC_PRINTER ||
257                       (pOptions && (pOptions->m_Flags & RENDER_PRINTPREVIEW));
258   if (bPrinting && (annot_flags & ANNOTFLAG_PRINT) == 0) {
259     return;
260   }
261   if (!bPrinting && (annot_flags & ANNOTFLAG_NOVIEW)) {
262     return;
263   }
264   CPDF_Dictionary* pBS = m_pAnnotDict->GetDict("BS");
265   char style_char;
266   FX_FLOAT width;
267   CPDF_Array* pDashArray = NULL;
268   if (!pBS) {
269     CPDF_Array* pBorderArray = m_pAnnotDict->GetArray("Border");
270     style_char = 'S';
271     if (pBorderArray) {
272       width = pBorderArray->GetNumber(2);
273       if (pBorderArray->GetCount() == 4) {
274         pDashArray = pBorderArray->GetArray(3);
275         if (!pDashArray) {
276           return;
277         }
278         int nLen = pDashArray->GetCount();
279         int i = 0;
280         for (; i < nLen; ++i) {
281           CPDF_Object* pObj = pDashArray->GetElementValue(i);
282           if (pObj && pObj->GetInteger()) {
283             break;
284           }
285         }
286         if (i == nLen) {
287           return;
288         }
289         style_char = 'D';
290       }
291     } else {
292       width = 1;
293     }
294   } else {
295     CFX_ByteString style = pBS->GetString("S");
296     pDashArray = pBS->GetArray("D");
297     style_char = style[1];
298     width = pBS->GetNumber("W");
299   }
300   if (width <= 0) {
301     return;
302   }
303   CPDF_Array* pColor = m_pAnnotDict->GetArray("C");
304   FX_DWORD argb = 0xff000000;
305   if (pColor) {
306     int R = (int32_t)(pColor->GetNumber(0) * 255);
307     int G = (int32_t)(pColor->GetNumber(1) * 255);
308     int B = (int32_t)(pColor->GetNumber(2) * 255);
309     argb = ArgbEncode(0xff, R, G, B);
310   }
311   CPDF_GraphStateData graph_state;
312   graph_state.m_LineWidth = width;
313   if (style_char == 'D') {
314     if (pDashArray) {
315       FX_DWORD dash_count = pDashArray->GetCount();
316       if (dash_count % 2) {
317         dash_count++;
318       }
319       graph_state.m_DashArray = FX_Alloc(FX_FLOAT, dash_count);
320       graph_state.m_DashCount = dash_count;
321       FX_DWORD i;
322       for (i = 0; i < pDashArray->GetCount(); ++i) {
323         graph_state.m_DashArray[i] = pDashArray->GetNumber(i);
324       }
325       if (i < dash_count) {
326         graph_state.m_DashArray[i] = graph_state.m_DashArray[i - 1];
327       }
328     } else {
329       graph_state.m_DashArray = FX_Alloc(FX_FLOAT, 2);
330       graph_state.m_DashCount = 2;
331       graph_state.m_DashArray[0] = graph_state.m_DashArray[1] = 3 * 1.0f;
332     }
333   }
334   CFX_FloatRect rect;
335   GetRect(rect);
336   CPDF_PathData path;
337   width /= 2;
338   path.AppendRect(rect.left + width, rect.bottom + width, rect.right - width,
339                   rect.top - width);
340   int fill_type = 0;
341   if (pOptions && (pOptions->m_Flags & RENDER_NOPATHSMOOTH)) {
342     fill_type |= FXFILL_NOPATHSMOOTH;
343   }
344   pDevice->DrawPath(&path, pUser2Device, &graph_state, argb, argb, fill_type);
345 }
346