xref: /aosp_15_r20/external/pdfium/fpdfsdk/cpdfsdk_appstream.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
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/containers/span.h"
42 #include "third_party/base/numerics/safe_conversions.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       case BorderStyle::kSolid:
469       case BorderStyle::kUnderline: {
470         sColor = GetStrokeColorAppStream(color);
471         if (sColor.GetLength() > 0) {
472           AutoClosedQCommand q2(&sAppStream);
473           sAppStream << fWidth << " " << kSetLineWidthOperator << "\n"
474                      << sColor << GetAP_Circle(rect_by_2) << " "
475                      << kStrokeOperator << "\n";
476         }
477       } break;
478       case BorderStyle::kDash: {
479         sColor = GetStrokeColorAppStream(color);
480         if (sColor.GetLength() > 0) {
481           AutoClosedQCommand q2(&sAppStream);
482           sAppStream << fWidth << " " << kSetLineWidthOperator << "\n"
483                      << "[" << dash.nDash << " " << dash.nGap << "] "
484                      << dash.nPhase << " " << kSetDashOperator << "\n"
485                      << sColor << GetAP_Circle(rect_by_2) << " "
486                      << kStrokeOperator << "\n";
487         }
488       } break;
489       case BorderStyle::kBeveled: {
490         sColor = GetStrokeColorAppStream(color);
491         if (sColor.GetLength() > 0) {
492           AutoClosedQCommand q2(&sAppStream);
493           sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n"
494                      << sColor << GetAP_Circle(rect) << " " << kStrokeOperator
495                      << "\n";
496         }
497         sColor = GetStrokeColorAppStream(crLeftTop);
498         if (sColor.GetLength() > 0) {
499           AutoClosedQCommand q2(&sAppStream);
500           sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n"
501                      << sColor << GetAP_HalfCircle(rect_by_75, FXSYS_PI / 4.0f)
502                      << " " << kStrokeOperator << "\n";
503         }
504         sColor = GetStrokeColorAppStream(crRightBottom);
505         if (sColor.GetLength() > 0) {
506           AutoClosedQCommand q2(&sAppStream);
507           sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n"
508                      << sColor
509                      << GetAP_HalfCircle(rect_by_75, FXSYS_PI * 5 / 4.0f) << " "
510                      << kStrokeOperator << "\n";
511         }
512       } break;
513       case BorderStyle::kInset: {
514         sColor = GetStrokeColorAppStream(color);
515         if (sColor.GetLength() > 0) {
516           AutoClosedQCommand q2(&sAppStream);
517           sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n"
518                      << sColor << GetAP_Circle(rect) << " " << kStrokeOperator
519                      << "\n";
520         }
521         sColor = GetStrokeColorAppStream(crLeftTop);
522         if (sColor.GetLength() > 0) {
523           AutoClosedQCommand q2(&sAppStream);
524           sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n"
525                      << sColor << GetAP_HalfCircle(rect_by_75, FXSYS_PI / 4.0f)
526                      << " " << kStrokeOperator << "\n";
527         }
528         sColor = GetStrokeColorAppStream(crRightBottom);
529         if (sColor.GetLength() > 0) {
530           AutoClosedQCommand q2(&sAppStream);
531           sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n"
532                      << sColor
533                      << GetAP_HalfCircle(rect_by_75, FXSYS_PI * 5 / 4.0f) << " "
534                      << kStrokeOperator << "\n";
535         }
536       } break;
537     }
538   }
539   return ByteString(sAppStream);
540 }
541 
GetCheckBoxAppStream(const CFX_FloatRect & rcBBox,CheckStyle nStyle,const CFX_Color & crText)542 ByteString GetCheckBoxAppStream(const CFX_FloatRect& rcBBox,
543                                 CheckStyle nStyle,
544                                 const CFX_Color& crText) {
545   CFX_FloatRect rcCenter = rcBBox.GetCenterSquare();
546   switch (nStyle) {
547     case CheckStyle::kCheck:
548       return GetAppStream_Check(rcCenter, crText);
549     case CheckStyle::kCircle:
550       rcCenter.ScaleFromCenterPoint(2.0f / 3.0f);
551       return GetAppStream_Circle(rcCenter, crText);
552     case CheckStyle::kCross:
553       return GetAppStream_Cross(rcCenter, crText);
554     case CheckStyle::kDiamond:
555       rcCenter.ScaleFromCenterPoint(2.0f / 3.0f);
556       return GetAppStream_Diamond(rcCenter, crText);
557     case CheckStyle::kSquare:
558       rcCenter.ScaleFromCenterPoint(2.0f / 3.0f);
559       return GetAppStream_Square(rcCenter, crText);
560     case CheckStyle::kStar:
561       rcCenter.ScaleFromCenterPoint(2.0f / 3.0f);
562       return GetAppStream_Star(rcCenter, crText);
563   }
564 }
565 
GetRadioButtonAppStream(const CFX_FloatRect & rcBBox,CheckStyle nStyle,const CFX_Color & crText)566 ByteString GetRadioButtonAppStream(const CFX_FloatRect& rcBBox,
567                                    CheckStyle nStyle,
568                                    const CFX_Color& crText) {
569   CFX_FloatRect rcCenter = rcBBox.GetCenterSquare();
570   switch (nStyle) {
571     case CheckStyle::kCheck:
572       return GetAppStream_Check(rcCenter, crText);
573     case CheckStyle::kCircle:
574       rcCenter.ScaleFromCenterPoint(1.0f / 2.0f);
575       return GetAppStream_Circle(rcCenter, crText);
576     case CheckStyle::kCross:
577       return GetAppStream_Cross(rcCenter, crText);
578     case CheckStyle::kDiamond:
579       rcCenter.ScaleFromCenterPoint(2.0f / 3.0f);
580       return GetAppStream_Diamond(rcCenter, crText);
581     case CheckStyle::kSquare:
582       rcCenter.ScaleFromCenterPoint(2.0f / 3.0f);
583       return GetAppStream_Square(rcCenter, crText);
584     case CheckStyle::kStar:
585       rcCenter.ScaleFromCenterPoint(2.0f / 3.0f);
586       return GetAppStream_Star(rcCenter, crText);
587   }
588 }
589 
GetFontSetString(IPVT_FontMap * pFontMap,int32_t nFontIndex,float fFontSize)590 ByteString GetFontSetString(IPVT_FontMap* pFontMap,
591                             int32_t nFontIndex,
592                             float fFontSize) {
593   if (!pFontMap)
594     return ByteString();
595 
596   ByteString sFontAlias = pFontMap->GetPDFFontAlias(nFontIndex);
597   if (sFontAlias.GetLength() <= 0 || fFontSize <= 0)
598     return ByteString();
599 
600   fxcrt::ostringstream sRet;
601   sRet << "/" << sFontAlias << " " << fFontSize << " "
602        << kSetTextFontAndSizeOperator << "\n";
603   return ByteString(sRet);
604 }
605 
GetWordRenderString(ByteStringView strWords)606 ByteString GetWordRenderString(ByteStringView strWords) {
607   if (strWords.IsEmpty())
608     return ByteString();
609   return PDF_EncodeString(strWords) + " " + kShowTextOperator + "\n";
610 }
611 
GetEditAppStream(CPWL_EditImpl * pEdit,const CFX_PointF & ptOffset,bool bContinuous,uint16_t SubWord)612 ByteString GetEditAppStream(CPWL_EditImpl* pEdit,
613                             const CFX_PointF& ptOffset,
614                             bool bContinuous,
615                             uint16_t SubWord) {
616   CPWL_EditImpl::Iterator* pIterator = pEdit->GetIterator();
617   pIterator->SetAt(0);
618 
619   fxcrt::ostringstream sEditStream;
620   int32_t nCurFontIndex = -1;
621   CFX_PointF ptOld;
622   CFX_PointF ptNew;
623   CPVT_WordPlace oldplace;
624   ByteString sWords;
625 
626   while (pIterator->NextWord()) {
627     CPVT_WordPlace place = pIterator->GetAt();
628     if (bContinuous) {
629       if (place.LineCmp(oldplace) != 0) {
630         if (!sWords.IsEmpty()) {
631           sEditStream << GetWordRenderString(sWords.AsStringView());
632           sWords.clear();
633         }
634 
635         CPVT_Word word;
636         if (pIterator->GetWord(word)) {
637           ptNew = CFX_PointF(word.ptWord.x + ptOffset.x,
638                              word.ptWord.y + ptOffset.y);
639         } else {
640           CPVT_Line line;
641           pIterator->GetLine(line);
642           ptNew = CFX_PointF(line.ptLine.x + ptOffset.x,
643                              line.ptLine.y + ptOffset.y);
644         }
645 
646         if (ptNew.x != ptOld.x || ptNew.y != ptOld.y) {
647           WritePoint(sEditStream, {ptNew.x - ptOld.x, ptNew.y - ptOld.y})
648               << " " << kMoveTextPositionOperator << "\n";
649 
650           ptOld = ptNew;
651         }
652       }
653 
654       CPVT_Word word;
655       if (pIterator->GetWord(word)) {
656         if (word.nFontIndex != nCurFontIndex) {
657           if (!sWords.IsEmpty()) {
658             sEditStream << GetWordRenderString(sWords.AsStringView());
659             sWords.clear();
660           }
661           sEditStream << GetFontSetString(pEdit->GetFontMap(), word.nFontIndex,
662                                           word.fFontSize);
663           nCurFontIndex = word.nFontIndex;
664         }
665 
666         sWords += pEdit->GetPDFWordString(nCurFontIndex, word.Word, SubWord);
667       }
668       oldplace = place;
669     } else {
670       CPVT_Word word;
671       if (pIterator->GetWord(word)) {
672         ptNew =
673             CFX_PointF(word.ptWord.x + ptOffset.x, word.ptWord.y + ptOffset.y);
674 
675         if (ptNew.x != ptOld.x || ptNew.y != ptOld.y) {
676           WritePoint(sEditStream, {ptNew.x - ptOld.x, ptNew.y - ptOld.y})
677               << " " << kMoveTextPositionOperator << "\n";
678           ptOld = ptNew;
679         }
680         if (word.nFontIndex != nCurFontIndex) {
681           sEditStream << GetFontSetString(pEdit->GetFontMap(), word.nFontIndex,
682                                           word.fFontSize);
683           nCurFontIndex = word.nFontIndex;
684         }
685         sEditStream << GetWordRenderString(
686             pEdit->GetPDFWordString(nCurFontIndex, word.Word, SubWord)
687                 .AsStringView());
688       }
689     }
690   }
691 
692   if (!sWords.IsEmpty())
693     sEditStream << GetWordRenderString(sWords.AsStringView());
694 
695   fxcrt::ostringstream sAppStream;
696   if (sEditStream.tellp() > 0) {
697     sAppStream << sEditStream.str();
698   }
699   return ByteString(sAppStream);
700 }
701 
GenerateIconAppStream(CPDF_IconFit & fit,RetainPtr<CPDF_Stream> pIconStream,const CFX_FloatRect & rcIcon)702 ByteString GenerateIconAppStream(CPDF_IconFit& fit,
703                                  RetainPtr<CPDF_Stream> pIconStream,
704                                  const CFX_FloatRect& rcIcon) {
705   if (rcIcon.IsEmpty() || !pIconStream)
706     return ByteString();
707 
708   CPWL_Wnd::CreateParams cp(nullptr, nullptr, nullptr);
709   cp.dwFlags = PWS_VISIBLE;
710   auto pWnd = std::make_unique<CPWL_Wnd>(cp, nullptr);
711   pWnd->Realize();
712   if (!pWnd->Move(rcIcon, false, false))
713     return ByteString();
714 
715   auto pPDFIcon = std::make_unique<CPDF_Icon>(std::move(pIconStream));
716   ByteString sAlias = pPDFIcon->GetImageAlias();
717   if (sAlias.GetLength() <= 0)
718     return ByteString();
719 
720   const CFX_FloatRect rcPlate = pWnd->GetClientRect();
721   const CFX_SizeF image_size = pPDFIcon->GetImageSize();
722   const CFX_Matrix mt = pPDFIcon->GetImageMatrix().GetInverse();
723   const CFX_VectorF scale = fit.GetScale(image_size, rcPlate);
724   const CFX_VectorF offset = fit.GetImageOffset(image_size, scale, rcPlate);
725 
726   fxcrt::ostringstream str;
727   {
728     AutoClosedQCommand q(&str);
729     WriteAppendRect(str, rcPlate);
730     str << kSetNonZeroWindingClipOperator << " "
731         << kEndPathNoFillOrStrokeOperator << "\n";
732 
733     CFX_Matrix scale_matrix(scale.x, 0, 0, scale.y, rcPlate.left + offset.x,
734                             rcPlate.bottom + offset.y);
735     WriteMatrix(str, scale_matrix) << " " << kConcatMatrixOperator << "\n";
736     WriteMatrix(str, mt) << " " << kConcatMatrixOperator << "\n";
737 
738     str << "0 " << kSetGrayOperator << " 0 " << kSetGrayStrokedOperator << " 1 "
739         << kSetLineWidthOperator << " /" << sAlias << " "
740         << kInvokeNamedXObjectOperator << "\n";
741   }
742   pWnd->Destroy();
743   return ByteString(str);
744 }
745 
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)746 ByteString GetPushButtonAppStream(const CFX_FloatRect& rcBBox,
747                                   IPVT_FontMap* pFontMap,
748                                   RetainPtr<CPDF_Stream> pIconStream,
749                                   CPDF_IconFit& IconFit,
750                                   const WideString& sLabel,
751                                   const CFX_Color& crText,
752                                   float fFontSize,
753                                   ButtonStyle nLayOut) {
754   const float fAutoFontScale = 1.0f / 3.0f;
755 
756   auto pEdit = std::make_unique<CPWL_EditImpl>();
757   pEdit->SetFontMap(pFontMap);
758   pEdit->SetAlignmentH(1);
759   pEdit->SetAlignmentV(1);
760   pEdit->SetMultiLine(false);
761   pEdit->SetAutoReturn(false);
762   if (FXSYS_IsFloatZero(fFontSize))
763     pEdit->SetAutoFontSize(true);
764   else
765     pEdit->SetFontSize(fFontSize);
766 
767   pEdit->Initialize();
768   pEdit->SetText(sLabel);
769   pEdit->Paint();
770 
771   CFX_FloatRect rcLabelContent = pEdit->GetContentRect();
772   CFX_FloatRect rcLabel;
773   CFX_FloatRect rcIcon;
774   float fWidth = 0.0f;
775   float fHeight = 0.0f;
776 
777   switch (nLayOut) {
778     case ButtonStyle::kLabel:
779       rcLabel = rcBBox;
780       break;
781     case ButtonStyle::kIcon:
782       rcIcon = rcBBox;
783       break;
784     case ButtonStyle::kIconTopLabelBottom:
785       if (pIconStream) {
786         if (FXSYS_IsFloatZero(fFontSize)) {
787           fHeight = rcBBox.Height();
788           rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcBBox.right,
789                                   rcBBox.bottom + fHeight * fAutoFontScale);
790           rcIcon =
791               CFX_FloatRect(rcBBox.left, rcLabel.top, rcBBox.right, rcBBox.top);
792         } else {
793           fHeight = rcLabelContent.Height();
794 
795           if (rcBBox.bottom + fHeight > rcBBox.top) {
796             rcLabel = rcBBox;
797           } else {
798             rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcBBox.right,
799                                     rcBBox.bottom + fHeight);
800             rcIcon = CFX_FloatRect(rcBBox.left, rcLabel.top, rcBBox.right,
801                                    rcBBox.top);
802           }
803         }
804       } else {
805         rcLabel = rcBBox;
806       }
807       break;
808     case ButtonStyle::kIconBottomLabelTop:
809       if (pIconStream) {
810         if (FXSYS_IsFloatZero(fFontSize)) {
811           fHeight = rcBBox.Height();
812           rcLabel =
813               CFX_FloatRect(rcBBox.left, rcBBox.top - fHeight * fAutoFontScale,
814                             rcBBox.right, rcBBox.top);
815           rcIcon = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcBBox.right,
816                                  rcLabel.bottom);
817         } else {
818           fHeight = rcLabelContent.Height();
819 
820           if (rcBBox.bottom + fHeight > rcBBox.top) {
821             rcLabel = rcBBox;
822           } else {
823             rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.top - fHeight,
824                                     rcBBox.right, rcBBox.top);
825             rcIcon = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcBBox.right,
826                                    rcLabel.bottom);
827           }
828         }
829       } else {
830         rcLabel = rcBBox;
831       }
832       break;
833     case ButtonStyle::kIconLeftLabelRight:
834       if (pIconStream) {
835         if (FXSYS_IsFloatZero(fFontSize)) {
836           fWidth = rcBBox.right - rcBBox.left;
837           if (rcLabelContent.Width() < fWidth * fAutoFontScale) {
838             rcLabel = CFX_FloatRect(rcBBox.right - fWidth * fAutoFontScale,
839                                     rcBBox.bottom, rcBBox.right, rcBBox.top);
840             rcIcon = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcLabel.left,
841                                    rcBBox.top);
842           } else {
843             if (rcLabelContent.Width() < fWidth) {
844               rcLabel = CFX_FloatRect(rcBBox.right - rcLabelContent.Width(),
845                                       rcBBox.bottom, rcBBox.right, rcBBox.top);
846               rcIcon = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcLabel.left,
847                                      rcBBox.top);
848             } else {
849               rcLabel = rcBBox;
850             }
851           }
852         } else {
853           fWidth = rcLabelContent.Width();
854           if (rcBBox.left + fWidth > rcBBox.right) {
855             rcLabel = rcBBox;
856           } else {
857             rcLabel = CFX_FloatRect(rcBBox.right - fWidth, rcBBox.bottom,
858                                     rcBBox.right, rcBBox.top);
859             rcIcon = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcLabel.left,
860                                    rcBBox.top);
861           }
862         }
863       } else {
864         rcLabel = rcBBox;
865       }
866       break;
867     case ButtonStyle::kIconRightLabelLeft:
868       if (pIconStream) {
869         if (FXSYS_IsFloatZero(fFontSize)) {
870           fWidth = rcBBox.right - rcBBox.left;
871           if (rcLabelContent.Width() < fWidth * fAutoFontScale) {
872             rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom,
873                                     rcBBox.left + fWidth * fAutoFontScale,
874                                     rcBBox.top);
875             rcIcon = CFX_FloatRect(rcLabel.right, rcBBox.bottom, rcBBox.right,
876                                    rcBBox.top);
877           } else {
878             if (rcLabelContent.Width() < fWidth) {
879               rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom,
880                                       rcBBox.left + rcLabelContent.Width(),
881                                       rcBBox.top);
882               rcIcon = CFX_FloatRect(rcLabel.right, rcBBox.bottom, rcBBox.right,
883                                      rcBBox.top);
884             } else {
885               rcLabel = rcBBox;
886             }
887           }
888         } else {
889           fWidth = rcLabelContent.Width();
890           if (rcBBox.left + fWidth > rcBBox.right) {
891             rcLabel = rcBBox;
892           } else {
893             rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom,
894                                     rcBBox.left + fWidth, rcBBox.top);
895             rcIcon = CFX_FloatRect(rcLabel.right, rcBBox.bottom, rcBBox.right,
896                                    rcBBox.top);
897           }
898         }
899       } else {
900         rcLabel = rcBBox;
901       }
902       break;
903     case ButtonStyle::kLabelOverIcon:
904       rcLabel = rcBBox;
905       rcIcon = rcBBox;
906       break;
907   }
908 
909   fxcrt::ostringstream sTemp;
910   sTemp << GenerateIconAppStream(IconFit, std::move(pIconStream), rcIcon);
911 
912   if (!rcLabel.IsEmpty()) {
913     pEdit->SetPlateRect(rcLabel);
914     pEdit->Paint();
915     ByteString sEdit =
916         GetEditAppStream(pEdit.get(), CFX_PointF(0.0f, 0.0f), true, 0);
917     if (sEdit.GetLength() > 0) {
918       AutoClosedCommand bt(&sTemp, kTextBeginOperator, kTextEndOperator);
919       sTemp << GetFillColorAppStream(crText) << sEdit;
920     }
921   }
922 
923   if (sTemp.tellp() <= 0)
924     return ByteString();
925 
926   fxcrt::ostringstream sAppStream;
927   {
928     AutoClosedQCommand q(&sAppStream);
929     WriteAppendRect(sAppStream, rcBBox);
930     sAppStream << kSetNonZeroWindingClipOperator << " "
931                << kEndPathNoFillOrStrokeOperator << "\n";
932     sAppStream << sTemp.str().c_str();
933   }
934   return ByteString(sAppStream);
935 }
936 
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)937 ByteString GetBorderAppStreamInternal(const CFX_FloatRect& rect,
938                                       float fWidth,
939                                       const CFX_Color& color,
940                                       const CFX_Color& crLeftTop,
941                                       const CFX_Color& crRightBottom,
942                                       BorderStyle nStyle,
943                                       const CPWL_Dash& dash) {
944   fxcrt::ostringstream sAppStream;
945   ByteString sColor;
946 
947   float fLeft = rect.left;
948   float fRight = rect.right;
949   float fTop = rect.top;
950   float fBottom = rect.bottom;
951 
952   if (fWidth > 0.0f) {
953     float fHalfWidth = fWidth / 2.0f;
954     AutoClosedQCommand q(&sAppStream);
955 
956     switch (nStyle) {
957       case BorderStyle::kSolid:
958         sColor = GetFillColorAppStream(color);
959         if (sColor.GetLength() > 0) {
960           sAppStream << sColor;
961           WriteAppendRect(sAppStream, {fLeft, fBottom, fRight, fTop});
962           WriteAppendRect(sAppStream, {fLeft + fWidth, fBottom + fWidth,
963                                        fRight - fWidth, fTop - fWidth});
964           sAppStream << kFillEvenOddOperator << "\n";
965         }
966         break;
967       case BorderStyle::kDash:
968         sColor = GetStrokeColorAppStream(color);
969         if (sColor.GetLength() > 0) {
970           sAppStream << sColor;
971           sAppStream << fWidth << " " << kSetLineWidthOperator << " ["
972                      << dash.nDash << " " << dash.nGap << "] " << dash.nPhase
973                      << " " << kSetDashOperator << "\n";
974           const CFX_PointF points[] = {
975               {fLeft + fWidth / 2, fBottom + fWidth / 2},
976               {fLeft + fWidth / 2, fTop - fWidth / 2},
977               {fRight - fWidth / 2, fTop - fWidth / 2},
978               {fRight - fWidth / 2, fBottom + fWidth / 2}};
979           WriteClosedLoop(sAppStream, points);
980           sAppStream << kStrokeOperator << "\n";
981         }
982         break;
983       case BorderStyle::kBeveled:
984       case BorderStyle::kInset:
985         sColor = GetFillColorAppStream(crLeftTop);
986         if (sColor.GetLength() > 0) {
987           sAppStream << sColor;
988           WriteMove(sAppStream, {fLeft + fHalfWidth, fBottom + fHalfWidth});
989           WriteLine(sAppStream, {fLeft + fHalfWidth, fTop - fHalfWidth});
990           WriteLine(sAppStream, {fRight - fHalfWidth, fTop - fHalfWidth});
991           WriteLine(sAppStream,
992                     {fRight - fHalfWidth * 2, fTop - fHalfWidth * 2});
993           WriteLine(sAppStream,
994                     {fLeft + fHalfWidth * 2, fTop - fHalfWidth * 2});
995           WriteLine(sAppStream,
996                     {fLeft + fHalfWidth * 2, fBottom + fHalfWidth * 2});
997           sAppStream << kFillOperator << "\n";
998         }
999         sColor = GetFillColorAppStream(crRightBottom);
1000         if (sColor.GetLength() > 0) {
1001           sAppStream << sColor;
1002           WriteMove(sAppStream, {fRight - fHalfWidth, fTop - fHalfWidth});
1003           WriteLine(sAppStream, {fRight - fHalfWidth, fBottom + fHalfWidth});
1004           WriteLine(sAppStream, {fLeft + fHalfWidth, fBottom + fHalfWidth});
1005           WriteLine(sAppStream,
1006                     {fLeft + fHalfWidth * 2, fBottom + fHalfWidth * 2});
1007           WriteLine(sAppStream,
1008                     {fRight - fHalfWidth * 2, fBottom + fHalfWidth * 2});
1009           WriteLine(sAppStream,
1010                     {fRight - fHalfWidth * 2, fTop - fHalfWidth * 2});
1011           sAppStream << kFillOperator << "\n";
1012         }
1013         sColor = GetFillColorAppStream(color);
1014         if (sColor.GetLength() > 0) {
1015           sAppStream << sColor;
1016           WriteAppendRect(sAppStream, {fLeft, fBottom, fRight, fTop});
1017           WriteAppendRect(sAppStream, {fLeft + fHalfWidth, fBottom + fHalfWidth,
1018                                        fRight - fHalfWidth, fTop - fHalfWidth});
1019           sAppStream << kFillEvenOddOperator << "\n";
1020         }
1021         break;
1022       case BorderStyle::kUnderline:
1023         sColor = GetStrokeColorAppStream(color);
1024         if (sColor.GetLength() > 0) {
1025           sAppStream << sColor;
1026           sAppStream << fWidth << " " << kSetLineWidthOperator << "\n";
1027           WriteMove(sAppStream, {fLeft, fBottom + fWidth / 2});
1028           WriteLine(sAppStream, {fRight, fBottom + fWidth / 2});
1029           sAppStream << kStrokeOperator << "\n";
1030         }
1031         break;
1032     }
1033   }
1034   return ByteString(sAppStream);
1035 }
1036 
GetDropButtonAppStream(const CFX_FloatRect & rcBBox)1037 ByteString GetDropButtonAppStream(const CFX_FloatRect& rcBBox) {
1038   if (rcBBox.IsEmpty())
1039     return ByteString();
1040 
1041   fxcrt::ostringstream sAppStream;
1042   {
1043     AutoClosedQCommand q(&sAppStream);
1044     sAppStream << GetFillColorAppStream(
1045         CFX_Color(CFX_Color::Type::kRGB, 220.0f / 255.0f, 220.0f / 255.0f,
1046                   220.0f / 255.0f));
1047     WriteAppendRect(sAppStream, rcBBox);
1048     sAppStream << kFillOperator << "\n";
1049   }
1050 
1051   {
1052     AutoClosedQCommand q(&sAppStream);
1053     sAppStream << GetBorderAppStreamInternal(
1054         rcBBox, 2, CFX_Color(CFX_Color::Type::kGray, 0),
1055         CFX_Color(CFX_Color::Type::kGray, 1),
1056         CFX_Color(CFX_Color::Type::kGray, 0.5), BorderStyle::kBeveled,
1057         CPWL_Dash(3, 0, 0));
1058   }
1059 
1060   CFX_PointF ptCenter = CFX_PointF((rcBBox.left + rcBBox.right) / 2,
1061                                    (rcBBox.top + rcBBox.bottom) / 2);
1062   if (FXSYS_IsFloatBigger(rcBBox.right - rcBBox.left, 6) &&
1063       FXSYS_IsFloatBigger(rcBBox.top - rcBBox.bottom, 6)) {
1064     AutoClosedQCommand q(&sAppStream);
1065     const CFX_PointF points[] = {{ptCenter.x - 3, ptCenter.y + 1.5f},
1066                                  {ptCenter.x + 3, ptCenter.y + 1.5f},
1067                                  {ptCenter.x, ptCenter.y - 1.5f}};
1068     sAppStream << " 0 " << kSetGrayOperator << "\n";
1069     WriteClosedLoop(sAppStream, points);
1070     sAppStream << kFillOperator << "\n";
1071   }
1072 
1073   return ByteString(sAppStream);
1074 }
1075 
GetRectFillAppStream(const CFX_FloatRect & rect,const CFX_Color & color)1076 ByteString GetRectFillAppStream(const CFX_FloatRect& rect,
1077                                 const CFX_Color& color) {
1078   fxcrt::ostringstream sAppStream;
1079   ByteString sColor = GetFillColorAppStream(color);
1080   if (sColor.GetLength() > 0) {
1081     AutoClosedQCommand q(&sAppStream);
1082     sAppStream << sColor;
1083     WriteAppendRect(sAppStream, rect);
1084     sAppStream << kFillOperator << "\n";
1085   }
1086 
1087   return ByteString(sAppStream);
1088 }
1089 
SetDefaultIconName(CPDF_Stream * pIcon,const char * name)1090 void SetDefaultIconName(CPDF_Stream* pIcon, const char* name) {
1091   if (!pIcon)
1092     return;
1093 
1094   RetainPtr<CPDF_Dictionary> pImageDict = pIcon->GetMutableDict();
1095   if (!pImageDict)
1096     return;
1097 
1098   if (pImageDict->KeyExist("Name"))
1099     return;
1100 
1101   pImageDict->SetNewFor<CPDF_String>("Name", name, false);
1102 }
1103 
CheckStyleFromCaption(const WideString & caption)1104 absl::optional<CheckStyle> CheckStyleFromCaption(const WideString& caption) {
1105   if (caption.IsEmpty())
1106     return absl::nullopt;
1107 
1108   // Character values are ZapfDingbats encodings of named glyphs.
1109   switch (caption[0]) {
1110     case L'4':
1111       return CheckStyle::kCheck;
1112     case L'8':
1113       return CheckStyle::kCross;
1114     case L'H':
1115       return CheckStyle::kStar;
1116     case L'l':
1117       return CheckStyle::kCircle;
1118     case L'n':
1119       return CheckStyle::kSquare;
1120     case L'u':
1121       return CheckStyle::kDiamond;
1122     default:
1123       return absl::nullopt;
1124   }
1125 }
1126 
1127 }  // namespace
1128 
CPDFSDK_AppStream(CPDFSDK_Widget * widget,CPDF_Dictionary * dict)1129 CPDFSDK_AppStream::CPDFSDK_AppStream(CPDFSDK_Widget* widget,
1130                                      CPDF_Dictionary* dict)
1131     : widget_(widget), dict_(dict) {}
1132 
1133 CPDFSDK_AppStream::~CPDFSDK_AppStream() = default;
1134 
SetAsPushButton()1135 void CPDFSDK_AppStream::SetAsPushButton() {
1136   CPDF_FormControl* pControl = widget_->GetFormControl();
1137   CFX_FloatRect rcWindow = widget_->GetRotatedRect();
1138   ButtonStyle nLayout = ButtonStyle::kLabel;
1139   switch (pControl->GetTextPosition()) {
1140     case TEXTPOS_ICON:
1141       nLayout = ButtonStyle::kIcon;
1142       break;
1143     case TEXTPOS_BELOW:
1144       nLayout = ButtonStyle::kIconTopLabelBottom;
1145       break;
1146     case TEXTPOS_ABOVE:
1147       nLayout = ButtonStyle::kIconBottomLabelTop;
1148       break;
1149     case TEXTPOS_RIGHT:
1150       nLayout = ButtonStyle::kIconLeftLabelRight;
1151       break;
1152     case TEXTPOS_LEFT:
1153       nLayout = ButtonStyle::kIconRightLabelLeft;
1154       break;
1155     case TEXTPOS_OVERLAID:
1156       nLayout = ButtonStyle::kLabelOverIcon;
1157       break;
1158     default:
1159       nLayout = ButtonStyle::kLabel;
1160       break;
1161   }
1162 
1163   CFX_Color crBackground = pControl->GetOriginalBackgroundColor();
1164   CFX_Color crBorder = pControl->GetOriginalBorderColor();
1165 
1166   float fBorderWidth = static_cast<float>(widget_->GetBorderWidth());
1167   CPWL_Dash dsBorder(3, 0, 0);
1168   CFX_Color crLeftTop;
1169   CFX_Color crRightBottom;
1170 
1171   BorderStyle nBorderStyle = widget_->GetBorderStyle();
1172   switch (nBorderStyle) {
1173     case BorderStyle::kDash:
1174       dsBorder = CPWL_Dash(3, 3, 0);
1175       break;
1176     case BorderStyle::kBeveled:
1177       fBorderWidth *= 2;
1178       crLeftTop = CFX_Color(CFX_Color::Type::kGray, 1);
1179       crRightBottom = crBackground / 2.0f;
1180       break;
1181     case BorderStyle::kInset:
1182       fBorderWidth *= 2;
1183       crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0.5);
1184       crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.75);
1185       break;
1186     default:
1187       break;
1188   }
1189 
1190   CFX_FloatRect rcClient = rcWindow.GetDeflated(fBorderWidth, fBorderWidth);
1191   CPDF_DefaultAppearance da = pControl->GetDefaultAppearance();
1192   absl::optional<CFX_Color> color = da.GetColor();
1193   CFX_Color crText = color.value_or(CFX_Color(CFX_Color::Type::kGray, 0));
1194 
1195   float fFontSize;
1196   ByteString csNameTag;
1197   absl::optional<ByteString> font = da.GetFont(&fFontSize);
1198   if (font.has_value())
1199     csNameTag = font.value();
1200   else
1201     fFontSize = 12.0f;
1202 
1203   WideString csWCaption;
1204   WideString csNormalCaption;
1205   WideString csRolloverCaption;
1206   WideString csDownCaption;
1207   if (pControl->HasMKEntry(pdfium::appearance::kCA))
1208     csNormalCaption = pControl->GetNormalCaption();
1209 
1210   if (pControl->HasMKEntry(pdfium::appearance::kRC))
1211     csRolloverCaption = pControl->GetRolloverCaption();
1212 
1213   if (pControl->HasMKEntry(pdfium::appearance::kAC))
1214     csDownCaption = pControl->GetDownCaption();
1215 
1216   RetainPtr<CPDF_Stream> pNormalIcon;
1217   RetainPtr<CPDF_Stream> pRolloverIcon;
1218   RetainPtr<CPDF_Stream> pDownIcon;
1219   if (pControl->HasMKEntry(pdfium::appearance::kI))
1220     pNormalIcon = pControl->GetNormalIcon();
1221 
1222   if (pControl->HasMKEntry(pdfium::appearance::kRI))
1223     pRolloverIcon = pControl->GetRolloverIcon();
1224 
1225   if (pControl->HasMKEntry(pdfium::appearance::kIX))
1226     pDownIcon = pControl->GetDownIcon();
1227 
1228   SetDefaultIconName(pNormalIcon.Get(), "ImgA");
1229   SetDefaultIconName(pRolloverIcon.Get(), "ImgB");
1230   SetDefaultIconName(pDownIcon.Get(), "ImgC");
1231 
1232   CPDF_IconFit iconFit = pControl->GetIconFit();
1233   {
1234     CPDF_BAFontMap font_map(widget_->GetPDFPage()->GetDocument(),
1235                             widget_->GetPDFAnnot()->GetMutableAnnotDict(), "N");
1236     ByteString csAP =
1237         GetRectFillAppStream(rcWindow, crBackground) +
1238         GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
1239                                    crRightBottom, nBorderStyle, dsBorder) +
1240         GetPushButtonAppStream(iconFit.GetFittingBounds() ? rcWindow : rcClient,
1241                                &font_map, pNormalIcon, iconFit, csNormalCaption,
1242                                crText, fFontSize, nLayout);
1243 
1244     Write("N", csAP, ByteString());
1245     if (pNormalIcon)
1246       AddImage("N", pNormalIcon.Get());
1247 
1248     CPDF_FormControl::HighlightingMode eHLM = pControl->GetHighlightingMode();
1249     if (eHLM != CPDF_FormControl::kPush && eHLM != CPDF_FormControl::kToggle) {
1250       Remove("D");
1251       Remove("R");
1252       return;
1253     }
1254 
1255     if (csRolloverCaption.IsEmpty() && !pRolloverIcon) {
1256       csRolloverCaption = csNormalCaption;
1257       pRolloverIcon = pNormalIcon;
1258     }
1259   }
1260   {
1261     CPDF_BAFontMap font_map(widget_->GetPDFPage()->GetDocument(),
1262                             widget_->GetPDFAnnot()->GetMutableAnnotDict(), "R");
1263     ByteString csAP =
1264         GetRectFillAppStream(rcWindow, crBackground) +
1265         GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
1266                                    crRightBottom, nBorderStyle, dsBorder) +
1267         GetPushButtonAppStream(iconFit.GetFittingBounds() ? rcWindow : rcClient,
1268                                &font_map, pRolloverIcon, iconFit,
1269                                csRolloverCaption, crText, fFontSize, nLayout);
1270 
1271     Write("R", csAP, ByteString());
1272     if (pRolloverIcon)
1273       AddImage("R", pRolloverIcon.Get());
1274 
1275     if (csDownCaption.IsEmpty() && !pDownIcon) {
1276       csDownCaption = csNormalCaption;
1277       pDownIcon = pNormalIcon;
1278     }
1279 
1280     switch (nBorderStyle) {
1281       case BorderStyle::kBeveled: {
1282         CFX_Color crTemp = crLeftTop;
1283         crLeftTop = crRightBottom;
1284         crRightBottom = crTemp;
1285         break;
1286       }
1287       case BorderStyle::kInset: {
1288         crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0);
1289         crRightBottom = CFX_Color(CFX_Color::Type::kGray, 1);
1290         break;
1291       }
1292       default:
1293         break;
1294     }
1295   }
1296   {
1297     CPDF_BAFontMap font_map(widget_->GetPDFPage()->GetDocument(),
1298                             widget_->GetPDFAnnot()->GetMutableAnnotDict(), "D");
1299     ByteString csAP =
1300         GetRectFillAppStream(rcWindow, crBackground - 0.25f) +
1301         GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
1302                                    crRightBottom, nBorderStyle, dsBorder) +
1303         GetPushButtonAppStream(iconFit.GetFittingBounds() ? rcWindow : rcClient,
1304                                &font_map, pDownIcon, iconFit, csDownCaption,
1305                                crText, fFontSize, nLayout);
1306 
1307     Write("D", csAP, ByteString());
1308     if (pDownIcon)
1309       AddImage("D", pDownIcon.Get());
1310   }
1311 }
1312 
SetAsCheckBox()1313 void CPDFSDK_AppStream::SetAsCheckBox() {
1314   CPDF_FormControl* pControl = widget_->GetFormControl();
1315   CFX_Color crBackground = pControl->GetOriginalBackgroundColor();
1316   CFX_Color crBorder = pControl->GetOriginalBorderColor();
1317   float fBorderWidth = static_cast<float>(widget_->GetBorderWidth());
1318   CPWL_Dash dsBorder(3, 0, 0);
1319   CFX_Color crLeftTop;
1320   CFX_Color crRightBottom;
1321 
1322   BorderStyle nBorderStyle = widget_->GetBorderStyle();
1323   switch (nBorderStyle) {
1324     case BorderStyle::kDash:
1325       dsBorder = CPWL_Dash(3, 3, 0);
1326       break;
1327     case BorderStyle::kBeveled:
1328       fBorderWidth *= 2;
1329       crLeftTop = CFX_Color(CFX_Color::Type::kGray, 1);
1330       crRightBottom = crBackground / 2.0f;
1331       break;
1332     case BorderStyle::kInset:
1333       fBorderWidth *= 2;
1334       crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0.5);
1335       crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.75);
1336       break;
1337     default:
1338       break;
1339   }
1340 
1341   CFX_FloatRect rcWindow = widget_->GetRotatedRect();
1342   CFX_FloatRect rcClient = rcWindow.GetDeflated(fBorderWidth, fBorderWidth);
1343   absl::optional<CFX_Color> color = pControl->GetDefaultAppearance().GetColor();
1344   CFX_Color crText = color.value_or(CFX_Color());
1345 
1346   CheckStyle nStyle = CheckStyleFromCaption(pControl->GetNormalCaption())
1347                           .value_or(CheckStyle::kCheck);
1348   ByteString csAP_N_ON =
1349       GetRectFillAppStream(rcWindow, crBackground) +
1350       GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
1351                                  crRightBottom, nBorderStyle, dsBorder);
1352 
1353   ByteString csAP_N_OFF = csAP_N_ON;
1354 
1355   switch (nBorderStyle) {
1356     case BorderStyle::kBeveled: {
1357       CFX_Color crTemp = crLeftTop;
1358       crLeftTop = crRightBottom;
1359       crRightBottom = crTemp;
1360       break;
1361     }
1362     case BorderStyle::kInset: {
1363       crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0);
1364       crRightBottom = CFX_Color(CFX_Color::Type::kGray, 1);
1365       break;
1366     }
1367     default:
1368       break;
1369   }
1370 
1371   ByteString csAP_D_ON =
1372       GetRectFillAppStream(rcWindow, crBackground - 0.25f) +
1373       GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
1374                                  crRightBottom, nBorderStyle, dsBorder);
1375 
1376   ByteString csAP_D_OFF = csAP_D_ON;
1377 
1378   csAP_N_ON += GetCheckBoxAppStream(rcClient, nStyle, crText);
1379   csAP_D_ON += GetCheckBoxAppStream(rcClient, nStyle, crText);
1380 
1381   Write("N", csAP_N_ON, pControl->GetCheckedAPState());
1382   Write("N", csAP_N_OFF, "Off");
1383 
1384   Write("D", csAP_D_ON, pControl->GetCheckedAPState());
1385   Write("D", csAP_D_OFF, "Off");
1386 
1387   ByteString csAS = widget_->GetAppState();
1388   if (csAS.IsEmpty())
1389     widget_->SetAppStateOff();
1390 }
1391 
SetAsRadioButton()1392 void CPDFSDK_AppStream::SetAsRadioButton() {
1393   CPDF_FormControl* pControl = widget_->GetFormControl();
1394   CFX_Color crBackground = pControl->GetOriginalBackgroundColor();
1395   CFX_Color crBorder = pControl->GetOriginalBorderColor();
1396   float fBorderWidth = static_cast<float>(widget_->GetBorderWidth());
1397   CPWL_Dash dsBorder(3, 0, 0);
1398   CFX_Color crLeftTop;
1399   CFX_Color crRightBottom;
1400 
1401   BorderStyle nBorderStyle = widget_->GetBorderStyle();
1402   switch (nBorderStyle) {
1403     case BorderStyle::kDash:
1404       dsBorder = CPWL_Dash(3, 3, 0);
1405       break;
1406     case BorderStyle::kBeveled:
1407       fBorderWidth *= 2;
1408       crLeftTop = CFX_Color(CFX_Color::Type::kGray, 1);
1409       crRightBottom = crBackground / 2.0f;
1410       break;
1411     case BorderStyle::kInset:
1412       fBorderWidth *= 2;
1413       crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0.5);
1414       crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.75);
1415       break;
1416     default:
1417       break;
1418   }
1419 
1420   CFX_FloatRect rcWindow = widget_->GetRotatedRect();
1421   CFX_FloatRect rcClient = rcWindow.GetDeflated(fBorderWidth, fBorderWidth);
1422   absl::optional<CFX_Color> color = pControl->GetDefaultAppearance().GetColor();
1423   CFX_Color crText = color.value_or(CFX_Color());
1424   CheckStyle nStyle = CheckStyleFromCaption(pControl->GetNormalCaption())
1425                           .value_or(CheckStyle::kCircle);
1426 
1427   ByteString csAP_N_ON;
1428   CFX_FloatRect rcCenter = rcWindow.GetCenterSquare().GetDeflated(1.0f, 1.0f);
1429   if (nStyle == CheckStyle::kCircle) {
1430     if (nBorderStyle == BorderStyle::kBeveled) {
1431       crLeftTop = CFX_Color(CFX_Color::Type::kGray, 1);
1432       crRightBottom = crBackground - 0.25f;
1433     } else if (nBorderStyle == BorderStyle::kInset) {
1434       crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0.5f);
1435       crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.75f);
1436     }
1437 
1438     csAP_N_ON =
1439         GetCircleFillAppStream(rcCenter, crBackground) +
1440         GetCircleBorderAppStream(rcCenter, fBorderWidth, crBorder, crLeftTop,
1441                                  crRightBottom, nBorderStyle, dsBorder);
1442   } else {
1443     csAP_N_ON =
1444         GetRectFillAppStream(rcWindow, crBackground) +
1445         GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
1446                                    crRightBottom, nBorderStyle, dsBorder);
1447   }
1448 
1449   ByteString csAP_N_OFF = csAP_N_ON;
1450 
1451   switch (nBorderStyle) {
1452     case BorderStyle::kBeveled: {
1453       CFX_Color crTemp = crLeftTop;
1454       crLeftTop = crRightBottom;
1455       crRightBottom = crTemp;
1456       break;
1457     }
1458     case BorderStyle::kInset: {
1459       crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0);
1460       crRightBottom = CFX_Color(CFX_Color::Type::kGray, 1);
1461       break;
1462     }
1463     default:
1464       break;
1465   }
1466 
1467   ByteString csAP_D_ON;
1468 
1469   if (nStyle == CheckStyle::kCircle) {
1470     CFX_Color crBK = crBackground - 0.25f;
1471     if (nBorderStyle == BorderStyle::kBeveled) {
1472       crLeftTop = crBackground - 0.25f;
1473       crRightBottom = CFX_Color(CFX_Color::Type::kGray, 1);
1474       crBK = crBackground;
1475     } else if (nBorderStyle == BorderStyle::kInset) {
1476       crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0);
1477       crRightBottom = CFX_Color(CFX_Color::Type::kGray, 1);
1478     }
1479 
1480     csAP_D_ON =
1481         GetCircleFillAppStream(rcCenter, crBK) +
1482         GetCircleBorderAppStream(rcCenter, fBorderWidth, crBorder, crLeftTop,
1483                                  crRightBottom, nBorderStyle, dsBorder);
1484   } else {
1485     csAP_D_ON =
1486         GetRectFillAppStream(rcWindow, crBackground - 0.25f) +
1487         GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
1488                                    crRightBottom, nBorderStyle, dsBorder);
1489   }
1490 
1491   ByteString csAP_D_OFF = csAP_D_ON;
1492 
1493   ByteString app_stream = GetRadioButtonAppStream(rcClient, nStyle, crText);
1494   csAP_N_ON += app_stream;
1495   csAP_D_ON += app_stream;
1496 
1497   Write("N", csAP_N_ON, pControl->GetCheckedAPState());
1498   Write("N", csAP_N_OFF, "Off");
1499 
1500   Write("D", csAP_D_ON, pControl->GetCheckedAPState());
1501   Write("D", csAP_D_OFF, "Off");
1502 
1503   ByteString csAS = widget_->GetAppState();
1504   if (csAS.IsEmpty())
1505     widget_->SetAppStateOff();
1506 }
1507 
SetAsComboBox(absl::optional<WideString> sValue)1508 void CPDFSDK_AppStream::SetAsComboBox(absl::optional<WideString> sValue) {
1509   CPDF_FormControl* pControl = widget_->GetFormControl();
1510   CPDF_FormField* pField = pControl->GetField();
1511   fxcrt::ostringstream sBody;
1512 
1513   CFX_FloatRect rcClient = widget_->GetClientRect();
1514   CFX_FloatRect rcButton = rcClient;
1515   rcButton.left = rcButton.right - 13;
1516   rcButton.Normalize();
1517 
1518   // Font map must outlive |pEdit|.
1519   CPDF_BAFontMap font_map(widget_->GetPDFPage()->GetDocument(),
1520                           widget_->GetPDFAnnot()->GetMutableAnnotDict(), "N");
1521 
1522   auto pEdit = std::make_unique<CPWL_EditImpl>();
1523   pEdit->EnableRefresh(false);
1524   pEdit->SetFontMap(&font_map);
1525 
1526   CFX_FloatRect rcEdit = rcClient;
1527   rcEdit.right = rcButton.left;
1528   rcEdit.Normalize();
1529 
1530   pEdit->SetPlateRect(rcEdit);
1531   pEdit->SetAlignmentV(1);
1532 
1533   float fFontSize = widget_->GetFontSize();
1534   if (FXSYS_IsFloatZero(fFontSize))
1535     pEdit->SetAutoFontSize(true);
1536   else
1537     pEdit->SetFontSize(fFontSize);
1538 
1539   pEdit->Initialize();
1540   if (sValue.has_value()) {
1541     pEdit->SetText(sValue.value());
1542   } else {
1543     int32_t nCurSel = pField->GetSelectedIndex(0);
1544     if (nCurSel < 0) {
1545       pEdit->SetText(pField->GetValue());
1546     } else {
1547       pEdit->SetText(pField->GetOptionLabel(nCurSel));
1548     }
1549   }
1550   pEdit->Paint();
1551 
1552   CFX_FloatRect rcContent = pEdit->GetContentRect();
1553   ByteString sEdit = GetEditAppStream(pEdit.get(), CFX_PointF(), true, 0);
1554   if (sEdit.GetLength() > 0) {
1555     sBody << "/Tx ";
1556     AutoClosedCommand bmc(&sBody, kMarkedSequenceBeginOperator,
1557                           kMarkedSequenceEndOperator);
1558     AutoClosedQCommand q(&sBody);
1559 
1560     if (rcContent.Width() > rcEdit.Width() ||
1561         rcContent.Height() > rcEdit.Height()) {
1562       WriteAppendRect(sBody, rcEdit);
1563       sBody << kSetNonZeroWindingClipOperator << "\n"
1564             << kEndPathNoFillOrStrokeOperator << "\n";
1565     }
1566 
1567     CFX_Color crText = widget_->GetTextPWLColor();
1568     AutoClosedCommand bt(&sBody, kTextBeginOperator, kTextEndOperator);
1569     sBody << GetFillColorAppStream(crText) << sEdit;
1570   }
1571 
1572   sBody << GetDropButtonAppStream(rcButton);
1573   Write("N",
1574         GetBackgroundAppStream() + GetBorderAppStream() + ByteString(sBody),
1575         ByteString());
1576 }
1577 
SetAsListBox()1578 void CPDFSDK_AppStream::SetAsListBox() {
1579   CPDF_FormControl* pControl = widget_->GetFormControl();
1580   CPDF_FormField* pField = pControl->GetField();
1581   CFX_FloatRect rcClient = widget_->GetClientRect();
1582   fxcrt::ostringstream sBody;
1583 
1584   // Font map must outlive |pEdit|.
1585   CPDF_BAFontMap font_map(widget_->GetPDFPage()->GetDocument(),
1586                           widget_->GetPDFAnnot()->GetMutableAnnotDict(), "N");
1587 
1588   auto pEdit = std::make_unique<CPWL_EditImpl>();
1589   pEdit->EnableRefresh(false);
1590   pEdit->SetFontMap(&font_map);
1591   pEdit->SetPlateRect(CFX_FloatRect(rcClient.left, 0.0f, rcClient.right, 0.0f));
1592 
1593   float fFontSize = widget_->GetFontSize();
1594   pEdit->SetFontSize(FXSYS_IsFloatZero(fFontSize) ? 12.0f : fFontSize);
1595   pEdit->Initialize();
1596 
1597   fxcrt::ostringstream sList;
1598   float fy = rcClient.top;
1599 
1600   int32_t nTop = pField->GetTopVisibleIndex();
1601   int32_t nCount = pField->CountOptions();
1602   int32_t nSelCount = pField->CountSelectedItems();
1603 
1604   for (int32_t i = nTop; i < nCount; ++i) {
1605     bool bSelected = false;
1606     for (int32_t j = 0; j < nSelCount; ++j) {
1607       if (pField->GetSelectedIndex(j) == i) {
1608         bSelected = true;
1609         break;
1610       }
1611     }
1612 
1613     pEdit->SetText(pField->GetOptionLabel(i));
1614     pEdit->Paint();
1615 
1616     CFX_FloatRect rcContent = pEdit->GetContentRect();
1617     float fItemHeight = rcContent.Height();
1618 
1619     if (bSelected) {
1620       CFX_FloatRect rcItem =
1621           CFX_FloatRect(rcClient.left, fy - fItemHeight, rcClient.right, fy);
1622       {
1623         AutoClosedQCommand q(&sList);
1624         sList << GetFillColorAppStream(CFX_Color(
1625             CFX_Color::Type::kRGB, 0, 51.0f / 255.0f, 113.0f / 255.0f));
1626         WriteAppendRect(sList, rcItem);
1627         sList << kFillOperator << "\n";
1628       }
1629 
1630       AutoClosedCommand bt(&sList, kTextBeginOperator, kTextEndOperator);
1631       sList << GetFillColorAppStream(CFX_Color(CFX_Color::Type::kGray, 1))
1632             << GetEditAppStream(pEdit.get(), CFX_PointF(0.0f, fy), true, 0);
1633     } else {
1634       CFX_Color crText = widget_->GetTextPWLColor();
1635 
1636       AutoClosedCommand bt(&sList, kTextBeginOperator, kTextEndOperator);
1637       sList << GetFillColorAppStream(crText)
1638             << GetEditAppStream(pEdit.get(), CFX_PointF(0.0f, fy), true, 0);
1639     }
1640 
1641     fy -= fItemHeight;
1642   }
1643 
1644   if (sList.tellp() > 0) {
1645     sBody << "/Tx ";
1646     AutoClosedCommand bmc(&sBody, kMarkedSequenceBeginOperator,
1647                           kMarkedSequenceEndOperator);
1648     AutoClosedQCommand q(&sBody);
1649 
1650     WriteAppendRect(sBody, rcClient);
1651     sBody << kSetNonZeroWindingClipOperator << "\n"
1652           << kEndPathNoFillOrStrokeOperator << "\n"
1653           << sList.str();
1654   }
1655   Write("N",
1656         GetBackgroundAppStream() + GetBorderAppStream() + ByteString(sBody),
1657         ByteString());
1658 }
1659 
SetAsTextField(absl::optional<WideString> sValue)1660 void CPDFSDK_AppStream::SetAsTextField(absl::optional<WideString> sValue) {
1661   CPDF_FormControl* pControl = widget_->GetFormControl();
1662   CPDF_FormField* pField = pControl->GetField();
1663   fxcrt::ostringstream sBody;
1664   fxcrt::ostringstream sLines;
1665 
1666   // Font map must outlive |pEdit|.
1667   CPDF_BAFontMap font_map(widget_->GetPDFPage()->GetDocument(),
1668                           widget_->GetPDFAnnot()->GetMutableAnnotDict(), "N");
1669 
1670   auto pEdit = std::make_unique<CPWL_EditImpl>();
1671   pEdit->EnableRefresh(false);
1672   pEdit->SetFontMap(&font_map);
1673 
1674   CFX_FloatRect rcClient = widget_->GetClientRect();
1675   pEdit->SetPlateRect(rcClient);
1676   pEdit->SetAlignmentH(pControl->GetControlAlignment());
1677 
1678   uint32_t dwFieldFlags = pField->GetFieldFlags();
1679   bool bMultiLine = dwFieldFlags & pdfium::form_flags::kTextMultiline;
1680   if (bMultiLine) {
1681     pEdit->SetMultiLine(true);
1682     pEdit->SetAutoReturn(true);
1683   } else {
1684     pEdit->SetAlignmentV(1);
1685   }
1686 
1687   uint16_t subWord = 0;
1688   if (dwFieldFlags & pdfium::form_flags::kTextPassword) {
1689     subWord = '*';
1690     pEdit->SetPasswordChar(subWord);
1691   }
1692 
1693   int nMaxLen = pField->GetMaxLen();
1694   bool bCharArray = dwFieldFlags & pdfium::form_flags::kTextComb;
1695   float fFontSize = widget_->GetFontSize();
1696 
1697 #ifdef PDF_ENABLE_XFA
1698   if (!sValue.has_value() && widget_->GetMixXFAWidget())
1699     sValue = widget_->GetValue();
1700 #endif  // PDF_ENABLE_XFA
1701 
1702   if (nMaxLen > 0) {
1703     if (bCharArray) {
1704       pEdit->SetCharArray(nMaxLen);
1705       if (FXSYS_IsFloatZero(fFontSize)) {
1706         fFontSize = CPWL_Edit::GetCharArrayAutoFontSize(
1707             font_map.GetPDFFont(0).Get(), rcClient, nMaxLen);
1708       }
1709     } else {
1710       if (sValue.has_value())
1711         nMaxLen = pdfium::base::checked_cast<int>(sValue.value().GetLength());
1712       pEdit->SetLimitChar(nMaxLen);
1713     }
1714   }
1715 
1716   if (FXSYS_IsFloatZero(fFontSize))
1717     pEdit->SetAutoFontSize(true);
1718   else
1719     pEdit->SetFontSize(fFontSize);
1720 
1721   pEdit->Initialize();
1722   pEdit->SetText(sValue.value_or(pField->GetValue()));
1723   pEdit->Paint();
1724 
1725   CFX_FloatRect rcContent = pEdit->GetContentRect();
1726   ByteString sEdit =
1727       GetEditAppStream(pEdit.get(), CFX_PointF(), !bCharArray, subWord);
1728 
1729   if (sEdit.GetLength() > 0) {
1730     sBody << "/Tx ";
1731     AutoClosedCommand bmc(&sBody, kMarkedSequenceBeginOperator,
1732                           kMarkedSequenceEndOperator);
1733     AutoClosedQCommand q(&sBody);
1734 
1735     if (rcContent.Width() > rcClient.Width() ||
1736         rcContent.Height() > rcClient.Height()) {
1737       WriteAppendRect(sBody, rcClient);
1738       sBody << kSetNonZeroWindingClipOperator << "\n"
1739             << kEndPathNoFillOrStrokeOperator << "\n";
1740     }
1741     CFX_Color crText = widget_->GetTextPWLColor();
1742 
1743     AutoClosedCommand bt(&sBody, kTextBeginOperator, kTextEndOperator);
1744     sBody << GetFillColorAppStream(crText) << sEdit;
1745   }
1746 
1747   if (bCharArray) {
1748     switch (widget_->GetBorderStyle()) {
1749       case BorderStyle::kSolid: {
1750         ByteString sColor =
1751             GetStrokeColorAppStream(widget_->GetBorderPWLColor());
1752         if (sColor.GetLength() > 0) {
1753           AutoClosedQCommand q(&sLines);
1754           sLines << widget_->GetBorderWidth() << " " << kSetLineWidthOperator
1755                  << "\n"
1756                  << GetStrokeColorAppStream(widget_->GetBorderPWLColor())
1757                  << " 2 " << kSetLineCapStyleOperator << " 0 "
1758                  << kSetLineJoinStyleOperator << "\n";
1759 
1760           const float width = rcClient.right - rcClient.left;
1761           for (int32_t i = 1; i < nMaxLen; ++i) {
1762             const float left = rcClient.left + (width / nMaxLen) * i;
1763             WriteMove(sLines, {left, rcClient.bottom});
1764             WriteLine(sLines, {left, rcClient.top});
1765             sLines << kStrokeOperator << "\n";
1766           }
1767         }
1768         break;
1769       }
1770       case BorderStyle::kDash: {
1771         ByteString sColor =
1772             GetStrokeColorAppStream(widget_->GetBorderPWLColor());
1773         if (sColor.GetLength() > 0) {
1774           CPWL_Dash dsBorder = CPWL_Dash(3, 3, 0);
1775           AutoClosedQCommand q(&sLines);
1776           sLines << widget_->GetBorderWidth() << " " << kSetLineWidthOperator
1777                  << "\n"
1778                  << GetStrokeColorAppStream(widget_->GetBorderPWLColor()) << "["
1779                  << dsBorder.nDash << " " << dsBorder.nGap << "] "
1780                  << dsBorder.nPhase << " " << kSetDashOperator << "\n";
1781 
1782           const float width = rcClient.right - rcClient.left;
1783           for (int32_t i = 1; i < nMaxLen; ++i) {
1784             const float left = rcClient.left + (width / nMaxLen) * i;
1785             WriteMove(sLines, {left, rcClient.bottom});
1786             WriteLine(sLines, {left, rcClient.top});
1787             sLines << kStrokeOperator << "\n";
1788           }
1789         }
1790         break;
1791       }
1792       default:
1793         break;
1794     }
1795   }
1796 
1797   Write("N",
1798         GetBackgroundAppStream() + GetBorderAppStream() + ByteString(sLines) +
1799             ByteString(sBody),
1800         ByteString());
1801 }
1802 
AddImage(const ByteString & sAPType,CPDF_Stream * pImage)1803 void CPDFSDK_AppStream::AddImage(const ByteString& sAPType,
1804                                  CPDF_Stream* pImage) {
1805   RetainPtr<CPDF_Stream> pStream = dict_->GetMutableStreamFor(sAPType);
1806   RetainPtr<CPDF_Dictionary> pStreamDict = pStream->GetMutableDict();
1807   ByteString sImageAlias = "IMG";
1808 
1809   RetainPtr<const CPDF_Dictionary> pImageDict = pImage->GetDict();
1810   if (pImageDict)
1811     sImageAlias = pImageDict->GetByteStringFor("Name");
1812 
1813   RetainPtr<CPDF_Dictionary> pStreamResList =
1814       pStreamDict->GetOrCreateDictFor("Resources");
1815   auto pXObject = pStreamResList->SetNewFor<CPDF_Dictionary>("XObject");
1816   pXObject->SetNewFor<CPDF_Reference>(sImageAlias,
1817                                       widget_->GetPageView()->GetPDFDocument(),
1818                                       pImage->GetObjNum());
1819 }
1820 
Write(const ByteString & sAPType,const ByteString & sContents,const ByteString & sAPState)1821 void CPDFSDK_AppStream::Write(const ByteString& sAPType,
1822                               const ByteString& sContents,
1823                               const ByteString& sAPState) {
1824   RetainPtr<CPDF_Dictionary> pParentDict;
1825   ByteString key;
1826   if (sAPState.IsEmpty()) {
1827     pParentDict = dict_;
1828     key = sAPType;
1829   } else {
1830     pParentDict = dict_->GetOrCreateDictFor(sAPType);
1831     key = sAPState;
1832   }
1833 
1834   RetainPtr<CPDF_Dictionary> pOrigStreamDict;
1835 
1836   // If `pStream` is created by CreateModifiedAPStream(), then it is safe to
1837   // edit, as it is not shared.
1838   RetainPtr<CPDF_Stream> pStream = pParentDict->GetMutableStreamFor(key);
1839   CPDF_Document* doc = widget_->GetPageView()->GetPDFDocument();
1840   if (!doc->IsModifiedAPStream(pStream.Get())) {
1841     if (pStream)
1842       pOrigStreamDict = pStream->GetMutableDict();
1843     pStream.Reset(doc->CreateModifiedAPStream());
1844     pParentDict->SetNewFor<CPDF_Reference>(key, doc, pStream->GetObjNum());
1845   }
1846 
1847   RetainPtr<CPDF_Dictionary> pStreamDict = pStream->GetMutableDict();
1848   if (!pStreamDict) {
1849     pStreamDict = doc->New<CPDF_Dictionary>();
1850     pStreamDict->SetNewFor<CPDF_Name>("Type", "XObject");
1851     pStreamDict->SetNewFor<CPDF_Name>("Subtype", "Form");
1852     pStreamDict->SetNewFor<CPDF_Number>("FormType", 1);
1853 
1854     if (pOrigStreamDict) {
1855       RetainPtr<const CPDF_Dictionary> pResources =
1856           pOrigStreamDict->GetDictFor("Resources");
1857       if (pResources)
1858         pStreamDict->SetFor("Resources", pResources->Clone());
1859     }
1860 
1861     pStream->InitStreamWithEmptyData(pStreamDict);
1862   }
1863   pStreamDict->SetMatrixFor("Matrix", widget_->GetMatrix());
1864   pStreamDict->SetRectFor("BBox", widget_->GetRotatedRect());
1865   pStream->SetDataAndRemoveFilter(sContents.raw_span());
1866 }
1867 
Remove(ByteStringView sAPType)1868 void CPDFSDK_AppStream::Remove(ByteStringView sAPType) {
1869   dict_->RemoveFor(sAPType);
1870 }
1871 
GetBackgroundAppStream() const1872 ByteString CPDFSDK_AppStream::GetBackgroundAppStream() const {
1873   CFX_Color crBackground = widget_->GetFillPWLColor();
1874   if (crBackground.nColorType != CFX_Color::Type::kTransparent)
1875     return GetRectFillAppStream(widget_->GetRotatedRect(), crBackground);
1876 
1877   return ByteString();
1878 }
1879 
GetBorderAppStream() const1880 ByteString CPDFSDK_AppStream::GetBorderAppStream() const {
1881   CFX_FloatRect rcWindow = widget_->GetRotatedRect();
1882   CFX_Color crBorder = widget_->GetBorderPWLColor();
1883   CFX_Color crBackground = widget_->GetFillPWLColor();
1884   CFX_Color crLeftTop;
1885   CFX_Color crRightBottom;
1886 
1887   float fBorderWidth = static_cast<float>(widget_->GetBorderWidth());
1888   CPWL_Dash dsBorder(3, 0, 0);
1889 
1890   BorderStyle nBorderStyle = widget_->GetBorderStyle();
1891   switch (nBorderStyle) {
1892     case BorderStyle::kDash:
1893       dsBorder = CPWL_Dash(3, 3, 0);
1894       break;
1895     case BorderStyle::kBeveled:
1896       fBorderWidth *= 2;
1897       crLeftTop = CFX_Color(CFX_Color::Type::kGray, 1);
1898       crRightBottom = crBackground / 2.0f;
1899       break;
1900     case BorderStyle::kInset:
1901       fBorderWidth *= 2;
1902       crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0.5);
1903       crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.75);
1904       break;
1905     default:
1906       break;
1907   }
1908 
1909   return GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop,
1910                                     crRightBottom, nBorderStyle, dsBorder);
1911 }
1912