Replace FX_NEW with new, remove tests in fpdfdoc.
[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 "../../include/fpdfdoc/fpdf_doc.h"
8 #include "../../include/fpdfapi/fpdf_pageobj.h"
9 CPDF_AnnotList::CPDF_AnnotList(CPDF_Page* pPage)
10 {
11     ASSERT(pPage != NULL);
12     m_pPageDict = pPage->m_pFormDict;
13     if (m_pPageDict == NULL) {
14         return;
15     }
16     m_pDocument = pPage->m_pDocument;
17     CPDF_Array* pAnnots = m_pPageDict->GetArray("Annots");
18     if (pAnnots == NULL) {
19         return;
20     }
21     CPDF_Dictionary* pRoot = m_pDocument->GetRoot();
22     CPDF_Dictionary* pAcroForm = pRoot->GetDict("AcroForm");
23     FX_BOOL bRegenerateAP = pAcroForm && pAcroForm->GetBoolean("NeedAppearances");
24     for (FX_DWORD i = 0; i < pAnnots->GetCount(); i ++) {
25         CPDF_Dictionary* pDict = (CPDF_Dictionary*)pAnnots->GetElementValue(i);
26         if (pDict == NULL || pDict->GetType() != PDFOBJ_DICTIONARY) {
27             continue;
28         }
29         FX_DWORD dwObjNum = pDict->GetObjNum();
30         if (dwObjNum == 0) {
31             dwObjNum = m_pDocument->AddIndirectObject(pDict);
32             CPDF_Reference* pAction = CPDF_Reference::Create(m_pDocument, dwObjNum);
33             if (pAction == NULL) {
34                 break;
35             }
36             pAnnots->InsertAt(i, pAction);
37             pAnnots->RemoveAt(i + 1);
38             pDict = pAnnots->GetDict(i);
39         }
40         CPDF_Annot* pAnnot = new CPDF_Annot(pDict);
41         pAnnot->m_pList = this;
42         m_AnnotList.Add(pAnnot);
43         if (bRegenerateAP && pDict->GetConstString(FX_BSTRC("Subtype")) == FX_BSTRC("Widget"))
44             if (CPDF_InterForm::UpdatingAPEnabled()) {
45                 FPDF_GenerateAP(m_pDocument, pDict);
46             }
47     }
48 }
49 CPDF_AnnotList::~CPDF_AnnotList()
50 {
51     int i = 0;
52     for (i = 0; i < m_AnnotList.GetSize(); i ++) {
53         delete (CPDF_Annot*)m_AnnotList[i];
54     }
55     for (i = 0; i < m_Borders.GetSize(); ++i) {
56         delete (CPDF_PageObjects*)m_Borders[i];
57     }
58 }
59 void CPDF_AnnotList::DisplayPass(const CPDF_Page* pPage, CFX_RenderDevice* pDevice,
60                                  CPDF_RenderContext* pContext, FX_BOOL bPrinting, CFX_AffineMatrix* pMatrix,
61                                  FX_BOOL bWidgetPass, CPDF_RenderOptions* pOptions, FX_RECT* clip_rect)
62 {
63     for (int i = 0; i < m_AnnotList.GetSize(); i ++) {
64         CPDF_Annot* pAnnot = (CPDF_Annot*)m_AnnotList[i];
65         FX_BOOL bWidget = pAnnot->GetSubType() == "Widget";
66         if ((bWidgetPass && !bWidget) || (!bWidgetPass && bWidget)) {
67             continue;
68         }
69         FX_DWORD annot_flags = pAnnot->GetFlags();
70         if (annot_flags & ANNOTFLAG_HIDDEN) {
71             continue;
72         }
73         if (bPrinting && (annot_flags & ANNOTFLAG_PRINT) == 0) {
74             continue;
75         }
76         if (!bPrinting && (annot_flags & ANNOTFLAG_NOVIEW)) {
77             continue;
78         }
79         if (pOptions != NULL) {
80             IPDF_OCContext* pOCContext = pOptions->m_pOCContext;
81             CPDF_Dictionary* pAnnotDict = pAnnot->m_pAnnotDict;
82             if (pOCContext != NULL && pAnnotDict != NULL &&
83                     !pOCContext->CheckOCGVisible(pAnnotDict->GetDict(FX_BSTRC("OC")))) {
84                 continue;
85             }
86         }
87         CPDF_Rect annot_rect_f;
88         pAnnot->GetRect(annot_rect_f);
89         CFX_Matrix matrix;
90         matrix = *pMatrix;
91         if (clip_rect) {
92             annot_rect_f.Transform(&matrix);
93             FX_RECT annot_rect = annot_rect_f.GetOutterRect();
94             annot_rect.Intersect(*clip_rect);
95             if (annot_rect.IsEmpty()) {
96                 continue;
97             }
98         }
99         if (pContext) {
100             pAnnot->DrawInContext(pPage, pContext, &matrix, CPDF_Annot::Normal);
101         } else if (!pAnnot->DrawAppearance(pPage, pDevice, &matrix, CPDF_Annot::Normal, pOptions)) {
102             pAnnot->DrawBorder(pDevice, &matrix, pOptions);
103         }
104     }
105 }
106 void CPDF_AnnotList::DisplayAnnots(const CPDF_Page* pPage, CFX_RenderDevice* pDevice,
107                                    CFX_AffineMatrix* pUser2Device,
108                                    FX_BOOL bShowWidget, CPDF_RenderOptions* pOptions)
109 {
110     FX_RECT clip_rect;
111     if (pDevice) {
112         clip_rect = pDevice->GetClipBox();
113     }
114     FX_BOOL bPrinting = pDevice->GetDeviceClass() == FXDC_PRINTER || (pOptions && (pOptions->m_Flags & RENDER_PRINTPREVIEW));
115     DisplayAnnots(pPage, pDevice, NULL, bPrinting, pUser2Device, bShowWidget ? 3 : 1, pOptions, &clip_rect);
116 }
117 void CPDF_AnnotList::DisplayAnnots(const CPDF_Page* pPage, CFX_RenderDevice* pDevice, CPDF_RenderContext* pContext,
118                                    FX_BOOL bPrinting, CFX_AffineMatrix* pUser2Device, FX_DWORD dwAnnotFlags,
119                                    CPDF_RenderOptions* pOptions, FX_RECT* pClipRect)
120 {
121     if (dwAnnotFlags & 0x01) {
122         DisplayPass(pPage, pDevice, pContext, bPrinting, pUser2Device, FALSE, pOptions, pClipRect);
123     }
124     if (dwAnnotFlags & 0x02) {
125         DisplayPass(pPage, pDevice, pContext, bPrinting, pUser2Device, TRUE, pOptions, pClipRect);
126     }
127 }
128 int CPDF_AnnotList::GetIndex(CPDF_Annot* pAnnot)
129 {
130     for (int i = 0; i < m_AnnotList.GetSize(); i ++)
131         if (m_AnnotList[i] == (FX_LPVOID)pAnnot) {
132             return i;
133         }
134     return -1;
135 }
136 CPDF_Annot::CPDF_Annot(CPDF_Dictionary* pDict)
137 {
138     m_pList = NULL;
139     m_pAnnotDict = pDict;
140 }
141 CPDF_Annot::~CPDF_Annot()
142 {
143     ClearCachedAP();
144 }
145 CPDF_Reference* CPDF_Annot::NewAnnotRef()
146 {
147     if (m_pAnnotDict->GetObjNum() == 0) {
148         m_pList->m_pDocument->AddIndirectObject(m_pAnnotDict);
149     }
150     return CPDF_Reference::Create(m_pList->m_pDocument, m_pAnnotDict->GetObjNum());
151 }
152 void CPDF_Annot::ClearCachedAP()
153 {
154     FX_POSITION pos = m_APMap.GetStartPosition();
155     while (pos) {
156         void* pForm;
157         void* pObjects;
158         m_APMap.GetNextAssoc(pos, pForm, pObjects);
159         delete (CPDF_PageObjects*)pObjects;
160     }
161     m_APMap.RemoveAll();
162 }
163 CFX_ByteString CPDF_Annot::GetSubType() const
164 {
165     return m_pAnnotDict ? m_pAnnotDict->GetConstString(FX_BSTRC("Subtype")) : CFX_ByteStringC();
166 }
167 void CPDF_Annot::GetRect(CPDF_Rect& rect) const
168 {
169     if (m_pAnnotDict == NULL) {
170         return;
171     }
172     rect = m_pAnnotDict->GetRect("Rect");
173     rect.Normalize();
174 }
175 CPDF_Stream* FPDFDOC_GetAnnotAP(CPDF_Dictionary* pAnnotDict, CPDF_Annot::AppearanceMode mode)
176 {
177     CPDF_Dictionary* pAP = pAnnotDict->GetDict("AP");
178     if (pAP == NULL) {
179         return NULL;
180     }
181     const FX_CHAR* ap_entry = "N";
182     if (mode == CPDF_Annot::Down) {
183         ap_entry = "D";
184     } else if (mode == CPDF_Annot::Rollover) {
185         ap_entry = "R";
186     }
187     if (!pAP->KeyExist(ap_entry)) {
188         ap_entry = "N";
189     }
190     CPDF_Object* psub = pAP->GetElementValue(ap_entry);
191     if (psub == NULL) {
192         return NULL;
193     }
194     CPDF_Stream* pStream = NULL;
195     if (psub->GetType() == PDFOBJ_STREAM) {
196         pStream = (CPDF_Stream*)psub;
197     } else if (psub->GetType() == PDFOBJ_DICTIONARY) {
198         CFX_ByteString as = pAnnotDict->GetString("AS");
199         if (as.IsEmpty()) {
200             CFX_ByteString value = pAnnotDict->GetString(FX_BSTRC("V"));
201             if (value.IsEmpty()) {
202                 CPDF_Dictionary* pDict = pAnnotDict->GetDict(FX_BSTRC("Parent"));
203                 value = pDict ? pDict->GetString(FX_BSTRC("V")) : CFX_ByteString();
204             }
205             if (value.IsEmpty() || !((CPDF_Dictionary*)psub)->KeyExist(value)) {
206                 as = FX_BSTRC("Off");
207             } else {
208                 as = value;
209             }
210         }
211         pStream = ((CPDF_Dictionary*)psub)->GetStream(as);
212     }
213     return pStream;
214 }
215 CPDF_Form* CPDF_Annot::GetAPForm(const CPDF_Page* pPage, AppearanceMode mode)
216 {
217     CPDF_Stream* pStream = FPDFDOC_GetAnnotAP(m_pAnnotDict, mode);
218     if (pStream == NULL) {
219         return NULL;
220     }
221     CPDF_Form* pForm;
222     if (m_APMap.Lookup(pStream, (void*&)pForm)) {
223         return pForm;
224     }
225     pForm = new CPDF_Form(m_pList->m_pDocument, pPage->m_pResources, pStream);
226     pForm->ParseContent(NULL, NULL, NULL, NULL);
227     m_APMap.SetAt(pStream, pForm);
228     return pForm;
229 }
230 static CPDF_Form* FPDFDOC_Annot_GetMatrix(const CPDF_Page* pPage, CPDF_Annot* pAnnot, CPDF_Annot::AppearanceMode mode, const CFX_AffineMatrix* pUser2Device, CFX_Matrix &matrix)
231 {
232     CPDF_Form* pForm = pAnnot->GetAPForm(pPage, mode);
233     if (!pForm) {
234         return NULL;
235     }
236     CFX_FloatRect form_bbox = pForm->m_pFormDict->GetRect(FX_BSTRC("BBox"));
237     CFX_Matrix form_matrix = pForm->m_pFormDict->GetMatrix(FX_BSTRC("Matrix"));
238     form_matrix.TransformRect(form_bbox);
239     CPDF_Rect arect;
240     pAnnot->GetRect(arect);
241     matrix.MatchRect(arect, form_bbox);
242     matrix.Concat(*pUser2Device);
243     return pForm;
244 }
245 FX_BOOL CPDF_Annot::DrawAppearance(const CPDF_Page* pPage, CFX_RenderDevice* pDevice, const CFX_AffineMatrix* pUser2Device,
246                                    AppearanceMode mode, const CPDF_RenderOptions* pOptions)
247 {
248     CFX_Matrix matrix;
249     CPDF_Form* pForm = FPDFDOC_Annot_GetMatrix(pPage, this, mode, pUser2Device, matrix);
250     if (!pForm) {
251         return FALSE;
252     }
253     CPDF_RenderContext context;
254     context.Create((CPDF_Page*)pPage);
255     context.DrawObjectList(pDevice, pForm, &matrix, pOptions);
256     return TRUE;
257 }
258 FX_BOOL CPDF_Annot::DrawInContext(const CPDF_Page* pPage, const CPDF_RenderContext* pContext, const CFX_AffineMatrix* pUser2Device, AppearanceMode mode)
259 {
260     CFX_Matrix matrix;
261     CPDF_Form* pForm = FPDFDOC_Annot_GetMatrix(pPage, this, mode, pUser2Device, matrix);
262     if (!pForm) {
263         return FALSE;
264     }
265     ((CPDF_RenderContext*)pContext)->AppendObjectList(pForm, &matrix);
266     return TRUE;
267 }
268 CPDF_PageObject* CPDF_Annot::GetBorder(FX_BOOL bPrint, const CPDF_RenderOptions* pOptions)
269 {
270     if (GetSubType() == "Popup") {
271         return NULL;
272     }
273     FX_DWORD annot_flags = GetFlags();
274     if (annot_flags & ANNOTFLAG_HIDDEN) {
275         return NULL;
276     }
277     FX_BOOL bPrinting = bPrint || (pOptions && (pOptions->m_Flags & RENDER_PRINTPREVIEW));
278     if (bPrinting && (annot_flags & ANNOTFLAG_PRINT) == 0) {
279         return NULL;
280     }
281     if (!bPrinting && (annot_flags & ANNOTFLAG_NOVIEW)) {
282         return NULL;
283     }
284     CPDF_Dictionary* pBS = m_pAnnotDict->GetDict("BS");
285     char style_char;
286     FX_FLOAT width;
287     CPDF_Array* pDashArray = NULL;
288     if (pBS == NULL) {
289         CPDF_Array* pBorderArray = m_pAnnotDict->GetArray("Border");
290         style_char = 'S';
291         if (pBorderArray) {
292             width = pBorderArray->GetNumber(2);
293             if (pBorderArray->GetCount() == 4) {
294                 pDashArray = pBorderArray->GetArray(3);
295                 if (pDashArray == NULL) {
296                     return NULL;
297                 }
298                 style_char = 'D';
299             }
300         } else {
301             width = 1;
302         }
303     } else {
304         CFX_ByteString style = pBS->GetString("S");
305         pDashArray = pBS->GetArray("D");
306         style_char = style[1];
307         width = pBS->GetNumber("W");
308     }
309     if (width <= 0) {
310         return NULL;
311     }
312     CPDF_Array* pColor = m_pAnnotDict->GetArray("C");
313     FX_DWORD argb = 0xff000000;
314     if (pColor != NULL) {
315         int R = (FX_INT32)(pColor->GetNumber(0) * 255);
316         int G = (FX_INT32)(pColor->GetNumber(1) * 255);
317         int B = (FX_INT32)(pColor->GetNumber(2) * 255);
318         argb = ArgbEncode(0xff, R, G, B);
319     }
320     CPDF_PathObject *pPathObject = new CPDF_PathObject();
321     CPDF_GraphStateData *pGraphState = pPathObject->m_GraphState.GetModify();
322     if (!pGraphState) {
323         pPathObject->Release();
324         return NULL;
325     }
326     pGraphState->m_LineWidth = width;
327     CPDF_ColorStateData *pColorData = pPathObject->m_ColorState.GetModify();
328     if (!pColorData) {
329         pPathObject->Release();
330         return NULL;
331     }
332     pColorData->m_StrokeRGB = argb;
333     pPathObject->m_bStroke = TRUE;
334     pPathObject->m_FillType = 0;
335     if (style_char == 'D') {
336         if (pDashArray) {
337             FX_DWORD dash_count = pDashArray->GetCount();
338             if (dash_count % 2) {
339                 dash_count ++;
340             }
341             pGraphState->m_DashArray = FX_Alloc(FX_FLOAT, dash_count);
342             if (pGraphState->m_DashArray == NULL) {
343                 pPathObject->Release();
344                 return NULL;
345             }
346             pGraphState->m_DashCount = dash_count;
347             FX_DWORD i;
348             for (i = 0; i < pDashArray->GetCount(); i ++) {
349                 pGraphState->m_DashArray[i] = pDashArray->GetNumber(i);
350             }
351             if (i < dash_count) {
352                 pGraphState->m_DashArray[i] = pGraphState->m_DashArray[i - 1];
353             }
354         } else {
355             pGraphState->m_DashArray = FX_Alloc(FX_FLOAT, 2);
356             if (pGraphState->m_DashArray == NULL) {
357                 pPathObject->Release();
358                 return NULL;
359             }
360             pGraphState->m_DashCount = 2;
361             pGraphState->m_DashArray[0] = pGraphState->m_DashArray[1] = 3 * 1.0f;
362         }
363     }
364     CFX_FloatRect rect;
365     GetRect(rect);
366     width /= 2;
367     CPDF_PathData *pPathData = pPathObject->m_Path.GetModify();
368     if (pPathData) {
369         pPathData->AppendRect(rect.left + width, rect.bottom + width, rect.right - width, rect.top - width);
370     }
371     pPathObject->CalcBoundingBox();
372     return pPathObject;
373 }
374 void CPDF_Annot::DrawBorder(CFX_RenderDevice* pDevice, const CFX_AffineMatrix* pUser2Device, const CPDF_RenderOptions* pOptions)
375 {
376     if (GetSubType() == "Popup") {
377         return;
378     }
379     FX_DWORD annot_flags = GetFlags();
380     if (annot_flags & ANNOTFLAG_HIDDEN) {
381         return;
382     }
383     FX_BOOL bPrinting = pDevice->GetDeviceClass() == FXDC_PRINTER || (pOptions && (pOptions->m_Flags & RENDER_PRINTPREVIEW));
384     if (bPrinting && (annot_flags & ANNOTFLAG_PRINT) == 0) {
385         return;
386     }
387     if (!bPrinting && (annot_flags & ANNOTFLAG_NOVIEW)) {
388         return;
389     }
390     CPDF_Dictionary* pBS = m_pAnnotDict->GetDict("BS");
391     char style_char;
392     FX_FLOAT width;
393     CPDF_Array* pDashArray = NULL;
394     if (pBS == NULL) {
395         CPDF_Array* pBorderArray = m_pAnnotDict->GetArray("Border");
396         style_char = 'S';
397         if (pBorderArray) {
398             width = pBorderArray->GetNumber(2);
399             if (pBorderArray->GetCount() == 4) {
400                 pDashArray = pBorderArray->GetArray(3);
401                 if (pDashArray == NULL) {
402                     return;
403                 }
404                 int nLen = pDashArray->GetCount();
405                 int i = 0;
406                 for (; i < nLen; ++i) {
407                     CPDF_Object*pObj = pDashArray->GetElementValue(i);
408                     if (pObj && pObj->GetInteger()) {
409                         break;
410                     }
411                 }
412                 if (i == nLen) {
413                     return;
414                 }
415                 style_char = 'D';
416             }
417         } else {
418             width = 1;
419         }
420     } else {
421         CFX_ByteString style = pBS->GetString("S");
422         pDashArray = pBS->GetArray("D");
423         style_char = style[1];
424         width = pBS->GetNumber("W");
425     }
426     if (width <= 0) {
427         return;
428     }
429     CPDF_Array* pColor = m_pAnnotDict->GetArray("C");
430     FX_DWORD argb = 0xff000000;
431     if (pColor != NULL) {
432         int R = (FX_INT32)(pColor->GetNumber(0) * 255);
433         int G = (FX_INT32)(pColor->GetNumber(1) * 255);
434         int B = (FX_INT32)(pColor->GetNumber(2) * 255);
435         argb = ArgbEncode(0xff, R, G, B);
436     }
437     CPDF_GraphStateData graph_state;
438     graph_state.m_LineWidth = width;
439     if (style_char == 'D') {
440         if (pDashArray) {
441             FX_DWORD dash_count = pDashArray->GetCount();
442             if (dash_count % 2) {
443                 dash_count ++;
444             }
445             graph_state.m_DashArray = FX_Alloc(FX_FLOAT, dash_count);
446             if (graph_state.m_DashArray == NULL) {
447                 return ;
448             }
449             graph_state.m_DashCount = dash_count;
450             FX_DWORD i;
451             for (i = 0; i < pDashArray->GetCount(); i ++) {
452                 graph_state.m_DashArray[i] = pDashArray->GetNumber(i);
453             }
454             if (i < dash_count) {
455                 graph_state.m_DashArray[i] = graph_state.m_DashArray[i - 1];
456             }
457         } else {
458             graph_state.m_DashArray = FX_Alloc(FX_FLOAT, 2);
459             if (graph_state.m_DashArray == NULL) {
460                 return ;
461             }
462             graph_state.m_DashCount = 2;
463             graph_state.m_DashArray[0] = graph_state.m_DashArray[1] = 3 * 1.0f;
464         }
465     }
466     CFX_FloatRect rect;
467     GetRect(rect);
468     CPDF_PathData path;
469     width /= 2;
470     path.AppendRect(rect.left + width, rect.bottom + width, rect.right - width, rect.top - width);
471     int fill_type = 0;
472     if (pOptions && (pOptions->m_Flags & RENDER_NOPATHSMOOTH)) {
473         fill_type |= FXFILL_NOPATHSMOOTH;
474     }
475     pDevice->DrawPath(&path, pUser2Device, &graph_state, argb, argb, fill_type);
476 }
477 int CPDF_Annot::CountIRTNotes()
478 {
479     int count = 0;
480     for (int i = 0; i < m_pList->Count(); i ++) {
481         CPDF_Annot* pAnnot = m_pList->GetAt(i);
482         if (pAnnot == NULL) {
483             continue;
484         }
485         CPDF_Dictionary* pIRT = pAnnot->m_pAnnotDict->GetDict("IRT");
486         if (pIRT != m_pAnnotDict) {
487             continue;
488         }
489         count ++;
490     }
491     return count;
492 }
493 CPDF_Annot* CPDF_Annot::GetIRTNote(int index)
494 {
495     int count = 0;
496     for (int i = 0; i < m_pList->Count(); i ++) {
497         CPDF_Annot* pAnnot = m_pList->GetAt(i);
498         if (pAnnot == NULL) {
499             continue;
500         }
501         CPDF_Dictionary* pIRT = pAnnot->m_pAnnotDict->GetDict("IRT");
502         if (pIRT != m_pAnnotDict) {
503             continue;
504         }
505         if (count == index) {
506             return pAnnot;
507         }
508         count ++;
509     }
510     return NULL;
511 }