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