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
31 namespace {
32
GenerateWidgetAP(CPDF_Document * pDoc,CPDF_Dictionary * pAnnotDict,const int32_t & nWidgetType)33 bool 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
GetColorStringWithDefault(CPDF_Array * pColor,const CPVT_Color & crDefaultColor,PaintOperation nOperation)455 CFX_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
GetBorderWidth(const CPDF_Dictionary & pAnnotDict)466 FX_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
GetDashArray(const CPDF_Dictionary & pAnnotDict)480 CPDF_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
GetDashPatternString(const CPDF_Dictionary & pAnnotDict)494 CFX_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
GetPopupContentsString(CPDF_Document * pDoc,const CPDF_Dictionary & pAnnotDict,CPDF_Font * pDefFont,const CFX_ByteString & sFontName)511 CFX_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
GenerateExtGStateDict(const CPDF_Dictionary & pAnnotDict,const CFX_ByteString & sExtGSDictName,const CFX_ByteString & sBlendMode)547 std::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
GenerateResourceFontDict(CPDF_Document * pDoc,const CFX_ByteString & sFontDictName)568 std::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
GenerateResourceDict(CPDF_Document * pDoc,std::unique_ptr<CPDF_Dictionary> pExtGStateDict,std::unique_ptr<CPDF_Dictionary> pResourceFontDict)584 std::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
GenerateAndSetAPDict(CPDF_Document * pDoc,CPDF_Dictionary * pAnnotDict,const CFX_ByteTextBuf & sAppStream,std::unique_ptr<CPDF_Dictionary> pResourceDict,bool bIsTextMarkupAnnotation)597 void 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
GetPaintOperatorString(bool bIsStrokeRect,bool bIsFillRect)620 CFX_ByteString GetPaintOperatorString(bool bIsStrokeRect, bool bIsFillRect) {
621 if (bIsStrokeRect)
622 return bIsFillRect ? "b" : "s";
623 return bIsFillRect ? "f" : "n";
624 }
625
GenerateTextSymbolAP(const CFX_FloatRect & rect)626 CFX_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
FPDF_GenerateAP(CPDF_Document * pDoc,CPDF_Dictionary * pAnnotDict)678 bool 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.
GenerateComboBoxAP(CPDF_Document * pDoc,CPDF_Dictionary * pAnnotDict)715 bool CPVT_GenerateAP::GenerateComboBoxAP(CPDF_Document* pDoc,
716 CPDF_Dictionary* pAnnotDict) {
717 return GenerateWidgetAP(pDoc, pAnnotDict, 1);
718 }
719
720 // Static.
GenerateListBoxAP(CPDF_Document * pDoc,CPDF_Dictionary * pAnnotDict)721 bool CPVT_GenerateAP::GenerateListBoxAP(CPDF_Document* pDoc,
722 CPDF_Dictionary* pAnnotDict) {
723 return GenerateWidgetAP(pDoc, pAnnotDict, 2);
724 }
725
726 // Static.
GenerateTextFieldAP(CPDF_Document * pDoc,CPDF_Dictionary * pAnnotDict)727 bool CPVT_GenerateAP::GenerateTextFieldAP(CPDF_Document* pDoc,
728 CPDF_Dictionary* pAnnotDict) {
729 return GenerateWidgetAP(pDoc, pAnnotDict, 0);
730 }
731
GenerateCircleAP(CPDF_Document * pDoc,CPDF_Dictionary * pAnnotDict)732 bool 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
GenerateHighlightAP(CPDF_Document * pDoc,CPDF_Dictionary * pAnnotDict)806 bool 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
GenerateInkAP(CPDF_Document * pDoc,CPDF_Dictionary * pAnnotDict)834 bool 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
GenerateTextAP(CPDF_Document * pDoc,CPDF_Dictionary * pAnnotDict)888 bool 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
GenerateUnderlineAP(CPDF_Document * pDoc,CPDF_Dictionary * pAnnotDict)911 bool 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
GeneratePopupAP(CPDF_Document * pDoc,CPDF_Dictionary * pAnnotDict)938 bool 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
GenerateSquareAP(CPDF_Document * pDoc,CPDF_Dictionary * pAnnotDict)976 bool 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
GenerateSquigglyAP(CPDF_Document * pDoc,CPDF_Dictionary * pAnnotDict)1024 bool 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
GenerateStrikeOutAP(CPDF_Document * pDoc,CPDF_Dictionary * pAnnotDict)1073 bool 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.
GenerateEditAP(IPVT_FontMap * pFontMap,CPDF_VariableText::Iterator * pIterator,const CFX_PointF & ptOffset,bool bContinuous,uint16_t SubWord)1101 CFX_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.
GenerateBorderAP(const CFX_FloatRect & rect,FX_FLOAT fWidth,const CPVT_Color & color,const CPVT_Color & crLeftTop,const CPVT_Color & crRightBottom,BorderStyle nStyle,const CPVT_Dash & dash)1185 CFX_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.
GenerateColorAP(const CPVT_Color & color,PaintOperation nOperation)1293 CFX_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.
GetPDFWordString(IPVT_FontMap * pFontMap,int32_t nFontIndex,uint16_t Word,uint16_t SubWord)1321 CFX_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.
GetWordRenderString(const CFX_ByteString & strWords)1348 CFX_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.
GetFontSetString(IPVT_FontMap * pFontMap,int32_t nFontIndex,FX_FLOAT fFontSize)1356 CFX_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