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/cpvt_generateap.h"
8
9#include <algorithm>
10#include <memory>
11#include <utility>
12
13#include "core/fpdfapi/font/cpdf_font.h"
14#include "core/fpdfapi/parser/cpdf_boolean.h"
15#include "core/fpdfapi/parser/cpdf_dictionary.h"
16#include "core/fpdfapi/parser/cpdf_document.h"
17#include "core/fpdfapi/parser/cpdf_name.h"
18#include "core/fpdfapi/parser/cpdf_number.h"
19#include "core/fpdfapi/parser/cpdf_reference.h"
20#include "core/fpdfapi/parser/cpdf_simple_parser.h"
21#include "core/fpdfapi/parser/cpdf_stream.h"
22#include "core/fpdfapi/parser/cpdf_string.h"
23#include "core/fpdfapi/parser/fpdf_parser_decode.h"
24#include "core/fpdfdoc/cpdf_annot.h"
25#include "core/fpdfdoc/cpdf_formfield.h"
26#include "core/fpdfdoc/cpvt_color.h"
27#include "core/fpdfdoc/cpvt_fontmap.h"
28#include "core/fpdfdoc/cpvt_word.h"
29#include "third_party/base/ptr_util.h"
30
31namespace {
32
33bool GenerateWidgetAP(CPDF_Document* pDoc,
34                      CPDF_Dictionary* pAnnotDict,
35                      const int32_t& nWidgetType) {
36  CPDF_Dictionary* pFormDict = nullptr;
37  if (CPDF_Dictionary* pRootDict = pDoc->GetRoot())
38    pFormDict = pRootDict->GetDictFor("AcroForm");
39  if (!pFormDict)
40    return false;
41
42  CFX_ByteString DA;
43  if (CPDF_Object* pDAObj = FPDF_GetFieldAttr(pAnnotDict, "DA"))
44    DA = pDAObj->GetString();
45  if (DA.IsEmpty())
46    DA = pFormDict->GetStringFor("DA");
47  if (DA.IsEmpty())
48    return false;
49
50  CPDF_SimpleParser syntax(DA.AsStringC());
51  syntax.FindTagParamFromStart("Tf", 2);
52  CFX_ByteString sFontName(syntax.GetWord());
53  sFontName = PDF_NameDecode(sFontName);
54  if (sFontName.IsEmpty())
55    return false;
56
57  FX_FLOAT fFontSize = FX_atof(syntax.GetWord());
58  CPVT_Color crText = CPVT_Color::ParseColor(DA);
59  CPDF_Dictionary* pDRDict = pFormDict->GetDictFor("DR");
60  if (!pDRDict)
61    return false;
62
63  CPDF_Dictionary* pDRFontDict = pDRDict->GetDictFor("Font");
64  if (!pDRFontDict)
65    return false;
66
67  CPDF_Dictionary* pFontDict = pDRFontDict->GetDictFor(sFontName.Mid(1));
68  if (!pFontDict) {
69    pFontDict = pDoc->NewIndirect<CPDF_Dictionary>();
70    pFontDict->SetNewFor<CPDF_Name>("Type", "Font");
71    pFontDict->SetNewFor<CPDF_Name>("Subtype", "Type1");
72    pFontDict->SetNewFor<CPDF_Name>("BaseFont", "Helvetica");
73    pFontDict->SetNewFor<CPDF_Name>("Encoding", "WinAnsiEncoding");
74    pDRFontDict->SetNewFor<CPDF_Reference>(sFontName.Mid(1), pDoc,
75                                           pFontDict->GetObjNum());
76  }
77  CPDF_Font* pDefFont = pDoc->LoadFont(pFontDict);
78  if (!pDefFont)
79    return false;
80
81  CFX_FloatRect rcAnnot = pAnnotDict->GetRectFor("Rect");
82  int32_t nRotate = 0;
83  if (CPDF_Dictionary* pMKDict = pAnnotDict->GetDictFor("MK"))
84    nRotate = pMKDict->GetIntegerFor("R");
85
86  CFX_FloatRect rcBBox;
87  CFX_Matrix matrix;
88  switch (nRotate % 360) {
89    case 0:
90      rcBBox = CFX_FloatRect(0, 0, rcAnnot.right - rcAnnot.left,
91                             rcAnnot.top - rcAnnot.bottom);
92      break;
93    case 90:
94      matrix = CFX_Matrix(0, 1, -1, 0, rcAnnot.right - rcAnnot.left, 0);
95      rcBBox = CFX_FloatRect(0, 0, rcAnnot.top - rcAnnot.bottom,
96                             rcAnnot.right - rcAnnot.left);
97      break;
98    case 180:
99      matrix = CFX_Matrix(-1, 0, 0, -1, rcAnnot.right - rcAnnot.left,
100                          rcAnnot.top - rcAnnot.bottom);
101      rcBBox = CFX_FloatRect(0, 0, rcAnnot.right - rcAnnot.left,
102                             rcAnnot.top - rcAnnot.bottom);
103      break;
104    case 270:
105      matrix = CFX_Matrix(0, -1, 1, 0, 0, rcAnnot.top - rcAnnot.bottom);
106      rcBBox = CFX_FloatRect(0, 0, rcAnnot.top - rcAnnot.bottom,
107                             rcAnnot.right - rcAnnot.left);
108      break;
109  }
110
111  BorderStyle nBorderStyle = BorderStyle::SOLID;
112  FX_FLOAT fBorderWidth = 1;
113  CPVT_Dash dsBorder(3, 0, 0);
114  CPVT_Color crLeftTop, crRightBottom;
115  if (CPDF_Dictionary* pBSDict = pAnnotDict->GetDictFor("BS")) {
116    if (pBSDict->KeyExist("W"))
117      fBorderWidth = pBSDict->GetNumberFor("W");
118
119    if (CPDF_Array* pArray = pBSDict->GetArrayFor("D")) {
120      dsBorder = CPVT_Dash(pArray->GetIntegerAt(0), pArray->GetIntegerAt(1),
121                           pArray->GetIntegerAt(2));
122    }
123    switch (pBSDict->GetStringFor("S").GetAt(0)) {
124      case 'S':
125        nBorderStyle = BorderStyle::SOLID;
126        break;
127      case 'D':
128        nBorderStyle = BorderStyle::DASH;
129        break;
130      case 'B':
131        nBorderStyle = BorderStyle::BEVELED;
132        fBorderWidth *= 2;
133        crLeftTop = CPVT_Color(CPVT_Color::kGray, 1);
134        crRightBottom = CPVT_Color(CPVT_Color::kGray, 0.5);
135        break;
136      case 'I':
137        nBorderStyle = BorderStyle::INSET;
138        fBorderWidth *= 2;
139        crLeftTop = CPVT_Color(CPVT_Color::kGray, 0.5);
140        crRightBottom = CPVT_Color(CPVT_Color::kGray, 0.75);
141        break;
142      case 'U':
143        nBorderStyle = BorderStyle::UNDERLINE;
144        break;
145    }
146  }
147  CPVT_Color crBorder, crBG;
148  if (CPDF_Dictionary* pMKDict = pAnnotDict->GetDictFor("MK")) {
149    if (CPDF_Array* pArray = pMKDict->GetArrayFor("BC"))
150      crBorder = CPVT_Color::ParseColor(*pArray);
151    if (CPDF_Array* pArray = pMKDict->GetArrayFor("BG"))
152      crBG = CPVT_Color::ParseColor(*pArray);
153  }
154  CFX_ByteTextBuf sAppStream;
155  CFX_ByteString sBG =
156      CPVT_GenerateAP::GenerateColorAP(crBG, PaintOperation::FILL);
157  if (sBG.GetLength() > 0) {
158    sAppStream << "q\n" << sBG << rcBBox.left << " " << rcBBox.bottom << " "
159               << rcBBox.Width() << " " << rcBBox.Height() << " re f\n"
160               << "Q\n";
161  }
162  CFX_ByteString sBorderStream = CPVT_GenerateAP::GenerateBorderAP(
163      rcBBox, fBorderWidth, crBorder, crLeftTop, crRightBottom, nBorderStyle,
164      dsBorder);
165  if (sBorderStream.GetLength() > 0)
166    sAppStream << "q\n" << sBorderStream << "Q\n";
167
168  CFX_FloatRect rcBody =
169      CFX_FloatRect(rcBBox.left + fBorderWidth, rcBBox.bottom + fBorderWidth,
170                    rcBBox.right - fBorderWidth, rcBBox.top - fBorderWidth);
171  rcBody.Normalize();
172
173  CPDF_Dictionary* pAPDict = pAnnotDict->GetDictFor("AP");
174  if (!pAPDict)
175    pAPDict = pAnnotDict->SetNewFor<CPDF_Dictionary>("AP");
176
177  CPDF_Stream* pNormalStream = pAPDict->GetStreamFor("N");
178  if (!pNormalStream) {
179    pNormalStream = pDoc->NewIndirect<CPDF_Stream>();
180    pAPDict->SetNewFor<CPDF_Reference>("N", pDoc, pNormalStream->GetObjNum());
181  }
182  CPDF_Dictionary* pStreamDict = pNormalStream->GetDict();
183  if (pStreamDict) {
184    pStreamDict->SetMatrixFor("Matrix", matrix);
185    pStreamDict->SetRectFor("BBox", rcBBox);
186    CPDF_Dictionary* pStreamResList = pStreamDict->GetDictFor("Resources");
187    if (pStreamResList) {
188      CPDF_Dictionary* pStreamResFontList = pStreamResList->GetDictFor("Font");
189      if (!pStreamResFontList)
190        pStreamResFontList = pStreamResList->SetNewFor<CPDF_Dictionary>("Font");
191      if (!pStreamResFontList->KeyExist(sFontName)) {
192        pStreamResFontList->SetNewFor<CPDF_Reference>(sFontName, pDoc,
193                                                      pFontDict->GetObjNum());
194      }
195    } else {
196      pStreamDict->SetFor("Resources", pFormDict->GetDictFor("DR")->Clone());
197      pStreamResList = pStreamDict->GetDictFor("Resources");
198    }
199  }
200  switch (nWidgetType) {
201    case 0: {
202      CFX_WideString swValue =
203          FPDF_GetFieldAttr(pAnnotDict, "V")
204              ? FPDF_GetFieldAttr(pAnnotDict, "V")->GetUnicodeText()
205              : CFX_WideString();
206      int32_t nAlign = FPDF_GetFieldAttr(pAnnotDict, "Q")
207                           ? FPDF_GetFieldAttr(pAnnotDict, "Q")->GetInteger()
208                           : 0;
209      uint32_t dwFlags = FPDF_GetFieldAttr(pAnnotDict, "Ff")
210                             ? FPDF_GetFieldAttr(pAnnotDict, "Ff")->GetInteger()
211                             : 0;
212      uint32_t dwMaxLen =
213          FPDF_GetFieldAttr(pAnnotDict, "MaxLen")
214              ? FPDF_GetFieldAttr(pAnnotDict, "MaxLen")->GetInteger()
215              : 0;
216      CPVT_FontMap map(
217          pDoc, pStreamDict ? pStreamDict->GetDictFor("Resources") : nullptr,
218          pDefFont, sFontName.Right(sFontName.GetLength() - 1));
219      CPDF_VariableText::Provider prd(&map);
220      CPDF_VariableText vt;
221      vt.SetProvider(&prd);
222      vt.SetPlateRect(rcBody);
223      vt.SetAlignment(nAlign);
224      if (IsFloatZero(fFontSize))
225        vt.SetAutoFontSize(true);
226      else
227        vt.SetFontSize(fFontSize);
228
229      bool bMultiLine = (dwFlags >> 12) & 1;
230      if (bMultiLine) {
231        vt.SetMultiLine(true);
232        vt.SetAutoReturn(true);
233      }
234      uint16_t subWord = 0;
235      if ((dwFlags >> 13) & 1) {
236        subWord = '*';
237        vt.SetPasswordChar(subWord);
238      }
239      bool bCharArray = (dwFlags >> 24) & 1;
240      if (bCharArray)
241        vt.SetCharArray(dwMaxLen);
242      else
243        vt.SetLimitChar(dwMaxLen);
244
245      vt.Initialize();
246      vt.SetText(swValue);
247      vt.RearrangeAll();
248      CFX_FloatRect rcContent = vt.GetContentRect();
249      CFX_PointF ptOffset;
250      if (!bMultiLine) {
251        ptOffset =
252            CFX_PointF(0.0f, (rcContent.Height() - rcBody.Height()) / 2.0f);
253      }
254      CFX_ByteString sBody = CPVT_GenerateAP::GenerateEditAP(
255          &map, vt.GetIterator(), ptOffset, !bCharArray, subWord);
256      if (sBody.GetLength() > 0) {
257        sAppStream << "/Tx BMC\n"
258                   << "q\n";
259        if (rcContent.Width() > rcBody.Width() ||
260            rcContent.Height() > rcBody.Height()) {
261          sAppStream << rcBody.left << " " << rcBody.bottom << " "
262                     << rcBody.Width() << " " << rcBody.Height()
263                     << " re\nW\nn\n";
264        }
265        sAppStream << "BT\n"
266                   << CPVT_GenerateAP::GenerateColorAP(crText,
267                                                       PaintOperation::FILL)
268                   << sBody << "ET\n"
269                   << "Q\nEMC\n";
270      }
271    } break;
272    case 1: {
273      CFX_WideString swValue =
274          FPDF_GetFieldAttr(pAnnotDict, "V")
275              ? FPDF_GetFieldAttr(pAnnotDict, "V")->GetUnicodeText()
276              : CFX_WideString();
277      CPVT_FontMap map(
278          pDoc, pStreamDict ? pStreamDict->GetDictFor("Resources") : nullptr,
279          pDefFont, sFontName.Right(sFontName.GetLength() - 1));
280      CPDF_VariableText::Provider prd(&map);
281      CPDF_VariableText vt;
282      vt.SetProvider(&prd);
283      CFX_FloatRect rcButton = rcBody;
284      rcButton.left = rcButton.right - 13;
285      rcButton.Normalize();
286      CFX_FloatRect rcEdit = rcBody;
287      rcEdit.right = rcButton.left;
288      rcEdit.Normalize();
289      vt.SetPlateRect(rcEdit);
290      if (IsFloatZero(fFontSize))
291        vt.SetAutoFontSize(true);
292      else
293        vt.SetFontSize(fFontSize);
294
295      vt.Initialize();
296      vt.SetText(swValue);
297      vt.RearrangeAll();
298      CFX_FloatRect rcContent = vt.GetContentRect();
299      CFX_PointF ptOffset =
300          CFX_PointF(0.0f, (rcContent.Height() - rcEdit.Height()) / 2.0f);
301      CFX_ByteString sEdit = CPVT_GenerateAP::GenerateEditAP(
302          &map, vt.GetIterator(), ptOffset, true, 0);
303      if (sEdit.GetLength() > 0) {
304        sAppStream << "/Tx BMC\n"
305                   << "q\n";
306        sAppStream << rcEdit.left << " " << rcEdit.bottom << " "
307                   << rcEdit.Width() << " " << rcEdit.Height() << " re\nW\nn\n";
308        sAppStream << "BT\n"
309                   << CPVT_GenerateAP::GenerateColorAP(crText,
310                                                       PaintOperation::FILL)
311                   << sEdit << "ET\n"
312                   << "Q\nEMC\n";
313      }
314      CFX_ByteString sButton = CPVT_GenerateAP::GenerateColorAP(
315          CPVT_Color(CPVT_Color::kRGB, 220.0f / 255.0f, 220.0f / 255.0f,
316                     220.0f / 255.0f),
317          PaintOperation::FILL);
318      if (sButton.GetLength() > 0 && !rcButton.IsEmpty()) {
319        sAppStream << "q\n" << sButton;
320        sAppStream << rcButton.left << " " << rcButton.bottom << " "
321                   << rcButton.Width() << " " << rcButton.Height() << " re f\n";
322        sAppStream << "Q\n";
323        CFX_ByteString sButtonBorder = CPVT_GenerateAP::GenerateBorderAP(
324            rcButton, 2, CPVT_Color(CPVT_Color::kGray, 0),
325            CPVT_Color(CPVT_Color::kGray, 1),
326            CPVT_Color(CPVT_Color::kGray, 0.5), BorderStyle::BEVELED,
327            CPVT_Dash(3, 0, 0));
328        if (sButtonBorder.GetLength() > 0)
329          sAppStream << "q\n" << sButtonBorder << "Q\n";
330
331        CFX_PointF ptCenter = CFX_PointF((rcButton.left + rcButton.right) / 2,
332                                         (rcButton.top + rcButton.bottom) / 2);
333        if (IsFloatBigger(rcButton.Width(), 6) &&
334            IsFloatBigger(rcButton.Height(), 6)) {
335          sAppStream << "q\n"
336                     << " 0 g\n";
337          sAppStream << ptCenter.x - 3 << " " << ptCenter.y + 1.5f << " m\n";
338          sAppStream << ptCenter.x + 3 << " " << ptCenter.y + 1.5f << " l\n";
339          sAppStream << ptCenter.x << " " << ptCenter.y - 1.5f << " l\n";
340          sAppStream << ptCenter.x - 3 << " " << ptCenter.y + 1.5f << " l f\n";
341          sAppStream << sButton << "Q\n";
342        }
343      }
344    } break;
345    case 2: {
346      CPVT_FontMap map(
347          pDoc, pStreamDict ? pStreamDict->GetDictFor("Resources") : nullptr,
348          pDefFont, sFontName.Right(sFontName.GetLength() - 1));
349      CPDF_VariableText::Provider prd(&map);
350      CPDF_Array* pOpts = ToArray(FPDF_GetFieldAttr(pAnnotDict, "Opt"));
351      CPDF_Array* pSels = ToArray(FPDF_GetFieldAttr(pAnnotDict, "I"));
352      CPDF_Object* pTi = FPDF_GetFieldAttr(pAnnotDict, "TI");
353      int32_t nTop = pTi ? pTi->GetInteger() : 0;
354      CFX_ByteTextBuf sBody;
355      if (pOpts) {
356        FX_FLOAT fy = rcBody.top;
357        for (size_t i = nTop, sz = pOpts->GetCount(); i < sz; i++) {
358          if (IsFloatSmaller(fy, rcBody.bottom))
359            break;
360
361          if (CPDF_Object* pOpt = pOpts->GetDirectObjectAt(i)) {
362            CFX_WideString swItem;
363            if (pOpt->IsString())
364              swItem = pOpt->GetUnicodeText();
365            else if (CPDF_Array* pArray = pOpt->AsArray())
366              swItem = pArray->GetDirectObjectAt(1)->GetUnicodeText();
367
368            bool bSelected = false;
369            if (pSels) {
370              for (size_t s = 0, ssz = pSels->GetCount(); s < ssz; s++) {
371                int value = pSels->GetIntegerAt(s);
372                if (value >= 0 && i == static_cast<size_t>(value)) {
373                  bSelected = true;
374                  break;
375                }
376              }
377            }
378            CPDF_VariableText vt;
379            vt.SetProvider(&prd);
380            vt.SetPlateRect(
381                CFX_FloatRect(rcBody.left, 0.0f, rcBody.right, 0.0f));
382            vt.SetFontSize(IsFloatZero(fFontSize) ? 12.0f : fFontSize);
383
384            vt.Initialize();
385            vt.SetText(swItem);
386            vt.RearrangeAll();
387            FX_FLOAT fItemHeight = vt.GetContentRect().Height();
388            if (bSelected) {
389              CFX_FloatRect rcItem = CFX_FloatRect(
390                  rcBody.left, fy - fItemHeight, rcBody.right, fy);
391              sBody << "q\n"
392                    << CPVT_GenerateAP::GenerateColorAP(
393                           CPVT_Color(CPVT_Color::kRGB, 0, 51.0f / 255.0f,
394                                      113.0f / 255.0f),
395                           PaintOperation::FILL)
396                    << rcItem.left << " " << rcItem.bottom << " "
397                    << rcItem.Width() << " " << rcItem.Height() << " re f\n"
398                    << "Q\n";
399              sBody << "BT\n"
400                    << CPVT_GenerateAP::GenerateColorAP(
401                           CPVT_Color(CPVT_Color::kGray, 1),
402                           PaintOperation::FILL)
403                    << CPVT_GenerateAP::GenerateEditAP(&map, vt.GetIterator(),
404                                                       CFX_PointF(0.0f, fy),
405                                                       true, 0)
406                    << "ET\n";
407            } else {
408              sBody << "BT\n"
409                    << CPVT_GenerateAP::GenerateColorAP(crText,
410                                                        PaintOperation::FILL)
411                    << CPVT_GenerateAP::GenerateEditAP(&map, vt.GetIterator(),
412                                                       CFX_PointF(0.0f, fy),
413                                                       true, 0)
414                    << "ET\n";
415            }
416            fy -= fItemHeight;
417          }
418        }
419      }
420      if (sBody.GetSize() > 0) {
421        sAppStream << "/Tx BMC\nq\n"
422                   << rcBody.left << " " << rcBody.bottom << " "
423                   << rcBody.Width() << " " << rcBody.Height() << " re\nW\nn\n"
424                   << sBody.AsStringC() << "Q\nEMC\n";
425      }
426    } break;
427  }
428  if (pNormalStream) {
429    pNormalStream->SetData(sAppStream.GetBuffer(), sAppStream.GetSize());
430    pStreamDict = pNormalStream->GetDict();
431    if (pStreamDict) {
432      pStreamDict->SetMatrixFor("Matrix", matrix);
433      pStreamDict->SetRectFor("BBox", rcBBox);
434      CPDF_Dictionary* pStreamResList = pStreamDict->GetDictFor("Resources");
435      if (pStreamResList) {
436        CPDF_Dictionary* pStreamResFontList =
437            pStreamResList->GetDictFor("Font");
438        if (!pStreamResFontList) {
439          pStreamResFontList =
440              pStreamResList->SetNewFor<CPDF_Dictionary>("Font");
441        }
442        if (!pStreamResFontList->KeyExist(sFontName)) {
443          pStreamResFontList->SetNewFor<CPDF_Reference>(sFontName, pDoc,
444                                                        pFontDict->GetObjNum());
445        }
446      } else {
447        pStreamDict->SetFor("Resources", pFormDict->GetDictFor("DR")->Clone());
448        pStreamResList = pStreamDict->GetDictFor("Resources");
449      }
450    }
451  }
452  return true;
453}
454
455CFX_ByteString GetColorStringWithDefault(CPDF_Array* pColor,
456                                         const CPVT_Color& crDefaultColor,
457                                         PaintOperation nOperation) {
458  if (pColor) {
459    CPVT_Color color = CPVT_Color::ParseColor(*pColor);
460    return CPVT_GenerateAP::GenerateColorAP(color, nOperation);
461  }
462
463  return CPVT_GenerateAP::GenerateColorAP(crDefaultColor, nOperation);
464}
465
466FX_FLOAT GetBorderWidth(const CPDF_Dictionary& pAnnotDict) {
467  if (CPDF_Dictionary* pBorderStyleDict = pAnnotDict.GetDictFor("BS")) {
468    if (pBorderStyleDict->KeyExist("W"))
469      return pBorderStyleDict->GetNumberFor("W");
470  }
471
472  if (CPDF_Array* pBorderArray = pAnnotDict.GetArrayFor("Border")) {
473    if (pBorderArray->GetCount() > 2)
474      return pBorderArray->GetNumberAt(2);
475  }
476
477  return 1;
478}
479
480CPDF_Array* GetDashArray(const CPDF_Dictionary& pAnnotDict) {
481  if (CPDF_Dictionary* pBorderStyleDict = pAnnotDict.GetDictFor("BS")) {
482    if (pBorderStyleDict->GetStringFor("S") == "D")
483      return pBorderStyleDict->GetArrayFor("D");
484  }
485
486  if (CPDF_Array* pBorderArray = pAnnotDict.GetArrayFor("Border")) {
487    if (pBorderArray->GetCount() == 4)
488      return pBorderArray->GetArrayAt(3);
489  }
490
491  return nullptr;
492}
493
494CFX_ByteString GetDashPatternString(const CPDF_Dictionary& pAnnotDict) {
495  CPDF_Array* pDashArray = GetDashArray(pAnnotDict);
496  if (!pDashArray || pDashArray->IsEmpty())
497    return CFX_ByteString();
498
499  // Support maximum of ten elements in the dash array.
500  size_t pDashArrayCount = std::min<size_t>(pDashArray->GetCount(), 10);
501  CFX_ByteTextBuf sDashStream;
502
503  sDashStream << "[";
504  for (size_t i = 0; i < pDashArrayCount; ++i)
505    sDashStream << pDashArray->GetNumberAt(i) << " ";
506  sDashStream << "] 0 d\n";
507
508  return sDashStream.MakeString();
509}
510
511CFX_ByteString GetPopupContentsString(CPDF_Document* pDoc,
512                                      const CPDF_Dictionary& pAnnotDict,
513                                      CPDF_Font* pDefFont,
514                                      const CFX_ByteString& sFontName) {
515  CFX_WideString swValue(pAnnotDict.GetUnicodeTextFor("T"));
516  swValue += L'\n';
517  swValue += pAnnotDict.GetUnicodeTextFor("Contents");
518  CPVT_FontMap map(pDoc, nullptr, pDefFont, sFontName);
519
520  CPDF_VariableText::Provider prd(&map);
521  CPDF_VariableText vt;
522  vt.SetProvider(&prd);
523  vt.SetPlateRect(pAnnotDict.GetRectFor("Rect"));
524  vt.SetFontSize(12);
525  vt.SetAutoReturn(true);
526  vt.SetMultiLine(true);
527
528  vt.Initialize();
529  vt.SetText(swValue);
530  vt.RearrangeAll();
531  CFX_PointF ptOffset(3.0f, -3.0f);
532  CFX_ByteString sContent = CPVT_GenerateAP::GenerateEditAP(
533      &map, vt.GetIterator(), ptOffset, false, 0);
534
535  if (sContent.IsEmpty())
536    return CFX_ByteString();
537
538  CFX_ByteTextBuf sAppStream;
539  sAppStream << "BT\n"
540             << CPVT_GenerateAP::GenerateColorAP(
541                    CPVT_Color(CPVT_Color::kRGB, 0, 0, 0), PaintOperation::FILL)
542             << sContent << "ET\n"
543             << "Q\n";
544  return sAppStream.MakeString();
545}
546
547std::unique_ptr<CPDF_Dictionary> GenerateExtGStateDict(
548    const CPDF_Dictionary& pAnnotDict,
549    const CFX_ByteString& sExtGSDictName,
550    const CFX_ByteString& sBlendMode) {
551  auto pGSDict =
552      pdfium::MakeUnique<CPDF_Dictionary>(pAnnotDict.GetByteStringPool());
553  pGSDict->SetNewFor<CPDF_String>("Type", "ExtGState", false);
554
555  FX_FLOAT fOpacity =
556      pAnnotDict.KeyExist("CA") ? pAnnotDict.GetNumberFor("CA") : 1;
557  pGSDict->SetNewFor<CPDF_Number>("CA", fOpacity);
558  pGSDict->SetNewFor<CPDF_Number>("ca", fOpacity);
559  pGSDict->SetNewFor<CPDF_Boolean>("AIS", false);
560  pGSDict->SetNewFor<CPDF_String>("BM", sBlendMode, false);
561
562  auto pExtGStateDict =
563      pdfium::MakeUnique<CPDF_Dictionary>(pAnnotDict.GetByteStringPool());
564  pExtGStateDict->SetFor(sExtGSDictName, std::move(pGSDict));
565  return pExtGStateDict;
566}
567
568std::unique_ptr<CPDF_Dictionary> GenerateResourceFontDict(
569    CPDF_Document* pDoc,
570    const CFX_ByteString& sFontDictName) {
571  CPDF_Dictionary* pFontDict = pDoc->NewIndirect<CPDF_Dictionary>();
572  pFontDict->SetNewFor<CPDF_Name>("Type", "Font");
573  pFontDict->SetNewFor<CPDF_Name>("Subtype", "Type1");
574  pFontDict->SetNewFor<CPDF_Name>("BaseFont", "Helvetica");
575  pFontDict->SetNewFor<CPDF_Name>("Encoding", "WinAnsiEncoding");
576
577  auto pResourceFontDict =
578      pdfium::MakeUnique<CPDF_Dictionary>(pDoc->GetByteStringPool());
579  pResourceFontDict->SetNewFor<CPDF_Reference>(sFontDictName, pDoc,
580                                               pFontDict->GetObjNum());
581  return pResourceFontDict;
582}
583
584std::unique_ptr<CPDF_Dictionary> GenerateResourceDict(
585    CPDF_Document* pDoc,
586    std::unique_ptr<CPDF_Dictionary> pExtGStateDict,
587    std::unique_ptr<CPDF_Dictionary> pResourceFontDict) {
588  auto pResourceDict =
589      pdfium::MakeUnique<CPDF_Dictionary>(pDoc->GetByteStringPool());
590  if (pExtGStateDict)
591    pResourceDict->SetFor("ExtGState", std::move(pExtGStateDict));
592  if (pResourceFontDict)
593    pResourceDict->SetFor("Font", std::move(pResourceFontDict));
594  return pResourceDict;
595}
596
597void GenerateAndSetAPDict(CPDF_Document* pDoc,
598                          CPDF_Dictionary* pAnnotDict,
599                          const CFX_ByteTextBuf& sAppStream,
600                          std::unique_ptr<CPDF_Dictionary> pResourceDict,
601                          bool bIsTextMarkupAnnotation) {
602  CPDF_Stream* pNormalStream = pDoc->NewIndirect<CPDF_Stream>();
603  pNormalStream->SetData(sAppStream.GetBuffer(), sAppStream.GetSize());
604
605  CPDF_Dictionary* pAPDict = pAnnotDict->SetNewFor<CPDF_Dictionary>("AP");
606  pAPDict->SetNewFor<CPDF_Reference>("N", pDoc, pNormalStream->GetObjNum());
607
608  CPDF_Dictionary* pStreamDict = pNormalStream->GetDict();
609  pStreamDict->SetNewFor<CPDF_Number>("FormType", 1);
610  pStreamDict->SetNewFor<CPDF_String>("Subtype", "Form", false);
611  pStreamDict->SetMatrixFor("Matrix", CFX_Matrix());
612
613  CFX_FloatRect rect = bIsTextMarkupAnnotation
614                           ? CPDF_Annot::RectFromQuadPoints(pAnnotDict)
615                           : pAnnotDict->GetRectFor("Rect");
616  pStreamDict->SetRectFor("BBox", rect);
617  pStreamDict->SetFor("Resources", std::move(pResourceDict));
618}
619
620CFX_ByteString GetPaintOperatorString(bool bIsStrokeRect, bool bIsFillRect) {
621  if (bIsStrokeRect)
622    return bIsFillRect ? "b" : "s";
623  return bIsFillRect ? "f" : "n";
624}
625
626CFX_ByteString GenerateTextSymbolAP(const CFX_FloatRect& rect) {
627  CFX_ByteTextBuf sAppStream;
628  sAppStream << CPVT_GenerateAP::GenerateColorAP(
629      CPVT_Color(CPVT_Color::kRGB, 1, 1, 0), PaintOperation::FILL);
630  sAppStream << CPVT_GenerateAP::GenerateColorAP(
631      CPVT_Color(CPVT_Color::kRGB, 0, 0, 0), PaintOperation::STROKE);
632
633  const FX_FLOAT fBorderWidth = 1;
634  sAppStream << fBorderWidth << " w\n";
635
636  const FX_FLOAT fHalfWidth = fBorderWidth / 2;
637  const FX_FLOAT fTipDelta = 4;
638
639  CFX_FloatRect outerRect1 = rect;
640  outerRect1.Deflate(fHalfWidth, fHalfWidth);
641  outerRect1.bottom += fTipDelta;
642
643  CFX_FloatRect outerRect2 = outerRect1;
644  outerRect2.left += fTipDelta;
645  outerRect2.right = outerRect2.left + fTipDelta;
646  outerRect2.top = outerRect2.bottom - fTipDelta;
647  FX_FLOAT outerRect2Middle = (outerRect2.left + outerRect2.right) / 2;
648
649  // Draw outer boxes.
650  sAppStream << outerRect1.left << " " << outerRect1.bottom << " m\n"
651             << outerRect1.left << " " << outerRect1.top << " l\n"
652             << outerRect1.right << " " << outerRect1.top << " l\n"
653             << outerRect1.right << " " << outerRect1.bottom << " l\n"
654             << outerRect2.right << " " << outerRect2.bottom << " l\n"
655             << outerRect2Middle << " " << outerRect2.top << " l\n"
656             << outerRect2.left << " " << outerRect2.bottom << " l\n"
657             << outerRect1.left << " " << outerRect1.bottom << " l\n";
658
659  // Draw inner lines.
660  CFX_FloatRect lineRect = outerRect1;
661  const FX_FLOAT fXDelta = 2;
662  const FX_FLOAT fYDelta = (lineRect.top - lineRect.bottom) / 4;
663
664  lineRect.left += fXDelta;
665  lineRect.right -= fXDelta;
666  for (int i = 0; i < 3; ++i) {
667    lineRect.top -= fYDelta;
668    sAppStream << lineRect.left << " " << lineRect.top << " m\n"
669               << lineRect.right << " " << lineRect.top << " l\n";
670  }
671  sAppStream << "B*\n";
672
673  return sAppStream.MakeString();
674}
675
676}  // namespace
677
678bool FPDF_GenerateAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
679  if (!pAnnotDict || pAnnotDict->GetStringFor("Subtype") != "Widget")
680    return false;
681
682  CPDF_Object* pFieldTypeObj = FPDF_GetFieldAttr(pAnnotDict, "FT");
683  if (!pFieldTypeObj)
684    return false;
685
686  CFX_ByteString field_type = pFieldTypeObj->GetString();
687  if (field_type == "Tx")
688    return CPVT_GenerateAP::GenerateTextFieldAP(pDoc, pAnnotDict);
689
690  CPDF_Object* pFieldFlagsObj = FPDF_GetFieldAttr(pAnnotDict, "Ff");
691  uint32_t flags = pFieldFlagsObj ? pFieldFlagsObj->GetInteger() : 0;
692  if (field_type == "Ch") {
693    return (flags & (1 << 17))
694               ? CPVT_GenerateAP::GenerateComboBoxAP(pDoc, pAnnotDict)
695               : CPVT_GenerateAP::GenerateListBoxAP(pDoc, pAnnotDict);
696  }
697
698  if (field_type == "Btn") {
699    if (!(flags & (1 << 16))) {
700      if (!pAnnotDict->KeyExist("AS")) {
701        if (CPDF_Dictionary* pParentDict = pAnnotDict->GetDictFor("Parent")) {
702          if (pParentDict->KeyExist("AS")) {
703            pAnnotDict->SetNewFor<CPDF_String>(
704                "AS", pParentDict->GetStringFor("AS"), false);
705          }
706        }
707      }
708    }
709  }
710
711  return false;
712}
713
714// Static.
715bool CPVT_GenerateAP::GenerateComboBoxAP(CPDF_Document* pDoc,
716                                         CPDF_Dictionary* pAnnotDict) {
717  return GenerateWidgetAP(pDoc, pAnnotDict, 1);
718}
719
720// Static.
721bool CPVT_GenerateAP::GenerateListBoxAP(CPDF_Document* pDoc,
722                                        CPDF_Dictionary* pAnnotDict) {
723  return GenerateWidgetAP(pDoc, pAnnotDict, 2);
724}
725
726// Static.
727bool CPVT_GenerateAP::GenerateTextFieldAP(CPDF_Document* pDoc,
728                                          CPDF_Dictionary* pAnnotDict) {
729  return GenerateWidgetAP(pDoc, pAnnotDict, 0);
730}
731
732bool CPVT_GenerateAP::GenerateCircleAP(CPDF_Document* pDoc,
733                                       CPDF_Dictionary* pAnnotDict) {
734  CFX_ByteTextBuf sAppStream;
735  CFX_ByteString sExtGSDictName = "GS";
736  sAppStream << "/" << sExtGSDictName << " gs ";
737
738  CPDF_Array* pInteriorColor = pAnnotDict->GetArrayFor("IC");
739  sAppStream << GetColorStringWithDefault(pInteriorColor,
740                                          CPVT_Color(CPVT_Color::kTransparent),
741                                          PaintOperation::FILL);
742
743  sAppStream << GetColorStringWithDefault(pAnnotDict->GetArrayFor("C"),
744                                          CPVT_Color(CPVT_Color::kRGB, 0, 0, 0),
745                                          PaintOperation::STROKE);
746
747  FX_FLOAT fBorderWidth = GetBorderWidth(*pAnnotDict);
748  bool bIsStrokeRect = fBorderWidth > 0;
749
750  if (bIsStrokeRect) {
751    sAppStream << fBorderWidth << " w ";
752    sAppStream << GetDashPatternString(*pAnnotDict);
753  }
754
755  CFX_FloatRect rect = pAnnotDict->GetRectFor("Rect");
756  rect.Normalize();
757
758  if (bIsStrokeRect) {
759    // Deflating rect because stroking a path entails painting all points whose
760    // perpendicular distance from the path in user space is less than or equal
761    // to half the line width.
762    rect.Deflate(fBorderWidth / 2, fBorderWidth / 2);
763  }
764
765  const FX_FLOAT fMiddleX = (rect.left + rect.right) / 2;
766  const FX_FLOAT fMiddleY = (rect.top + rect.bottom) / 2;
767
768  // |fL| is precalculated approximate value of 4 * tan((3.14 / 2) / 4) / 3,
769  // where |fL| * radius is a good approximation of control points for
770  // arc with 90 degrees.
771  const FX_FLOAT fL = 0.5523f;
772  const FX_FLOAT fDeltaX = fL * rect.Width() / 2.0;
773  const FX_FLOAT fDeltaY = fL * rect.Height() / 2.0;
774
775  // Starting point
776  sAppStream << fMiddleX << " " << rect.top << " m\n";
777  // First Bezier Curve
778  sAppStream << fMiddleX + fDeltaX << " " << rect.top << " " << rect.right
779             << " " << fMiddleY + fDeltaY << " " << rect.right << " "
780             << fMiddleY << " c\n";
781  // Second Bezier Curve
782  sAppStream << rect.right << " " << fMiddleY - fDeltaY << " "
783             << fMiddleX + fDeltaX << " " << rect.bottom << " " << fMiddleX
784             << " " << rect.bottom << " c\n";
785  // Third Bezier Curve
786  sAppStream << fMiddleX - fDeltaX << " " << rect.bottom << " " << rect.left
787             << " " << fMiddleY - fDeltaY << " " << rect.left << " " << fMiddleY
788             << " c\n";
789  // Fourth Bezier Curve
790  sAppStream << rect.left << " " << fMiddleY + fDeltaY << " "
791             << fMiddleX - fDeltaX << " " << rect.top << " " << fMiddleX << " "
792             << rect.top << " c\n";
793
794  bool bIsFillRect = pInteriorColor && !pInteriorColor->IsEmpty();
795  sAppStream << GetPaintOperatorString(bIsStrokeRect, bIsFillRect) << "\n";
796
797  auto pExtGStateDict =
798      GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
799  auto pResourceDict =
800      GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
801  GenerateAndSetAPDict(pDoc, pAnnotDict, sAppStream, std::move(pResourceDict),
802                       false /*IsTextMarkupAnnotation*/);
803  return true;
804}
805
806bool CPVT_GenerateAP::GenerateHighlightAP(CPDF_Document* pDoc,
807                                          CPDF_Dictionary* pAnnotDict) {
808  CFX_ByteTextBuf sAppStream;
809  CFX_ByteString sExtGSDictName = "GS";
810  sAppStream << "/" << sExtGSDictName << " gs ";
811
812  sAppStream << GetColorStringWithDefault(pAnnotDict->GetArrayFor("C"),
813                                          CPVT_Color(CPVT_Color::kRGB, 1, 1, 0),
814                                          PaintOperation::FILL);
815
816  CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict);
817  rect.Normalize();
818
819  sAppStream << rect.left << " " << rect.top << " m " << rect.right << " "
820             << rect.top << " l " << rect.right << " " << rect.bottom << " l "
821             << rect.left << " " << rect.bottom << " l "
822             << "h f\n";
823
824  auto pExtGStateDict =
825      GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Multiply");
826  auto pResourceDict =
827      GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
828  GenerateAndSetAPDict(pDoc, pAnnotDict, sAppStream, std::move(pResourceDict),
829                       true /*IsTextMarkupAnnotation*/);
830
831  return true;
832}
833
834bool CPVT_GenerateAP::GenerateInkAP(CPDF_Document* pDoc,
835                                    CPDF_Dictionary* pAnnotDict) {
836  FX_FLOAT fBorderWidth = GetBorderWidth(*pAnnotDict);
837  bool bIsStroke = fBorderWidth > 0;
838
839  if (!bIsStroke)
840    return false;
841
842  CPDF_Array* pInkList = pAnnotDict->GetArrayFor("InkList");
843  if (!pInkList || pInkList->IsEmpty())
844    return false;
845
846  CFX_ByteTextBuf sAppStream;
847  CFX_ByteString sExtGSDictName = "GS";
848  sAppStream << "/" << sExtGSDictName << " gs ";
849
850  sAppStream << GetColorStringWithDefault(pAnnotDict->GetArrayFor("C"),
851                                          CPVT_Color(CPVT_Color::kRGB, 0, 0, 0),
852                                          PaintOperation::STROKE);
853
854  sAppStream << fBorderWidth << " w ";
855  sAppStream << GetDashPatternString(*pAnnotDict);
856
857  // Set inflated rect as a new rect because paths near the border with large
858  // width should not be clipped to the original rect.
859  CFX_FloatRect rect = pAnnotDict->GetRectFor("Rect");
860  rect.Inflate(fBorderWidth / 2, fBorderWidth / 2);
861  pAnnotDict->SetRectFor("Rect", rect);
862
863  for (size_t i = 0; i < pInkList->GetCount(); i++) {
864    CPDF_Array* pInkCoordList = pInkList->GetArrayAt(i);
865    if (!pInkCoordList || pInkCoordList->GetCount() < 2)
866      continue;
867
868    sAppStream << pInkCoordList->GetNumberAt(0) << " "
869               << pInkCoordList->GetNumberAt(1) << " m ";
870
871    for (size_t j = 0; j < pInkCoordList->GetCount() - 1; j += 2) {
872      sAppStream << pInkCoordList->GetNumberAt(j) << " "
873                 << pInkCoordList->GetNumberAt(j + 1) << " l ";
874    }
875
876    sAppStream << "S\n";
877  }
878
879  auto pExtGStateDict =
880      GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
881  auto pResourceDict =
882      GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
883  GenerateAndSetAPDict(pDoc, pAnnotDict, sAppStream, std::move(pResourceDict),
884                       false /*IsTextMarkupAnnotation*/);
885  return true;
886}
887
888bool CPVT_GenerateAP::GenerateTextAP(CPDF_Document* pDoc,
889                                     CPDF_Dictionary* pAnnotDict) {
890  CFX_ByteTextBuf sAppStream;
891  CFX_ByteString sExtGSDictName = "GS";
892  sAppStream << "/" << sExtGSDictName << " gs ";
893
894  CFX_FloatRect rect = pAnnotDict->GetRectFor("Rect");
895  const FX_FLOAT fNoteLength = 20;
896  CFX_FloatRect noteRect(rect.left, rect.bottom, rect.left + fNoteLength,
897                         rect.bottom + fNoteLength);
898  pAnnotDict->SetRectFor("Rect", noteRect);
899
900  sAppStream << GenerateTextSymbolAP(noteRect);
901
902  auto pExtGStateDict =
903      GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
904  auto pResourceDict =
905      GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
906  GenerateAndSetAPDict(pDoc, pAnnotDict, sAppStream, std::move(pResourceDict),
907                       false /*IsTextMarkupAnnotation*/);
908  return true;
909}
910
911bool CPVT_GenerateAP::GenerateUnderlineAP(CPDF_Document* pDoc,
912                                          CPDF_Dictionary* pAnnotDict) {
913  CFX_ByteTextBuf sAppStream;
914  CFX_ByteString sExtGSDictName = "GS";
915  sAppStream << "/" << sExtGSDictName << " gs ";
916
917  sAppStream << GetColorStringWithDefault(pAnnotDict->GetArrayFor("C"),
918                                          CPVT_Color(CPVT_Color::kRGB, 0, 0, 0),
919                                          PaintOperation::STROKE);
920
921  CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict);
922  rect.Normalize();
923
924  FX_FLOAT fLineWidth = 1.0;
925  sAppStream << fLineWidth << " w " << rect.left << " "
926             << rect.bottom + fLineWidth << " m " << rect.right << " "
927             << rect.bottom + fLineWidth << " l S\n";
928
929  auto pExtGStateDict =
930      GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
931  auto pResourceDict =
932      GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
933  GenerateAndSetAPDict(pDoc, pAnnotDict, sAppStream, std::move(pResourceDict),
934                       true /*IsTextMarkupAnnotation*/);
935  return true;
936}
937
938bool CPVT_GenerateAP::GeneratePopupAP(CPDF_Document* pDoc,
939                                      CPDF_Dictionary* pAnnotDict) {
940  CFX_ByteTextBuf sAppStream;
941  CFX_ByteString sExtGSDictName = "GS";
942  sAppStream << "/" << sExtGSDictName << " gs\n";
943
944  sAppStream << GenerateColorAP(CPVT_Color(CPVT_Color::kRGB, 1, 1, 0),
945                                PaintOperation::FILL);
946  sAppStream << GenerateColorAP(CPVT_Color(CPVT_Color::kRGB, 0, 0, 0),
947                                PaintOperation::STROKE);
948
949  const FX_FLOAT fBorderWidth = 1;
950  sAppStream << fBorderWidth << " w\n";
951
952  CFX_FloatRect rect = pAnnotDict->GetRectFor("Rect");
953  rect.Normalize();
954  rect.Deflate(fBorderWidth / 2, fBorderWidth / 2);
955
956  sAppStream << rect.left << " " << rect.bottom << " " << rect.Width() << " "
957             << rect.Height() << " re b\n";
958
959  CFX_ByteString sFontName = "FONT";
960  auto pResourceFontDict = GenerateResourceFontDict(pDoc, sFontName);
961  CPDF_Font* pDefFont = pDoc->LoadFont(pResourceFontDict.get());
962  if (!pDefFont)
963    return false;
964
965  auto pExtGStateDict =
966      GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
967  auto pResourceDict = GenerateResourceDict(pDoc, std::move(pResourceFontDict),
968                                            std::move(pExtGStateDict));
969
970  sAppStream << GetPopupContentsString(pDoc, *pAnnotDict, pDefFont, sFontName);
971  GenerateAndSetAPDict(pDoc, pAnnotDict, sAppStream, std::move(pResourceDict),
972                       false /*IsTextMarkupAnnotation*/);
973  return true;
974}
975
976bool CPVT_GenerateAP::GenerateSquareAP(CPDF_Document* pDoc,
977                                       CPDF_Dictionary* pAnnotDict) {
978  CFX_ByteTextBuf sAppStream;
979  CFX_ByteString sExtGSDictName = "GS";
980  sAppStream << "/" << sExtGSDictName << " gs ";
981
982  CPDF_Array* pInteriorColor = pAnnotDict->GetArrayFor("IC");
983  sAppStream << GetColorStringWithDefault(pInteriorColor,
984                                          CPVT_Color(CPVT_Color::kTransparent),
985                                          PaintOperation::FILL);
986
987  sAppStream << GetColorStringWithDefault(pAnnotDict->GetArrayFor("C"),
988                                          CPVT_Color(CPVT_Color::kRGB, 0, 0, 0),
989                                          PaintOperation::STROKE);
990
991  FX_FLOAT fBorderWidth = GetBorderWidth(*pAnnotDict);
992  bool bIsStrokeRect = fBorderWidth > 0;
993
994  if (bIsStrokeRect) {
995    sAppStream << fBorderWidth << " w ";
996    sAppStream << GetDashPatternString(*pAnnotDict);
997  }
998
999  CFX_FloatRect rect = pAnnotDict->GetRectFor("Rect");
1000  rect.Normalize();
1001
1002  if (bIsStrokeRect) {
1003    // Deflating rect because stroking a path entails painting all points whose
1004    // perpendicular distance from the path in user space is less than or equal
1005    // to half the line width.
1006    rect.Deflate(fBorderWidth / 2, fBorderWidth / 2);
1007  }
1008
1009  bool bIsFillRect = pInteriorColor && (pInteriorColor->GetCount() > 0);
1010
1011  sAppStream << rect.left << " " << rect.bottom << " " << rect.Width() << " "
1012             << rect.Height() << " re "
1013             << GetPaintOperatorString(bIsStrokeRect, bIsFillRect) << "\n";
1014
1015  auto pExtGStateDict =
1016      GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
1017  auto pResourceDict =
1018      GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
1019  GenerateAndSetAPDict(pDoc, pAnnotDict, sAppStream, std::move(pResourceDict),
1020                       false /*IsTextMarkupAnnotation*/);
1021  return true;
1022}
1023
1024bool CPVT_GenerateAP::GenerateSquigglyAP(CPDF_Document* pDoc,
1025                                         CPDF_Dictionary* pAnnotDict) {
1026  CFX_ByteTextBuf sAppStream;
1027  CFX_ByteString sExtGSDictName = "GS";
1028  sAppStream << "/" << sExtGSDictName << " gs ";
1029
1030  sAppStream << GetColorStringWithDefault(pAnnotDict->GetArrayFor("C"),
1031                                          CPVT_Color(CPVT_Color::kRGB, 0, 0, 0),
1032                                          PaintOperation::STROKE);
1033
1034  CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict);
1035  rect.Normalize();
1036
1037  FX_FLOAT fLineWidth = 1.0;
1038  sAppStream << fLineWidth << " w ";
1039
1040  const FX_FLOAT fDelta = 2.0;
1041  const FX_FLOAT fTop = rect.bottom + fDelta;
1042  const FX_FLOAT fBottom = rect.bottom;
1043
1044  sAppStream << rect.left << " " << fTop << " m ";
1045
1046  FX_FLOAT fX = rect.left + fDelta;
1047  bool isUpwards = false;
1048
1049  while (fX < rect.right) {
1050    sAppStream << fX << " " << (isUpwards ? fTop : fBottom) << " l ";
1051
1052    fX += fDelta;
1053    isUpwards = !isUpwards;
1054  }
1055
1056  FX_FLOAT fRemainder = rect.right - (fX - fDelta);
1057  if (isUpwards)
1058    sAppStream << rect.right << " " << fBottom + fRemainder << " l ";
1059  else
1060    sAppStream << rect.right << " " << fTop - fRemainder << " l ";
1061
1062  sAppStream << "S\n";
1063
1064  auto pExtGStateDict =
1065      GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
1066  auto pResourceDict =
1067      GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
1068  GenerateAndSetAPDict(pDoc, pAnnotDict, sAppStream, std::move(pResourceDict),
1069                       true /*IsTextMarkupAnnotation*/);
1070  return true;
1071}
1072
1073bool CPVT_GenerateAP::GenerateStrikeOutAP(CPDF_Document* pDoc,
1074                                          CPDF_Dictionary* pAnnotDict) {
1075  CFX_ByteTextBuf sAppStream;
1076  CFX_ByteString sExtGSDictName = "GS";
1077  sAppStream << "/" << sExtGSDictName << " gs ";
1078
1079  sAppStream << GetColorStringWithDefault(pAnnotDict->GetArrayFor("C"),
1080                                          CPVT_Color(CPVT_Color::kRGB, 0, 0, 0),
1081                                          PaintOperation::STROKE);
1082
1083  CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict);
1084  rect.Normalize();
1085
1086  FX_FLOAT fLineWidth = 1.0;
1087  FX_FLOAT fY = (rect.top + rect.bottom) / 2;
1088  sAppStream << fLineWidth << " w " << rect.left << " " << fY << " m "
1089             << rect.right << " " << fY << " l S\n";
1090
1091  auto pExtGStateDict =
1092      GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
1093  auto pResourceDict =
1094      GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
1095  GenerateAndSetAPDict(pDoc, pAnnotDict, sAppStream, std::move(pResourceDict),
1096                       true /*IsTextMarkupAnnotation*/);
1097  return true;
1098}
1099
1100// Static.
1101CFX_ByteString CPVT_GenerateAP::GenerateEditAP(
1102    IPVT_FontMap* pFontMap,
1103    CPDF_VariableText::Iterator* pIterator,
1104    const CFX_PointF& ptOffset,
1105    bool bContinuous,
1106    uint16_t SubWord) {
1107  CFX_ByteTextBuf sEditStream;
1108  CFX_ByteTextBuf sLineStream;
1109  CFX_ByteTextBuf sWords;
1110  CFX_PointF ptOld;
1111  CFX_PointF ptNew;
1112  int32_t nCurFontIndex = -1;
1113  CPVT_WordPlace oldplace;
1114
1115  pIterator->SetAt(0);
1116  while (pIterator->NextWord()) {
1117    CPVT_WordPlace place = pIterator->GetAt();
1118    if (bContinuous) {
1119      if (place.LineCmp(oldplace) != 0) {
1120        if (sWords.GetSize() > 0) {
1121          sLineStream << GetWordRenderString(sWords.MakeString());
1122          sEditStream << sLineStream;
1123          sLineStream.Clear();
1124          sWords.Clear();
1125        }
1126        CPVT_Word word;
1127        if (pIterator->GetWord(word)) {
1128          ptNew = CFX_PointF(word.ptWord.x + ptOffset.x,
1129                             word.ptWord.y + ptOffset.y);
1130        } else {
1131          CPVT_Line line;
1132          pIterator->GetLine(line);
1133          ptNew = CFX_PointF(line.ptLine.x + ptOffset.x,
1134                             line.ptLine.y + ptOffset.y);
1135        }
1136        if (ptNew != ptOld) {
1137          sLineStream << ptNew.x - ptOld.x << " " << ptNew.y - ptOld.y
1138                      << " Td\n";
1139          ptOld = ptNew;
1140        }
1141      }
1142      CPVT_Word word;
1143      if (pIterator->GetWord(word)) {
1144        if (word.nFontIndex != nCurFontIndex) {
1145          if (sWords.GetSize() > 0) {
1146            sLineStream << GetWordRenderString(sWords.MakeString());
1147            sWords.Clear();
1148          }
1149          sLineStream << GetFontSetString(pFontMap, word.nFontIndex,
1150                                          word.fFontSize);
1151          nCurFontIndex = word.nFontIndex;
1152        }
1153        sWords << GetPDFWordString(pFontMap, nCurFontIndex, word.Word, SubWord);
1154      }
1155      oldplace = place;
1156    } else {
1157      CPVT_Word word;
1158      if (pIterator->GetWord(word)) {
1159        ptNew =
1160            CFX_PointF(word.ptWord.x + ptOffset.x, word.ptWord.y + ptOffset.y);
1161        if (ptNew != ptOld) {
1162          sEditStream << ptNew.x - ptOld.x << " " << ptNew.y - ptOld.y
1163                      << " Td\n";
1164          ptOld = ptNew;
1165        }
1166        if (word.nFontIndex != nCurFontIndex) {
1167          sEditStream << GetFontSetString(pFontMap, word.nFontIndex,
1168                                          word.fFontSize);
1169          nCurFontIndex = word.nFontIndex;
1170        }
1171        sEditStream << GetWordRenderString(
1172            GetPDFWordString(pFontMap, nCurFontIndex, word.Word, SubWord));
1173      }
1174    }
1175  }
1176  if (sWords.GetSize() > 0) {
1177    sLineStream << GetWordRenderString(sWords.MakeString());
1178    sEditStream << sLineStream;
1179    sWords.Clear();
1180  }
1181  return sEditStream.MakeString();
1182}
1183
1184// Static.
1185CFX_ByteString CPVT_GenerateAP::GenerateBorderAP(
1186    const CFX_FloatRect& rect,
1187    FX_FLOAT fWidth,
1188    const CPVT_Color& color,
1189    const CPVT_Color& crLeftTop,
1190    const CPVT_Color& crRightBottom,
1191    BorderStyle nStyle,
1192    const CPVT_Dash& dash) {
1193  CFX_ByteTextBuf sAppStream;
1194  CFX_ByteString sColor;
1195  FX_FLOAT fLeft = rect.left;
1196  FX_FLOAT fRight = rect.right;
1197  FX_FLOAT fTop = rect.top;
1198  FX_FLOAT fBottom = rect.bottom;
1199  if (fWidth > 0.0f) {
1200    FX_FLOAT fHalfWidth = fWidth / 2.0f;
1201    switch (nStyle) {
1202      default:
1203      case BorderStyle::SOLID:
1204        sColor = GenerateColorAP(color, PaintOperation::FILL);
1205        if (sColor.GetLength() > 0) {
1206          sAppStream << sColor;
1207          sAppStream << fLeft << " " << fBottom << " " << fRight - fLeft << " "
1208                     << fTop - fBottom << " re\n";
1209          sAppStream << fLeft + fWidth << " " << fBottom + fWidth << " "
1210                     << fRight - fLeft - fWidth * 2 << " "
1211                     << fTop - fBottom - fWidth * 2 << " re\n";
1212          sAppStream << "f*\n";
1213        }
1214        break;
1215      case BorderStyle::DASH:
1216        sColor = GenerateColorAP(color, PaintOperation::STROKE);
1217        if (sColor.GetLength() > 0) {
1218          sAppStream << sColor;
1219          sAppStream << fWidth << " w"
1220                     << " [" << dash.nDash << " " << dash.nGap << "] "
1221                     << dash.nPhase << " d\n";
1222          sAppStream << fLeft + fWidth / 2 << " " << fBottom + fWidth / 2
1223                     << " m\n";
1224          sAppStream << fLeft + fWidth / 2 << " " << fTop - fWidth / 2
1225                     << " l\n";
1226          sAppStream << fRight - fWidth / 2 << " " << fTop - fWidth / 2
1227                     << " l\n";
1228          sAppStream << fRight - fWidth / 2 << " " << fBottom + fWidth / 2
1229                     << " l\n";
1230          sAppStream << fLeft + fWidth / 2 << " " << fBottom + fWidth / 2
1231                     << " l S\n";
1232        }
1233        break;
1234      case BorderStyle::BEVELED:
1235      case BorderStyle::INSET:
1236        sColor = GenerateColorAP(crLeftTop, PaintOperation::FILL);
1237        if (sColor.GetLength() > 0) {
1238          sAppStream << sColor;
1239          sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth
1240                     << " m\n";
1241          sAppStream << fLeft + fHalfWidth << " " << fTop - fHalfWidth
1242                     << " l\n";
1243          sAppStream << fRight - fHalfWidth << " " << fTop - fHalfWidth
1244                     << " l\n";
1245          sAppStream << fRight - fHalfWidth * 2 << " " << fTop - fHalfWidth * 2
1246                     << " l\n";
1247          sAppStream << fLeft + fHalfWidth * 2 << " " << fTop - fHalfWidth * 2
1248                     << " l\n";
1249          sAppStream << fLeft + fHalfWidth * 2 << " "
1250                     << fBottom + fHalfWidth * 2 << " l f\n";
1251        }
1252        sColor = GenerateColorAP(crRightBottom, PaintOperation::FILL);
1253        if (sColor.GetLength() > 0) {
1254          sAppStream << sColor;
1255          sAppStream << fRight - fHalfWidth << " " << fTop - fHalfWidth
1256                     << " m\n";
1257          sAppStream << fRight - fHalfWidth << " " << fBottom + fHalfWidth
1258                     << " l\n";
1259          sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth
1260                     << " l\n";
1261          sAppStream << fLeft + fHalfWidth * 2 << " "
1262                     << fBottom + fHalfWidth * 2 << " l\n";
1263          sAppStream << fRight - fHalfWidth * 2 << " "
1264                     << fBottom + fHalfWidth * 2 << " l\n";
1265          sAppStream << fRight - fHalfWidth * 2 << " " << fTop - fHalfWidth * 2
1266                     << " l f\n";
1267        }
1268        sColor = GenerateColorAP(color, PaintOperation::FILL);
1269        if (sColor.GetLength() > 0) {
1270          sAppStream << sColor;
1271          sAppStream << fLeft << " " << fBottom << " " << fRight - fLeft << " "
1272                     << fTop - fBottom << " re\n";
1273          sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth << " "
1274                     << fRight - fLeft - fHalfWidth * 2 << " "
1275                     << fTop - fBottom - fHalfWidth * 2 << " re f*\n";
1276        }
1277        break;
1278      case BorderStyle::UNDERLINE:
1279        sColor = GenerateColorAP(color, PaintOperation::STROKE);
1280        if (sColor.GetLength() > 0) {
1281          sAppStream << sColor;
1282          sAppStream << fWidth << " w\n";
1283          sAppStream << fLeft << " " << fBottom + fWidth / 2 << " m\n";
1284          sAppStream << fRight << " " << fBottom + fWidth / 2 << " l S\n";
1285        }
1286        break;
1287    }
1288  }
1289  return sAppStream.MakeString();
1290}
1291
1292// Static.
1293CFX_ByteString CPVT_GenerateAP::GenerateColorAP(const CPVT_Color& color,
1294                                                PaintOperation nOperation) {
1295  CFX_ByteTextBuf sColorStream;
1296  switch (color.nColorType) {
1297    case CPVT_Color::kRGB:
1298      sColorStream << color.fColor1 << " " << color.fColor2 << " "
1299                   << color.fColor3 << " "
1300                   << (nOperation == PaintOperation::STROKE ? "RG" : "rg")
1301                   << "\n";
1302      break;
1303    case CPVT_Color::kGray:
1304      sColorStream << color.fColor1 << " "
1305                   << (nOperation == PaintOperation::STROKE ? "G" : "g")
1306                   << "\n";
1307      break;
1308    case CPVT_Color::kCMYK:
1309      sColorStream << color.fColor1 << " " << color.fColor2 << " "
1310                   << color.fColor3 << " " << color.fColor4 << " "
1311                   << (nOperation == PaintOperation::STROKE ? "K" : "k")
1312                   << "\n";
1313      break;
1314    case CPVT_Color::kTransparent:
1315      break;
1316  }
1317  return sColorStream.MakeString();
1318}
1319
1320// Static.
1321CFX_ByteString CPVT_GenerateAP::GetPDFWordString(IPVT_FontMap* pFontMap,
1322                                                 int32_t nFontIndex,
1323                                                 uint16_t Word,
1324                                                 uint16_t SubWord) {
1325  CFX_ByteString sWord;
1326  if (SubWord > 0) {
1327    sWord.Format("%c", SubWord);
1328    return sWord;
1329  }
1330
1331  if (!pFontMap)
1332    return sWord;
1333
1334  if (CPDF_Font* pPDFFont = pFontMap->GetPDFFont(nFontIndex)) {
1335    if (pPDFFont->GetBaseFont().Compare("Symbol") == 0 ||
1336        pPDFFont->GetBaseFont().Compare("ZapfDingbats") == 0) {
1337      sWord.Format("%c", Word);
1338    } else {
1339      uint32_t dwCharCode = pPDFFont->CharCodeFromUnicode(Word);
1340      if (dwCharCode != CPDF_Font::kInvalidCharCode)
1341        pPDFFont->AppendChar(sWord, dwCharCode);
1342    }
1343  }
1344  return sWord;
1345}
1346
1347// Static.
1348CFX_ByteString CPVT_GenerateAP::GetWordRenderString(
1349    const CFX_ByteString& strWords) {
1350  if (strWords.GetLength() > 0)
1351    return PDF_EncodeString(strWords) + " Tj\n";
1352  return "";
1353}
1354
1355// Static.
1356CFX_ByteString CPVT_GenerateAP::GetFontSetString(IPVT_FontMap* pFontMap,
1357                                                 int32_t nFontIndex,
1358                                                 FX_FLOAT fFontSize) {
1359  CFX_ByteTextBuf sRet;
1360  if (pFontMap) {
1361    CFX_ByteString sFontAlias = pFontMap->GetPDFFontAlias(nFontIndex);
1362    if (sFontAlias.GetLength() > 0 && fFontSize > 0)
1363      sRet << "/" << sFontAlias << " " << fFontSize << " Tf\n";
1364  }
1365  return sRet.MakeString();
1366}
1367