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__anon182c7e500111::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 default:
222 case BorderStyle::kSolid:
223 sColor = GenerateColorAP(color, PaintOperation::kFill);
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::kDash:
235 sColor = GenerateColorAP(color, PaintOperation::kStroke);
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::kBeveled:
254 case BorderStyle::kInset:
255 sColor = GenerateColorAP(crLeftTop, PaintOperation::kFill);
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::kFill);
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::kFill);
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::kUnderline:
298 sColor = GenerateColorAP(color, PaintOperation::kStroke);
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(const CPDF_Array * pColor,const CFX_Color & crDefaultColor,PaintOperation nOperation)311 ByteString GetColorStringWithDefault(const 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 * pDict)322 float GetBorderWidth(const CPDF_Dictionary* pDict) {
323 RetainPtr<const CPDF_Dictionary> pBorderStyleDict = pDict->GetDictFor("BS");
324 if (pBorderStyleDict && pBorderStyleDict->KeyExist("W"))
325 return pBorderStyleDict->GetFloatFor("W");
326
327 auto pBorderArray = pDict->GetArrayFor(pdfium::annotation::kBorder);
328 if (pBorderArray && pBorderArray->size() > 2)
329 return pBorderArray->GetFloatAt(2);
330
331 return 1;
332 }
333
GetDashArray(const CPDF_Dictionary * pDict)334 RetainPtr<const CPDF_Array> GetDashArray(const CPDF_Dictionary* pDict) {
335 RetainPtr<const CPDF_Dictionary> pBorderStyleDict = pDict->GetDictFor("BS");
336 if (pBorderStyleDict && pBorderStyleDict->GetByteStringFor("S") == "D")
337 return pBorderStyleDict->GetArrayFor("D");
338
339 RetainPtr<const CPDF_Array> pBorderArray =
340 pDict->GetArrayFor(pdfium::annotation::kBorder);
341 if (pBorderArray && pBorderArray->size() == 4)
342 return pBorderArray->GetArrayAt(3);
343
344 return nullptr;
345 }
346
GetDashPatternString(const CPDF_Dictionary * pDict)347 ByteString GetDashPatternString(const CPDF_Dictionary* pDict) {
348 RetainPtr<const CPDF_Array> pDashArray = GetDashArray(pDict);
349 if (!pDashArray || pDashArray->IsEmpty())
350 return ByteString();
351
352 // Support maximum of ten elements in the dash array.
353 size_t pDashArrayCount = std::min<size_t>(pDashArray->size(), 10);
354 fxcrt::ostringstream sDashStream;
355
356 sDashStream << "[";
357 for (size_t i = 0; i < pDashArrayCount; ++i)
358 sDashStream << pDashArray->GetFloatAt(i) << " ";
359 sDashStream << "] 0 d\n";
360
361 return ByteString(sDashStream);
362 }
363
GetPopupContentsString(CPDF_Document * pDoc,const CPDF_Dictionary & pAnnotDict,RetainPtr<CPDF_Font> pDefFont,const ByteString & sFontName)364 ByteString GetPopupContentsString(CPDF_Document* pDoc,
365 const CPDF_Dictionary& pAnnotDict,
366 RetainPtr<CPDF_Font> pDefFont,
367 const ByteString& sFontName) {
368 WideString swValue(pAnnotDict.GetUnicodeTextFor(pdfium::form_fields::kT));
369 swValue += L'\n';
370 swValue += pAnnotDict.GetUnicodeTextFor(pdfium::annotation::kContents);
371
372 CPVT_FontMap map(pDoc, nullptr, std::move(pDefFont), sFontName);
373 CPVT_VariableText::Provider prd(&map);
374 CPVT_VariableText vt(&prd);
375 vt.SetPlateRect(pAnnotDict.GetRectFor(pdfium::annotation::kRect));
376 vt.SetFontSize(12);
377 vt.SetAutoReturn(true);
378 vt.SetMultiLine(true);
379 vt.Initialize();
380 vt.SetText(swValue);
381 vt.RearrangeAll();
382
383 CFX_PointF ptOffset(3.0f, -3.0f);
384 ByteString sContent =
385 GenerateEditAP(&map, vt.GetIterator(), ptOffset, false, 0);
386
387 if (sContent.IsEmpty())
388 return ByteString();
389
390 ByteString sColorAP = GenerateColorAP(
391 CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0), PaintOperation::kFill);
392
393 return ByteString{"BT\n", sColorAP.AsStringView(), sContent.AsStringView(),
394 "ET\n", "Q\n"};
395 }
396
GenerateResourceFontDict(CPDF_Document * pDoc,const ByteString & sFontDictName)397 RetainPtr<CPDF_Dictionary> GenerateResourceFontDict(
398 CPDF_Document* pDoc,
399 const ByteString& sFontDictName) {
400 auto pFontDict = pDoc->NewIndirect<CPDF_Dictionary>();
401 pFontDict->SetNewFor<CPDF_Name>("Type", "Font");
402 pFontDict->SetNewFor<CPDF_Name>("Subtype", "Type1");
403 pFontDict->SetNewFor<CPDF_Name>("BaseFont", CFX_Font::kDefaultAnsiFontName);
404 pFontDict->SetNewFor<CPDF_Name>("Encoding",
405 pdfium::font_encodings::kWinAnsiEncoding);
406
407 auto pResourceFontDict = pDoc->New<CPDF_Dictionary>();
408 pResourceFontDict->SetNewFor<CPDF_Reference>(sFontDictName, pDoc,
409 pFontDict->GetObjNum());
410 return pResourceFontDict;
411 }
412
GetPaintOperatorString(bool bIsStrokeRect,bool bIsFillRect)413 ByteString GetPaintOperatorString(bool bIsStrokeRect, bool bIsFillRect) {
414 if (bIsStrokeRect)
415 return bIsFillRect ? "b" : "s";
416 return bIsFillRect ? "f" : "n";
417 }
418
GenerateTextSymbolAP(const CFX_FloatRect & rect)419 ByteString GenerateTextSymbolAP(const CFX_FloatRect& rect) {
420 fxcrt::ostringstream sAppStream;
421 sAppStream << GenerateColorAP(CFX_Color(CFX_Color::Type::kRGB, 1, 1, 0),
422 PaintOperation::kFill);
423 sAppStream << GenerateColorAP(CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0),
424 PaintOperation::kStroke);
425
426 const float fBorderWidth = 1;
427 sAppStream << fBorderWidth << " w\n";
428
429 const float fHalfWidth = fBorderWidth / 2;
430 const float fTipDelta = 4;
431
432 CFX_FloatRect outerRect1 = rect;
433 outerRect1.Deflate(fHalfWidth, fHalfWidth);
434 outerRect1.bottom += fTipDelta;
435
436 CFX_FloatRect outerRect2 = outerRect1;
437 outerRect2.left += fTipDelta;
438 outerRect2.right = outerRect2.left + fTipDelta;
439 outerRect2.top = outerRect2.bottom - fTipDelta;
440 float outerRect2Middle = (outerRect2.left + outerRect2.right) / 2;
441
442 // Draw outer boxes.
443 sAppStream << outerRect1.left << " " << outerRect1.bottom << " m\n"
444 << outerRect1.left << " " << outerRect1.top << " l\n"
445 << outerRect1.right << " " << outerRect1.top << " l\n"
446 << outerRect1.right << " " << outerRect1.bottom << " l\n"
447 << outerRect2.right << " " << outerRect2.bottom << " l\n"
448 << outerRect2Middle << " " << outerRect2.top << " l\n"
449 << outerRect2.left << " " << outerRect2.bottom << " l\n"
450 << outerRect1.left << " " << outerRect1.bottom << " l\n";
451
452 // Draw inner lines.
453 CFX_FloatRect lineRect = outerRect1;
454 const float fXDelta = 2;
455 const float fYDelta = (lineRect.top - lineRect.bottom) / 4;
456
457 lineRect.left += fXDelta;
458 lineRect.right -= fXDelta;
459 for (int i = 0; i < 3; ++i) {
460 lineRect.top -= fYDelta;
461 sAppStream << lineRect.left << " " << lineRect.top << " m\n"
462 << lineRect.right << " " << lineRect.top << " l\n";
463 }
464 sAppStream << "B*\n";
465
466 return ByteString(sAppStream);
467 }
468
GenerateExtGStateDict(const CPDF_Dictionary & pAnnotDict,const ByteString & sExtGSDictName,const ByteString & sBlendMode)469 RetainPtr<CPDF_Dictionary> GenerateExtGStateDict(
470 const CPDF_Dictionary& pAnnotDict,
471 const ByteString& sExtGSDictName,
472 const ByteString& sBlendMode) {
473 auto pGSDict =
474 pdfium::MakeRetain<CPDF_Dictionary>(pAnnotDict.GetByteStringPool());
475 pGSDict->SetNewFor<CPDF_Name>("Type", "ExtGState");
476
477 float fOpacity = pAnnotDict.KeyExist("CA") ? pAnnotDict.GetFloatFor("CA") : 1;
478 pGSDict->SetNewFor<CPDF_Number>("CA", fOpacity);
479 pGSDict->SetNewFor<CPDF_Number>("ca", fOpacity);
480 pGSDict->SetNewFor<CPDF_Boolean>("AIS", false);
481 pGSDict->SetNewFor<CPDF_Name>("BM", sBlendMode);
482
483 auto pExtGStateDict =
484 pdfium::MakeRetain<CPDF_Dictionary>(pAnnotDict.GetByteStringPool());
485 pExtGStateDict->SetFor(sExtGSDictName, pGSDict);
486 return pExtGStateDict;
487 }
488
GenerateResourceDict(CPDF_Document * pDoc,RetainPtr<CPDF_Dictionary> pExtGStateDict,RetainPtr<CPDF_Dictionary> pResourceFontDict)489 RetainPtr<CPDF_Dictionary> GenerateResourceDict(
490 CPDF_Document* pDoc,
491 RetainPtr<CPDF_Dictionary> pExtGStateDict,
492 RetainPtr<CPDF_Dictionary> pResourceFontDict) {
493 auto pResourceDict = pDoc->New<CPDF_Dictionary>();
494 if (pExtGStateDict)
495 pResourceDict->SetFor("ExtGState", pExtGStateDict);
496 if (pResourceFontDict)
497 pResourceDict->SetFor("Font", pResourceFontDict);
498 return pResourceDict;
499 }
500
GenerateAndSetAPDict(CPDF_Document * pDoc,CPDF_Dictionary * pAnnotDict,fxcrt::ostringstream * psAppStream,RetainPtr<CPDF_Dictionary> pResourceDict,bool bIsTextMarkupAnnotation)501 void GenerateAndSetAPDict(CPDF_Document* pDoc,
502 CPDF_Dictionary* pAnnotDict,
503 fxcrt::ostringstream* psAppStream,
504 RetainPtr<CPDF_Dictionary> pResourceDict,
505 bool bIsTextMarkupAnnotation) {
506 auto pNormalStream = pDoc->NewIndirect<CPDF_Stream>();
507 pNormalStream->SetDataFromStringstream(psAppStream);
508
509 RetainPtr<CPDF_Dictionary> pAPDict =
510 pAnnotDict->GetOrCreateDictFor(pdfium::annotation::kAP);
511 pAPDict->SetNewFor<CPDF_Reference>("N", pDoc, pNormalStream->GetObjNum());
512
513 RetainPtr<CPDF_Dictionary> pStreamDict = pNormalStream->GetMutableDict();
514 pStreamDict->SetNewFor<CPDF_Number>("FormType", 1);
515 pStreamDict->SetNewFor<CPDF_Name>("Type", "XObject");
516 pStreamDict->SetNewFor<CPDF_Name>("Subtype", "Form");
517 pStreamDict->SetMatrixFor("Matrix", CFX_Matrix());
518
519 CFX_FloatRect rect = bIsTextMarkupAnnotation
520 ? CPDF_Annot::BoundingRectFromQuadPoints(pAnnotDict)
521 : pAnnotDict->GetRectFor(pdfium::annotation::kRect);
522 pStreamDict->SetRectFor("BBox", rect);
523 pStreamDict->SetFor("Resources", pResourceDict);
524 }
525
GenerateCircleAP(CPDF_Document * pDoc,CPDF_Dictionary * pAnnotDict)526 bool GenerateCircleAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
527 fxcrt::ostringstream sAppStream;
528 ByteString sExtGSDictName = "GS";
529 sAppStream << "/" << sExtGSDictName << " gs ";
530
531 RetainPtr<const CPDF_Array> pInteriorColor = pAnnotDict->GetArrayFor("IC");
532 sAppStream << GetColorStringWithDefault(
533 pInteriorColor.Get(), CFX_Color(CFX_Color::Type::kTransparent),
534 PaintOperation::kFill);
535
536 sAppStream << GetColorStringWithDefault(
537 pAnnotDict->GetArrayFor(pdfium::annotation::kC).Get(),
538 CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0), PaintOperation::kStroke);
539
540 float fBorderWidth = GetBorderWidth(pAnnotDict);
541 bool bIsStrokeRect = fBorderWidth > 0;
542
543 if (bIsStrokeRect) {
544 sAppStream << fBorderWidth << " w ";
545 sAppStream << GetDashPatternString(pAnnotDict);
546 }
547
548 CFX_FloatRect rect = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
549 rect.Normalize();
550
551 if (bIsStrokeRect) {
552 // Deflating rect because stroking a path entails painting all points
553 // whose perpendicular distance from the path in user space is less than
554 // or equal to half the line width.
555 rect.Deflate(fBorderWidth / 2, fBorderWidth / 2);
556 }
557
558 const float fMiddleX = (rect.left + rect.right) / 2;
559 const float fMiddleY = (rect.top + rect.bottom) / 2;
560
561 // |fL| is precalculated approximate value of 4 * tan((3.14 / 2) / 4) / 3,
562 // where |fL| * radius is a good approximation of control points for
563 // arc with 90 degrees.
564 const float fL = 0.5523f;
565 const float fDeltaX = fL * rect.Width() / 2.0;
566 const float fDeltaY = fL * rect.Height() / 2.0;
567
568 // Starting point
569 sAppStream << fMiddleX << " " << rect.top << " m\n";
570 // First Bezier Curve
571 sAppStream << fMiddleX + fDeltaX << " " << rect.top << " " << rect.right
572 << " " << fMiddleY + fDeltaY << " " << rect.right << " "
573 << fMiddleY << " c\n";
574 // Second Bezier Curve
575 sAppStream << rect.right << " " << fMiddleY - fDeltaY << " "
576 << fMiddleX + fDeltaX << " " << rect.bottom << " " << fMiddleX
577 << " " << rect.bottom << " c\n";
578 // Third Bezier Curve
579 sAppStream << fMiddleX - fDeltaX << " " << rect.bottom << " " << rect.left
580 << " " << fMiddleY - fDeltaY << " " << rect.left << " " << fMiddleY
581 << " c\n";
582 // Fourth Bezier Curve
583 sAppStream << rect.left << " " << fMiddleY + fDeltaY << " "
584 << fMiddleX - fDeltaX << " " << rect.top << " " << fMiddleX << " "
585 << rect.top << " c\n";
586
587 bool bIsFillRect = pInteriorColor && !pInteriorColor->IsEmpty();
588 sAppStream << GetPaintOperatorString(bIsStrokeRect, bIsFillRect) << "\n";
589
590 auto pExtGStateDict =
591 GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
592 auto pResourceDict =
593 GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
594 GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
595 false /*IsTextMarkupAnnotation*/);
596 return true;
597 }
598
GenerateHighlightAP(CPDF_Document * pDoc,CPDF_Dictionary * pAnnotDict)599 bool GenerateHighlightAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
600 fxcrt::ostringstream sAppStream;
601 ByteString sExtGSDictName = "GS";
602 sAppStream << "/" << sExtGSDictName << " gs ";
603
604 sAppStream << GetColorStringWithDefault(
605 pAnnotDict->GetArrayFor(pdfium::annotation::kC).Get(),
606 CFX_Color(CFX_Color::Type::kRGB, 1, 1, 0), PaintOperation::kFill);
607
608 RetainPtr<const CPDF_Array> pArray = pAnnotDict->GetArrayFor("QuadPoints");
609 if (pArray) {
610 size_t nQuadPointCount = CPDF_Annot::QuadPointCount(pArray.Get());
611 for (size_t i = 0; i < nQuadPointCount; ++i) {
612 CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict, i);
613 rect.Normalize();
614
615 sAppStream << rect.left << " " << rect.top << " m " << rect.right << " "
616 << rect.top << " l " << rect.right << " " << rect.bottom
617 << " l " << rect.left << " " << rect.bottom << " l h f\n";
618 }
619 }
620
621 auto pExtGStateDict =
622 GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Multiply");
623 auto pResourceDict =
624 GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
625 GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
626 true /*IsTextMarkupAnnotation*/);
627
628 return true;
629 }
630
GenerateInkAP(CPDF_Document * pDoc,CPDF_Dictionary * pAnnotDict)631 bool GenerateInkAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
632 RetainPtr<const CPDF_Array> pInkList = pAnnotDict->GetArrayFor("InkList");
633 if (!pInkList || pInkList->IsEmpty())
634 return false;
635
636 float fBorderWidth = GetBorderWidth(pAnnotDict);
637 const bool bIsStroke = fBorderWidth > 0;
638 if (!bIsStroke)
639 return false;
640
641 ByteString sExtGSDictName = "GS";
642 fxcrt::ostringstream sAppStream;
643 sAppStream << "/" << sExtGSDictName << " gs ";
644 sAppStream << GetColorStringWithDefault(
645 pAnnotDict->GetArrayFor(pdfium::annotation::kC).Get(),
646 CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0), PaintOperation::kStroke);
647
648 sAppStream << fBorderWidth << " w ";
649 sAppStream << GetDashPatternString(pAnnotDict);
650
651 // Set inflated rect as a new rect because paths near the border with large
652 // width should not be clipped to the original rect.
653 CFX_FloatRect rect = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
654 rect.Inflate(fBorderWidth / 2, fBorderWidth / 2);
655 pAnnotDict->SetRectFor(pdfium::annotation::kRect, rect);
656
657 for (size_t i = 0; i < pInkList->size(); i++) {
658 RetainPtr<const CPDF_Array> pInkCoordList = pInkList->GetArrayAt(i);
659 if (!pInkCoordList || pInkCoordList->size() < 2)
660 continue;
661
662 sAppStream << pInkCoordList->GetFloatAt(0) << " "
663 << pInkCoordList->GetFloatAt(1) << " m ";
664
665 for (size_t j = 0; j < pInkCoordList->size() - 1; j += 2) {
666 sAppStream << pInkCoordList->GetFloatAt(j) << " "
667 << pInkCoordList->GetFloatAt(j + 1) << " l ";
668 }
669
670 sAppStream << "S\n";
671 }
672
673 auto pExtGStateDict =
674 GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
675 auto pResourceDict =
676 GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
677 GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
678 false /*IsTextMarkupAnnotation*/);
679 return true;
680 }
681
GenerateTextAP(CPDF_Document * pDoc,CPDF_Dictionary * pAnnotDict)682 bool GenerateTextAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
683 fxcrt::ostringstream sAppStream;
684 ByteString sExtGSDictName = "GS";
685 sAppStream << "/" << sExtGSDictName << " gs ";
686
687 CFX_FloatRect rect = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
688 const float fNoteLength = 20;
689 CFX_FloatRect noteRect(rect.left, rect.bottom, rect.left + fNoteLength,
690 rect.bottom + fNoteLength);
691 pAnnotDict->SetRectFor(pdfium::annotation::kRect, noteRect);
692
693 sAppStream << GenerateTextSymbolAP(noteRect);
694
695 auto pExtGStateDict =
696 GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
697 auto pResourceDict =
698 GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
699 GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
700 false /*IsTextMarkupAnnotation*/);
701 return true;
702 }
703
GenerateUnderlineAP(CPDF_Document * pDoc,CPDF_Dictionary * pAnnotDict)704 bool GenerateUnderlineAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
705 fxcrt::ostringstream sAppStream;
706 ByteString sExtGSDictName = "GS";
707 sAppStream << "/" << sExtGSDictName << " gs ";
708
709 sAppStream << GetColorStringWithDefault(
710 pAnnotDict->GetArrayFor(pdfium::annotation::kC).Get(),
711 CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0), PaintOperation::kStroke);
712
713 RetainPtr<const CPDF_Array> pArray = pAnnotDict->GetArrayFor("QuadPoints");
714 if (pArray) {
715 static constexpr float kLineWidth = 1.0f;
716 sAppStream << kLineWidth << " w ";
717 size_t nQuadPointCount = CPDF_Annot::QuadPointCount(pArray.Get());
718 for (size_t i = 0; i < nQuadPointCount; ++i) {
719 CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict, i);
720 rect.Normalize();
721 sAppStream << rect.left << " " << rect.bottom + kLineWidth << " m "
722 << rect.right << " " << rect.bottom + kLineWidth << " l S\n";
723 }
724 }
725
726 auto pExtGStateDict =
727 GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
728 auto pResourceDict =
729 GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
730 GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
731 true /*IsTextMarkupAnnotation*/);
732 return true;
733 }
734
GeneratePopupAP(CPDF_Document * pDoc,CPDF_Dictionary * pAnnotDict)735 bool GeneratePopupAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
736 fxcrt::ostringstream sAppStream;
737 ByteString sExtGSDictName = "GS";
738 sAppStream << "/" << sExtGSDictName << " gs\n";
739
740 sAppStream << GenerateColorAP(CFX_Color(CFX_Color::Type::kRGB, 1, 1, 0),
741 PaintOperation::kFill);
742 sAppStream << GenerateColorAP(CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0),
743 PaintOperation::kStroke);
744
745 const float fBorderWidth = 1;
746 sAppStream << fBorderWidth << " w\n";
747
748 CFX_FloatRect rect = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
749 rect.Normalize();
750 rect.Deflate(fBorderWidth / 2, fBorderWidth / 2);
751
752 sAppStream << rect.left << " " << rect.bottom << " " << rect.Width() << " "
753 << rect.Height() << " re b\n";
754
755 ByteString sFontName = "FONT";
756 RetainPtr<CPDF_Dictionary> pResourceFontDict =
757 GenerateResourceFontDict(pDoc, sFontName);
758
759 auto* pData = CPDF_DocPageData::FromDocument(pDoc);
760 RetainPtr<CPDF_Font> pDefFont = pData->GetFont(pResourceFontDict);
761 if (!pDefFont)
762 return false;
763
764 RetainPtr<CPDF_Dictionary> pExtGStateDict =
765 GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
766 RetainPtr<CPDF_Dictionary> pResourceDict = GenerateResourceDict(
767 pDoc, std::move(pExtGStateDict), std::move(pResourceFontDict));
768
769 sAppStream << GetPopupContentsString(pDoc, *pAnnotDict, std::move(pDefFont),
770 sFontName);
771 GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
772 false /*IsTextMarkupAnnotation*/);
773 return true;
774 }
775
GenerateSquareAP(CPDF_Document * pDoc,CPDF_Dictionary * pAnnotDict)776 bool GenerateSquareAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
777 const ByteString sExtGSDictName = "GS";
778 fxcrt::ostringstream sAppStream;
779 sAppStream << "/" << sExtGSDictName << " gs ";
780
781 RetainPtr<const CPDF_Array> pInteriorColor = pAnnotDict->GetArrayFor("IC");
782 sAppStream << GetColorStringWithDefault(
783 pInteriorColor.Get(), CFX_Color(CFX_Color::Type::kTransparent),
784 PaintOperation::kFill);
785
786 sAppStream << GetColorStringWithDefault(
787 pAnnotDict->GetArrayFor(pdfium::annotation::kC).Get(),
788 CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0), PaintOperation::kStroke);
789
790 float fBorderWidth = GetBorderWidth(pAnnotDict);
791 const bool bIsStrokeRect = fBorderWidth > 0;
792 if (bIsStrokeRect) {
793 sAppStream << fBorderWidth << " w ";
794 sAppStream << GetDashPatternString(pAnnotDict);
795 }
796
797 CFX_FloatRect rect = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
798 rect.Normalize();
799
800 if (bIsStrokeRect) {
801 // Deflating rect because stroking a path entails painting all points
802 // whose perpendicular distance from the path in user space is less than
803 // or equal to half the line width.
804 rect.Deflate(fBorderWidth / 2, fBorderWidth / 2);
805 }
806
807 const bool bIsFillRect = pInteriorColor && (pInteriorColor->size() > 0);
808 sAppStream << rect.left << " " << rect.bottom << " " << rect.Width() << " "
809 << rect.Height() << " re "
810 << GetPaintOperatorString(bIsStrokeRect, bIsFillRect) << "\n";
811
812 auto pExtGStateDict =
813 GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
814 auto pResourceDict =
815 GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
816 GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
817 false /*IsTextMarkupAnnotation*/);
818 return true;
819 }
820
GenerateSquigglyAP(CPDF_Document * pDoc,CPDF_Dictionary * pAnnotDict)821 bool GenerateSquigglyAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
822 fxcrt::ostringstream sAppStream;
823 ByteString sExtGSDictName = "GS";
824 sAppStream << "/" << sExtGSDictName << " gs ";
825
826 sAppStream << GetColorStringWithDefault(
827 pAnnotDict->GetArrayFor(pdfium::annotation::kC).Get(),
828 CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0), PaintOperation::kStroke);
829
830 RetainPtr<const CPDF_Array> pArray = pAnnotDict->GetArrayFor("QuadPoints");
831 if (pArray) {
832 static constexpr float kLineWidth = 1.0f;
833 static constexpr float kDelta = 2.0f;
834 sAppStream << kLineWidth << " w ";
835 size_t nQuadPointCount = CPDF_Annot::QuadPointCount(pArray.Get());
836 for (size_t i = 0; i < nQuadPointCount; ++i) {
837 CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict, i);
838 rect.Normalize();
839
840 const float fTop = rect.bottom + kDelta;
841 const float fBottom = rect.bottom;
842 sAppStream << rect.left << " " << fTop << " m ";
843
844 float fX = rect.left + kDelta;
845 bool isUpwards = false;
846 while (fX < rect.right) {
847 sAppStream << fX << " " << (isUpwards ? fTop : fBottom) << " l ";
848 fX += kDelta;
849 isUpwards = !isUpwards;
850 }
851
852 float fRemainder = rect.right - (fX - kDelta);
853 if (isUpwards)
854 sAppStream << rect.right << " " << fBottom + fRemainder << " l ";
855 else
856 sAppStream << rect.right << " " << fTop - fRemainder << " l ";
857
858 sAppStream << "S\n";
859 }
860 }
861
862 auto pExtGStateDict =
863 GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
864 auto pResourceDict =
865 GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
866 GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
867 true /*IsTextMarkupAnnotation*/);
868 return true;
869 }
870
GenerateStrikeOutAP(CPDF_Document * pDoc,CPDF_Dictionary * pAnnotDict)871 bool GenerateStrikeOutAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) {
872 fxcrt::ostringstream sAppStream;
873 ByteString sExtGSDictName = "GS";
874 sAppStream << "/" << sExtGSDictName << " gs ";
875
876 sAppStream << GetColorStringWithDefault(
877 pAnnotDict->GetArrayFor(pdfium::annotation::kC).Get(),
878 CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0), PaintOperation::kStroke);
879
880 RetainPtr<const CPDF_Array> pArray = pAnnotDict->GetArrayFor("QuadPoints");
881 if (pArray) {
882 static constexpr float kLineWidth = 1.0f;
883 size_t nQuadPointCount = CPDF_Annot::QuadPointCount(pArray.Get());
884 for (size_t i = 0; i < nQuadPointCount; ++i) {
885 CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict, i);
886 rect.Normalize();
887
888 float fY = (rect.top + rect.bottom) / 2;
889 sAppStream << kLineWidth << " w " << rect.left << " " << fY << " m "
890 << rect.right << " " << fY << " l S\n";
891 }
892 }
893
894 auto pExtGStateDict =
895 GenerateExtGStateDict(*pAnnotDict, sExtGSDictName, "Normal");
896 auto pResourceDict =
897 GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
898 GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict),
899 true /*IsTextMarkupAnnotation*/);
900 return true;
901 }
902
903 } // namespace
904
905 // static
GenerateFormAP(CPDF_Document * pDoc,CPDF_Dictionary * pAnnotDict,FormType type)906 void CPDF_GenerateAP::GenerateFormAP(CPDF_Document* pDoc,
907 CPDF_Dictionary* pAnnotDict,
908 FormType type) {
909 RetainPtr<CPDF_Dictionary> pRootDict = pDoc->GetMutableRoot();
910 if (!pRootDict)
911 return;
912
913 RetainPtr<CPDF_Dictionary> pFormDict =
914 pRootDict->GetMutableDictFor("AcroForm");
915 if (!pFormDict)
916 return;
917
918 ByteString DA;
919 RetainPtr<const CPDF_Object> pDAObj =
920 CPDF_FormField::GetFieldAttrForDict(pAnnotDict, "DA");
921 if (pDAObj)
922 DA = pDAObj->GetString();
923 if (DA.IsEmpty())
924 DA = pFormDict->GetByteStringFor("DA");
925 if (DA.IsEmpty())
926 return;
927
928 CPDF_DefaultAppearance appearance(DA);
929
930 float fFontSize = 0;
931 absl::optional<ByteString> font = appearance.GetFont(&fFontSize);
932 if (!font.has_value())
933 return;
934
935 ByteString font_name = font.value();
936
937 CFX_Color crText = fpdfdoc::CFXColorFromString(DA);
938 RetainPtr<CPDF_Dictionary> pDRDict = pFormDict->GetMutableDictFor("DR");
939 if (!pDRDict)
940 return;
941
942 RetainPtr<CPDF_Dictionary> pDRFontDict = pDRDict->GetMutableDictFor("Font");
943 if (!ValidateFontResourceDict(pDRFontDict.Get()))
944 return;
945
946 RetainPtr<CPDF_Dictionary> pFontDict =
947 pDRFontDict->GetMutableDictFor(font_name);
948 if (!pFontDict) {
949 pFontDict = pDoc->NewIndirect<CPDF_Dictionary>();
950 pFontDict->SetNewFor<CPDF_Name>("Type", "Font");
951 pFontDict->SetNewFor<CPDF_Name>("Subtype", "Type1");
952 pFontDict->SetNewFor<CPDF_Name>("BaseFont", CFX_Font::kDefaultAnsiFontName);
953 pFontDict->SetNewFor<CPDF_Name>("Encoding",
954 pdfium::font_encodings::kWinAnsiEncoding);
955 pDRFontDict->SetNewFor<CPDF_Reference>(font_name, pDoc,
956 pFontDict->GetObjNum());
957 }
958 auto* pData = CPDF_DocPageData::FromDocument(pDoc);
959 RetainPtr<CPDF_Font> pDefFont = pData->GetFont(pFontDict);
960 if (!pDefFont)
961 return;
962
963 CFX_FloatRect rcAnnot = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
964 RetainPtr<const CPDF_Dictionary> pMKDict = pAnnotDict->GetDictFor("MK");
965 int32_t nRotate =
966 pMKDict ? pMKDict->GetIntegerFor(pdfium::appearance::kR) : 0;
967
968 CFX_FloatRect rcBBox;
969 CFX_Matrix matrix;
970 switch (nRotate % 360) {
971 case 0:
972 rcBBox = CFX_FloatRect(0, 0, rcAnnot.right - rcAnnot.left,
973 rcAnnot.top - rcAnnot.bottom);
974 break;
975 case 90:
976 matrix = CFX_Matrix(0, 1, -1, 0, rcAnnot.right - rcAnnot.left, 0);
977 rcBBox = CFX_FloatRect(0, 0, rcAnnot.top - rcAnnot.bottom,
978 rcAnnot.right - rcAnnot.left);
979 break;
980 case 180:
981 matrix = CFX_Matrix(-1, 0, 0, -1, rcAnnot.right - rcAnnot.left,
982 rcAnnot.top - rcAnnot.bottom);
983 rcBBox = CFX_FloatRect(0, 0, rcAnnot.right - rcAnnot.left,
984 rcAnnot.top - rcAnnot.bottom);
985 break;
986 case 270:
987 matrix = CFX_Matrix(0, -1, 1, 0, 0, rcAnnot.top - rcAnnot.bottom);
988 rcBBox = CFX_FloatRect(0, 0, rcAnnot.top - rcAnnot.bottom,
989 rcAnnot.right - rcAnnot.left);
990 break;
991 }
992
993 BorderStyle nBorderStyle = BorderStyle::kSolid;
994 float fBorderWidth = 1;
995 CPVT_Dash dsBorder(3, 0, 0);
996 CFX_Color crLeftTop;
997 CFX_Color crRightBottom;
998 if (RetainPtr<const CPDF_Dictionary> pBSDict = pAnnotDict->GetDictFor("BS")) {
999 if (pBSDict->KeyExist("W"))
1000 fBorderWidth = pBSDict->GetFloatFor("W");
1001
1002 if (RetainPtr<const CPDF_Array> pArray = pBSDict->GetArrayFor("D")) {
1003 dsBorder = CPVT_Dash(pArray->GetIntegerAt(0), pArray->GetIntegerAt(1),
1004 pArray->GetIntegerAt(2));
1005 }
1006 if (pBSDict->GetByteStringFor("S").GetLength()) {
1007 switch (pBSDict->GetByteStringFor("S")[0]) {
1008 case 'S':
1009 nBorderStyle = BorderStyle::kSolid;
1010 break;
1011 case 'D':
1012 nBorderStyle = BorderStyle::kDash;
1013 break;
1014 case 'B':
1015 nBorderStyle = BorderStyle::kBeveled;
1016 fBorderWidth *= 2;
1017 crLeftTop = CFX_Color(CFX_Color::Type::kGray, 1);
1018 crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.5);
1019 break;
1020 case 'I':
1021 nBorderStyle = BorderStyle::kInset;
1022 fBorderWidth *= 2;
1023 crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0.5);
1024 crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.75);
1025 break;
1026 case 'U':
1027 nBorderStyle = BorderStyle::kUnderline;
1028 break;
1029 }
1030 }
1031 }
1032 CFX_Color crBorder;
1033 CFX_Color crBG;
1034 if (pMKDict) {
1035 RetainPtr<const CPDF_Array> pArray =
1036 pMKDict->GetArrayFor(pdfium::appearance::kBC);
1037 if (pArray)
1038 crBorder = fpdfdoc::CFXColorFromArray(*pArray);
1039 pArray = pMKDict->GetArrayFor(pdfium::appearance::kBG);
1040 if (pArray)
1041 crBG = fpdfdoc::CFXColorFromArray(*pArray);
1042 }
1043 fxcrt::ostringstream sAppStream;
1044 ByteString sBG = GenerateColorAP(crBG, PaintOperation::kFill);
1045 if (sBG.GetLength() > 0) {
1046 sAppStream << "q\n"
1047 << sBG << rcBBox.left << " " << rcBBox.bottom << " "
1048 << rcBBox.Width() << " " << rcBBox.Height() << " re f\n"
1049 << "Q\n";
1050 }
1051 ByteString sBorderStream =
1052 GenerateBorderAP(rcBBox, fBorderWidth, crBorder, crLeftTop, crRightBottom,
1053 nBorderStyle, dsBorder);
1054 if (sBorderStream.GetLength() > 0)
1055 sAppStream << "q\n" << sBorderStream << "Q\n";
1056
1057 CFX_FloatRect rcBody =
1058 CFX_FloatRect(rcBBox.left + fBorderWidth, rcBBox.bottom + fBorderWidth,
1059 rcBBox.right - fBorderWidth, rcBBox.top - fBorderWidth);
1060 rcBody.Normalize();
1061
1062 RetainPtr<CPDF_Dictionary> pAPDict =
1063 pAnnotDict->GetOrCreateDictFor(pdfium::annotation::kAP);
1064 RetainPtr<CPDF_Stream> pNormalStream = pAPDict->GetMutableStreamFor("N");
1065 if (!pNormalStream) {
1066 pNormalStream = pDoc->NewIndirect<CPDF_Stream>();
1067 pAPDict->SetNewFor<CPDF_Reference>("N", pDoc, pNormalStream->GetObjNum());
1068 }
1069 RetainPtr<CPDF_Dictionary> pStreamDict = pNormalStream->GetMutableDict();
1070 if (pStreamDict) {
1071 RetainPtr<CPDF_Dictionary> pStreamResList =
1072 pStreamDict->GetMutableDictFor("Resources");
1073 if (pStreamResList) {
1074 RetainPtr<CPDF_Dictionary> pStreamResFontList =
1075 pStreamResList->GetMutableDictFor("Font");
1076 if (pStreamResFontList) {
1077 if (!ValidateFontResourceDict(pStreamResFontList.Get()))
1078 return;
1079 } else {
1080 pStreamResFontList = pStreamResList->SetNewFor<CPDF_Dictionary>("Font");
1081 }
1082 if (!pStreamResFontList->KeyExist(font_name)) {
1083 pStreamResFontList->SetNewFor<CPDF_Reference>(font_name, pDoc,
1084 pFontDict->GetObjNum());
1085 }
1086 } else {
1087 pStreamDict->SetFor("Resources", pFormDict->GetDictFor("DR")->Clone());
1088 }
1089 pStreamDict->SetMatrixFor("Matrix", matrix);
1090 pStreamDict->SetRectFor("BBox", rcBBox);
1091 }
1092 CPVT_FontMap map(
1093 pDoc, pStreamDict ? pStreamDict->GetMutableDictFor("Resources") : nullptr,
1094 std::move(pDefFont), font_name);
1095 CPVT_VariableText::Provider prd(&map);
1096
1097 switch (type) {
1098 case CPDF_GenerateAP::kTextField: {
1099 RetainPtr<const CPDF_Object> pV = CPDF_FormField::GetFieldAttrForDict(
1100 pAnnotDict, pdfium::form_fields::kV);
1101 WideString swValue = pV ? pV->GetUnicodeText() : WideString();
1102 RetainPtr<const CPDF_Object> pQ =
1103 CPDF_FormField::GetFieldAttrForDict(pAnnotDict, "Q");
1104 int32_t nAlign = pQ ? pQ->GetInteger() : 0;
1105 RetainPtr<const CPDF_Object> pFf = CPDF_FormField::GetFieldAttrForDict(
1106 pAnnotDict, pdfium::form_fields::kFf);
1107 uint32_t dwFlags = pFf ? pFf->GetInteger() : 0;
1108 RetainPtr<const CPDF_Object> pMaxLen =
1109 CPDF_FormField::GetFieldAttrForDict(pAnnotDict, "MaxLen");
1110 uint32_t dwMaxLen = pMaxLen ? pMaxLen->GetInteger() : 0;
1111 CPVT_VariableText vt(&prd);
1112 vt.SetPlateRect(rcBody);
1113 vt.SetAlignment(nAlign);
1114 if (FXSYS_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::kFill) << sBody
1157 << "ET\n"
1158 << "Q\nEMC\n";
1159 }
1160 break;
1161 }
1162 case CPDF_GenerateAP::kComboBox: {
1163 RetainPtr<const CPDF_Object> pV = CPDF_FormField::GetFieldAttrForDict(
1164 pAnnotDict, pdfium::form_fields::kV);
1165 WideString swValue = pV ? pV->GetUnicodeText() : WideString();
1166 CPVT_VariableText vt(&prd);
1167 CFX_FloatRect rcButton = rcBody;
1168 rcButton.left = rcButton.right - 13;
1169 rcButton.Normalize();
1170 CFX_FloatRect rcEdit = rcBody;
1171 rcEdit.right = rcButton.left;
1172 rcEdit.Normalize();
1173 vt.SetPlateRect(rcEdit);
1174 if (FXSYS_IsFloatZero(fFontSize))
1175 vt.SetAutoFontSize(true);
1176 else
1177 vt.SetFontSize(fFontSize);
1178
1179 vt.Initialize();
1180 vt.SetText(swValue);
1181 vt.RearrangeAll();
1182 CFX_FloatRect rcContent = vt.GetContentRect();
1183 CFX_PointF ptOffset =
1184 CFX_PointF(0.0f, (rcContent.Height() - rcEdit.Height()) / 2.0f);
1185 ByteString sEdit =
1186 GenerateEditAP(&map, vt.GetIterator(), ptOffset, true, 0);
1187 if (sEdit.GetLength() > 0) {
1188 sAppStream << "/Tx BMC\n"
1189 << "q\n";
1190 sAppStream << rcEdit.left << " " << rcEdit.bottom << " "
1191 << rcEdit.Width() << " " << rcEdit.Height() << " re\nW\nn\n";
1192 sAppStream << "BT\n"
1193 << GenerateColorAP(crText, PaintOperation::kFill) << sEdit
1194 << "ET\n"
1195 << "Q\nEMC\n";
1196 }
1197 ByteString sButton =
1198 GenerateColorAP(CFX_Color(CFX_Color::Type::kRGB, 220.0f / 255.0f,
1199 220.0f / 255.0f, 220.0f / 255.0f),
1200 PaintOperation::kFill);
1201 if (sButton.GetLength() > 0 && !rcButton.IsEmpty()) {
1202 sAppStream << "q\n" << sButton;
1203 sAppStream << rcButton.left << " " << rcButton.bottom << " "
1204 << rcButton.Width() << " " << rcButton.Height() << " re f\n";
1205 sAppStream << "Q\n";
1206 ByteString sButtonBorder =
1207 GenerateBorderAP(rcButton, 2, CFX_Color(CFX_Color::Type::kGray, 0),
1208 CFX_Color(CFX_Color::Type::kGray, 1),
1209 CFX_Color(CFX_Color::Type::kGray, 0.5),
1210 BorderStyle::kBeveled, CPVT_Dash(3, 0, 0));
1211 if (sButtonBorder.GetLength() > 0)
1212 sAppStream << "q\n" << sButtonBorder << "Q\n";
1213
1214 CFX_PointF ptCenter = CFX_PointF((rcButton.left + rcButton.right) / 2,
1215 (rcButton.top + rcButton.bottom) / 2);
1216 if (FXSYS_IsFloatBigger(rcButton.Width(), 6) &&
1217 FXSYS_IsFloatBigger(rcButton.Height(), 6)) {
1218 sAppStream << "q\n"
1219 << " 0 g\n";
1220 sAppStream << ptCenter.x - 3 << " " << ptCenter.y + 1.5f << " m\n";
1221 sAppStream << ptCenter.x + 3 << " " << ptCenter.y + 1.5f << " l\n";
1222 sAppStream << ptCenter.x << " " << ptCenter.y - 1.5f << " l\n";
1223 sAppStream << ptCenter.x - 3 << " " << ptCenter.y + 1.5f << " l f\n";
1224 sAppStream << sButton << "Q\n";
1225 }
1226 }
1227 break;
1228 }
1229 case CPDF_GenerateAP::kListBox: {
1230 RetainPtr<const CPDF_Array> pOpts =
1231 ToArray(CPDF_FormField::GetFieldAttrForDict(pAnnotDict, "Opt"));
1232 RetainPtr<const CPDF_Array> pSels =
1233 ToArray(CPDF_FormField::GetFieldAttrForDict(pAnnotDict, "I"));
1234 RetainPtr<const CPDF_Object> pTi =
1235 CPDF_FormField::GetFieldAttrForDict(pAnnotDict, "TI");
1236 int32_t nTop = pTi ? pTi->GetInteger() : 0;
1237 fxcrt::ostringstream sBody;
1238 if (pOpts) {
1239 float fy = rcBody.top;
1240 for (size_t i = nTop, sz = pOpts->size(); i < sz; i++) {
1241 if (FXSYS_IsFloatSmaller(fy, rcBody.bottom))
1242 break;
1243
1244 if (RetainPtr<const CPDF_Object> pOpt = pOpts->GetDirectObjectAt(i)) {
1245 WideString swItem;
1246 if (pOpt->IsString()) {
1247 swItem = pOpt->GetUnicodeText();
1248 } else if (const CPDF_Array* pArray = pOpt->AsArray()) {
1249 RetainPtr<const CPDF_Object> pDirectObj =
1250 pArray->GetDirectObjectAt(1);
1251 if (pDirectObj)
1252 swItem = pDirectObj->GetUnicodeText();
1253 }
1254 bool bSelected = false;
1255 if (pSels) {
1256 for (size_t s = 0, ssz = pSels->size(); s < ssz; s++) {
1257 int value = pSels->GetIntegerAt(s);
1258 if (value >= 0 && i == static_cast<size_t>(value)) {
1259 bSelected = true;
1260 break;
1261 }
1262 }
1263 }
1264 CPVT_VariableText vt(&prd);
1265 vt.SetPlateRect(
1266 CFX_FloatRect(rcBody.left, 0.0f, rcBody.right, 0.0f));
1267 vt.SetFontSize(FXSYS_IsFloatZero(fFontSize) ? 12.0f : fFontSize);
1268 vt.Initialize();
1269 vt.SetText(swItem);
1270 vt.RearrangeAll();
1271
1272 float fItemHeight = vt.GetContentRect().Height();
1273 if (bSelected) {
1274 CFX_FloatRect rcItem = CFX_FloatRect(
1275 rcBody.left, fy - fItemHeight, rcBody.right, fy);
1276 sBody << "q\n"
1277 << GenerateColorAP(
1278 CFX_Color(CFX_Color::Type::kRGB, 0, 51.0f / 255.0f,
1279 113.0f / 255.0f),
1280 PaintOperation::kFill)
1281 << rcItem.left << " " << rcItem.bottom << " "
1282 << rcItem.Width() << " " << rcItem.Height() << " re f\n"
1283 << "Q\n";
1284 sBody << "BT\n"
1285 << GenerateColorAP(CFX_Color(CFX_Color::Type::kGray, 1),
1286 PaintOperation::kFill)
1287 << GenerateEditAP(&map, vt.GetIterator(),
1288 CFX_PointF(0.0f, fy), true, 0)
1289 << "ET\n";
1290 } else {
1291 sBody << "BT\n"
1292 << GenerateColorAP(crText, PaintOperation::kFill)
1293 << GenerateEditAP(&map, vt.GetIterator(),
1294 CFX_PointF(0.0f, fy), true, 0)
1295 << "ET\n";
1296 }
1297 fy -= fItemHeight;
1298 }
1299 }
1300 }
1301 if (sBody.tellp() > 0) {
1302 sAppStream << "/Tx BMC\nq\n"
1303 << rcBody.left << " " << rcBody.bottom << " "
1304 << rcBody.Width() << " " << rcBody.Height() << " re\nW\nn\n"
1305 << sBody.str() << "Q\nEMC\n";
1306 }
1307 break;
1308 }
1309 }
1310
1311 if (!pNormalStream)
1312 return;
1313
1314 pNormalStream->SetDataFromStringstreamAndRemoveFilter(&sAppStream);
1315 pStreamDict = pNormalStream->GetMutableDict();
1316 if (!pStreamDict)
1317 return;
1318
1319 pStreamDict->SetMatrixFor("Matrix", matrix);
1320 pStreamDict->SetRectFor("BBox", rcBBox);
1321 RetainPtr<CPDF_Dictionary> pStreamResList =
1322 pStreamDict->GetMutableDictFor("Resources");
1323 if (!pStreamResList) {
1324 pStreamDict->SetFor("Resources", pFormDict->GetDictFor("DR")->Clone());
1325 return;
1326 }
1327
1328 RetainPtr<CPDF_Dictionary> pStreamResFontList =
1329 pStreamResList->GetMutableDictFor("Font");
1330 if (pStreamResFontList) {
1331 if (!ValidateFontResourceDict(pStreamResFontList.Get()))
1332 return;
1333 } else {
1334 pStreamResFontList = pStreamResList->SetNewFor<CPDF_Dictionary>("Font");
1335 }
1336
1337 if (!pStreamResFontList->KeyExist(font_name)) {
1338 pStreamResFontList->SetNewFor<CPDF_Reference>(font_name, pDoc,
1339 pFontDict->GetObjNum());
1340 }
1341 }
1342
1343 // static
GenerateEmptyAP(CPDF_Document * pDoc,CPDF_Dictionary * pAnnotDict)1344 void CPDF_GenerateAP::GenerateEmptyAP(CPDF_Document* pDoc,
1345 CPDF_Dictionary* pAnnotDict) {
1346 auto pExtGStateDict = GenerateExtGStateDict(*pAnnotDict, "GS", "Normal");
1347 auto pResourceDict =
1348 GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr);
1349
1350 fxcrt::ostringstream sStream;
1351 GenerateAndSetAPDict(pDoc, pAnnotDict, &sStream, std::move(pResourceDict),
1352 false);
1353 }
1354
1355 // static
GenerateAnnotAP(CPDF_Document * pDoc,CPDF_Dictionary * pAnnotDict,CPDF_Annot::Subtype subtype)1356 bool CPDF_GenerateAP::GenerateAnnotAP(CPDF_Document* pDoc,
1357 CPDF_Dictionary* pAnnotDict,
1358 CPDF_Annot::Subtype subtype) {
1359 switch (subtype) {
1360 case CPDF_Annot::Subtype::CIRCLE:
1361 return GenerateCircleAP(pDoc, pAnnotDict);
1362 case CPDF_Annot::Subtype::HIGHLIGHT:
1363 return GenerateHighlightAP(pDoc, pAnnotDict);
1364 case CPDF_Annot::Subtype::INK:
1365 return GenerateInkAP(pDoc, pAnnotDict);
1366 case CPDF_Annot::Subtype::POPUP:
1367 return GeneratePopupAP(pDoc, pAnnotDict);
1368 case CPDF_Annot::Subtype::SQUARE:
1369 return GenerateSquareAP(pDoc, pAnnotDict);
1370 case CPDF_Annot::Subtype::SQUIGGLY:
1371 return GenerateSquigglyAP(pDoc, pAnnotDict);
1372 case CPDF_Annot::Subtype::STRIKEOUT:
1373 return GenerateStrikeOutAP(pDoc, pAnnotDict);
1374 case CPDF_Annot::Subtype::TEXT:
1375 return GenerateTextAP(pDoc, pAnnotDict);
1376 case CPDF_Annot::Subtype::UNDERLINE:
1377 return GenerateUnderlineAP(pDoc, pAnnotDict);
1378 default:
1379 return false;
1380 }
1381 }
1382