• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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