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