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