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