1 // Copyright 2017 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 "fpdfsdk/cpdfsdk_appstream.h"
8
9 #include <math.h>
10
11 #include <iterator>
12 #include <memory>
13 #include <sstream>
14 #include <utility>
15
16 #include "constants/appearance.h"
17 #include "constants/form_flags.h"
18 #include "core/fpdfapi/edit/cpdf_contentstream_write_utils.h"
19 #include "core/fpdfapi/font/cpdf_font.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_bafontmap.h"
30 #include "core/fpdfdoc/cpdf_formcontrol.h"
31 #include "core/fpdfdoc/cpdf_icon.h"
32 #include "core/fpdfdoc/cpvt_word.h"
33 #include "core/fxcrt/fx_string_wrappers.h"
34 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
35 #include "fpdfsdk/cpdfsdk_interactiveform.h"
36 #include "fpdfsdk/cpdfsdk_pageview.h"
37 #include "fpdfsdk/cpdfsdk_widget.h"
38 #include "fpdfsdk/pwl/cpwl_edit.h"
39 #include "fpdfsdk/pwl/cpwl_edit_impl.h"
40 #include "fpdfsdk/pwl/cpwl_wnd.h"
41 #include "third_party/base/numerics/safe_conversions.h"
42 #include "third_party/base/span.h"
43
44 namespace {
45
46 // Checkbox & radiobutton styles.
47 enum class CheckStyle { kCheck = 0, kCircle, kCross, kDiamond, kSquare, kStar };
48
49 // Pushbutton layout styles.
50 enum class ButtonStyle {
51 kLabel = 0,
52 kIcon,
53 kIconTopLabelBottom,
54 kIconBottomLabelTop,
55 kIconLeftLabelRight,
56 kIconRightLabelLeft,
57 kLabelOverIcon
58 };
59
60 const char kAppendRectOperator[] = "re";
61 const char kConcatMatrixOperator[] = "cm";
62 const char kCurveToOperator[] = "c";
63 const char kEndPathNoFillOrStrokeOperator[] = "n";
64 const char kFillOperator[] = "f";
65 const char kFillEvenOddOperator[] = "f*";
66 const char kInvokeNamedXObjectOperator[] = "Do";
67 const char kLineToOperator[] = "l";
68 const char kMarkedSequenceBeginOperator[] = "BMC";
69 const char kMarkedSequenceEndOperator[] = "EMC";
70 const char kMoveTextPositionOperator[] = "Td";
71 const char kMoveToOperator[] = "m";
72 const char kSetCMYKOperator[] = "k";
73 const char kSetCMKYStrokedOperator[] = "K";
74 const char kSetDashOperator[] = "d";
75 const char kSetGrayOperator[] = "g";
76 const char kSetGrayStrokedOperator[] = "G";
77 const char kSetLineCapStyleOperator[] = "J";
78 const char kSetLineJoinStyleOperator[] = "j";
79 const char kSetLineWidthOperator[] = "w";
80 const char kSetNonZeroWindingClipOperator[] = "W";
81 const char kSetRGBOperator[] = "rg";
82 const char kSetRGBStrokedOperator[] = "RG";
83 const char kSetTextFontAndSizeOperator[] = "Tf";
84 const char kShowTextOperator[] = "Tj";
85 const char kStateRestoreOperator[] = "Q";
86 const char kStateSaveOperator[] = "q";
87 const char kStrokeOperator[] = "S";
88 const char kTextBeginOperator[] = "BT";
89 const char kTextEndOperator[] = "ET";
90
91 class AutoClosedCommand {
92 public:
AutoClosedCommand(fxcrt::ostringstream * stream,ByteString open,ByteString close)93 AutoClosedCommand(fxcrt::ostringstream* stream,
94 ByteString open,
95 ByteString close)
96 : stream_(stream), close_(close) {
97 *stream_ << open << "\n";
98 }
99
~AutoClosedCommand()100 virtual ~AutoClosedCommand() { *stream_ << close_ << "\n"; }
101
102 private:
103 UnownedPtr<fxcrt::ostringstream> const stream_;
104 ByteString close_;
105 };
106
107 class AutoClosedQCommand final : public AutoClosedCommand {
108 public:
AutoClosedQCommand(fxcrt::ostringstream * stream)109 explicit AutoClosedQCommand(fxcrt::ostringstream* stream)
110 : AutoClosedCommand(stream, kStateSaveOperator, kStateRestoreOperator) {}
111 ~AutoClosedQCommand() override = default;
112 };
113
WriteMove(fxcrt::ostringstream & stream,const CFX_PointF & point)114 void WriteMove(fxcrt::ostringstream& stream, const CFX_PointF& point) {
115 WritePoint(stream, point) << " " << kMoveToOperator << "\n";
116 }
117
WriteLine(fxcrt::ostringstream & stream,const CFX_PointF & point)118 void WriteLine(fxcrt::ostringstream& stream, const CFX_PointF& point) {
119 WritePoint(stream, point) << " " << kLineToOperator << "\n";
120 }
121
WriteClosedLoop(fxcrt::ostringstream & stream,pdfium::span<const CFX_PointF> points)122 void WriteClosedLoop(fxcrt::ostringstream& stream,
123 pdfium::span<const CFX_PointF> points) {
124 WriteMove(stream, points[0]);
125 for (const auto& point : points.subspan(1))
126 WriteLine(stream, point);
127 WriteLine(stream, points[0]);
128 }
129
WriteBezierCurve(fxcrt::ostringstream & stream,const CFX_PointF & point1,const CFX_PointF & point2,const CFX_PointF & point3)130 void WriteBezierCurve(fxcrt::ostringstream& stream,
131 const CFX_PointF& point1,
132 const CFX_PointF& point2,
133 const CFX_PointF& point3) {
134 WritePoint(stream, point1) << " ";
135 WritePoint(stream, point2) << " ";
136 WritePoint(stream, point3) << " " << kCurveToOperator << "\n";
137 }
138
WriteAppendRect(fxcrt::ostringstream & stream,const CFX_FloatRect & rect)139 void WriteAppendRect(fxcrt::ostringstream& stream, const CFX_FloatRect& rect) {
140 WriteRect(stream, rect) << " " << kAppendRectOperator << "\n";
141 }
142
GetStrokeColorAppStream(const CFX_Color & color)143 ByteString GetStrokeColorAppStream(const CFX_Color& color) {
144 fxcrt::ostringstream sColorStream;
145 switch (color.nColorType) {
146 case CFX_Color::Type::kTransparent:
147 break;
148 case CFX_Color::Type::kGray:
149 sColorStream << color.fColor1 << " " << kSetGrayStrokedOperator << "\n";
150 break;
151 case CFX_Color::Type::kRGB:
152 sColorStream << color.fColor1 << " " << color.fColor2 << " "
153 << color.fColor3 << " " << kSetRGBStrokedOperator << "\n";
154 break;
155 case CFX_Color::Type::kCMYK:
156 sColorStream << color.fColor1 << " " << color.fColor2 << " "
157 << color.fColor3 << " " << color.fColor4 << " "
158 << kSetCMKYStrokedOperator << "\n";
159 break;
160 }
161 return ByteString(sColorStream);
162 }
163
GetFillColorAppStream(const CFX_Color & color)164 ByteString GetFillColorAppStream(const CFX_Color& color) {
165 fxcrt::ostringstream sColorStream;
166 switch (color.nColorType) {
167 case CFX_Color::Type::kTransparent:
168 break;
169 case CFX_Color::Type::kGray:
170 sColorStream << color.fColor1 << " " << kSetGrayOperator << "\n";
171 break;
172 case CFX_Color::Type::kRGB:
173 sColorStream << color.fColor1 << " " << color.fColor2 << " "
174 << color.fColor3 << " " << kSetRGBOperator << "\n";
175 break;
176 case CFX_Color::Type::kCMYK:
177 sColorStream << color.fColor1 << " " << color.fColor2 << " "
178 << color.fColor3 << " " << color.fColor4 << " "
179 << kSetCMYKOperator << "\n";
180 break;
181 }
182 return ByteString(sColorStream);
183 }
184
GetAP_Check(const CFX_FloatRect & crBBox)185 ByteString GetAP_Check(const CFX_FloatRect& crBBox) {
186 const float fWidth = crBBox.Width();
187 const float fHeight = crBBox.Height();
188
189 CFX_PointF pts[8][3] = {{CFX_PointF(0.28f, 0.52f), CFX_PointF(0.27f, 0.48f),
190 CFX_PointF(0.29f, 0.40f)},
191 {CFX_PointF(0.30f, 0.33f), CFX_PointF(0.31f, 0.29f),
192 CFX_PointF(0.31f, 0.28f)},
193 {CFX_PointF(0.39f, 0.28f), CFX_PointF(0.49f, 0.29f),
194 CFX_PointF(0.77f, 0.67f)},
195 {CFX_PointF(0.76f, 0.68f), CFX_PointF(0.78f, 0.69f),
196 CFX_PointF(0.76f, 0.75f)},
197 {CFX_PointF(0.76f, 0.75f), CFX_PointF(0.73f, 0.80f),
198 CFX_PointF(0.68f, 0.75f)},
199 {CFX_PointF(0.68f, 0.74f), CFX_PointF(0.68f, 0.74f),
200 CFX_PointF(0.44f, 0.47f)},
201 {CFX_PointF(0.43f, 0.47f), CFX_PointF(0.40f, 0.47f),
202 CFX_PointF(0.41f, 0.58f)},
203 {CFX_PointF(0.40f, 0.60f), CFX_PointF(0.28f, 0.66f),
204 CFX_PointF(0.30f, 0.56f)}};
205
206 for (size_t i = 0; i < std::size(pts); ++i) {
207 for (size_t j = 0; j < std::size(pts[0]); ++j) {
208 pts[i][j].x = pts[i][j].x * fWidth + crBBox.left;
209 pts[i][j].y *= pts[i][j].y * fHeight + crBBox.bottom;
210 }
211 }
212
213 fxcrt::ostringstream csAP;
214 WriteMove(csAP, pts[0][0]);
215
216 for (size_t i = 0; i < std::size(pts); ++i) {
217 size_t nNext = i < std::size(pts) - 1 ? i + 1 : 0;
218 const CFX_PointF& pt_next = pts[nNext][0];
219
220 float px1 = pts[i][1].x - pts[i][0].x;
221 float py1 = pts[i][1].y - pts[i][0].y;
222 float px2 = pts[i][2].x - pt_next.x;
223 float py2 = pts[i][2].y - pt_next.y;
224
225 WriteBezierCurve(
226 csAP,
227 {pts[i][0].x + px1 * FXSYS_BEZIER, pts[i][0].y + py1 * FXSYS_BEZIER},
228 {pt_next.x + px2 * FXSYS_BEZIER, pt_next.y + py2 * FXSYS_BEZIER},
229 pt_next);
230 }
231
232 return ByteString(csAP);
233 }
234
GetAP_Circle(const CFX_FloatRect & crBBox)235 ByteString GetAP_Circle(const CFX_FloatRect& crBBox) {
236 fxcrt::ostringstream csAP;
237
238 float fWidth = crBBox.Width();
239 float fHeight = crBBox.Height();
240
241 CFX_PointF pt1(crBBox.left, crBBox.bottom + fHeight / 2);
242 CFX_PointF pt2(crBBox.left + fWidth / 2, crBBox.top);
243 CFX_PointF pt3(crBBox.right, crBBox.bottom + fHeight / 2);
244 CFX_PointF pt4(crBBox.left + fWidth / 2, crBBox.bottom);
245
246 WriteMove(csAP, pt1);
247
248 float px = pt2.x - pt1.x;
249 float py = pt2.y - pt1.y;
250
251 WriteBezierCurve(csAP, {pt1.x, pt1.y + py * FXSYS_BEZIER},
252 {pt2.x - px * FXSYS_BEZIER, pt2.y}, pt2);
253
254 px = pt3.x - pt2.x;
255 py = pt2.y - pt3.y;
256
257 WriteBezierCurve(csAP, {pt2.x + px * FXSYS_BEZIER, pt2.y},
258 {pt3.x, pt3.y + py * FXSYS_BEZIER}, pt3);
259
260 px = pt3.x - pt4.x;
261 py = pt3.y - pt4.y;
262
263 WriteBezierCurve(csAP, {pt3.x, pt3.y - py * FXSYS_BEZIER},
264 {pt4.x + px * FXSYS_BEZIER, pt4.y}, pt4);
265
266 px = pt4.x - pt1.x;
267 py = pt1.y - pt4.y;
268
269 WriteBezierCurve(csAP, {pt4.x - px * FXSYS_BEZIER, pt4.y},
270 {pt1.x, pt1.y - py * FXSYS_BEZIER}, pt1);
271
272 return ByteString(csAP);
273 }
274
GetAP_Cross(const CFX_FloatRect & crBBox)275 ByteString GetAP_Cross(const CFX_FloatRect& crBBox) {
276 fxcrt::ostringstream csAP;
277
278 WriteMove(csAP, {crBBox.left, crBBox.top});
279 WriteLine(csAP, {crBBox.right, crBBox.bottom});
280 WriteMove(csAP, {crBBox.left, crBBox.bottom});
281 WriteLine(csAP, {crBBox.right, crBBox.top});
282
283 return ByteString(csAP);
284 }
285
GetAP_Diamond(const CFX_FloatRect & crBBox)286 ByteString GetAP_Diamond(const CFX_FloatRect& crBBox) {
287 fxcrt::ostringstream csAP;
288
289 float fWidth = crBBox.Width();
290 float fHeight = crBBox.Height();
291
292 const CFX_PointF points[] = {{crBBox.left, crBBox.bottom + fHeight / 2},
293 {crBBox.left + fWidth / 2, crBBox.top},
294 {crBBox.right, crBBox.bottom + fHeight / 2},
295 {crBBox.left + fWidth / 2, crBBox.bottom}};
296 WriteClosedLoop(csAP, points);
297
298 return ByteString(csAP);
299 }
300
GetAP_Square(const CFX_FloatRect & crBBox)301 ByteString GetAP_Square(const CFX_FloatRect& crBBox) {
302 fxcrt::ostringstream csAP;
303
304 const CFX_PointF points[] = {{crBBox.left, crBBox.top},
305 {crBBox.right, crBBox.top},
306 {crBBox.right, crBBox.bottom},
307 {crBBox.left, crBBox.bottom}};
308 WriteClosedLoop(csAP, points);
309
310 return ByteString(csAP);
311 }
312
GetAP_Star(const CFX_FloatRect & crBBox)313 ByteString GetAP_Star(const CFX_FloatRect& crBBox) {
314 fxcrt::ostringstream csAP;
315
316 float fRadius = (crBBox.top - crBBox.bottom) / (1 + cosf(FXSYS_PI / 5.0f));
317 CFX_PointF ptCenter = CFX_PointF((crBBox.left + crBBox.right) / 2.0f,
318 (crBBox.top + crBBox.bottom) / 2.0f);
319
320 CFX_PointF points[5];
321 float fAngle = FXSYS_PI / 10.0f;
322 for (auto& point : points) {
323 point =
324 ptCenter + CFX_PointF(fRadius * cosf(fAngle), fRadius * sinf(fAngle));
325 fAngle += FXSYS_PI * 2 / 5.0f;
326 }
327
328 WriteMove(csAP, points[0]);
329
330 int next = 0;
331 for (size_t i = 0; i < std::size(points); ++i) {
332 next = (next + 2) % std::size(points);
333 WriteLine(csAP, points[next]);
334 }
335
336 return ByteString(csAP);
337 }
338
GetAP_HalfCircle(const CFX_FloatRect & crBBox,float fRotate)339 ByteString GetAP_HalfCircle(const CFX_FloatRect& crBBox, float fRotate) {
340 fxcrt::ostringstream csAP;
341
342 float fWidth = crBBox.Width();
343 float fHeight = crBBox.Height();
344
345 CFX_PointF pt1(-fWidth / 2, 0);
346 CFX_PointF pt2(0, fHeight / 2);
347 CFX_PointF pt3(fWidth / 2, 0);
348
349 CFX_Matrix rotate_matrix(cos(fRotate), sin(fRotate), -sin(fRotate),
350 cos(fRotate), crBBox.left + fWidth / 2,
351 crBBox.bottom + fHeight / 2);
352 WriteMatrix(csAP, rotate_matrix) << " " << kConcatMatrixOperator << "\n";
353
354 WriteMove(csAP, pt1);
355
356 float px = pt2.x - pt1.x;
357 float py = pt2.y - pt1.y;
358
359 WriteBezierCurve(csAP, {pt1.x, pt1.y + py * FXSYS_BEZIER},
360 {pt2.x - px * FXSYS_BEZIER, pt2.y}, pt2);
361
362 px = pt3.x - pt2.x;
363 py = pt2.y - pt3.y;
364
365 WriteBezierCurve(csAP, {pt2.x + px * FXSYS_BEZIER, pt2.y},
366 {pt3.x, pt3.y + py * FXSYS_BEZIER}, pt3);
367
368 return ByteString(csAP);
369 }
370
GetAppStream_Check(const CFX_FloatRect & rcBBox,const CFX_Color & crText)371 ByteString GetAppStream_Check(const CFX_FloatRect& rcBBox,
372 const CFX_Color& crText) {
373 fxcrt::ostringstream sAP;
374 {
375 AutoClosedQCommand q(&sAP);
376 sAP << GetFillColorAppStream(crText) << GetAP_Check(rcBBox) << kFillOperator
377 << "\n";
378 }
379 return ByteString(sAP);
380 }
381
GetAppStream_Circle(const CFX_FloatRect & rcBBox,const CFX_Color & crText)382 ByteString GetAppStream_Circle(const CFX_FloatRect& rcBBox,
383 const CFX_Color& crText) {
384 fxcrt::ostringstream sAP;
385 {
386 AutoClosedQCommand q(&sAP);
387 sAP << GetFillColorAppStream(crText) << GetAP_Circle(rcBBox)
388 << kFillOperator << "\n";
389 }
390 return ByteString(sAP);
391 }
392
GetAppStream_Cross(const CFX_FloatRect & rcBBox,const CFX_Color & crText)393 ByteString GetAppStream_Cross(const CFX_FloatRect& rcBBox,
394 const CFX_Color& crText) {
395 fxcrt::ostringstream sAP;
396 {
397 AutoClosedQCommand q(&sAP);
398 sAP << GetStrokeColorAppStream(crText) << GetAP_Cross(rcBBox)
399 << kStrokeOperator << "\n";
400 }
401 return ByteString(sAP);
402 }
403
GetAppStream_Diamond(const CFX_FloatRect & rcBBox,const CFX_Color & crText)404 ByteString GetAppStream_Diamond(const CFX_FloatRect& rcBBox,
405 const CFX_Color& crText) {
406 fxcrt::ostringstream sAP;
407 {
408 AutoClosedQCommand q(&sAP);
409 sAP << "1 " << kSetLineWidthOperator << "\n"
410 << GetFillColorAppStream(crText) << GetAP_Diamond(rcBBox)
411 << kFillOperator << "\n";
412 }
413 return ByteString(sAP);
414 }
415
GetAppStream_Square(const CFX_FloatRect & rcBBox,const CFX_Color & crText)416 ByteString GetAppStream_Square(const CFX_FloatRect& rcBBox,
417 const CFX_Color& crText) {
418 fxcrt::ostringstream sAP;
419 {
420 AutoClosedQCommand q(&sAP);
421 sAP << GetFillColorAppStream(crText) << GetAP_Square(rcBBox)
422 << kFillOperator << "\n";
423 }
424 return ByteString(sAP);
425 }
426
GetAppStream_Star(const CFX_FloatRect & rcBBox,const CFX_Color & crText)427 ByteString GetAppStream_Star(const CFX_FloatRect& rcBBox,
428 const CFX_Color& crText) {
429 fxcrt::ostringstream sAP;
430 {
431 AutoClosedQCommand q(&sAP);
432 sAP << GetFillColorAppStream(crText) << GetAP_Star(rcBBox) << kFillOperator
433 << "\n";
434 }
435 return ByteString(sAP);
436 }
437
GetCircleFillAppStream(const CFX_FloatRect & rect,const CFX_Color & color)438 ByteString GetCircleFillAppStream(const CFX_FloatRect& rect,
439 const CFX_Color& color) {
440 fxcrt::ostringstream sAppStream;
441 ByteString sColor = GetFillColorAppStream(color);
442 if (sColor.GetLength() > 0) {
443 AutoClosedQCommand q(&sAppStream);
444 sAppStream << sColor << GetAP_Circle(rect) << kFillOperator << "\n";
445 }
446 return ByteString(sAppStream);
447 }
448
GetCircleBorderAppStream(const CFX_FloatRect & rect,float fWidth,const CFX_Color & color,const CFX_Color & crLeftTop,const CFX_Color & crRightBottom,BorderStyle nStyle,const CPWL_Dash & dash)449 ByteString GetCircleBorderAppStream(const CFX_FloatRect& rect,
450 float fWidth,
451 const CFX_Color& color,
452 const CFX_Color& crLeftTop,
453 const CFX_Color& crRightBottom,
454 BorderStyle nStyle,
455 const CPWL_Dash& dash) {
456 fxcrt::ostringstream sAppStream;
457 ByteString sColor;
458
459 if (fWidth > 0.0f) {
460 AutoClosedQCommand q(&sAppStream);
461
462 float fHalfWidth = fWidth / 2.0f;
463 CFX_FloatRect rect_by_2 = rect.GetDeflated(fHalfWidth, fHalfWidth);
464
465 float div = fHalfWidth * 0.75f;
466 CFX_FloatRect rect_by_75 = rect.GetDeflated(div, div);
467 switch (nStyle) {
468 default:
469 case BorderStyle::kSolid:
470 case BorderStyle::kUnderline: {
471 sColor = GetStrokeColorAppStream(color);
472 if (sColor.GetLength() > 0) {
473 AutoClosedQCommand q2(&sAppStream);
474 sAppStream << fWidth << " " << kSetLineWidthOperator << "\n"
475 << sColor << GetAP_Circle(rect_by_2) << " "
476 << kStrokeOperator << "\n";
477 }
478 } break;
479 case BorderStyle::kDash: {
480 sColor = GetStrokeColorAppStream(color);
481 if (sColor.GetLength() > 0) {
482 AutoClosedQCommand q2(&sAppStream);
483 sAppStream << fWidth << " " << kSetLineWidthOperator << "\n"
484 << "[" << dash.nDash << " " << dash.nGap << "] "
485 << dash.nPhase << " " << kSetDashOperator << "\n"
486 << sColor << GetAP_Circle(rect_by_2) << " "
487 << kStrokeOperator << "\n";
488 }
489 } break;
490 case BorderStyle::kBeveled: {
491 sColor = GetStrokeColorAppStream(color);
492 if (sColor.GetLength() > 0) {
493 AutoClosedQCommand q2(&sAppStream);
494 sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n"
495 << sColor << GetAP_Circle(rect) << " " << kStrokeOperator
496 << "\n";
497 }
498 sColor = GetStrokeColorAppStream(crLeftTop);
499 if (sColor.GetLength() > 0) {
500 AutoClosedQCommand q2(&sAppStream);
501 sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n"
502 << sColor << GetAP_HalfCircle(rect_by_75, FXSYS_PI / 4.0f)
503 << " " << kStrokeOperator << "\n";
504 }
505 sColor = GetStrokeColorAppStream(crRightBottom);
506 if (sColor.GetLength() > 0) {
507 AutoClosedQCommand q2(&sAppStream);
508 sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n"
509 << sColor
510 << GetAP_HalfCircle(rect_by_75, FXSYS_PI * 5 / 4.0f) << " "
511 << kStrokeOperator << "\n";
512 }
513 } break;
514 case BorderStyle::kInset: {
515 sColor = GetStrokeColorAppStream(color);
516 if (sColor.GetLength() > 0) {
517 AutoClosedQCommand q2(&sAppStream);
518 sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n"
519 << sColor << GetAP_Circle(rect) << " " << kStrokeOperator
520 << "\n";
521 }
522 sColor = GetStrokeColorAppStream(crLeftTop);
523 if (sColor.GetLength() > 0) {
524 AutoClosedQCommand q2(&sAppStream);
525 sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n"
526 << sColor << GetAP_HalfCircle(rect_by_75, FXSYS_PI / 4.0f)
527 << " " << kStrokeOperator << "\n";
528 }
529 sColor = GetStrokeColorAppStream(crRightBottom);
530 if (sColor.GetLength() > 0) {
531 AutoClosedQCommand q2(&sAppStream);
532 sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n"
533 << sColor
534 << GetAP_HalfCircle(rect_by_75, FXSYS_PI * 5 / 4.0f) << " "
535 << kStrokeOperator << "\n";
536 }
537 } break;
538 }
539 }
540 return ByteString(sAppStream);
541 }
542
GetCheckBoxAppStream(const CFX_FloatRect & rcBBox,CheckStyle nStyle,const CFX_Color & crText)543 ByteString GetCheckBoxAppStream(const CFX_FloatRect& rcBBox,
544 CheckStyle nStyle,
545 const CFX_Color& crText) {
546 CFX_FloatRect rcCenter = rcBBox.GetCenterSquare();
547 switch (nStyle) {
548 default:
549 case CheckStyle::kCheck:
550 return GetAppStream_Check(rcCenter, crText);
551 case CheckStyle::kCircle:
552 rcCenter.ScaleFromCenterPoint(2.0f / 3.0f);
553 return GetAppStream_Circle(rcCenter, crText);
554 case CheckStyle::kCross:
555 return GetAppStream_Cross(rcCenter, crText);
556 case CheckStyle::kDiamond:
557 rcCenter.ScaleFromCenterPoint(2.0f / 3.0f);
558 return GetAppStream_Diamond(rcCenter, crText);
559 case CheckStyle::kSquare:
560 rcCenter.ScaleFromCenterPoint(2.0f / 3.0f);
561 return GetAppStream_Square(rcCenter, crText);
562 case CheckStyle::kStar:
563 rcCenter.ScaleFromCenterPoint(2.0f / 3.0f);
564 return GetAppStream_Star(rcCenter, crText);
565 }
566 }
567
GetRadioButtonAppStream(const CFX_FloatRect & rcBBox,CheckStyle nStyle,const CFX_Color & crText)568 ByteString GetRadioButtonAppStream(const CFX_FloatRect& rcBBox,
569 CheckStyle nStyle,
570 const CFX_Color& crText) {
571 CFX_FloatRect rcCenter = rcBBox.GetCenterSquare();
572 switch (nStyle) {
573 default:
574 case CheckStyle::kCheck:
575 return GetAppStream_Check(rcCenter, crText);
576 case CheckStyle::kCircle:
577 rcCenter.ScaleFromCenterPoint(1.0f / 2.0f);
578 return GetAppStream_Circle(rcCenter, crText);
579 case CheckStyle::kCross:
580 return GetAppStream_Cross(rcCenter, crText);
581 case CheckStyle::kDiamond:
582 rcCenter.ScaleFromCenterPoint(2.0f / 3.0f);
583 return GetAppStream_Diamond(rcCenter, crText);
584 case CheckStyle::kSquare:
585 rcCenter.ScaleFromCenterPoint(2.0f / 3.0f);
586 return GetAppStream_Square(rcCenter, crText);
587 case CheckStyle::kStar:
588 rcCenter.ScaleFromCenterPoint(2.0f / 3.0f);
589 return GetAppStream_Star(rcCenter, crText);
590 }
591 }
592
GetFontSetString(IPVT_FontMap * pFontMap,int32_t nFontIndex,float fFontSize)593 ByteString GetFontSetString(IPVT_FontMap* pFontMap,
594 int32_t nFontIndex,
595 float fFontSize) {
596 if (!pFontMap)
597 return ByteString();
598
599 ByteString sFontAlias = pFontMap->GetPDFFontAlias(nFontIndex);
600 if (sFontAlias.GetLength() <= 0 || fFontSize <= 0)
601 return ByteString();
602
603 fxcrt::ostringstream sRet;
604 sRet << "/" << sFontAlias << " " << fFontSize << " "
605 << kSetTextFontAndSizeOperator << "\n";
606 return ByteString(sRet);
607 }
608
GetWordRenderString(ByteStringView strWords)609 ByteString GetWordRenderString(ByteStringView strWords) {
610 if (strWords.IsEmpty())
611 return ByteString();
612 return PDF_EncodeString(strWords) + " " + kShowTextOperator + "\n";
613 }
614
GetEditAppStream(CPWL_EditImpl * pEdit,const CFX_PointF & ptOffset,bool bContinuous,uint16_t SubWord)615 ByteString GetEditAppStream(CPWL_EditImpl* pEdit,
616 const CFX_PointF& ptOffset,
617 bool bContinuous,
618 uint16_t SubWord) {
619 CPWL_EditImpl::Iterator* pIterator = pEdit->GetIterator();
620 pIterator->SetAt(0);
621
622 fxcrt::ostringstream sEditStream;
623 int32_t nCurFontIndex = -1;
624 CFX_PointF ptOld;
625 CFX_PointF ptNew;
626 CPVT_WordPlace oldplace;
627 ByteString sWords;
628
629 while (pIterator->NextWord()) {
630 CPVT_WordPlace place = pIterator->GetAt();
631 if (bContinuous) {
632 if (place.LineCmp(oldplace) != 0) {
633 if (!sWords.IsEmpty()) {
634 sEditStream << GetWordRenderString(sWords.AsStringView());
635 sWords.clear();
636 }
637
638 CPVT_Word word;
639 if (pIterator->GetWord(word)) {
640 ptNew = CFX_PointF(word.ptWord.x + ptOffset.x,
641 word.ptWord.y + ptOffset.y);
642 } else {
643 CPVT_Line line;
644 pIterator->GetLine(line);
645 ptNew = CFX_PointF(line.ptLine.x + ptOffset.x,
646 line.ptLine.y + ptOffset.y);
647 }
648
649 if (ptNew.x != ptOld.x || ptNew.y != ptOld.y) {
650 WritePoint(sEditStream, {ptNew.x - ptOld.x, ptNew.y - ptOld.y})
651 << " " << kMoveTextPositionOperator << "\n";
652
653 ptOld = ptNew;
654 }
655 }
656
657 CPVT_Word word;
658 if (pIterator->GetWord(word)) {
659 if (word.nFontIndex != nCurFontIndex) {
660 if (!sWords.IsEmpty()) {
661 sEditStream << GetWordRenderString(sWords.AsStringView());
662 sWords.clear();
663 }
664 sEditStream << GetFontSetString(pEdit->GetFontMap(), word.nFontIndex,
665 word.fFontSize);
666 nCurFontIndex = word.nFontIndex;
667 }
668
669 sWords += pEdit->GetPDFWordString(nCurFontIndex, word.Word, SubWord);
670 }
671 oldplace = place;
672 } else {
673 CPVT_Word word;
674 if (pIterator->GetWord(word)) {
675 ptNew =
676 CFX_PointF(word.ptWord.x + ptOffset.x, word.ptWord.y + ptOffset.y);
677
678 if (ptNew.x != ptOld.x || ptNew.y != ptOld.y) {
679 WritePoint(sEditStream, {ptNew.x - ptOld.x, ptNew.y - ptOld.y})
680 << " " << kMoveTextPositionOperator << "\n";
681 ptOld = ptNew;
682 }
683 if (word.nFontIndex != nCurFontIndex) {
684 sEditStream << GetFontSetString(pEdit->GetFontMap(), word.nFontIndex,
685 word.fFontSize);
686 nCurFontIndex = word.nFontIndex;
687 }
688 sEditStream << GetWordRenderString(
689 pEdit->GetPDFWordString(nCurFontIndex, word.Word, SubWord)
690 .AsStringView());
691 }
692 }
693 }
694
695 if (!sWords.IsEmpty())
696 sEditStream << GetWordRenderString(sWords.AsStringView());
697
698 fxcrt::ostringstream sAppStream;
699 if (sEditStream.tellp() > 0) {
700 sAppStream << sEditStream.str();
701 }
702 return ByteString(sAppStream);
703 }
704
GenerateIconAppStream(CPDF_IconFit & fit,RetainPtr<CPDF_Stream> pIconStream,const CFX_FloatRect & rcIcon)705 ByteString GenerateIconAppStream(CPDF_IconFit& fit,
706 RetainPtr<CPDF_Stream> pIconStream,
707 const CFX_FloatRect& rcIcon) {
708 if (rcIcon.IsEmpty() || !pIconStream)
709 return ByteString();
710
711 CPWL_Wnd::CreateParams cp(nullptr, nullptr, nullptr);
712 cp.dwFlags = PWS_VISIBLE;
713 auto pWnd = std::make_unique<CPWL_Wnd>(cp, nullptr);
714 pWnd->Realize();
715 if (!pWnd->Move(rcIcon, false, false))
716 return ByteString();
717
718 auto pPDFIcon = std::make_unique<CPDF_Icon>(std::move(pIconStream));
719 ByteString sAlias = pPDFIcon->GetImageAlias();
720 if (sAlias.GetLength() <= 0)
721 return ByteString();
722
723 const CFX_FloatRect rcPlate = pWnd->GetClientRect();
724 const CFX_SizeF image_size = pPDFIcon->GetImageSize();
725 const CFX_Matrix mt = pPDFIcon->GetImageMatrix().GetInverse();
726 const CFX_VectorF scale = fit.GetScale(image_size, rcPlate);
727 const CFX_VectorF offset = fit.GetImageOffset(image_size, scale, rcPlate);
728
729 fxcrt::ostringstream str;
730 {
731 AutoClosedQCommand q(&str);
732 WriteAppendRect(str, rcPlate);
733 str << kSetNonZeroWindingClipOperator << " "
734 << kEndPathNoFillOrStrokeOperator << "\n";
735
736 CFX_Matrix scale_matrix(scale.x, 0, 0, scale.y, rcPlate.left + offset.x,
737 rcPlate.bottom + offset.y);
738 WriteMatrix(str, scale_matrix) << " " << kConcatMatrixOperator << "\n";
739 WriteMatrix(str, mt) << " " << kConcatMatrixOperator << "\n";
740
741 str << "0 " << kSetGrayOperator << " 0 " << kSetGrayStrokedOperator << " 1 "
742 << kSetLineWidthOperator << " /" << sAlias << " "
743 << kInvokeNamedXObjectOperator << "\n";
744 }
745 pWnd->Destroy();
746 return ByteString(str);
747 }
748
GetPushButtonAppStream(const CFX_FloatRect & rcBBox,IPVT_FontMap * pFontMap,RetainPtr<CPDF_Stream> pIconStream,CPDF_IconFit & IconFit,const WideString & sLabel,const CFX_Color & crText,float fFontSize,ButtonStyle nLayOut)749 ByteString GetPushButtonAppStream(const CFX_FloatRect& rcBBox,
750 IPVT_FontMap* pFontMap,
751 RetainPtr<CPDF_Stream> pIconStream,
752 CPDF_IconFit& IconFit,
753 const WideString& sLabel,
754 const CFX_Color& crText,
755 float fFontSize,
756 ButtonStyle nLayOut) {
757 const float fAutoFontScale = 1.0f / 3.0f;
758
759 auto pEdit = std::make_unique<CPWL_EditImpl>();
760 pEdit->SetFontMap(pFontMap);
761 pEdit->SetAlignmentH(1);
762 pEdit->SetAlignmentV(1);
763 pEdit->SetMultiLine(false);
764 pEdit->SetAutoReturn(false);
765 if (FXSYS_IsFloatZero(fFontSize))
766 pEdit->SetAutoFontSize(true);
767 else
768 pEdit->SetFontSize(fFontSize);
769
770 pEdit->Initialize();
771 pEdit->SetText(sLabel);
772 pEdit->Paint();
773
774 CFX_FloatRect rcLabelContent = pEdit->GetContentRect();
775 CFX_FloatRect rcLabel;
776 CFX_FloatRect rcIcon;
777 float fWidth = 0.0f;
778 float fHeight = 0.0f;
779
780 switch (nLayOut) {
781 case ButtonStyle::kLabel:
782 rcLabel = rcBBox;
783 break;
784 case ButtonStyle::kIcon:
785 rcIcon = rcBBox;
786 break;
787 case ButtonStyle::kIconTopLabelBottom:
788 if (pIconStream) {
789 if (FXSYS_IsFloatZero(fFontSize)) {
790 fHeight = rcBBox.Height();
791 rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcBBox.right,
792 rcBBox.bottom + fHeight * fAutoFontScale);
793 rcIcon =
794 CFX_FloatRect(rcBBox.left, rcLabel.top, rcBBox.right, rcBBox.top);
795 } else {
796 fHeight = rcLabelContent.Height();
797
798 if (rcBBox.bottom + fHeight > rcBBox.top) {
799 rcLabel = rcBBox;
800 } else {
801 rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcBBox.right,
802 rcBBox.bottom + fHeight);
803 rcIcon = CFX_FloatRect(rcBBox.left, rcLabel.top, rcBBox.right,
804 rcBBox.top);
805 }
806 }
807 } else {
808 rcLabel = rcBBox;
809 }
810 break;
811 case ButtonStyle::kIconBottomLabelTop:
812 if (pIconStream) {
813 if (FXSYS_IsFloatZero(fFontSize)) {
814 fHeight = rcBBox.Height();
815 rcLabel =
816 CFX_FloatRect(rcBBox.left, rcBBox.top - fHeight * fAutoFontScale,
817 rcBBox.right, rcBBox.top);
818 rcIcon = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcBBox.right,
819 rcLabel.bottom);
820 } else {
821 fHeight = rcLabelContent.Height();
822
823 if (rcBBox.bottom + fHeight > rcBBox.top) {
824 rcLabel = rcBBox;
825 } else {
826 rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.top - fHeight,
827 rcBBox.right, rcBBox.top);
828 rcIcon = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcBBox.right,
829 rcLabel.bottom);
830 }
831 }
832 } else {
833 rcLabel = rcBBox;
834 }
835 break;
836 case ButtonStyle::kIconLeftLabelRight:
837 if (pIconStream) {
838 if (FXSYS_IsFloatZero(fFontSize)) {
839 fWidth = rcBBox.right - rcBBox.left;
840 if (rcLabelContent.Width() < fWidth * fAutoFontScale) {
841 rcLabel = CFX_FloatRect(rcBBox.right - fWidth * fAutoFontScale,
842 rcBBox.bottom, rcBBox.right, rcBBox.top);
843 rcIcon = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcLabel.left,
844 rcBBox.top);
845 } else {
846 if (rcLabelContent.Width() < fWidth) {
847 rcLabel = CFX_FloatRect(rcBBox.right - rcLabelContent.Width(),
848 rcBBox.bottom, rcBBox.right, rcBBox.top);
849 rcIcon = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcLabel.left,
850 rcBBox.top);
851 } else {
852 rcLabel = rcBBox;
853 }
854 }
855 } else {
856 fWidth = rcLabelContent.Width();
857 if (rcBBox.left + fWidth > rcBBox.right) {
858 rcLabel = rcBBox;
859 } else {
860 rcLabel = CFX_FloatRect(rcBBox.right - fWidth, rcBBox.bottom,
861 rcBBox.right, rcBBox.top);
862 rcIcon = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcLabel.left,
863 rcBBox.top);
864 }
865 }
866 } else {
867 rcLabel = rcBBox;
868 }
869 break;
870 case ButtonStyle::kIconRightLabelLeft:
871 if (pIconStream) {
872 if (FXSYS_IsFloatZero(fFontSize)) {
873 fWidth = rcBBox.right - rcBBox.left;
874 if (rcLabelContent.Width() < fWidth * fAutoFontScale) {
875 rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom,
876 rcBBox.left + fWidth * fAutoFontScale,
877 rcBBox.top);
878 rcIcon = CFX_FloatRect(rcLabel.right, rcBBox.bottom, rcBBox.right,
879 rcBBox.top);
880 } else {
881 if (rcLabelContent.Width() < fWidth) {
882 rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom,
883 rcBBox.left + rcLabelContent.Width(),
884 rcBBox.top);
885 rcIcon = CFX_FloatRect(rcLabel.right, rcBBox.bottom, rcBBox.right,
886 rcBBox.top);
887 } else {
888 rcLabel = rcBBox;
889 }
890 }
891 } else {
892 fWidth = rcLabelContent.Width();
893 if (rcBBox.left + fWidth > rcBBox.right) {
894 rcLabel = rcBBox;
895 } else {
896 rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom,
897 rcBBox.left + fWidth, rcBBox.top);
898 rcIcon = CFX_FloatRect(rcLabel.right, rcBBox.bottom, rcBBox.right,
899 rcBBox.top);
900 }
901 }
902 } else {
903 rcLabel = rcBBox;
904 }
905 break;
906 case ButtonStyle::kLabelOverIcon:
907 rcLabel = rcBBox;
908 rcIcon = rcBBox;
909 break;
910 }
911
912 fxcrt::ostringstream sTemp;
913 sTemp << GenerateIconAppStream(IconFit, std::move(pIconStream), rcIcon);
914
915 if (!rcLabel.IsEmpty()) {
916 pEdit->SetPlateRect(rcLabel);
917 pEdit->Paint();
918 ByteString sEdit =
919 GetEditAppStream(pEdit.get(), CFX_PointF(0.0f, 0.0f), true, 0);
920 if (sEdit.GetLength() > 0) {
921 AutoClosedCommand bt(&sTemp, kTextBeginOperator, kTextEndOperator);
922 sTemp << GetFillColorAppStream(crText) << sEdit;
923 }
924 }
925
926 if (sTemp.tellp() <= 0)
927 return ByteString();
928
929 fxcrt::ostringstream sAppStream;
930 {
931 AutoClosedQCommand q(&sAppStream);
932 WriteAppendRect(sAppStream, rcBBox);
933 sAppStream << kSetNonZeroWindingClipOperator << " "
934 << kEndPathNoFillOrStrokeOperator << "\n";
935 sAppStream << sTemp.str().c_str();
936 }
937 return ByteString(sAppStream);
938 }
939
GetBorderAppStreamInternal(const CFX_FloatRect & rect,float fWidth,const CFX_Color & color,const CFX_Color & crLeftTop,const CFX_Color & crRightBottom,BorderStyle nStyle,const CPWL_Dash & dash)940 ByteString GetBorderAppStreamInternal(const CFX_FloatRect& rect,
941 float fWidth,
942 const CFX_Color& color,
943 const CFX_Color& crLeftTop,
944 const CFX_Color& crRightBottom,
945 BorderStyle nStyle,
946 const CPWL_Dash& dash) {
947 fxcrt::ostringstream sAppStream;
948 ByteString sColor;
949
950 float fLeft = rect.left;
951 float fRight = rect.right;
952 float fTop = rect.top;
953 float fBottom = rect.bottom;
954
955 if (fWidth > 0.0f) {
956 float fHalfWidth = fWidth / 2.0f;
957 AutoClosedQCommand q(&sAppStream);
958
959 switch (nStyle) {
960 default:
961 case BorderStyle::kSolid:
962 sColor = GetFillColorAppStream(color);
963 if (sColor.GetLength() > 0) {
964 sAppStream << sColor;
965 WriteAppendRect(sAppStream, {fLeft, fBottom, fRight, fTop});
966 WriteAppendRect(sAppStream, {fLeft + fWidth, fBottom + fWidth,
967 fRight - fWidth, fTop - fWidth});
968 sAppStream << kFillEvenOddOperator << "\n";
969 }
970 break;
971 case BorderStyle::kDash:
972 sColor = GetStrokeColorAppStream(color);
973 if (sColor.GetLength() > 0) {
974 sAppStream << sColor;
975 sAppStream << fWidth << " " << kSetLineWidthOperator << " ["
976 << dash.nDash << " " << dash.nGap << "] " << dash.nPhase
977 << " " << kSetDashOperator << "\n";
978 const CFX_PointF points[] = {
979 {fLeft + fWidth / 2, fBottom + fWidth / 2},
980 {fLeft + fWidth / 2, fTop - fWidth / 2},
981 {fRight - fWidth / 2, fTop - fWidth / 2},
982 {fRight - fWidth / 2, fBottom + fWidth / 2}};
983 WriteClosedLoop(sAppStream, points);
984 sAppStream << kStrokeOperator << "\n";
985 }
986 break;
987 case BorderStyle::kBeveled:
988 case BorderStyle::kInset:
989 sColor = GetFillColorAppStream(crLeftTop);
990 if (sColor.GetLength() > 0) {
991 sAppStream << sColor;
992 WriteMove(sAppStream, {fLeft + fHalfWidth, fBottom + fHalfWidth});
993 WriteLine(sAppStream, {fLeft + fHalfWidth, fTop - fHalfWidth});
994 WriteLine(sAppStream, {fRight - fHalfWidth, fTop - fHalfWidth});
995 WriteLine(sAppStream,
996 {fRight - fHalfWidth * 2, fTop - fHalfWidth * 2});
997 WriteLine(sAppStream,
998 {fLeft + fHalfWidth * 2, fTop - fHalfWidth * 2});
999 WriteLine(sAppStream,
1000 {fLeft + fHalfWidth * 2, fBottom + fHalfWidth * 2});
1001 sAppStream << kFillOperator << "\n";
1002 }
1003 sColor = GetFillColorAppStream(crRightBottom);
1004 if (sColor.GetLength() > 0) {
1005 sAppStream << sColor;
1006 WriteMove(sAppStream, {fRight - fHalfWidth, fTop - fHalfWidth});
1007 WriteLine(sAppStream, {fRight - fHalfWidth, fBottom + fHalfWidth});
1008 WriteLine(sAppStream, {fLeft + fHalfWidth, fBottom + fHalfWidth});
1009 WriteLine(sAppStream,
1010 {fLeft + fHalfWidth * 2, fBottom + fHalfWidth * 2});
1011 WriteLine(sAppStream,
1012 {fRight - fHalfWidth * 2, fBottom + fHalfWidth * 2});
1013 WriteLine(sAppStream,
1014 {fRight - fHalfWidth * 2, fTop - fHalfWidth * 2});
1015 sAppStream << kFillOperator << "\n";
1016 }
1017 sColor = GetFillColorAppStream(color);
1018 if (sColor.GetLength() > 0) {
1019 sAppStream << sColor;
1020 WriteAppendRect(sAppStream, {fLeft, fBottom, fRight, fTop});
1021 WriteAppendRect(sAppStream, {fLeft + fHalfWidth, fBottom + fHalfWidth,
1022 fRight - fHalfWidth, fTop - fHalfWidth});
1023 sAppStream << kFillEvenOddOperator << "\n";
1024 }
1025 break;
1026 case BorderStyle::kUnderline:
1027 sColor = GetStrokeColorAppStream(color);
1028 if (sColor.GetLength() > 0) {
1029 sAppStream << sColor;
1030 sAppStream << fWidth << " " << kSetLineWidthOperator << "\n";
1031 WriteMove(sAppStream, {fLeft, fBottom + fWidth / 2});
1032 WriteLine(sAppStream, {fRight, fBottom + fWidth / 2});
1033 sAppStream << kStrokeOperator << "\n";
1034 }
1035 break;
1036 }
1037 }
1038 return ByteString(sAppStream);
1039 }
1040
GetDropButtonAppStream(const CFX_FloatRect & rcBBox)1041 ByteString GetDropButtonAppStream(const CFX_FloatRect& rcBBox) {
1042 if (rcBBox.IsEmpty())
1043 return ByteString();
1044
1045 fxcrt::ostringstream sAppStream;
1046 {
1047 AutoClosedQCommand q(&sAppStream);
1048 sAppStream << GetFillColorAppStream(
1049 CFX_Color(CFX_Color::Type::kRGB, 220.0f / 255.0f, 220.0f / 255.0f,
1050 220.0f / 255.0f));
1051 WriteAppendRect(sAppStream, rcBBox);
1052 sAppStream << kFillOperator << "\n";
1053 }
1054
1055 {
1056 AutoClosedQCommand q(&sAppStream);
1057 sAppStream << GetBorderAppStreamInternal(
1058 rcBBox, 2, CFX_Color(CFX_Color::Type::kGray, 0),
1059 CFX_Color(CFX_Color::Type::kGray, 1),
1060 CFX_Color(CFX_Color::Type::kGray, 0.5), BorderStyle::kBeveled,
1061 CPWL_Dash(3, 0, 0));
1062 }
1063
1064 CFX_PointF ptCenter = CFX_PointF((rcBBox.left + rcBBox.right) / 2,
1065 (rcBBox.top + rcBBox.bottom) / 2);
1066 if (FXSYS_IsFloatBigger(rcBBox.right - rcBBox.left, 6) &&
1067 FXSYS_IsFloatBigger(rcBBox.top - rcBBox.bottom, 6)) {
1068 AutoClosedQCommand q(&sAppStream);
1069 const CFX_PointF points[] = {{ptCenter.x - 3, ptCenter.y + 1.5f},
1070 {ptCenter.x + 3, ptCenter.y + 1.5f},
1071 {ptCenter.x, ptCenter.y - 1.5f}};
1072 sAppStream << " 0 " << kSetGrayOperator << "\n";
1073 WriteClosedLoop(sAppStream, points);
1074 sAppStream << kFillOperator << "\n";
1075 }
1076
1077 return ByteString(sAppStream);
1078 }
1079
GetRectFillAppStream(const CFX_FloatRect & rect,const CFX_Color & color)1080 ByteString GetRectFillAppStream(const CFX_FloatRect& rect,
1081 const CFX_Color& color) {
1082 fxcrt::ostringstream sAppStream;
1083 ByteString sColor = GetFillColorAppStream(color);
1084 if (sColor.GetLength() > 0) {
1085 AutoClosedQCommand q(&sAppStream);
1086 sAppStream << sColor;
1087 WriteAppendRect(sAppStream, rect);
1088 sAppStream << kFillOperator << "\n";
1089 }
1090
1091 return ByteString(sAppStream);
1092 }
1093
SetDefaultIconName(CPDF_Stream * pIcon,const char * name)1094 void SetDefaultIconName(CPDF_Stream* pIcon, const char* name) {
1095 if (!pIcon)
1096 return;
1097
1098 RetainPtr<CPDF_Dictionary> pImageDict = pIcon->GetMutableDict();
1099 if (!pImageDict)
1100 return;
1101
1102 if (pImageDict->KeyExist("Name"))
1103 return;
1104
1105 pImageDict->SetNewFor<CPDF_String>("Name", name, false);
1106 }
1107
CheckStyleFromCaption(const WideString & caption)1108 absl::optional<CheckStyle> CheckStyleFromCaption(const WideString& caption) {
1109 if (caption.IsEmpty())
1110 return absl::nullopt;
1111
1112 // Character values are ZapfDingbats encodings of named glyphs.
1113 switch (caption[0]) {
1114 case L'4':
1115 return CheckStyle::kCheck;
1116 case L'8':
1117 return CheckStyle::kCross;
1118 case L'H':
1119 return CheckStyle::kStar;
1120 case L'l':
1121 return CheckStyle::kCircle;
1122 case L'n':
1123 return CheckStyle::kSquare;
1124 case L'u':
1125 return CheckStyle::kDiamond;
1126 default:
1127 return absl::nullopt;
1128 }
1129 }
1130
1131 } // namespace
1132
CPDFSDK_AppStream(CPDFSDK_Widget * widget,CPDF_Dictionary * dict)1133 CPDFSDK_AppStream::CPDFSDK_AppStream(CPDFSDK_Widget* widget,
1134 CPDF_Dictionary* dict)
1135 : widget_(widget), dict_(dict) {}
1136
1137 CPDFSDK_AppStream::~CPDFSDK_AppStream() = default;
1138
SetAsPushButton()1139 void CPDFSDK_AppStream::SetAsPushButton() {
1140 CPDF_FormControl* pControl = widget_->GetFormControl();
1141 CFX_FloatRect rcWindow = widget_->GetRotatedRect();
1142 ButtonStyle nLayout = ButtonStyle::kLabel;
1143 switch (pControl->GetTextPosition()) {
1144 case TEXTPOS_ICON:
1145 nLayout = ButtonStyle::kIcon;
1146 break;
1147 case TEXTPOS_BELOW:
1148 nLayout = ButtonStyle::kIconTopLabelBottom;
1149 break;
1150 case TEXTPOS_ABOVE:
1151 nLayout = ButtonStyle::kIconBottomLabelTop;
1152 break;
1153 case TEXTPOS_RIGHT:
1154 nLayout = ButtonStyle::kIconLeftLabelRight;
1155 break;
1156 case TEXTPOS_LEFT:
1157 nLayout = ButtonStyle::kIconRightLabelLeft;
1158 break;
1159 case TEXTPOS_OVERLAID:
1160 nLayout = ButtonStyle::kLabelOverIcon;
1161 break;
1162 default:
1163 nLayout = ButtonStyle::kLabel;
1164 break;
1165 }
1166
1167 CFX_Color crBackground = pControl->GetOriginalBackgroundColor();
1168 CFX_Color crBorder = pControl->GetOriginalBorderColor();
1169
1170 float fBorderWidth = static_cast<float>(widget_->GetBorderWidth());
1171 CPWL_Dash dsBorder(3, 0, 0);
1172 CFX_Color crLeftTop;
1173 CFX_Color crRightBottom;
1174
1175 BorderStyle nBorderStyle = widget_->GetBorderStyle();
1176 switch (nBorderStyle) {
1177 case BorderStyle::kDash:
1178 dsBorder = CPWL_Dash(3, 3, 0);
1179 break;
1180 case BorderStyle::kBeveled:
1181 fBorderWidth *= 2;
1182 crLeftTop = CFX_Color(CFX_Color::Type::kGray, 1);
1183 crRightBottom = crBackground / 2.0f;
1184 break;
1185 case BorderStyle::kInset:
1186 fBorderWidth *= 2;
1187 crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0.5);
1188 crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.75);
1189 break;
1190 default:
1191 break;
1192 }
1193
1194 CFX_FloatRect rcClient = rcWindow.GetDeflated(fBorderWidth, fBorderWidth);
1195 CPDF_DefaultAppearance da = pControl->GetDefaultAppearance();
1196 absl::optional<CFX_Color> color = da.GetColor();
1197 CFX_Color crText = color.value_or(CFX_Color(CFX_Color::Type::kGray, 0));
1198
1199 float fFontSize;
1200 ByteString csNameTag;
1201 absl::optional<ByteString> font = da.GetFont(&fFontSize);
1202 if (font.has_value())
1203 csNameTag = font.value();
1204 else
1205 fFontSize = 12.0f;
1206
1207 WideString csWCaption;
1208 WideString csNormalCaption;
1209 WideString csRolloverCaption;
1210 WideString csDownCaption;
1211 if (pControl->HasMKEntry(pdfium::appearance::kCA))
1212 csNormalCaption = pControl->GetNormalCaption();
1213
1214 if (pControl->HasMKEntry(pdfium::appearance::kRC))
1215 csRolloverCaption = pControl->GetRolloverCaption();
1216
1217 if (pControl->HasMKEntry(pdfium::appearance::kAC))
1218 csDownCaption = pControl->GetDownCaption();
1219
1220 RetainPtr<CPDF_Stream> pNormalIcon;
1221 RetainPtr<CPDF_Stream> pRolloverIcon;
1222 RetainPtr<CPDF_Stream> pDownIcon;
1223 if (pControl->HasMKEntry(pdfium::appearance::kI))
1224 pNormalIcon = pControl->GetNormalIcon();
1225
1226 if (pControl->HasMKEntry(pdfium::appearance::kRI))
1227 pRolloverIcon = pControl->GetRolloverIcon();
1228
1229 if (pControl->HasMKEntry(pdfium::appearance::kIX))
1230 pDownIcon = pControl->GetDownIcon();
1231
1232 SetDefaultIconName(pNormalIcon.Get(), "ImgA");
1233 SetDefaultIconName(pRolloverIcon.Get(), "ImgB");
1234 SetDefaultIconName(pDownIcon.Get(), "ImgC");
1235
1236 CPDF_IconFit iconFit = pControl->GetIconFit();
1237 {
1238 CPDF_BAFontMap font_map(widget_->GetPDFPage()->GetDocument(),
1239 widget_->GetPDFAnnot()->GetMutableAnnotDict(), "N");
1240 ByteString csAP =
1241 GetRectFillAppStream(rcWindow, crBackground) +
1242 GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
1243 crRightBottom, nBorderStyle, dsBorder) +
1244 GetPushButtonAppStream(iconFit.GetFittingBounds() ? rcWindow : rcClient,
1245 &font_map, pNormalIcon, iconFit, csNormalCaption,
1246 crText, fFontSize, nLayout);
1247
1248 Write("N", csAP, ByteString());
1249 if (pNormalIcon)
1250 AddImage("N", pNormalIcon.Get());
1251
1252 CPDF_FormControl::HighlightingMode eHLM = pControl->GetHighlightingMode();
1253 if (eHLM != CPDF_FormControl::kPush && eHLM != CPDF_FormControl::kToggle) {
1254 Remove("D");
1255 Remove("R");
1256 return;
1257 }
1258
1259 if (csRolloverCaption.IsEmpty() && !pRolloverIcon) {
1260 csRolloverCaption = csNormalCaption;
1261 pRolloverIcon = pNormalIcon;
1262 }
1263 }
1264 {
1265 CPDF_BAFontMap font_map(widget_->GetPDFPage()->GetDocument(),
1266 widget_->GetPDFAnnot()->GetMutableAnnotDict(), "R");
1267 ByteString csAP =
1268 GetRectFillAppStream(rcWindow, crBackground) +
1269 GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
1270 crRightBottom, nBorderStyle, dsBorder) +
1271 GetPushButtonAppStream(iconFit.GetFittingBounds() ? rcWindow : rcClient,
1272 &font_map, pRolloverIcon, iconFit,
1273 csRolloverCaption, crText, fFontSize, nLayout);
1274
1275 Write("R", csAP, ByteString());
1276 if (pRolloverIcon)
1277 AddImage("R", pRolloverIcon.Get());
1278
1279 if (csDownCaption.IsEmpty() && !pDownIcon) {
1280 csDownCaption = csNormalCaption;
1281 pDownIcon = pNormalIcon;
1282 }
1283
1284 switch (nBorderStyle) {
1285 case BorderStyle::kBeveled: {
1286 CFX_Color crTemp = crLeftTop;
1287 crLeftTop = crRightBottom;
1288 crRightBottom = crTemp;
1289 break;
1290 }
1291 case BorderStyle::kInset: {
1292 crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0);
1293 crRightBottom = CFX_Color(CFX_Color::Type::kGray, 1);
1294 break;
1295 }
1296 default:
1297 break;
1298 }
1299 }
1300 {
1301 CPDF_BAFontMap font_map(widget_->GetPDFPage()->GetDocument(),
1302 widget_->GetPDFAnnot()->GetMutableAnnotDict(), "D");
1303 ByteString csAP =
1304 GetRectFillAppStream(rcWindow, crBackground - 0.25f) +
1305 GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
1306 crRightBottom, nBorderStyle, dsBorder) +
1307 GetPushButtonAppStream(iconFit.GetFittingBounds() ? rcWindow : rcClient,
1308 &font_map, pDownIcon, iconFit, csDownCaption,
1309 crText, fFontSize, nLayout);
1310
1311 Write("D", csAP, ByteString());
1312 if (pDownIcon)
1313 AddImage("D", pDownIcon.Get());
1314 }
1315 }
1316
SetAsCheckBox()1317 void CPDFSDK_AppStream::SetAsCheckBox() {
1318 CPDF_FormControl* pControl = widget_->GetFormControl();
1319 CFX_Color crBackground = pControl->GetOriginalBackgroundColor();
1320 CFX_Color crBorder = pControl->GetOriginalBorderColor();
1321 float fBorderWidth = static_cast<float>(widget_->GetBorderWidth());
1322 CPWL_Dash dsBorder(3, 0, 0);
1323 CFX_Color crLeftTop;
1324 CFX_Color crRightBottom;
1325
1326 BorderStyle nBorderStyle = widget_->GetBorderStyle();
1327 switch (nBorderStyle) {
1328 case BorderStyle::kDash:
1329 dsBorder = CPWL_Dash(3, 3, 0);
1330 break;
1331 case BorderStyle::kBeveled:
1332 fBorderWidth *= 2;
1333 crLeftTop = CFX_Color(CFX_Color::Type::kGray, 1);
1334 crRightBottom = crBackground / 2.0f;
1335 break;
1336 case BorderStyle::kInset:
1337 fBorderWidth *= 2;
1338 crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0.5);
1339 crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.75);
1340 break;
1341 default:
1342 break;
1343 }
1344
1345 CFX_FloatRect rcWindow = widget_->GetRotatedRect();
1346 CFX_FloatRect rcClient = rcWindow.GetDeflated(fBorderWidth, fBorderWidth);
1347 absl::optional<CFX_Color> color = pControl->GetDefaultAppearance().GetColor();
1348 CFX_Color crText = color.value_or(CFX_Color());
1349
1350 CheckStyle nStyle = CheckStyleFromCaption(pControl->GetNormalCaption())
1351 .value_or(CheckStyle::kCheck);
1352 ByteString csAP_N_ON =
1353 GetRectFillAppStream(rcWindow, crBackground) +
1354 GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
1355 crRightBottom, nBorderStyle, dsBorder);
1356
1357 ByteString csAP_N_OFF = csAP_N_ON;
1358
1359 switch (nBorderStyle) {
1360 case BorderStyle::kBeveled: {
1361 CFX_Color crTemp = crLeftTop;
1362 crLeftTop = crRightBottom;
1363 crRightBottom = crTemp;
1364 break;
1365 }
1366 case BorderStyle::kInset: {
1367 crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0);
1368 crRightBottom = CFX_Color(CFX_Color::Type::kGray, 1);
1369 break;
1370 }
1371 default:
1372 break;
1373 }
1374
1375 ByteString csAP_D_ON =
1376 GetRectFillAppStream(rcWindow, crBackground - 0.25f) +
1377 GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
1378 crRightBottom, nBorderStyle, dsBorder);
1379
1380 ByteString csAP_D_OFF = csAP_D_ON;
1381
1382 csAP_N_ON += GetCheckBoxAppStream(rcClient, nStyle, crText);
1383 csAP_D_ON += GetCheckBoxAppStream(rcClient, nStyle, crText);
1384
1385 Write("N", csAP_N_ON, pControl->GetCheckedAPState());
1386 Write("N", csAP_N_OFF, "Off");
1387
1388 Write("D", csAP_D_ON, pControl->GetCheckedAPState());
1389 Write("D", csAP_D_OFF, "Off");
1390
1391 ByteString csAS = widget_->GetAppState();
1392 if (csAS.IsEmpty())
1393 widget_->SetAppStateOff();
1394 }
1395
SetAsRadioButton()1396 void CPDFSDK_AppStream::SetAsRadioButton() {
1397 CPDF_FormControl* pControl = widget_->GetFormControl();
1398 CFX_Color crBackground = pControl->GetOriginalBackgroundColor();
1399 CFX_Color crBorder = pControl->GetOriginalBorderColor();
1400 float fBorderWidth = static_cast<float>(widget_->GetBorderWidth());
1401 CPWL_Dash dsBorder(3, 0, 0);
1402 CFX_Color crLeftTop;
1403 CFX_Color crRightBottom;
1404
1405 BorderStyle nBorderStyle = widget_->GetBorderStyle();
1406 switch (nBorderStyle) {
1407 case BorderStyle::kDash:
1408 dsBorder = CPWL_Dash(3, 3, 0);
1409 break;
1410 case BorderStyle::kBeveled:
1411 fBorderWidth *= 2;
1412 crLeftTop = CFX_Color(CFX_Color::Type::kGray, 1);
1413 crRightBottom = crBackground / 2.0f;
1414 break;
1415 case BorderStyle::kInset:
1416 fBorderWidth *= 2;
1417 crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0.5);
1418 crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.75);
1419 break;
1420 default:
1421 break;
1422 }
1423
1424 CFX_FloatRect rcWindow = widget_->GetRotatedRect();
1425 CFX_FloatRect rcClient = rcWindow.GetDeflated(fBorderWidth, fBorderWidth);
1426 absl::optional<CFX_Color> color = pControl->GetDefaultAppearance().GetColor();
1427 CFX_Color crText = color.value_or(CFX_Color());
1428 CheckStyle nStyle = CheckStyleFromCaption(pControl->GetNormalCaption())
1429 .value_or(CheckStyle::kCircle);
1430
1431 ByteString csAP_N_ON;
1432 CFX_FloatRect rcCenter = rcWindow.GetCenterSquare().GetDeflated(1.0f, 1.0f);
1433 if (nStyle == CheckStyle::kCircle) {
1434 if (nBorderStyle == BorderStyle::kBeveled) {
1435 crLeftTop = CFX_Color(CFX_Color::Type::kGray, 1);
1436 crRightBottom = crBackground - 0.25f;
1437 } else if (nBorderStyle == BorderStyle::kInset) {
1438 crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0.5f);
1439 crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.75f);
1440 }
1441
1442 csAP_N_ON =
1443 GetCircleFillAppStream(rcCenter, crBackground) +
1444 GetCircleBorderAppStream(rcCenter, fBorderWidth, crBorder, crLeftTop,
1445 crRightBottom, nBorderStyle, dsBorder);
1446 } else {
1447 csAP_N_ON =
1448 GetRectFillAppStream(rcWindow, crBackground) +
1449 GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
1450 crRightBottom, nBorderStyle, dsBorder);
1451 }
1452
1453 ByteString csAP_N_OFF = csAP_N_ON;
1454
1455 switch (nBorderStyle) {
1456 case BorderStyle::kBeveled: {
1457 CFX_Color crTemp = crLeftTop;
1458 crLeftTop = crRightBottom;
1459 crRightBottom = crTemp;
1460 break;
1461 }
1462 case BorderStyle::kInset: {
1463 crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0);
1464 crRightBottom = CFX_Color(CFX_Color::Type::kGray, 1);
1465 break;
1466 }
1467 default:
1468 break;
1469 }
1470
1471 ByteString csAP_D_ON;
1472
1473 if (nStyle == CheckStyle::kCircle) {
1474 CFX_Color crBK = crBackground - 0.25f;
1475 if (nBorderStyle == BorderStyle::kBeveled) {
1476 crLeftTop = crBackground - 0.25f;
1477 crRightBottom = CFX_Color(CFX_Color::Type::kGray, 1);
1478 crBK = crBackground;
1479 } else if (nBorderStyle == BorderStyle::kInset) {
1480 crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0);
1481 crRightBottom = CFX_Color(CFX_Color::Type::kGray, 1);
1482 }
1483
1484 csAP_D_ON =
1485 GetCircleFillAppStream(rcCenter, crBK) +
1486 GetCircleBorderAppStream(rcCenter, fBorderWidth, crBorder, crLeftTop,
1487 crRightBottom, nBorderStyle, dsBorder);
1488 } else {
1489 csAP_D_ON =
1490 GetRectFillAppStream(rcWindow, crBackground - 0.25f) +
1491 GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
1492 crRightBottom, nBorderStyle, dsBorder);
1493 }
1494
1495 ByteString csAP_D_OFF = csAP_D_ON;
1496
1497 ByteString app_stream = GetRadioButtonAppStream(rcClient, nStyle, crText);
1498 csAP_N_ON += app_stream;
1499 csAP_D_ON += app_stream;
1500
1501 Write("N", csAP_N_ON, pControl->GetCheckedAPState());
1502 Write("N", csAP_N_OFF, "Off");
1503
1504 Write("D", csAP_D_ON, pControl->GetCheckedAPState());
1505 Write("D", csAP_D_OFF, "Off");
1506
1507 ByteString csAS = widget_->GetAppState();
1508 if (csAS.IsEmpty())
1509 widget_->SetAppStateOff();
1510 }
1511
SetAsComboBox(absl::optional<WideString> sValue)1512 void CPDFSDK_AppStream::SetAsComboBox(absl::optional<WideString> sValue) {
1513 CPDF_FormControl* pControl = widget_->GetFormControl();
1514 CPDF_FormField* pField = pControl->GetField();
1515 fxcrt::ostringstream sBody;
1516
1517 CFX_FloatRect rcClient = widget_->GetClientRect();
1518 CFX_FloatRect rcButton = rcClient;
1519 rcButton.left = rcButton.right - 13;
1520 rcButton.Normalize();
1521
1522 // Font map must outlive |pEdit|.
1523 CPDF_BAFontMap font_map(widget_->GetPDFPage()->GetDocument(),
1524 widget_->GetPDFAnnot()->GetMutableAnnotDict(), "N");
1525
1526 auto pEdit = std::make_unique<CPWL_EditImpl>();
1527 pEdit->EnableRefresh(false);
1528 pEdit->SetFontMap(&font_map);
1529
1530 CFX_FloatRect rcEdit = rcClient;
1531 rcEdit.right = rcButton.left;
1532 rcEdit.Normalize();
1533
1534 pEdit->SetPlateRect(rcEdit);
1535 pEdit->SetAlignmentV(1);
1536
1537 float fFontSize = widget_->GetFontSize();
1538 if (FXSYS_IsFloatZero(fFontSize))
1539 pEdit->SetAutoFontSize(true);
1540 else
1541 pEdit->SetFontSize(fFontSize);
1542
1543 pEdit->Initialize();
1544 if (sValue.has_value()) {
1545 pEdit->SetText(sValue.value());
1546 } else {
1547 int32_t nCurSel = pField->GetSelectedIndex(0);
1548 if (nCurSel < 0) {
1549 pEdit->SetText(pField->GetValue());
1550 } else {
1551 pEdit->SetText(pField->GetOptionLabel(nCurSel));
1552 }
1553 }
1554 pEdit->Paint();
1555
1556 CFX_FloatRect rcContent = pEdit->GetContentRect();
1557 ByteString sEdit = GetEditAppStream(pEdit.get(), CFX_PointF(), true, 0);
1558 if (sEdit.GetLength() > 0) {
1559 sBody << "/Tx ";
1560 AutoClosedCommand bmc(&sBody, kMarkedSequenceBeginOperator,
1561 kMarkedSequenceEndOperator);
1562 AutoClosedQCommand q(&sBody);
1563
1564 if (rcContent.Width() > rcEdit.Width() ||
1565 rcContent.Height() > rcEdit.Height()) {
1566 WriteAppendRect(sBody, rcEdit);
1567 sBody << kSetNonZeroWindingClipOperator << "\n"
1568 << kEndPathNoFillOrStrokeOperator << "\n";
1569 }
1570
1571 CFX_Color crText = widget_->GetTextPWLColor();
1572 AutoClosedCommand bt(&sBody, kTextBeginOperator, kTextEndOperator);
1573 sBody << GetFillColorAppStream(crText) << sEdit;
1574 }
1575
1576 sBody << GetDropButtonAppStream(rcButton);
1577 Write("N",
1578 GetBackgroundAppStream() + GetBorderAppStream() + ByteString(sBody),
1579 ByteString());
1580 }
1581
SetAsListBox()1582 void CPDFSDK_AppStream::SetAsListBox() {
1583 CPDF_FormControl* pControl = widget_->GetFormControl();
1584 CPDF_FormField* pField = pControl->GetField();
1585 CFX_FloatRect rcClient = widget_->GetClientRect();
1586 fxcrt::ostringstream sBody;
1587
1588 // Font map must outlive |pEdit|.
1589 CPDF_BAFontMap font_map(widget_->GetPDFPage()->GetDocument(),
1590 widget_->GetPDFAnnot()->GetMutableAnnotDict(), "N");
1591
1592 auto pEdit = std::make_unique<CPWL_EditImpl>();
1593 pEdit->EnableRefresh(false);
1594 pEdit->SetFontMap(&font_map);
1595 pEdit->SetPlateRect(CFX_FloatRect(rcClient.left, 0.0f, rcClient.right, 0.0f));
1596
1597 float fFontSize = widget_->GetFontSize();
1598 pEdit->SetFontSize(FXSYS_IsFloatZero(fFontSize) ? 12.0f : fFontSize);
1599 pEdit->Initialize();
1600
1601 fxcrt::ostringstream sList;
1602 float fy = rcClient.top;
1603
1604 int32_t nTop = pField->GetTopVisibleIndex();
1605 int32_t nCount = pField->CountOptions();
1606 int32_t nSelCount = pField->CountSelectedItems();
1607
1608 for (int32_t i = nTop; i < nCount; ++i) {
1609 bool bSelected = false;
1610 for (int32_t j = 0; j < nSelCount; ++j) {
1611 if (pField->GetSelectedIndex(j) == i) {
1612 bSelected = true;
1613 break;
1614 }
1615 }
1616
1617 pEdit->SetText(pField->GetOptionLabel(i));
1618 pEdit->Paint();
1619
1620 CFX_FloatRect rcContent = pEdit->GetContentRect();
1621 float fItemHeight = rcContent.Height();
1622
1623 if (bSelected) {
1624 CFX_FloatRect rcItem =
1625 CFX_FloatRect(rcClient.left, fy - fItemHeight, rcClient.right, fy);
1626 {
1627 AutoClosedQCommand q(&sList);
1628 sList << GetFillColorAppStream(CFX_Color(
1629 CFX_Color::Type::kRGB, 0, 51.0f / 255.0f, 113.0f / 255.0f));
1630 WriteAppendRect(sList, rcItem);
1631 sList << kFillOperator << "\n";
1632 }
1633
1634 AutoClosedCommand bt(&sList, kTextBeginOperator, kTextEndOperator);
1635 sList << GetFillColorAppStream(CFX_Color(CFX_Color::Type::kGray, 1))
1636 << GetEditAppStream(pEdit.get(), CFX_PointF(0.0f, fy), true, 0);
1637 } else {
1638 CFX_Color crText = widget_->GetTextPWLColor();
1639
1640 AutoClosedCommand bt(&sList, kTextBeginOperator, kTextEndOperator);
1641 sList << GetFillColorAppStream(crText)
1642 << GetEditAppStream(pEdit.get(), CFX_PointF(0.0f, fy), true, 0);
1643 }
1644
1645 fy -= fItemHeight;
1646 }
1647
1648 if (sList.tellp() > 0) {
1649 sBody << "/Tx ";
1650 AutoClosedCommand bmc(&sBody, kMarkedSequenceBeginOperator,
1651 kMarkedSequenceEndOperator);
1652 AutoClosedQCommand q(&sBody);
1653
1654 WriteAppendRect(sBody, rcClient);
1655 sBody << kSetNonZeroWindingClipOperator << "\n"
1656 << kEndPathNoFillOrStrokeOperator << "\n"
1657 << sList.str();
1658 }
1659 Write("N",
1660 GetBackgroundAppStream() + GetBorderAppStream() + ByteString(sBody),
1661 ByteString());
1662 }
1663
SetAsTextField(absl::optional<WideString> sValue)1664 void CPDFSDK_AppStream::SetAsTextField(absl::optional<WideString> sValue) {
1665 CPDF_FormControl* pControl = widget_->GetFormControl();
1666 CPDF_FormField* pField = pControl->GetField();
1667 fxcrt::ostringstream sBody;
1668 fxcrt::ostringstream sLines;
1669
1670 // Font map must outlive |pEdit|.
1671 CPDF_BAFontMap font_map(widget_->GetPDFPage()->GetDocument(),
1672 widget_->GetPDFAnnot()->GetMutableAnnotDict(), "N");
1673
1674 auto pEdit = std::make_unique<CPWL_EditImpl>();
1675 pEdit->EnableRefresh(false);
1676 pEdit->SetFontMap(&font_map);
1677
1678 CFX_FloatRect rcClient = widget_->GetClientRect();
1679 pEdit->SetPlateRect(rcClient);
1680 pEdit->SetAlignmentH(pControl->GetControlAlignment());
1681
1682 uint32_t dwFieldFlags = pField->GetFieldFlags();
1683 bool bMultiLine = dwFieldFlags & pdfium::form_flags::kTextMultiline;
1684 if (bMultiLine) {
1685 pEdit->SetMultiLine(true);
1686 pEdit->SetAutoReturn(true);
1687 } else {
1688 pEdit->SetAlignmentV(1);
1689 }
1690
1691 uint16_t subWord = 0;
1692 if (dwFieldFlags & pdfium::form_flags::kTextPassword) {
1693 subWord = '*';
1694 pEdit->SetPasswordChar(subWord);
1695 }
1696
1697 int nMaxLen = pField->GetMaxLen();
1698 bool bCharArray = dwFieldFlags & pdfium::form_flags::kTextComb;
1699 float fFontSize = widget_->GetFontSize();
1700
1701 #ifdef PDF_ENABLE_XFA
1702 if (!sValue.has_value() && widget_->GetMixXFAWidget())
1703 sValue = widget_->GetValue();
1704 #endif // PDF_ENABLE_XFA
1705
1706 if (nMaxLen > 0) {
1707 if (bCharArray) {
1708 pEdit->SetCharArray(nMaxLen);
1709 if (FXSYS_IsFloatZero(fFontSize)) {
1710 fFontSize = CPWL_Edit::GetCharArrayAutoFontSize(
1711 font_map.GetPDFFont(0).Get(), rcClient, nMaxLen);
1712 }
1713 } else {
1714 if (sValue.has_value())
1715 nMaxLen = pdfium::base::checked_cast<int>(sValue.value().GetLength());
1716 pEdit->SetLimitChar(nMaxLen);
1717 }
1718 }
1719
1720 if (FXSYS_IsFloatZero(fFontSize))
1721 pEdit->SetAutoFontSize(true);
1722 else
1723 pEdit->SetFontSize(fFontSize);
1724
1725 pEdit->Initialize();
1726 pEdit->SetText(sValue.value_or(pField->GetValue()));
1727 pEdit->Paint();
1728
1729 CFX_FloatRect rcContent = pEdit->GetContentRect();
1730 ByteString sEdit =
1731 GetEditAppStream(pEdit.get(), CFX_PointF(), !bCharArray, subWord);
1732
1733 if (sEdit.GetLength() > 0) {
1734 sBody << "/Tx ";
1735 AutoClosedCommand bmc(&sBody, kMarkedSequenceBeginOperator,
1736 kMarkedSequenceEndOperator);
1737 AutoClosedQCommand q(&sBody);
1738
1739 if (rcContent.Width() > rcClient.Width() ||
1740 rcContent.Height() > rcClient.Height()) {
1741 WriteAppendRect(sBody, rcClient);
1742 sBody << kSetNonZeroWindingClipOperator << "\n"
1743 << kEndPathNoFillOrStrokeOperator << "\n";
1744 }
1745 CFX_Color crText = widget_->GetTextPWLColor();
1746
1747 AutoClosedCommand bt(&sBody, kTextBeginOperator, kTextEndOperator);
1748 sBody << GetFillColorAppStream(crText) << sEdit;
1749 }
1750
1751 if (bCharArray) {
1752 switch (widget_->GetBorderStyle()) {
1753 case BorderStyle::kSolid: {
1754 ByteString sColor =
1755 GetStrokeColorAppStream(widget_->GetBorderPWLColor());
1756 if (sColor.GetLength() > 0) {
1757 AutoClosedQCommand q(&sLines);
1758 sLines << widget_->GetBorderWidth() << " " << kSetLineWidthOperator
1759 << "\n"
1760 << GetStrokeColorAppStream(widget_->GetBorderPWLColor())
1761 << " 2 " << kSetLineCapStyleOperator << " 0 "
1762 << kSetLineJoinStyleOperator << "\n";
1763
1764 const float width = rcClient.right - rcClient.left;
1765 for (int32_t i = 1; i < nMaxLen; ++i) {
1766 const float left = rcClient.left + (width / nMaxLen) * i;
1767 WriteMove(sLines, {left, rcClient.bottom});
1768 WriteLine(sLines, {left, rcClient.top});
1769 sLines << kStrokeOperator << "\n";
1770 }
1771 }
1772 break;
1773 }
1774 case BorderStyle::kDash: {
1775 ByteString sColor =
1776 GetStrokeColorAppStream(widget_->GetBorderPWLColor());
1777 if (sColor.GetLength() > 0) {
1778 CPWL_Dash dsBorder = CPWL_Dash(3, 3, 0);
1779 AutoClosedQCommand q(&sLines);
1780 sLines << widget_->GetBorderWidth() << " " << kSetLineWidthOperator
1781 << "\n"
1782 << GetStrokeColorAppStream(widget_->GetBorderPWLColor()) << "["
1783 << dsBorder.nDash << " " << dsBorder.nGap << "] "
1784 << dsBorder.nPhase << " " << kSetDashOperator << "\n";
1785
1786 const float width = rcClient.right - rcClient.left;
1787 for (int32_t i = 1; i < nMaxLen; ++i) {
1788 const float left = rcClient.left + (width / nMaxLen) * i;
1789 WriteMove(sLines, {left, rcClient.bottom});
1790 WriteLine(sLines, {left, rcClient.top});
1791 sLines << kStrokeOperator << "\n";
1792 }
1793 }
1794 break;
1795 }
1796 default:
1797 break;
1798 }
1799 }
1800
1801 Write("N",
1802 GetBackgroundAppStream() + GetBorderAppStream() + ByteString(sLines) +
1803 ByteString(sBody),
1804 ByteString());
1805 }
1806
AddImage(const ByteString & sAPType,CPDF_Stream * pImage)1807 void CPDFSDK_AppStream::AddImage(const ByteString& sAPType,
1808 CPDF_Stream* pImage) {
1809 RetainPtr<CPDF_Stream> pStream = dict_->GetMutableStreamFor(sAPType);
1810 RetainPtr<CPDF_Dictionary> pStreamDict = pStream->GetMutableDict();
1811 ByteString sImageAlias = "IMG";
1812
1813 RetainPtr<const CPDF_Dictionary> pImageDict = pImage->GetDict();
1814 if (pImageDict)
1815 sImageAlias = pImageDict->GetByteStringFor("Name");
1816
1817 RetainPtr<CPDF_Dictionary> pStreamResList =
1818 pStreamDict->GetOrCreateDictFor("Resources");
1819 auto pXObject = pStreamResList->SetNewFor<CPDF_Dictionary>("XObject");
1820 pXObject->SetNewFor<CPDF_Reference>(sImageAlias,
1821 widget_->GetPageView()->GetPDFDocument(),
1822 pImage->GetObjNum());
1823 }
1824
Write(const ByteString & sAPType,const ByteString & sContents,const ByteString & sAPState)1825 void CPDFSDK_AppStream::Write(const ByteString& sAPType,
1826 const ByteString& sContents,
1827 const ByteString& sAPState) {
1828 RetainPtr<CPDF_Dictionary> pParentDict;
1829 ByteString key;
1830 if (sAPState.IsEmpty()) {
1831 pParentDict = dict_;
1832 key = sAPType;
1833 } else {
1834 pParentDict = dict_->GetOrCreateDictFor(sAPType);
1835 key = sAPState;
1836 }
1837
1838 RetainPtr<CPDF_Dictionary> pOrigStreamDict;
1839
1840 // If `pStream` is created by CreateModifiedAPStream(), then it is safe to
1841 // edit, as it is not shared.
1842 RetainPtr<CPDF_Stream> pStream = pParentDict->GetMutableStreamFor(key);
1843 CPDF_Document* doc = widget_->GetPageView()->GetPDFDocument();
1844 if (!doc->IsModifiedAPStream(pStream.Get())) {
1845 if (pStream)
1846 pOrigStreamDict = pStream->GetMutableDict();
1847 pStream.Reset(doc->CreateModifiedAPStream());
1848 pParentDict->SetNewFor<CPDF_Reference>(key, doc, pStream->GetObjNum());
1849 }
1850
1851 RetainPtr<CPDF_Dictionary> pStreamDict = pStream->GetMutableDict();
1852 if (!pStreamDict) {
1853 pStreamDict = doc->New<CPDF_Dictionary>();
1854 pStreamDict->SetNewFor<CPDF_Name>("Type", "XObject");
1855 pStreamDict->SetNewFor<CPDF_Name>("Subtype", "Form");
1856 pStreamDict->SetNewFor<CPDF_Number>("FormType", 1);
1857
1858 if (pOrigStreamDict) {
1859 RetainPtr<const CPDF_Dictionary> pResources =
1860 pOrigStreamDict->GetDictFor("Resources");
1861 if (pResources)
1862 pStreamDict->SetFor("Resources", pResources->Clone());
1863 }
1864
1865 pStream->InitStreamWithEmptyData(pStreamDict);
1866 }
1867 pStreamDict->SetMatrixFor("Matrix", widget_->GetMatrix());
1868 pStreamDict->SetRectFor("BBox", widget_->GetRotatedRect());
1869 pStream->SetDataAndRemoveFilter(sContents.raw_span());
1870 }
1871
Remove(ByteStringView sAPType)1872 void CPDFSDK_AppStream::Remove(ByteStringView sAPType) {
1873 dict_->RemoveFor(sAPType);
1874 }
1875
GetBackgroundAppStream() const1876 ByteString CPDFSDK_AppStream::GetBackgroundAppStream() const {
1877 CFX_Color crBackground = widget_->GetFillPWLColor();
1878 if (crBackground.nColorType != CFX_Color::Type::kTransparent)
1879 return GetRectFillAppStream(widget_->GetRotatedRect(), crBackground);
1880
1881 return ByteString();
1882 }
1883
GetBorderAppStream() const1884 ByteString CPDFSDK_AppStream::GetBorderAppStream() const {
1885 CFX_FloatRect rcWindow = widget_->GetRotatedRect();
1886 CFX_Color crBorder = widget_->GetBorderPWLColor();
1887 CFX_Color crBackground = widget_->GetFillPWLColor();
1888 CFX_Color crLeftTop;
1889 CFX_Color crRightBottom;
1890
1891 float fBorderWidth = static_cast<float>(widget_->GetBorderWidth());
1892 CPWL_Dash dsBorder(3, 0, 0);
1893
1894 BorderStyle nBorderStyle = widget_->GetBorderStyle();
1895 switch (nBorderStyle) {
1896 case BorderStyle::kDash:
1897 dsBorder = CPWL_Dash(3, 3, 0);
1898 break;
1899 case BorderStyle::kBeveled:
1900 fBorderWidth *= 2;
1901 crLeftTop = CFX_Color(CFX_Color::Type::kGray, 1);
1902 crRightBottom = crBackground / 2.0f;
1903 break;
1904 case BorderStyle::kInset:
1905 fBorderWidth *= 2;
1906 crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0.5);
1907 crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.75);
1908 break;
1909 default:
1910 break;
1911 }
1912
1913 return GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
1914 crRightBottom, nBorderStyle, dsBorder);
1915 }
1916