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