• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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