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_annotlist.h"
8
9#include <algorithm>
10#include <memory>
11#include <utility>
12
13#include "core/fpdfapi/page/cpdf_page.h"
14#include "core/fpdfapi/parser/cpdf_array.h"
15#include "core/fpdfapi/parser/cpdf_document.h"
16#include "core/fpdfapi/parser/cpdf_name.h"
17#include "core/fpdfapi/parser/cpdf_number.h"
18#include "core/fpdfapi/parser/cpdf_reference.h"
19#include "core/fpdfapi/parser/cpdf_string.h"
20#include "core/fpdfapi/render/cpdf_renderoptions.h"
21#include "core/fpdfdoc/cpdf_annot.h"
22#include "core/fpdfdoc/cpdf_interform.h"
23#include "core/fpdfdoc/cpdf_occontext.h"
24#include "core/fpdfdoc/cpvt_generateap.h"
25#include "core/fxge/cfx_renderdevice.h"
26#include "third_party/base/ptr_util.h"
27
28namespace {
29
30std::unique_ptr<CPDF_Annot> CreatePopupAnnot(CPDF_Annot* pAnnot,
31                                             CPDF_Document* pDocument,
32                                             CPDF_Page* pPage) {
33  CPDF_Dictionary* pParentDict = pAnnot->GetAnnotDict();
34  if (!pParentDict)
35    return nullptr;
36
37  // TODO(jaepark): We shouldn't strip BOM for some strings and not for others.
38  // See pdfium:593.
39  WideString sContents = pParentDict->GetUnicodeTextFor("Contents");
40  if (sContents.IsEmpty())
41    return nullptr;
42
43  auto pAnnotDict =
44      pdfium::MakeUnique<CPDF_Dictionary>(pDocument->GetByteStringPool());
45  pAnnotDict->SetNewFor<CPDF_Name>("Type", "Annot");
46  pAnnotDict->SetNewFor<CPDF_Name>("Subtype", "Popup");
47  pAnnotDict->SetNewFor<CPDF_String>("T", pParentDict->GetStringFor("T"),
48                                     false);
49  pAnnotDict->SetNewFor<CPDF_String>("Contents", sContents.UTF8Encode(), false);
50
51  CFX_FloatRect rect = pParentDict->GetRectFor("Rect");
52  rect.Normalize();
53  CFX_FloatRect popupRect(0, 0, 200, 200);
54  // Note that if the popup can set its own dimensions, then we will need to
55  // make sure that it isn't larger than the page size.
56  if (rect.left + popupRect.Width() > pPage->GetPageWidth() &&
57      rect.bottom - popupRect.Height() < 0) {
58    // If the annotation is on the bottom-right corner of the page, then place
59    // the popup above and to the left of the annotation.
60    popupRect.Translate(rect.right - popupRect.Width(), rect.top);
61  } else {
62    // Place the popup below and to the right of the annotation without getting
63    // clipped by page edges.
64    popupRect.Translate(
65        std::min(rect.left, pPage->GetPageWidth() - popupRect.Width()),
66        std::max(rect.bottom - popupRect.Height(), 0.f));
67  }
68
69  pAnnotDict->SetRectFor("Rect", popupRect);
70  pAnnotDict->SetNewFor<CPDF_Number>("F", 0);
71
72  auto pPopupAnnot =
73      pdfium::MakeUnique<CPDF_Annot>(std::move(pAnnotDict), pDocument);
74  pAnnot->SetPopupAnnot(pPopupAnnot.get());
75  return pPopupAnnot;
76}
77
78void GenerateAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
79  if (!pAnnotDict || pAnnotDict->GetStringFor("Subtype") != "Widget")
80    return;
81
82  CPDF_Object* pFieldTypeObj = FPDF_GetFieldAttr(pAnnotDict, "FT");
83  if (!pFieldTypeObj)
84    return;
85
86  ByteString field_type = pFieldTypeObj->GetString();
87  if (field_type == "Tx") {
88    CPVT_GenerateAP::GenerateFormAP(CPVT_GenerateAP::kTextField, pDoc,
89                                    pAnnotDict);
90    return;
91  }
92
93  CPDF_Object* pFieldFlagsObj = FPDF_GetFieldAttr(pAnnotDict, "Ff");
94  uint32_t flags = pFieldFlagsObj ? pFieldFlagsObj->GetInteger() : 0;
95  if (field_type == "Ch") {
96    CPVT_GenerateAP::GenerateFormAP((flags & (1 << 17))
97                                        ? CPVT_GenerateAP::kComboBox
98                                        : CPVT_GenerateAP::kListBox,
99                                    pDoc, pAnnotDict);
100    return;
101  }
102
103  if (field_type != "Btn")
104    return;
105  if (flags & (1 << 16))
106    return;
107  if (pAnnotDict->KeyExist("AS"))
108    return;
109
110  CPDF_Dictionary* pParentDict = pAnnotDict->GetDictFor("Parent");
111  if (!pParentDict || !pParentDict->KeyExist("AS"))
112    return;
113
114  pAnnotDict->SetNewFor<CPDF_String>("AS", pParentDict->GetStringFor("AS"),
115                                     false);
116  return;
117}
118
119}  // namespace
120
121CPDF_AnnotList::CPDF_AnnotList(CPDF_Page* pPage)
122    : m_pDocument(pPage->m_pDocument.Get()) {
123  if (!pPage->m_pFormDict)
124    return;
125
126  CPDF_Array* pAnnots = pPage->m_pFormDict->GetArrayFor("Annots");
127  if (!pAnnots)
128    return;
129
130  const CPDF_Dictionary* pRoot = m_pDocument->GetRoot();
131  CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm");
132  bool bRegenerateAP = pAcroForm && pAcroForm->GetBooleanFor("NeedAppearances");
133  for (size_t i = 0; i < pAnnots->GetCount(); ++i) {
134    CPDF_Dictionary* pDict = ToDictionary(pAnnots->GetDirectObjectAt(i));
135    if (!pDict)
136      continue;
137    const ByteString subtype = pDict->GetStringFor("Subtype");
138    if (subtype == "Popup") {
139      // Skip creating Popup annotations in the PDF document since PDFium
140      // provides its own Popup annotations.
141      continue;
142    }
143    pAnnots->ConvertToIndirectObjectAt(i, m_pDocument);
144    m_AnnotList.push_back(pdfium::MakeUnique<CPDF_Annot>(pDict, m_pDocument));
145    if (bRegenerateAP && subtype == "Widget" &&
146        CPDF_InterForm::IsUpdateAPEnabled() && !pDict->GetDictFor("AP")) {
147      GenerateAP(m_pDocument, pDict);
148    }
149  }
150
151  size_t nAnnotListSize = m_AnnotList.size();
152  for (size_t i = 0; i < nAnnotListSize; ++i) {
153    std::unique_ptr<CPDF_Annot> pPopupAnnot(
154        CreatePopupAnnot(m_AnnotList[i].get(), m_pDocument, pPage));
155    if (pPopupAnnot)
156      m_AnnotList.push_back(std::move(pPopupAnnot));
157  }
158}
159
160CPDF_AnnotList::~CPDF_AnnotList() {}
161
162void CPDF_AnnotList::DisplayPass(CPDF_Page* pPage,
163                                 CFX_RenderDevice* pDevice,
164                                 CPDF_RenderContext* pContext,
165                                 bool bPrinting,
166                                 const CFX_Matrix* pMatrix,
167                                 bool bWidgetPass,
168                                 CPDF_RenderOptions* pOptions,
169                                 FX_RECT* clip_rect) {
170  for (const auto& pAnnot : m_AnnotList) {
171    bool bWidget = pAnnot->GetSubtype() == CPDF_Annot::Subtype::WIDGET;
172    if ((bWidgetPass && !bWidget) || (!bWidgetPass && bWidget))
173      continue;
174
175    uint32_t annot_flags = pAnnot->GetFlags();
176    if (annot_flags & ANNOTFLAG_HIDDEN)
177      continue;
178
179    if (bPrinting && (annot_flags & ANNOTFLAG_PRINT) == 0)
180      continue;
181
182    if (!bPrinting && (annot_flags & ANNOTFLAG_NOVIEW))
183      continue;
184
185    if (pOptions) {
186      CPDF_Dictionary* pAnnotDict = pAnnot->GetAnnotDict();
187      if (pOptions->GetOCContext() && pAnnotDict &&
188          !pOptions->GetOCContext()->CheckOCGVisible(
189              pAnnotDict->GetDictFor("OC"))) {
190        continue;
191      }
192    }
193
194    CFX_Matrix matrix = *pMatrix;
195    if (clip_rect) {
196      FX_RECT annot_rect =
197          matrix.TransformRect(pAnnot->GetRect()).GetOuterRect();
198      annot_rect.Intersect(*clip_rect);
199      if (annot_rect.IsEmpty())
200        continue;
201    }
202    if (pContext) {
203      pAnnot->DrawInContext(pPage, pContext, &matrix, CPDF_Annot::Normal);
204    } else if (!pAnnot->DrawAppearance(pPage, pDevice, matrix,
205                                       CPDF_Annot::Normal, pOptions)) {
206      pAnnot->DrawBorder(pDevice, &matrix, pOptions);
207    }
208  }
209}
210
211void CPDF_AnnotList::DisplayAnnots(CPDF_Page* pPage,
212                                   CFX_RenderDevice* pDevice,
213                                   CPDF_RenderContext* pContext,
214                                   bool bPrinting,
215                                   const CFX_Matrix* pUser2Device,
216                                   uint32_t dwAnnotFlags,
217                                   CPDF_RenderOptions* pOptions,
218                                   FX_RECT* pClipRect) {
219  if (dwAnnotFlags & ANNOTFLAG_INVISIBLE) {
220    DisplayPass(pPage, pDevice, pContext, bPrinting, pUser2Device, false,
221                pOptions, pClipRect);
222  }
223  if (dwAnnotFlags & ANNOTFLAG_HIDDEN) {
224    DisplayPass(pPage, pDevice, pContext, bPrinting, pUser2Device, true,
225                pOptions, pClipRect);
226  }
227}
228
229void CPDF_AnnotList::DisplayAnnots(CPDF_Page* pPage,
230                                   CPDF_RenderContext* pContext,
231                                   bool bPrinting,
232                                   const CFX_Matrix* pMatrix,
233                                   bool bShowWidget,
234                                   CPDF_RenderOptions* pOptions) {
235  uint32_t dwAnnotFlags = bShowWidget ? ANNOTFLAG_INVISIBLE | ANNOTFLAG_HIDDEN
236                                      : ANNOTFLAG_INVISIBLE;
237  DisplayAnnots(pPage, nullptr, pContext, bPrinting, pMatrix, dwAnnotFlags,
238                pOptions, nullptr);
239}
240