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