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