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