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