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_formcontrol.h"
8
9#include <algorithm>
10
11#include "core/fpdfapi/page/cpdf_form.h"
12#include "core/fpdfapi/parser/cpdf_array.h"
13#include "core/fpdfapi/parser/cpdf_document.h"
14#include "core/fpdfapi/parser/cpdf_name.h"
15#include "core/fpdfapi/parser/cpdf_stream.h"
16#include "core/fpdfapi/parser/fpdf_parser_decode.h"
17#include "core/fpdfapi/render/cpdf_rendercontext.h"
18#include "core/fpdfdoc/cpdf_interform.h"
19#include "core/fxge/cfx_renderdevice.h"
20
21namespace {
22
23const FX_CHAR* const g_sHighlightingMode[] = {
24    // Must match order of HighlightingMode enum.
25    "N", "I", "O", "P", "T"};
26
27}  // namespace
28
29CPDF_FormControl::CPDF_FormControl(CPDF_FormField* pField,
30                                   CPDF_Dictionary* pWidgetDict)
31    : m_pField(pField),
32      m_pWidgetDict(pWidgetDict),
33      m_pForm(m_pField->m_pForm) {}
34
35CFX_ByteString CPDF_FormControl::GetOnStateName() const {
36  ASSERT(GetType() == CPDF_FormField::CheckBox ||
37         GetType() == CPDF_FormField::RadioButton);
38  CFX_ByteString csOn;
39  CPDF_Dictionary* pAP = m_pWidgetDict->GetDictFor("AP");
40  if (!pAP)
41    return csOn;
42
43  CPDF_Dictionary* pN = pAP->GetDictFor("N");
44  if (!pN)
45    return csOn;
46
47  for (const auto& it : *pN) {
48    if (it.first != "Off")
49      return it.first;
50  }
51  return CFX_ByteString();
52}
53
54void CPDF_FormControl::SetOnStateName(const CFX_ByteString& csOn) {
55  ASSERT(GetType() == CPDF_FormField::CheckBox ||
56         GetType() == CPDF_FormField::RadioButton);
57  CFX_ByteString csValue = csOn;
58  if (csValue.IsEmpty())
59    csValue = "Yes";
60  else if (csValue == "Off")
61    csValue = "Yes";
62
63  CFX_ByteString csAS = m_pWidgetDict->GetStringFor("AS", "Off");
64  if (csAS != "Off")
65    m_pWidgetDict->SetNewFor<CPDF_Name>("AS", csValue);
66
67  CPDF_Dictionary* pAP = m_pWidgetDict->GetDictFor("AP");
68  if (!pAP)
69    return;
70
71  for (const auto& it : *pAP) {
72    CPDF_Object* pObj1 = it.second.get();
73    if (!pObj1)
74      continue;
75
76    CPDF_Object* pObjDirect1 = pObj1->GetDirect();
77    CPDF_Dictionary* pSubDict = pObjDirect1->AsDictionary();
78    if (!pSubDict)
79      continue;
80
81    auto subdict_it = pSubDict->begin();
82    while (subdict_it != pSubDict->end()) {
83      const CFX_ByteString& csKey2 = subdict_it->first;
84      CPDF_Object* pObj2 = subdict_it->second.get();
85      ++subdict_it;
86      if (!pObj2)
87        continue;
88      if (csKey2 != "Off") {
89        pSubDict->ReplaceKey(csKey2, csValue);
90        break;
91      }
92    }
93  }
94}
95
96CFX_ByteString CPDF_FormControl::GetCheckedAPState() {
97  ASSERT(GetType() == CPDF_FormField::CheckBox ||
98         GetType() == CPDF_FormField::RadioButton);
99  CFX_ByteString csOn = GetOnStateName();
100  if (GetType() == CPDF_FormField::RadioButton ||
101      GetType() == CPDF_FormField::CheckBox) {
102    if (ToArray(FPDF_GetFieldAttr(m_pField->m_pDict, "Opt"))) {
103      int iIndex = m_pField->GetControlIndex(this);
104      csOn.Format("%d", iIndex);
105    }
106  }
107  if (csOn.IsEmpty())
108    csOn = "Yes";
109  return csOn;
110}
111
112CFX_WideString CPDF_FormControl::GetExportValue() const {
113  ASSERT(GetType() == CPDF_FormField::CheckBox ||
114         GetType() == CPDF_FormField::RadioButton);
115  CFX_ByteString csOn = GetOnStateName();
116  if (GetType() == CPDF_FormField::RadioButton ||
117      GetType() == CPDF_FormField::CheckBox) {
118    if (CPDF_Array* pArray =
119            ToArray(FPDF_GetFieldAttr(m_pField->m_pDict, "Opt"))) {
120      int iIndex = m_pField->GetControlIndex(this);
121      csOn = pArray->GetStringAt(iIndex);
122    }
123  }
124  if (csOn.IsEmpty())
125    csOn = "Yes";
126  return PDF_DecodeText(csOn);
127}
128
129bool CPDF_FormControl::IsChecked() const {
130  ASSERT(GetType() == CPDF_FormField::CheckBox ||
131         GetType() == CPDF_FormField::RadioButton);
132  CFX_ByteString csOn = GetOnStateName();
133  CFX_ByteString csAS = m_pWidgetDict->GetStringFor("AS");
134  return csAS == csOn;
135}
136
137bool CPDF_FormControl::IsDefaultChecked() const {
138  ASSERT(GetType() == CPDF_FormField::CheckBox ||
139         GetType() == CPDF_FormField::RadioButton);
140  CPDF_Object* pDV = FPDF_GetFieldAttr(m_pField->m_pDict, "DV");
141  if (!pDV)
142    return false;
143
144  CFX_ByteString csDV = pDV->GetString();
145  CFX_ByteString csOn = GetOnStateName();
146  return (csDV == csOn);
147}
148
149void CPDF_FormControl::CheckControl(bool bChecked) {
150  ASSERT(GetType() == CPDF_FormField::CheckBox ||
151         GetType() == CPDF_FormField::RadioButton);
152  CFX_ByteString csOn = GetOnStateName();
153  CFX_ByteString csOldAS = m_pWidgetDict->GetStringFor("AS", "Off");
154  CFX_ByteString csAS = "Off";
155  if (bChecked)
156    csAS = csOn;
157  if (csOldAS == csAS)
158    return;
159  m_pWidgetDict->SetNewFor<CPDF_Name>("AS", csAS);
160}
161
162void CPDF_FormControl::DrawControl(CFX_RenderDevice* pDevice,
163                                   CFX_Matrix* pMatrix,
164                                   CPDF_Page* pPage,
165                                   CPDF_Annot::AppearanceMode mode,
166                                   const CPDF_RenderOptions* pOptions) {
167  if (m_pWidgetDict->GetIntegerFor("F") & ANNOTFLAG_HIDDEN)
168    return;
169
170  CPDF_Stream* pStream = FPDFDOC_GetAnnotAP(m_pWidgetDict, mode);
171  if (!pStream)
172    return;
173
174  CFX_FloatRect form_bbox = pStream->GetDict()->GetRectFor("BBox");
175  CFX_Matrix form_matrix = pStream->GetDict()->GetMatrixFor("Matrix");
176  form_matrix.TransformRect(form_bbox);
177  CFX_FloatRect arect = m_pWidgetDict->GetRectFor("Rect");
178  CFX_Matrix matrix;
179  matrix.MatchRect(arect, form_bbox);
180  matrix.Concat(*pMatrix);
181  CPDF_Form form(m_pField->m_pForm->m_pDocument,
182                 m_pField->m_pForm->m_pFormDict->GetDictFor("DR"), pStream);
183  form.ParseContent(nullptr, nullptr, nullptr);
184  CPDF_RenderContext context(pPage);
185  context.AppendLayer(&form, &matrix);
186  context.Render(pDevice, pOptions, nullptr);
187}
188
189CPDF_FormControl::HighlightingMode CPDF_FormControl::GetHighlightingMode() {
190  if (!m_pWidgetDict)
191    return Invert;
192
193  CFX_ByteString csH = m_pWidgetDict->GetStringFor("H", "I");
194  for (size_t i = 0; i < FX_ArraySize(g_sHighlightingMode); ++i) {
195    if (csH == g_sHighlightingMode[i])
196      return static_cast<HighlightingMode>(i);
197  }
198  return Invert;
199}
200
201CPDF_ApSettings CPDF_FormControl::GetMK() const {
202  return CPDF_ApSettings(m_pWidgetDict ? m_pWidgetDict->GetDictFor("MK")
203                                       : nullptr);
204}
205
206bool CPDF_FormControl::HasMKEntry(const CFX_ByteString& csEntry) const {
207  return GetMK().HasMKEntry(csEntry);
208}
209
210int CPDF_FormControl::GetRotation() {
211  return GetMK().GetRotation();
212}
213
214FX_ARGB CPDF_FormControl::GetColor(int& iColorType,
215                                   const CFX_ByteString& csEntry) {
216  return GetMK().GetColor(iColorType, csEntry);
217}
218
219FX_FLOAT CPDF_FormControl::GetOriginalColor(int index,
220                                            const CFX_ByteString& csEntry) {
221  return GetMK().GetOriginalColor(index, csEntry);
222}
223
224void CPDF_FormControl::GetOriginalColor(int& iColorType,
225                                        FX_FLOAT fc[4],
226                                        const CFX_ByteString& csEntry) {
227  GetMK().GetOriginalColor(iColorType, fc, csEntry);
228}
229
230CFX_WideString CPDF_FormControl::GetCaption(const CFX_ByteString& csEntry) {
231  return GetMK().GetCaption(csEntry);
232}
233
234CPDF_Stream* CPDF_FormControl::GetIcon(const CFX_ByteString& csEntry) {
235  return GetMK().GetIcon(csEntry);
236}
237
238CPDF_IconFit CPDF_FormControl::GetIconFit() {
239  return GetMK().GetIconFit();
240}
241
242int CPDF_FormControl::GetTextPosition() {
243  return GetMK().GetTextPosition();
244}
245
246CPDF_Action CPDF_FormControl::GetAction() {
247  if (!m_pWidgetDict)
248    return CPDF_Action();
249
250  if (m_pWidgetDict->KeyExist("A"))
251    return CPDF_Action(m_pWidgetDict->GetDictFor("A"));
252
253  CPDF_Object* pObj = FPDF_GetFieldAttr(m_pField->m_pDict, "A");
254  if (!pObj)
255    return CPDF_Action();
256
257  return CPDF_Action(pObj->GetDict());
258}
259
260CPDF_AAction CPDF_FormControl::GetAdditionalAction() {
261  if (!m_pWidgetDict)
262    return CPDF_AAction();
263
264  if (m_pWidgetDict->KeyExist("AA"))
265    return CPDF_AAction(m_pWidgetDict->GetDictFor("AA"));
266  return m_pField->GetAdditionalAction();
267}
268
269CPDF_DefaultAppearance CPDF_FormControl::GetDefaultAppearance() {
270  if (!m_pWidgetDict)
271    return CPDF_DefaultAppearance();
272
273  if (m_pWidgetDict->KeyExist("DA"))
274    return CPDF_DefaultAppearance(m_pWidgetDict->GetStringFor("DA"));
275
276  CPDF_Object* pObj = FPDF_GetFieldAttr(m_pField->m_pDict, "DA");
277  if (pObj)
278    return CPDF_DefaultAppearance(pObj->GetString());
279  return m_pField->m_pForm->GetDefaultAppearance();
280}
281
282CPDF_Font* CPDF_FormControl::GetDefaultControlFont() {
283  CPDF_DefaultAppearance cDA = GetDefaultAppearance();
284  CFX_ByteString csFontNameTag;
285  FX_FLOAT fFontSize;
286  cDA.GetFont(csFontNameTag, fFontSize);
287  if (csFontNameTag.IsEmpty())
288    return nullptr;
289
290  CPDF_Object* pObj = FPDF_GetFieldAttr(m_pWidgetDict, "DR");
291  if (CPDF_Dictionary* pDict = ToDictionary(pObj)) {
292    CPDF_Dictionary* pFonts = pDict->GetDictFor("Font");
293    if (pFonts) {
294      CPDF_Dictionary* pElement = pFonts->GetDictFor(csFontNameTag);
295      if (pElement) {
296        CPDF_Font* pFont = m_pField->m_pForm->m_pDocument->LoadFont(pElement);
297        if (pFont)
298          return pFont;
299      }
300    }
301  }
302  if (CPDF_Font* pFormFont = m_pField->m_pForm->GetFormFont(csFontNameTag))
303    return pFormFont;
304
305  CPDF_Dictionary* pPageDict = m_pWidgetDict->GetDictFor("P");
306  pObj = FPDF_GetFieldAttr(pPageDict, "Resources");
307  if (CPDF_Dictionary* pDict = ToDictionary(pObj)) {
308    CPDF_Dictionary* pFonts = pDict->GetDictFor("Font");
309    if (pFonts) {
310      CPDF_Dictionary* pElement = pFonts->GetDictFor(csFontNameTag);
311      if (pElement) {
312        CPDF_Font* pFont = m_pField->m_pForm->m_pDocument->LoadFont(pElement);
313        if (pFont)
314          return pFont;
315      }
316    }
317  }
318  return nullptr;
319}
320
321int CPDF_FormControl::GetControlAlignment() {
322  if (!m_pWidgetDict)
323    return 0;
324  if (m_pWidgetDict->KeyExist("Q"))
325    return m_pWidgetDict->GetIntegerFor("Q", 0);
326
327  CPDF_Object* pObj = FPDF_GetFieldAttr(m_pField->m_pDict, "Q");
328  if (pObj)
329    return pObj->GetInteger();
330  return m_pField->m_pForm->GetFormAlignment();
331}
332