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