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