| // Copyright 2017 The PDFium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com |
| |
| #include "fpdfsdk/cpdfsdk_appstream.h" |
| |
| #include <math.h> |
| |
| #include <iterator> |
| #include <memory> |
| #include <sstream> |
| #include <utility> |
| |
| #include "constants/appearance.h" |
| #include "constants/form_flags.h" |
| #include "core/fpdfapi/edit/cpdf_contentstream_write_utils.h" |
| #include "core/fpdfapi/font/cpdf_font.h" |
| #include "core/fpdfapi/parser/cpdf_dictionary.h" |
| #include "core/fpdfapi/parser/cpdf_document.h" |
| #include "core/fpdfapi/parser/cpdf_name.h" |
| #include "core/fpdfapi/parser/cpdf_number.h" |
| #include "core/fpdfapi/parser/cpdf_reference.h" |
| #include "core/fpdfapi/parser/cpdf_stream.h" |
| #include "core/fpdfapi/parser/cpdf_string.h" |
| #include "core/fpdfapi/parser/fpdf_parser_decode.h" |
| #include "core/fpdfapi/parser/fpdf_parser_utility.h" |
| #include "core/fpdfdoc/cpdf_bafontmap.h" |
| #include "core/fpdfdoc/cpdf_formcontrol.h" |
| #include "core/fpdfdoc/cpdf_icon.h" |
| #include "core/fpdfdoc/cpvt_word.h" |
| #include "core/fxcrt/fx_string_wrappers.h" |
| #include "fpdfsdk/cpdfsdk_formfillenvironment.h" |
| #include "fpdfsdk/cpdfsdk_interactiveform.h" |
| #include "fpdfsdk/cpdfsdk_pageview.h" |
| #include "fpdfsdk/cpdfsdk_widget.h" |
| #include "fpdfsdk/pwl/cpwl_edit.h" |
| #include "fpdfsdk/pwl/cpwl_edit_impl.h" |
| #include "fpdfsdk/pwl/cpwl_wnd.h" |
| #include "third_party/base/numerics/safe_conversions.h" |
| #include "third_party/base/span.h" |
| |
| namespace { |
| |
| // Checkbox & radiobutton styles. |
| enum class CheckStyle { kCheck = 0, kCircle, kCross, kDiamond, kSquare, kStar }; |
| |
| // Pushbutton layout styles. |
| enum class ButtonStyle { |
| kLabel = 0, |
| kIcon, |
| kIconTopLabelBottom, |
| kIconBottomLabelTop, |
| kIconLeftLabelRight, |
| kIconRightLabelLeft, |
| kLabelOverIcon |
| }; |
| |
| const char kAppendRectOperator[] = "re"; |
| const char kConcatMatrixOperator[] = "cm"; |
| const char kCurveToOperator[] = "c"; |
| const char kEndPathNoFillOrStrokeOperator[] = "n"; |
| const char kFillOperator[] = "f"; |
| const char kFillEvenOddOperator[] = "f*"; |
| const char kInvokeNamedXObjectOperator[] = "Do"; |
| const char kLineToOperator[] = "l"; |
| const char kMarkedSequenceBeginOperator[] = "BMC"; |
| const char kMarkedSequenceEndOperator[] = "EMC"; |
| const char kMoveTextPositionOperator[] = "Td"; |
| const char kMoveToOperator[] = "m"; |
| const char kSetCMYKOperator[] = "k"; |
| const char kSetCMKYStrokedOperator[] = "K"; |
| const char kSetDashOperator[] = "d"; |
| const char kSetGrayOperator[] = "g"; |
| const char kSetGrayStrokedOperator[] = "G"; |
| const char kSetLineCapStyleOperator[] = "J"; |
| const char kSetLineJoinStyleOperator[] = "j"; |
| const char kSetLineWidthOperator[] = "w"; |
| const char kSetNonZeroWindingClipOperator[] = "W"; |
| const char kSetRGBOperator[] = "rg"; |
| const char kSetRGBStrokedOperator[] = "RG"; |
| const char kSetTextFontAndSizeOperator[] = "Tf"; |
| const char kShowTextOperator[] = "Tj"; |
| const char kStateRestoreOperator[] = "Q"; |
| const char kStateSaveOperator[] = "q"; |
| const char kStrokeOperator[] = "S"; |
| const char kTextBeginOperator[] = "BT"; |
| const char kTextEndOperator[] = "ET"; |
| |
| class AutoClosedCommand { |
| public: |
| AutoClosedCommand(fxcrt::ostringstream* stream, |
| ByteString open, |
| ByteString close) |
| : stream_(stream), close_(close) { |
| *stream_ << open << "\n"; |
| } |
| |
| virtual ~AutoClosedCommand() { *stream_ << close_ << "\n"; } |
| |
| private: |
| UnownedPtr<fxcrt::ostringstream> const stream_; |
| ByteString close_; |
| }; |
| |
| class AutoClosedQCommand final : public AutoClosedCommand { |
| public: |
| explicit AutoClosedQCommand(fxcrt::ostringstream* stream) |
| : AutoClosedCommand(stream, kStateSaveOperator, kStateRestoreOperator) {} |
| ~AutoClosedQCommand() override = default; |
| }; |
| |
| void WriteMove(fxcrt::ostringstream& stream, const CFX_PointF& point) { |
| WritePoint(stream, point) << " " << kMoveToOperator << "\n"; |
| } |
| |
| void WriteLine(fxcrt::ostringstream& stream, const CFX_PointF& point) { |
| WritePoint(stream, point) << " " << kLineToOperator << "\n"; |
| } |
| |
| void WriteClosedLoop(fxcrt::ostringstream& stream, |
| pdfium::span<const CFX_PointF> points) { |
| WriteMove(stream, points[0]); |
| for (const auto& point : points.subspan(1)) |
| WriteLine(stream, point); |
| WriteLine(stream, points[0]); |
| } |
| |
| void WriteBezierCurve(fxcrt::ostringstream& stream, |
| const CFX_PointF& point1, |
| const CFX_PointF& point2, |
| const CFX_PointF& point3) { |
| WritePoint(stream, point1) << " "; |
| WritePoint(stream, point2) << " "; |
| WritePoint(stream, point3) << " " << kCurveToOperator << "\n"; |
| } |
| |
| void WriteAppendRect(fxcrt::ostringstream& stream, const CFX_FloatRect& rect) { |
| WriteRect(stream, rect) << " " << kAppendRectOperator << "\n"; |
| } |
| |
| ByteString GetStrokeColorAppStream(const CFX_Color& color) { |
| fxcrt::ostringstream sColorStream; |
| switch (color.nColorType) { |
| case CFX_Color::Type::kTransparent: |
| break; |
| case CFX_Color::Type::kGray: |
| sColorStream << color.fColor1 << " " << kSetGrayStrokedOperator << "\n"; |
| break; |
| case CFX_Color::Type::kRGB: |
| sColorStream << color.fColor1 << " " << color.fColor2 << " " |
| << color.fColor3 << " " << kSetRGBStrokedOperator << "\n"; |
| break; |
| case CFX_Color::Type::kCMYK: |
| sColorStream << color.fColor1 << " " << color.fColor2 << " " |
| << color.fColor3 << " " << color.fColor4 << " " |
| << kSetCMKYStrokedOperator << "\n"; |
| break; |
| } |
| return ByteString(sColorStream); |
| } |
| |
| ByteString GetFillColorAppStream(const CFX_Color& color) { |
| fxcrt::ostringstream sColorStream; |
| switch (color.nColorType) { |
| case CFX_Color::Type::kTransparent: |
| break; |
| case CFX_Color::Type::kGray: |
| sColorStream << color.fColor1 << " " << kSetGrayOperator << "\n"; |
| break; |
| case CFX_Color::Type::kRGB: |
| sColorStream << color.fColor1 << " " << color.fColor2 << " " |
| << color.fColor3 << " " << kSetRGBOperator << "\n"; |
| break; |
| case CFX_Color::Type::kCMYK: |
| sColorStream << color.fColor1 << " " << color.fColor2 << " " |
| << color.fColor3 << " " << color.fColor4 << " " |
| << kSetCMYKOperator << "\n"; |
| break; |
| } |
| return ByteString(sColorStream); |
| } |
| |
| ByteString GetAP_Check(const CFX_FloatRect& crBBox) { |
| const float fWidth = crBBox.Width(); |
| const float fHeight = crBBox.Height(); |
| |
| CFX_PointF pts[8][3] = {{CFX_PointF(0.28f, 0.52f), CFX_PointF(0.27f, 0.48f), |
| CFX_PointF(0.29f, 0.40f)}, |
| {CFX_PointF(0.30f, 0.33f), CFX_PointF(0.31f, 0.29f), |
| CFX_PointF(0.31f, 0.28f)}, |
| {CFX_PointF(0.39f, 0.28f), CFX_PointF(0.49f, 0.29f), |
| CFX_PointF(0.77f, 0.67f)}, |
| {CFX_PointF(0.76f, 0.68f), CFX_PointF(0.78f, 0.69f), |
| CFX_PointF(0.76f, 0.75f)}, |
| {CFX_PointF(0.76f, 0.75f), CFX_PointF(0.73f, 0.80f), |
| CFX_PointF(0.68f, 0.75f)}, |
| {CFX_PointF(0.68f, 0.74f), CFX_PointF(0.68f, 0.74f), |
| CFX_PointF(0.44f, 0.47f)}, |
| {CFX_PointF(0.43f, 0.47f), CFX_PointF(0.40f, 0.47f), |
| CFX_PointF(0.41f, 0.58f)}, |
| {CFX_PointF(0.40f, 0.60f), CFX_PointF(0.28f, 0.66f), |
| CFX_PointF(0.30f, 0.56f)}}; |
| |
| for (size_t i = 0; i < std::size(pts); ++i) { |
| for (size_t j = 0; j < std::size(pts[0]); ++j) { |
| pts[i][j].x = pts[i][j].x * fWidth + crBBox.left; |
| pts[i][j].y *= pts[i][j].y * fHeight + crBBox.bottom; |
| } |
| } |
| |
| fxcrt::ostringstream csAP; |
| WriteMove(csAP, pts[0][0]); |
| |
| for (size_t i = 0; i < std::size(pts); ++i) { |
| size_t nNext = i < std::size(pts) - 1 ? i + 1 : 0; |
| const CFX_PointF& pt_next = pts[nNext][0]; |
| |
| float px1 = pts[i][1].x - pts[i][0].x; |
| float py1 = pts[i][1].y - pts[i][0].y; |
| float px2 = pts[i][2].x - pt_next.x; |
| float py2 = pts[i][2].y - pt_next.y; |
| |
| WriteBezierCurve( |
| csAP, |
| {pts[i][0].x + px1 * FXSYS_BEZIER, pts[i][0].y + py1 * FXSYS_BEZIER}, |
| {pt_next.x + px2 * FXSYS_BEZIER, pt_next.y + py2 * FXSYS_BEZIER}, |
| pt_next); |
| } |
| |
| return ByteString(csAP); |
| } |
| |
| ByteString GetAP_Circle(const CFX_FloatRect& crBBox) { |
| fxcrt::ostringstream csAP; |
| |
| float fWidth = crBBox.Width(); |
| float fHeight = crBBox.Height(); |
| |
| CFX_PointF pt1(crBBox.left, crBBox.bottom + fHeight / 2); |
| CFX_PointF pt2(crBBox.left + fWidth / 2, crBBox.top); |
| CFX_PointF pt3(crBBox.right, crBBox.bottom + fHeight / 2); |
| CFX_PointF pt4(crBBox.left + fWidth / 2, crBBox.bottom); |
| |
| WriteMove(csAP, pt1); |
| |
| float px = pt2.x - pt1.x; |
| float py = pt2.y - pt1.y; |
| |
| WriteBezierCurve(csAP, {pt1.x, pt1.y + py * FXSYS_BEZIER}, |
| {pt2.x - px * FXSYS_BEZIER, pt2.y}, pt2); |
| |
| px = pt3.x - pt2.x; |
| py = pt2.y - pt3.y; |
| |
| WriteBezierCurve(csAP, {pt2.x + px * FXSYS_BEZIER, pt2.y}, |
| {pt3.x, pt3.y + py * FXSYS_BEZIER}, pt3); |
| |
| px = pt3.x - pt4.x; |
| py = pt3.y - pt4.y; |
| |
| WriteBezierCurve(csAP, {pt3.x, pt3.y - py * FXSYS_BEZIER}, |
| {pt4.x + px * FXSYS_BEZIER, pt4.y}, pt4); |
| |
| px = pt4.x - pt1.x; |
| py = pt1.y - pt4.y; |
| |
| WriteBezierCurve(csAP, {pt4.x - px * FXSYS_BEZIER, pt4.y}, |
| {pt1.x, pt1.y - py * FXSYS_BEZIER}, pt1); |
| |
| return ByteString(csAP); |
| } |
| |
| ByteString GetAP_Cross(const CFX_FloatRect& crBBox) { |
| fxcrt::ostringstream csAP; |
| |
| WriteMove(csAP, {crBBox.left, crBBox.top}); |
| WriteLine(csAP, {crBBox.right, crBBox.bottom}); |
| WriteMove(csAP, {crBBox.left, crBBox.bottom}); |
| WriteLine(csAP, {crBBox.right, crBBox.top}); |
| |
| return ByteString(csAP); |
| } |
| |
| ByteString GetAP_Diamond(const CFX_FloatRect& crBBox) { |
| fxcrt::ostringstream csAP; |
| |
| float fWidth = crBBox.Width(); |
| float fHeight = crBBox.Height(); |
| |
| const CFX_PointF points[] = {{crBBox.left, crBBox.bottom + fHeight / 2}, |
| {crBBox.left + fWidth / 2, crBBox.top}, |
| {crBBox.right, crBBox.bottom + fHeight / 2}, |
| {crBBox.left + fWidth / 2, crBBox.bottom}}; |
| WriteClosedLoop(csAP, points); |
| |
| return ByteString(csAP); |
| } |
| |
| ByteString GetAP_Square(const CFX_FloatRect& crBBox) { |
| fxcrt::ostringstream csAP; |
| |
| const CFX_PointF points[] = {{crBBox.left, crBBox.top}, |
| {crBBox.right, crBBox.top}, |
| {crBBox.right, crBBox.bottom}, |
| {crBBox.left, crBBox.bottom}}; |
| WriteClosedLoop(csAP, points); |
| |
| return ByteString(csAP); |
| } |
| |
| ByteString GetAP_Star(const CFX_FloatRect& crBBox) { |
| fxcrt::ostringstream csAP; |
| |
| float fRadius = (crBBox.top - crBBox.bottom) / (1 + cosf(FXSYS_PI / 5.0f)); |
| CFX_PointF ptCenter = CFX_PointF((crBBox.left + crBBox.right) / 2.0f, |
| (crBBox.top + crBBox.bottom) / 2.0f); |
| |
| CFX_PointF points[5]; |
| float fAngle = FXSYS_PI / 10.0f; |
| for (auto& point : points) { |
| point = |
| ptCenter + CFX_PointF(fRadius * cosf(fAngle), fRadius * sinf(fAngle)); |
| fAngle += FXSYS_PI * 2 / 5.0f; |
| } |
| |
| WriteMove(csAP, points[0]); |
| |
| int next = 0; |
| for (size_t i = 0; i < std::size(points); ++i) { |
| next = (next + 2) % std::size(points); |
| WriteLine(csAP, points[next]); |
| } |
| |
| return ByteString(csAP); |
| } |
| |
| ByteString GetAP_HalfCircle(const CFX_FloatRect& crBBox, float fRotate) { |
| fxcrt::ostringstream csAP; |
| |
| float fWidth = crBBox.Width(); |
| float fHeight = crBBox.Height(); |
| |
| CFX_PointF pt1(-fWidth / 2, 0); |
| CFX_PointF pt2(0, fHeight / 2); |
| CFX_PointF pt3(fWidth / 2, 0); |
| |
| CFX_Matrix rotate_matrix(cos(fRotate), sin(fRotate), -sin(fRotate), |
| cos(fRotate), crBBox.left + fWidth / 2, |
| crBBox.bottom + fHeight / 2); |
| WriteMatrix(csAP, rotate_matrix) << " " << kConcatMatrixOperator << "\n"; |
| |
| WriteMove(csAP, pt1); |
| |
| float px = pt2.x - pt1.x; |
| float py = pt2.y - pt1.y; |
| |
| WriteBezierCurve(csAP, {pt1.x, pt1.y + py * FXSYS_BEZIER}, |
| {pt2.x - px * FXSYS_BEZIER, pt2.y}, pt2); |
| |
| px = pt3.x - pt2.x; |
| py = pt2.y - pt3.y; |
| |
| WriteBezierCurve(csAP, {pt2.x + px * FXSYS_BEZIER, pt2.y}, |
| {pt3.x, pt3.y + py * FXSYS_BEZIER}, pt3); |
| |
| return ByteString(csAP); |
| } |
| |
| ByteString GetAppStream_Check(const CFX_FloatRect& rcBBox, |
| const CFX_Color& crText) { |
| fxcrt::ostringstream sAP; |
| { |
| AutoClosedQCommand q(&sAP); |
| sAP << GetFillColorAppStream(crText) << GetAP_Check(rcBBox) << kFillOperator |
| << "\n"; |
| } |
| return ByteString(sAP); |
| } |
| |
| ByteString GetAppStream_Circle(const CFX_FloatRect& rcBBox, |
| const CFX_Color& crText) { |
| fxcrt::ostringstream sAP; |
| { |
| AutoClosedQCommand q(&sAP); |
| sAP << GetFillColorAppStream(crText) << GetAP_Circle(rcBBox) |
| << kFillOperator << "\n"; |
| } |
| return ByteString(sAP); |
| } |
| |
| ByteString GetAppStream_Cross(const CFX_FloatRect& rcBBox, |
| const CFX_Color& crText) { |
| fxcrt::ostringstream sAP; |
| { |
| AutoClosedQCommand q(&sAP); |
| sAP << GetStrokeColorAppStream(crText) << GetAP_Cross(rcBBox) |
| << kStrokeOperator << "\n"; |
| } |
| return ByteString(sAP); |
| } |
| |
| ByteString GetAppStream_Diamond(const CFX_FloatRect& rcBBox, |
| const CFX_Color& crText) { |
| fxcrt::ostringstream sAP; |
| { |
| AutoClosedQCommand q(&sAP); |
| sAP << "1 " << kSetLineWidthOperator << "\n" |
| << GetFillColorAppStream(crText) << GetAP_Diamond(rcBBox) |
| << kFillOperator << "\n"; |
| } |
| return ByteString(sAP); |
| } |
| |
| ByteString GetAppStream_Square(const CFX_FloatRect& rcBBox, |
| const CFX_Color& crText) { |
| fxcrt::ostringstream sAP; |
| { |
| AutoClosedQCommand q(&sAP); |
| sAP << GetFillColorAppStream(crText) << GetAP_Square(rcBBox) |
| << kFillOperator << "\n"; |
| } |
| return ByteString(sAP); |
| } |
| |
| ByteString GetAppStream_Star(const CFX_FloatRect& rcBBox, |
| const CFX_Color& crText) { |
| fxcrt::ostringstream sAP; |
| { |
| AutoClosedQCommand q(&sAP); |
| sAP << GetFillColorAppStream(crText) << GetAP_Star(rcBBox) << kFillOperator |
| << "\n"; |
| } |
| return ByteString(sAP); |
| } |
| |
| ByteString GetCircleFillAppStream(const CFX_FloatRect& rect, |
| const CFX_Color& color) { |
| fxcrt::ostringstream sAppStream; |
| ByteString sColor = GetFillColorAppStream(color); |
| if (sColor.GetLength() > 0) { |
| AutoClosedQCommand q(&sAppStream); |
| sAppStream << sColor << GetAP_Circle(rect) << kFillOperator << "\n"; |
| } |
| return ByteString(sAppStream); |
| } |
| |
| ByteString 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) { |
| fxcrt::ostringstream sAppStream; |
| ByteString sColor; |
| |
| if (fWidth > 0.0f) { |
| AutoClosedQCommand q(&sAppStream); |
| |
| float fHalfWidth = fWidth / 2.0f; |
| CFX_FloatRect rect_by_2 = rect.GetDeflated(fHalfWidth, fHalfWidth); |
| |
| float div = fHalfWidth * 0.75f; |
| CFX_FloatRect rect_by_75 = rect.GetDeflated(div, div); |
| switch (nStyle) { |
| default: |
| case BorderStyle::kSolid: |
| case BorderStyle::kUnderline: { |
| sColor = GetStrokeColorAppStream(color); |
| if (sColor.GetLength() > 0) { |
| AutoClosedQCommand q2(&sAppStream); |
| sAppStream << fWidth << " " << kSetLineWidthOperator << "\n" |
| << sColor << GetAP_Circle(rect_by_2) << " " |
| << kStrokeOperator << "\n"; |
| } |
| } break; |
| case BorderStyle::kDash: { |
| sColor = GetStrokeColorAppStream(color); |
| if (sColor.GetLength() > 0) { |
| AutoClosedQCommand q2(&sAppStream); |
| sAppStream << fWidth << " " << kSetLineWidthOperator << "\n" |
| << "[" << dash.nDash << " " << dash.nGap << "] " |
| << dash.nPhase << " " << kSetDashOperator << "\n" |
| << sColor << GetAP_Circle(rect_by_2) << " " |
| << kStrokeOperator << "\n"; |
| } |
| } break; |
| case BorderStyle::kBeveled: { |
| sColor = GetStrokeColorAppStream(color); |
| if (sColor.GetLength() > 0) { |
| AutoClosedQCommand q2(&sAppStream); |
| sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n" |
| << sColor << GetAP_Circle(rect) << " " << kStrokeOperator |
| << "\n"; |
| } |
| sColor = GetStrokeColorAppStream(crLeftTop); |
| if (sColor.GetLength() > 0) { |
| AutoClosedQCommand q2(&sAppStream); |
| sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n" |
| << sColor << GetAP_HalfCircle(rect_by_75, FXSYS_PI / 4.0f) |
| << " " << kStrokeOperator << "\n"; |
| } |
| sColor = GetStrokeColorAppStream(crRightBottom); |
| if (sColor.GetLength() > 0) { |
| AutoClosedQCommand q2(&sAppStream); |
| sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n" |
| << sColor |
| << GetAP_HalfCircle(rect_by_75, FXSYS_PI * 5 / 4.0f) << " " |
| << kStrokeOperator << "\n"; |
| } |
| } break; |
| case BorderStyle::kInset: { |
| sColor = GetStrokeColorAppStream(color); |
| if (sColor.GetLength() > 0) { |
| AutoClosedQCommand q2(&sAppStream); |
| sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n" |
| << sColor << GetAP_Circle(rect) << " " << kStrokeOperator |
| << "\n"; |
| } |
| sColor = GetStrokeColorAppStream(crLeftTop); |
| if (sColor.GetLength() > 0) { |
| AutoClosedQCommand q2(&sAppStream); |
| sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n" |
| << sColor << GetAP_HalfCircle(rect_by_75, FXSYS_PI / 4.0f) |
| << " " << kStrokeOperator << "\n"; |
| } |
| sColor = GetStrokeColorAppStream(crRightBottom); |
| if (sColor.GetLength() > 0) { |
| AutoClosedQCommand q2(&sAppStream); |
| sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n" |
| << sColor |
| << GetAP_HalfCircle(rect_by_75, FXSYS_PI * 5 / 4.0f) << " " |
| << kStrokeOperator << "\n"; |
| } |
| } break; |
| } |
| } |
| return ByteString(sAppStream); |
| } |
| |
| ByteString GetCheckBoxAppStream(const CFX_FloatRect& rcBBox, |
| CheckStyle nStyle, |
| const CFX_Color& crText) { |
| CFX_FloatRect rcCenter = rcBBox.GetCenterSquare(); |
| switch (nStyle) { |
| default: |
| case CheckStyle::kCheck: |
| return GetAppStream_Check(rcCenter, crText); |
| case CheckStyle::kCircle: |
| rcCenter.ScaleFromCenterPoint(2.0f / 3.0f); |
| return GetAppStream_Circle(rcCenter, crText); |
| case CheckStyle::kCross: |
| return GetAppStream_Cross(rcCenter, crText); |
| case CheckStyle::kDiamond: |
| rcCenter.ScaleFromCenterPoint(2.0f / 3.0f); |
| return GetAppStream_Diamond(rcCenter, crText); |
| case CheckStyle::kSquare: |
| rcCenter.ScaleFromCenterPoint(2.0f / 3.0f); |
| return GetAppStream_Square(rcCenter, crText); |
| case CheckStyle::kStar: |
| rcCenter.ScaleFromCenterPoint(2.0f / 3.0f); |
| return GetAppStream_Star(rcCenter, crText); |
| } |
| } |
| |
| ByteString GetRadioButtonAppStream(const CFX_FloatRect& rcBBox, |
| CheckStyle nStyle, |
| const CFX_Color& crText) { |
| CFX_FloatRect rcCenter = rcBBox.GetCenterSquare(); |
| switch (nStyle) { |
| default: |
| case CheckStyle::kCheck: |
| return GetAppStream_Check(rcCenter, crText); |
| case CheckStyle::kCircle: |
| rcCenter.ScaleFromCenterPoint(1.0f / 2.0f); |
| return GetAppStream_Circle(rcCenter, crText); |
| case CheckStyle::kCross: |
| return GetAppStream_Cross(rcCenter, crText); |
| case CheckStyle::kDiamond: |
| rcCenter.ScaleFromCenterPoint(2.0f / 3.0f); |
| return GetAppStream_Diamond(rcCenter, crText); |
| case CheckStyle::kSquare: |
| rcCenter.ScaleFromCenterPoint(2.0f / 3.0f); |
| return GetAppStream_Square(rcCenter, crText); |
| case CheckStyle::kStar: |
| rcCenter.ScaleFromCenterPoint(2.0f / 3.0f); |
| return GetAppStream_Star(rcCenter, crText); |
| } |
| } |
| |
| ByteString GetFontSetString(IPVT_FontMap* pFontMap, |
| int32_t nFontIndex, |
| float fFontSize) { |
| if (!pFontMap) |
| return ByteString(); |
| |
| ByteString sFontAlias = pFontMap->GetPDFFontAlias(nFontIndex); |
| if (sFontAlias.GetLength() <= 0 || fFontSize <= 0) |
| return ByteString(); |
| |
| fxcrt::ostringstream sRet; |
| sRet << "/" << sFontAlias << " " << fFontSize << " " |
| << kSetTextFontAndSizeOperator << "\n"; |
| return ByteString(sRet); |
| } |
| |
| ByteString GetWordRenderString(ByteStringView strWords) { |
| if (strWords.IsEmpty()) |
| return ByteString(); |
| return PDF_EncodeString(strWords) + " " + kShowTextOperator + "\n"; |
| } |
| |
| ByteString GetEditAppStream(CPWL_EditImpl* pEdit, |
| const CFX_PointF& ptOffset, |
| bool bContinuous, |
| uint16_t SubWord) { |
| CPWL_EditImpl::Iterator* pIterator = pEdit->GetIterator(); |
| pIterator->SetAt(0); |
| |
| fxcrt::ostringstream sEditStream; |
| int32_t nCurFontIndex = -1; |
| CFX_PointF ptOld; |
| CFX_PointF ptNew; |
| CPVT_WordPlace oldplace; |
| ByteString sWords; |
| |
| while (pIterator->NextWord()) { |
| CPVT_WordPlace place = pIterator->GetAt(); |
| if (bContinuous) { |
| if (place.LineCmp(oldplace) != 0) { |
| if (!sWords.IsEmpty()) { |
| sEditStream << GetWordRenderString(sWords.AsStringView()); |
| sWords.clear(); |
| } |
| |
| CPVT_Word word; |
| if (pIterator->GetWord(word)) { |
| ptNew = CFX_PointF(word.ptWord.x + ptOffset.x, |
| word.ptWord.y + ptOffset.y); |
| } else { |
| CPVT_Line line; |
| pIterator->GetLine(line); |
| ptNew = CFX_PointF(line.ptLine.x + ptOffset.x, |
| line.ptLine.y + ptOffset.y); |
| } |
| |
| if (ptNew.x != ptOld.x || ptNew.y != ptOld.y) { |
| WritePoint(sEditStream, {ptNew.x - ptOld.x, ptNew.y - ptOld.y}) |
| << " " << kMoveTextPositionOperator << "\n"; |
| |
| ptOld = ptNew; |
| } |
| } |
| |
| CPVT_Word word; |
| if (pIterator->GetWord(word)) { |
| if (word.nFontIndex != nCurFontIndex) { |
| if (!sWords.IsEmpty()) { |
| sEditStream << GetWordRenderString(sWords.AsStringView()); |
| sWords.clear(); |
| } |
| sEditStream << GetFontSetString(pEdit->GetFontMap(), word.nFontIndex, |
| word.fFontSize); |
| nCurFontIndex = word.nFontIndex; |
| } |
| |
| sWords += pEdit->GetPDFWordString(nCurFontIndex, word.Word, SubWord); |
| } |
| oldplace = place; |
| } else { |
| CPVT_Word word; |
| if (pIterator->GetWord(word)) { |
| ptNew = |
| CFX_PointF(word.ptWord.x + ptOffset.x, word.ptWord.y + ptOffset.y); |
| |
| if (ptNew.x != ptOld.x || ptNew.y != ptOld.y) { |
| WritePoint(sEditStream, {ptNew.x - ptOld.x, ptNew.y - ptOld.y}) |
| << " " << kMoveTextPositionOperator << "\n"; |
| ptOld = ptNew; |
| } |
| if (word.nFontIndex != nCurFontIndex) { |
| sEditStream << GetFontSetString(pEdit->GetFontMap(), word.nFontIndex, |
| word.fFontSize); |
| nCurFontIndex = word.nFontIndex; |
| } |
| sEditStream << GetWordRenderString( |
| pEdit->GetPDFWordString(nCurFontIndex, word.Word, SubWord) |
| .AsStringView()); |
| } |
| } |
| } |
| |
| if (!sWords.IsEmpty()) |
| sEditStream << GetWordRenderString(sWords.AsStringView()); |
| |
| fxcrt::ostringstream sAppStream; |
| if (sEditStream.tellp() > 0) { |
| sAppStream << sEditStream.str(); |
| } |
| return ByteString(sAppStream); |
| } |
| |
| ByteString GenerateIconAppStream(CPDF_IconFit& fit, |
| RetainPtr<CPDF_Stream> pIconStream, |
| const CFX_FloatRect& rcIcon) { |
| if (rcIcon.IsEmpty() || !pIconStream) |
| return ByteString(); |
| |
| CPWL_Wnd::CreateParams cp(nullptr, nullptr, nullptr); |
| cp.dwFlags = PWS_VISIBLE; |
| auto pWnd = std::make_unique<CPWL_Wnd>(cp, nullptr); |
| pWnd->Realize(); |
| if (!pWnd->Move(rcIcon, false, false)) |
| return ByteString(); |
| |
| auto pPDFIcon = std::make_unique<CPDF_Icon>(std::move(pIconStream)); |
| ByteString sAlias = pPDFIcon->GetImageAlias(); |
| if (sAlias.GetLength() <= 0) |
| return ByteString(); |
| |
| const CFX_FloatRect rcPlate = pWnd->GetClientRect(); |
| const CFX_SizeF image_size = pPDFIcon->GetImageSize(); |
| const CFX_Matrix mt = pPDFIcon->GetImageMatrix().GetInverse(); |
| const CFX_VectorF scale = fit.GetScale(image_size, rcPlate); |
| const CFX_VectorF offset = fit.GetImageOffset(image_size, scale, rcPlate); |
| |
| fxcrt::ostringstream str; |
| { |
| AutoClosedQCommand q(&str); |
| WriteAppendRect(str, rcPlate); |
| str << kSetNonZeroWindingClipOperator << " " |
| << kEndPathNoFillOrStrokeOperator << "\n"; |
| |
| CFX_Matrix scale_matrix(scale.x, 0, 0, scale.y, rcPlate.left + offset.x, |
| rcPlate.bottom + offset.y); |
| WriteMatrix(str, scale_matrix) << " " << kConcatMatrixOperator << "\n"; |
| WriteMatrix(str, mt) << " " << kConcatMatrixOperator << "\n"; |
| |
| str << "0 " << kSetGrayOperator << " 0 " << kSetGrayStrokedOperator << " 1 " |
| << kSetLineWidthOperator << " /" << sAlias << " " |
| << kInvokeNamedXObjectOperator << "\n"; |
| } |
| pWnd->Destroy(); |
| return ByteString(str); |
| } |
| |
| ByteString 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) { |
| const float fAutoFontScale = 1.0f / 3.0f; |
| |
| auto pEdit = std::make_unique<CPWL_EditImpl>(); |
| pEdit->SetFontMap(pFontMap); |
| pEdit->SetAlignmentH(1); |
| pEdit->SetAlignmentV(1); |
| pEdit->SetMultiLine(false); |
| pEdit->SetAutoReturn(false); |
| if (FXSYS_IsFloatZero(fFontSize)) |
| pEdit->SetAutoFontSize(true); |
| else |
| pEdit->SetFontSize(fFontSize); |
| |
| pEdit->Initialize(); |
| pEdit->SetText(sLabel); |
| pEdit->Paint(); |
| |
| CFX_FloatRect rcLabelContent = pEdit->GetContentRect(); |
| CFX_FloatRect rcLabel; |
| CFX_FloatRect rcIcon; |
| float fWidth = 0.0f; |
| float fHeight = 0.0f; |
| |
| switch (nLayOut) { |
| case ButtonStyle::kLabel: |
| rcLabel = rcBBox; |
| break; |
| case ButtonStyle::kIcon: |
| rcIcon = rcBBox; |
| break; |
| case ButtonStyle::kIconTopLabelBottom: |
| if (pIconStream) { |
| if (FXSYS_IsFloatZero(fFontSize)) { |
| fHeight = rcBBox.Height(); |
| rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcBBox.right, |
| rcBBox.bottom + fHeight * fAutoFontScale); |
| rcIcon = |
| CFX_FloatRect(rcBBox.left, rcLabel.top, rcBBox.right, rcBBox.top); |
| } else { |
| fHeight = rcLabelContent.Height(); |
| |
| if (rcBBox.bottom + fHeight > rcBBox.top) { |
| rcLabel = rcBBox; |
| } else { |
| rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcBBox.right, |
| rcBBox.bottom + fHeight); |
| rcIcon = CFX_FloatRect(rcBBox.left, rcLabel.top, rcBBox.right, |
| rcBBox.top); |
| } |
| } |
| } else { |
| rcLabel = rcBBox; |
| } |
| break; |
| case ButtonStyle::kIconBottomLabelTop: |
| if (pIconStream) { |
| if (FXSYS_IsFloatZero(fFontSize)) { |
| fHeight = rcBBox.Height(); |
| rcLabel = |
| CFX_FloatRect(rcBBox.left, rcBBox.top - fHeight * fAutoFontScale, |
| rcBBox.right, rcBBox.top); |
| rcIcon = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcBBox.right, |
| rcLabel.bottom); |
| } else { |
| fHeight = rcLabelContent.Height(); |
| |
| if (rcBBox.bottom + fHeight > rcBBox.top) { |
| rcLabel = rcBBox; |
| } else { |
| rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.top - fHeight, |
| rcBBox.right, rcBBox.top); |
| rcIcon = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcBBox.right, |
| rcLabel.bottom); |
| } |
| } |
| } else { |
| rcLabel = rcBBox; |
| } |
| break; |
| case ButtonStyle::kIconLeftLabelRight: |
| if (pIconStream) { |
| if (FXSYS_IsFloatZero(fFontSize)) { |
| fWidth = rcBBox.right - rcBBox.left; |
| if (rcLabelContent.Width() < fWidth * fAutoFontScale) { |
| rcLabel = CFX_FloatRect(rcBBox.right - fWidth * fAutoFontScale, |
| rcBBox.bottom, rcBBox.right, rcBBox.top); |
| rcIcon = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcLabel.left, |
| rcBBox.top); |
| } else { |
| if (rcLabelContent.Width() < fWidth) { |
| rcLabel = CFX_FloatRect(rcBBox.right - rcLabelContent.Width(), |
| rcBBox.bottom, rcBBox.right, rcBBox.top); |
| rcIcon = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcLabel.left, |
| rcBBox.top); |
| } else { |
| rcLabel = rcBBox; |
| } |
| } |
| } else { |
| fWidth = rcLabelContent.Width(); |
| if (rcBBox.left + fWidth > rcBBox.right) { |
| rcLabel = rcBBox; |
| } else { |
| rcLabel = CFX_FloatRect(rcBBox.right - fWidth, rcBBox.bottom, |
| rcBBox.right, rcBBox.top); |
| rcIcon = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcLabel.left, |
| rcBBox.top); |
| } |
| } |
| } else { |
| rcLabel = rcBBox; |
| } |
| break; |
| case ButtonStyle::kIconRightLabelLeft: |
| if (pIconStream) { |
| if (FXSYS_IsFloatZero(fFontSize)) { |
| fWidth = rcBBox.right - rcBBox.left; |
| if (rcLabelContent.Width() < fWidth * fAutoFontScale) { |
| rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom, |
| rcBBox.left + fWidth * fAutoFontScale, |
| rcBBox.top); |
| rcIcon = CFX_FloatRect(rcLabel.right, rcBBox.bottom, rcBBox.right, |
| rcBBox.top); |
| } else { |
| if (rcLabelContent.Width() < fWidth) { |
| rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom, |
| rcBBox.left + rcLabelContent.Width(), |
| rcBBox.top); |
| rcIcon = CFX_FloatRect(rcLabel.right, rcBBox.bottom, rcBBox.right, |
| rcBBox.top); |
| } else { |
| rcLabel = rcBBox; |
| } |
| } |
| } else { |
| fWidth = rcLabelContent.Width(); |
| if (rcBBox.left + fWidth > rcBBox.right) { |
| rcLabel = rcBBox; |
| } else { |
| rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom, |
| rcBBox.left + fWidth, rcBBox.top); |
| rcIcon = CFX_FloatRect(rcLabel.right, rcBBox.bottom, rcBBox.right, |
| rcBBox.top); |
| } |
| } |
| } else { |
| rcLabel = rcBBox; |
| } |
| break; |
| case ButtonStyle::kLabelOverIcon: |
| rcLabel = rcBBox; |
| rcIcon = rcBBox; |
| break; |
| } |
| |
| fxcrt::ostringstream sTemp; |
| sTemp << GenerateIconAppStream(IconFit, std::move(pIconStream), rcIcon); |
| |
| if (!rcLabel.IsEmpty()) { |
| pEdit->SetPlateRect(rcLabel); |
| pEdit->Paint(); |
| ByteString sEdit = |
| GetEditAppStream(pEdit.get(), CFX_PointF(0.0f, 0.0f), true, 0); |
| if (sEdit.GetLength() > 0) { |
| AutoClosedCommand bt(&sTemp, kTextBeginOperator, kTextEndOperator); |
| sTemp << GetFillColorAppStream(crText) << sEdit; |
| } |
| } |
| |
| if (sTemp.tellp() <= 0) |
| return ByteString(); |
| |
| fxcrt::ostringstream sAppStream; |
| { |
| AutoClosedQCommand q(&sAppStream); |
| WriteAppendRect(sAppStream, rcBBox); |
| sAppStream << kSetNonZeroWindingClipOperator << " " |
| << kEndPathNoFillOrStrokeOperator << "\n"; |
| sAppStream << sTemp.str().c_str(); |
| } |
| return ByteString(sAppStream); |
| } |
| |
| ByteString 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) { |
| fxcrt::ostringstream sAppStream; |
| ByteString sColor; |
| |
| float fLeft = rect.left; |
| float fRight = rect.right; |
| float fTop = rect.top; |
| float fBottom = rect.bottom; |
| |
| if (fWidth > 0.0f) { |
| float fHalfWidth = fWidth / 2.0f; |
| AutoClosedQCommand q(&sAppStream); |
| |
| switch (nStyle) { |
| default: |
| case BorderStyle::kSolid: |
| sColor = GetFillColorAppStream(color); |
| if (sColor.GetLength() > 0) { |
| sAppStream << sColor; |
| WriteAppendRect(sAppStream, {fLeft, fBottom, fRight, fTop}); |
| WriteAppendRect(sAppStream, {fLeft + fWidth, fBottom + fWidth, |
| fRight - fWidth, fTop - fWidth}); |
| sAppStream << kFillEvenOddOperator << "\n"; |
| } |
| break; |
| case BorderStyle::kDash: |
| sColor = GetStrokeColorAppStream(color); |
| if (sColor.GetLength() > 0) { |
| sAppStream << sColor; |
| sAppStream << fWidth << " " << kSetLineWidthOperator << " [" |
| << dash.nDash << " " << dash.nGap << "] " << dash.nPhase |
| << " " << kSetDashOperator << "\n"; |
| const CFX_PointF points[] = { |
| {fLeft + fWidth / 2, fBottom + fWidth / 2}, |
| {fLeft + fWidth / 2, fTop - fWidth / 2}, |
| {fRight - fWidth / 2, fTop - fWidth / 2}, |
| {fRight - fWidth / 2, fBottom + fWidth / 2}}; |
| WriteClosedLoop(sAppStream, points); |
| sAppStream << kStrokeOperator << "\n"; |
| } |
| break; |
| case BorderStyle::kBeveled: |
| case BorderStyle::kInset: |
| sColor = GetFillColorAppStream(crLeftTop); |
| if (sColor.GetLength() > 0) { |
| sAppStream << sColor; |
| WriteMove(sAppStream, {fLeft + fHalfWidth, fBottom + fHalfWidth}); |
| WriteLine(sAppStream, {fLeft + fHalfWidth, fTop - fHalfWidth}); |
| WriteLine(sAppStream, {fRight - fHalfWidth, fTop - fHalfWidth}); |
| WriteLine(sAppStream, |
| {fRight - fHalfWidth * 2, fTop - fHalfWidth * 2}); |
| WriteLine(sAppStream, |
| {fLeft + fHalfWidth * 2, fTop - fHalfWidth * 2}); |
| WriteLine(sAppStream, |
| {fLeft + fHalfWidth * 2, fBottom + fHalfWidth * 2}); |
| sAppStream << kFillOperator << "\n"; |
| } |
| sColor = GetFillColorAppStream(crRightBottom); |
| if (sColor.GetLength() > 0) { |
| sAppStream << sColor; |
| WriteMove(sAppStream, {fRight - fHalfWidth, fTop - fHalfWidth}); |
| WriteLine(sAppStream, {fRight - fHalfWidth, fBottom + fHalfWidth}); |
| WriteLine(sAppStream, {fLeft + fHalfWidth, fBottom + fHalfWidth}); |
| WriteLine(sAppStream, |
| {fLeft + fHalfWidth * 2, fBottom + fHalfWidth * 2}); |
| WriteLine(sAppStream, |
| {fRight - fHalfWidth * 2, fBottom + fHalfWidth * 2}); |
| WriteLine(sAppStream, |
| {fRight - fHalfWidth * 2, fTop - fHalfWidth * 2}); |
| sAppStream << kFillOperator << "\n"; |
| } |
| sColor = GetFillColorAppStream(color); |
| if (sColor.GetLength() > 0) { |
| sAppStream << sColor; |
| WriteAppendRect(sAppStream, {fLeft, fBottom, fRight, fTop}); |
| WriteAppendRect(sAppStream, {fLeft + fHalfWidth, fBottom + fHalfWidth, |
| fRight - fHalfWidth, fTop - fHalfWidth}); |
| sAppStream << kFillEvenOddOperator << "\n"; |
| } |
| break; |
| case BorderStyle::kUnderline: |
| sColor = GetStrokeColorAppStream(color); |
| if (sColor.GetLength() > 0) { |
| sAppStream << sColor; |
| sAppStream << fWidth << " " << kSetLineWidthOperator << "\n"; |
| WriteMove(sAppStream, {fLeft, fBottom + fWidth / 2}); |
| WriteLine(sAppStream, {fRight, fBottom + fWidth / 2}); |
| sAppStream << kStrokeOperator << "\n"; |
| } |
| break; |
| } |
| } |
| return ByteString(sAppStream); |
| } |
| |
| ByteString GetDropButtonAppStream(const CFX_FloatRect& rcBBox) { |
| if (rcBBox.IsEmpty()) |
| return ByteString(); |
| |
| fxcrt::ostringstream sAppStream; |
| { |
| AutoClosedQCommand q(&sAppStream); |
| sAppStream << GetFillColorAppStream( |
| CFX_Color(CFX_Color::Type::kRGB, 220.0f / 255.0f, 220.0f / 255.0f, |
| 220.0f / 255.0f)); |
| WriteAppendRect(sAppStream, rcBBox); |
| sAppStream << kFillOperator << "\n"; |
| } |
| |
| { |
| AutoClosedQCommand q(&sAppStream); |
| sAppStream << GetBorderAppStreamInternal( |
| rcBBox, 2, CFX_Color(CFX_Color::Type::kGray, 0), |
| CFX_Color(CFX_Color::Type::kGray, 1), |
| CFX_Color(CFX_Color::Type::kGray, 0.5), BorderStyle::kBeveled, |
| CPWL_Dash(3, 0, 0)); |
| } |
| |
| CFX_PointF ptCenter = CFX_PointF((rcBBox.left + rcBBox.right) / 2, |
| (rcBBox.top + rcBBox.bottom) / 2); |
| if (FXSYS_IsFloatBigger(rcBBox.right - rcBBox.left, 6) && |
| FXSYS_IsFloatBigger(rcBBox.top - rcBBox.bottom, 6)) { |
| AutoClosedQCommand q(&sAppStream); |
| const CFX_PointF points[] = {{ptCenter.x - 3, ptCenter.y + 1.5f}, |
| {ptCenter.x + 3, ptCenter.y + 1.5f}, |
| {ptCenter.x, ptCenter.y - 1.5f}}; |
| sAppStream << " 0 " << kSetGrayOperator << "\n"; |
| WriteClosedLoop(sAppStream, points); |
| sAppStream << kFillOperator << "\n"; |
| } |
| |
| return ByteString(sAppStream); |
| } |
| |
| ByteString GetRectFillAppStream(const CFX_FloatRect& rect, |
| const CFX_Color& color) { |
| fxcrt::ostringstream sAppStream; |
| ByteString sColor = GetFillColorAppStream(color); |
| if (sColor.GetLength() > 0) { |
| AutoClosedQCommand q(&sAppStream); |
| sAppStream << sColor; |
| WriteAppendRect(sAppStream, rect); |
| sAppStream << kFillOperator << "\n"; |
| } |
| |
| return ByteString(sAppStream); |
| } |
| |
| void SetDefaultIconName(CPDF_Stream* pIcon, const char* name) { |
| if (!pIcon) |
| return; |
| |
| RetainPtr<CPDF_Dictionary> pImageDict = pIcon->GetMutableDict(); |
| if (!pImageDict) |
| return; |
| |
| if (pImageDict->KeyExist("Name")) |
| return; |
| |
| pImageDict->SetNewFor<CPDF_String>("Name", name, false); |
| } |
| |
| absl::optional<CheckStyle> CheckStyleFromCaption(const WideString& caption) { |
| if (caption.IsEmpty()) |
| return absl::nullopt; |
| |
| // Character values are ZapfDingbats encodings of named glyphs. |
| switch (caption[0]) { |
| case L'4': |
| return CheckStyle::kCheck; |
| case L'8': |
| return CheckStyle::kCross; |
| case L'H': |
| return CheckStyle::kStar; |
| case L'l': |
| return CheckStyle::kCircle; |
| case L'n': |
| return CheckStyle::kSquare; |
| case L'u': |
| return CheckStyle::kDiamond; |
| default: |
| return absl::nullopt; |
| } |
| } |
| |
| } // namespace |
| |
| CPDFSDK_AppStream::CPDFSDK_AppStream(CPDFSDK_Widget* widget, |
| CPDF_Dictionary* dict) |
| : widget_(widget), dict_(dict) {} |
| |
| CPDFSDK_AppStream::~CPDFSDK_AppStream() = default; |
| |
| void CPDFSDK_AppStream::SetAsPushButton() { |
| CPDF_FormControl* pControl = widget_->GetFormControl(); |
| CFX_FloatRect rcWindow = widget_->GetRotatedRect(); |
| ButtonStyle nLayout = ButtonStyle::kLabel; |
| switch (pControl->GetTextPosition()) { |
| case TEXTPOS_ICON: |
| nLayout = ButtonStyle::kIcon; |
| break; |
| case TEXTPOS_BELOW: |
| nLayout = ButtonStyle::kIconTopLabelBottom; |
| break; |
| case TEXTPOS_ABOVE: |
| nLayout = ButtonStyle::kIconBottomLabelTop; |
| break; |
| case TEXTPOS_RIGHT: |
| nLayout = ButtonStyle::kIconLeftLabelRight; |
| break; |
| case TEXTPOS_LEFT: |
| nLayout = ButtonStyle::kIconRightLabelLeft; |
| break; |
| case TEXTPOS_OVERLAID: |
| nLayout = ButtonStyle::kLabelOverIcon; |
| break; |
| default: |
| nLayout = ButtonStyle::kLabel; |
| break; |
| } |
| |
| CFX_Color crBackground = pControl->GetOriginalBackgroundColor(); |
| CFX_Color crBorder = pControl->GetOriginalBorderColor(); |
| |
| float fBorderWidth = static_cast<float>(widget_->GetBorderWidth()); |
| CPWL_Dash dsBorder(3, 0, 0); |
| CFX_Color crLeftTop; |
| CFX_Color crRightBottom; |
| |
| BorderStyle nBorderStyle = widget_->GetBorderStyle(); |
| switch (nBorderStyle) { |
| case BorderStyle::kDash: |
| dsBorder = CPWL_Dash(3, 3, 0); |
| break; |
| case BorderStyle::kBeveled: |
| fBorderWidth *= 2; |
| crLeftTop = CFX_Color(CFX_Color::Type::kGray, 1); |
| crRightBottom = crBackground / 2.0f; |
| break; |
| case BorderStyle::kInset: |
| fBorderWidth *= 2; |
| crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0.5); |
| crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.75); |
| break; |
| default: |
| break; |
| } |
| |
| CFX_FloatRect rcClient = rcWindow.GetDeflated(fBorderWidth, fBorderWidth); |
| CPDF_DefaultAppearance da = pControl->GetDefaultAppearance(); |
| absl::optional<CFX_Color> color = da.GetColor(); |
| CFX_Color crText = color.value_or(CFX_Color(CFX_Color::Type::kGray, 0)); |
| |
| float fFontSize; |
| ByteString csNameTag; |
| absl::optional<ByteString> font = da.GetFont(&fFontSize); |
| if (font.has_value()) |
| csNameTag = font.value(); |
| else |
| fFontSize = 12.0f; |
| |
| WideString csWCaption; |
| WideString csNormalCaption; |
| WideString csRolloverCaption; |
| WideString csDownCaption; |
| if (pControl->HasMKEntry(pdfium::appearance::kCA)) |
| csNormalCaption = pControl->GetNormalCaption(); |
| |
| if (pControl->HasMKEntry(pdfium::appearance::kRC)) |
| csRolloverCaption = pControl->GetRolloverCaption(); |
| |
| if (pControl->HasMKEntry(pdfium::appearance::kAC)) |
| csDownCaption = pControl->GetDownCaption(); |
| |
| RetainPtr<CPDF_Stream> pNormalIcon; |
| RetainPtr<CPDF_Stream> pRolloverIcon; |
| RetainPtr<CPDF_Stream> pDownIcon; |
| if (pControl->HasMKEntry(pdfium::appearance::kI)) |
| pNormalIcon = pControl->GetNormalIcon(); |
| |
| if (pControl->HasMKEntry(pdfium::appearance::kRI)) |
| pRolloverIcon = pControl->GetRolloverIcon(); |
| |
| if (pControl->HasMKEntry(pdfium::appearance::kIX)) |
| pDownIcon = pControl->GetDownIcon(); |
| |
| SetDefaultIconName(pNormalIcon.Get(), "ImgA"); |
| SetDefaultIconName(pRolloverIcon.Get(), "ImgB"); |
| SetDefaultIconName(pDownIcon.Get(), "ImgC"); |
| |
| CPDF_IconFit iconFit = pControl->GetIconFit(); |
| { |
| CPDF_BAFontMap font_map(widget_->GetPDFPage()->GetDocument(), |
| widget_->GetPDFAnnot()->GetMutableAnnotDict(), "N"); |
| ByteString csAP = |
| GetRectFillAppStream(rcWindow, crBackground) + |
| GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop, |
| crRightBottom, nBorderStyle, dsBorder) + |
| GetPushButtonAppStream(iconFit.GetFittingBounds() ? rcWindow : rcClient, |
| &font_map, pNormalIcon, iconFit, csNormalCaption, |
| crText, fFontSize, nLayout); |
| |
| Write("N", csAP, ByteString()); |
| if (pNormalIcon) |
| AddImage("N", pNormalIcon.Get()); |
| |
| CPDF_FormControl::HighlightingMode eHLM = pControl->GetHighlightingMode(); |
| if (eHLM != CPDF_FormControl::kPush && eHLM != CPDF_FormControl::kToggle) { |
| Remove("D"); |
| Remove("R"); |
| return; |
| } |
| |
| if (csRolloverCaption.IsEmpty() && !pRolloverIcon) { |
| csRolloverCaption = csNormalCaption; |
| pRolloverIcon = pNormalIcon; |
| } |
| } |
| { |
| CPDF_BAFontMap font_map(widget_->GetPDFPage()->GetDocument(), |
| widget_->GetPDFAnnot()->GetMutableAnnotDict(), "R"); |
| ByteString csAP = |
| GetRectFillAppStream(rcWindow, crBackground) + |
| GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop, |
| crRightBottom, nBorderStyle, dsBorder) + |
| GetPushButtonAppStream(iconFit.GetFittingBounds() ? rcWindow : rcClient, |
| &font_map, pRolloverIcon, iconFit, |
| csRolloverCaption, crText, fFontSize, nLayout); |
| |
| Write("R", csAP, ByteString()); |
| if (pRolloverIcon) |
| AddImage("R", pRolloverIcon.Get()); |
| |
| if (csDownCaption.IsEmpty() && !pDownIcon) { |
| csDownCaption = csNormalCaption; |
| pDownIcon = pNormalIcon; |
| } |
| |
| switch (nBorderStyle) { |
| case BorderStyle::kBeveled: { |
| CFX_Color crTemp = crLeftTop; |
| crLeftTop = crRightBottom; |
| crRightBottom = crTemp; |
| break; |
| } |
| case BorderStyle::kInset: { |
| crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0); |
| crRightBottom = CFX_Color(CFX_Color::Type::kGray, 1); |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| { |
| CPDF_BAFontMap font_map(widget_->GetPDFPage()->GetDocument(), |
| widget_->GetPDFAnnot()->GetMutableAnnotDict(), "D"); |
| ByteString csAP = |
| GetRectFillAppStream(rcWindow, crBackground - 0.25f) + |
| GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop, |
| crRightBottom, nBorderStyle, dsBorder) + |
| GetPushButtonAppStream(iconFit.GetFittingBounds() ? rcWindow : rcClient, |
| &font_map, pDownIcon, iconFit, csDownCaption, |
| crText, fFontSize, nLayout); |
| |
| Write("D", csAP, ByteString()); |
| if (pDownIcon) |
| AddImage("D", pDownIcon.Get()); |
| } |
| } |
| |
| void CPDFSDK_AppStream::SetAsCheckBox() { |
| CPDF_FormControl* pControl = widget_->GetFormControl(); |
| CFX_Color crBackground = pControl->GetOriginalBackgroundColor(); |
| CFX_Color crBorder = pControl->GetOriginalBorderColor(); |
| float fBorderWidth = static_cast<float>(widget_->GetBorderWidth()); |
| CPWL_Dash dsBorder(3, 0, 0); |
| CFX_Color crLeftTop; |
| CFX_Color crRightBottom; |
| |
| BorderStyle nBorderStyle = widget_->GetBorderStyle(); |
| switch (nBorderStyle) { |
| case BorderStyle::kDash: |
| dsBorder = CPWL_Dash(3, 3, 0); |
| break; |
| case BorderStyle::kBeveled: |
| fBorderWidth *= 2; |
| crLeftTop = CFX_Color(CFX_Color::Type::kGray, 1); |
| crRightBottom = crBackground / 2.0f; |
| break; |
| case BorderStyle::kInset: |
| fBorderWidth *= 2; |
| crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0.5); |
| crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.75); |
| break; |
| default: |
| break; |
| } |
| |
| CFX_FloatRect rcWindow = widget_->GetRotatedRect(); |
| CFX_FloatRect rcClient = rcWindow.GetDeflated(fBorderWidth, fBorderWidth); |
| absl::optional<CFX_Color> color = pControl->GetDefaultAppearance().GetColor(); |
| CFX_Color crText = color.value_or(CFX_Color()); |
| |
| CheckStyle nStyle = CheckStyleFromCaption(pControl->GetNormalCaption()) |
| .value_or(CheckStyle::kCheck); |
| ByteString csAP_N_ON = |
| GetRectFillAppStream(rcWindow, crBackground) + |
| GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop, |
| crRightBottom, nBorderStyle, dsBorder); |
| |
| ByteString csAP_N_OFF = csAP_N_ON; |
| |
| switch (nBorderStyle) { |
| case BorderStyle::kBeveled: { |
| CFX_Color crTemp = crLeftTop; |
| crLeftTop = crRightBottom; |
| crRightBottom = crTemp; |
| break; |
| } |
| case BorderStyle::kInset: { |
| crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0); |
| crRightBottom = CFX_Color(CFX_Color::Type::kGray, 1); |
| break; |
| } |
| default: |
| break; |
| } |
| |
| ByteString csAP_D_ON = |
| GetRectFillAppStream(rcWindow, crBackground - 0.25f) + |
| GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop, |
| crRightBottom, nBorderStyle, dsBorder); |
| |
| ByteString csAP_D_OFF = csAP_D_ON; |
| |
| csAP_N_ON += GetCheckBoxAppStream(rcClient, nStyle, crText); |
| csAP_D_ON += GetCheckBoxAppStream(rcClient, nStyle, crText); |
| |
| Write("N", csAP_N_ON, pControl->GetCheckedAPState()); |
| Write("N", csAP_N_OFF, "Off"); |
| |
| Write("D", csAP_D_ON, pControl->GetCheckedAPState()); |
| Write("D", csAP_D_OFF, "Off"); |
| |
| ByteString csAS = widget_->GetAppState(); |
| if (csAS.IsEmpty()) |
| widget_->SetAppStateOff(); |
| } |
| |
| void CPDFSDK_AppStream::SetAsRadioButton() { |
| CPDF_FormControl* pControl = widget_->GetFormControl(); |
| CFX_Color crBackground = pControl->GetOriginalBackgroundColor(); |
| CFX_Color crBorder = pControl->GetOriginalBorderColor(); |
| float fBorderWidth = static_cast<float>(widget_->GetBorderWidth()); |
| CPWL_Dash dsBorder(3, 0, 0); |
| CFX_Color crLeftTop; |
| CFX_Color crRightBottom; |
| |
| BorderStyle nBorderStyle = widget_->GetBorderStyle(); |
| switch (nBorderStyle) { |
| case BorderStyle::kDash: |
| dsBorder = CPWL_Dash(3, 3, 0); |
| break; |
| case BorderStyle::kBeveled: |
| fBorderWidth *= 2; |
| crLeftTop = CFX_Color(CFX_Color::Type::kGray, 1); |
| crRightBottom = crBackground / 2.0f; |
| break; |
| case BorderStyle::kInset: |
| fBorderWidth *= 2; |
| crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0.5); |
| crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.75); |
| break; |
| default: |
| break; |
| } |
| |
| CFX_FloatRect rcWindow = widget_->GetRotatedRect(); |
| CFX_FloatRect rcClient = rcWindow.GetDeflated(fBorderWidth, fBorderWidth); |
| absl::optional<CFX_Color> color = pControl->GetDefaultAppearance().GetColor(); |
| CFX_Color crText = color.value_or(CFX_Color()); |
| CheckStyle nStyle = CheckStyleFromCaption(pControl->GetNormalCaption()) |
| .value_or(CheckStyle::kCircle); |
| |
| ByteString csAP_N_ON; |
| CFX_FloatRect rcCenter = rcWindow.GetCenterSquare().GetDeflated(1.0f, 1.0f); |
| if (nStyle == CheckStyle::kCircle) { |
| if (nBorderStyle == BorderStyle::kBeveled) { |
| crLeftTop = CFX_Color(CFX_Color::Type::kGray, 1); |
| crRightBottom = crBackground - 0.25f; |
| } else if (nBorderStyle == BorderStyle::kInset) { |
| crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0.5f); |
| crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.75f); |
| } |
| |
| csAP_N_ON = |
| GetCircleFillAppStream(rcCenter, crBackground) + |
| GetCircleBorderAppStream(rcCenter, fBorderWidth, crBorder, crLeftTop, |
| crRightBottom, nBorderStyle, dsBorder); |
| } else { |
| csAP_N_ON = |
| GetRectFillAppStream(rcWindow, crBackground) + |
| GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop, |
| crRightBottom, nBorderStyle, dsBorder); |
| } |
| |
| ByteString csAP_N_OFF = csAP_N_ON; |
| |
| switch (nBorderStyle) { |
| case BorderStyle::kBeveled: { |
| CFX_Color crTemp = crLeftTop; |
| crLeftTop = crRightBottom; |
| crRightBottom = crTemp; |
| break; |
| } |
| case BorderStyle::kInset: { |
| crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0); |
| crRightBottom = CFX_Color(CFX_Color::Type::kGray, 1); |
| break; |
| } |
| default: |
| break; |
| } |
| |
| ByteString csAP_D_ON; |
| |
| if (nStyle == CheckStyle::kCircle) { |
| CFX_Color crBK = crBackground - 0.25f; |
| if (nBorderStyle == BorderStyle::kBeveled) { |
| crLeftTop = crBackground - 0.25f; |
| crRightBottom = CFX_Color(CFX_Color::Type::kGray, 1); |
| crBK = crBackground; |
| } else if (nBorderStyle == BorderStyle::kInset) { |
| crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0); |
| crRightBottom = CFX_Color(CFX_Color::Type::kGray, 1); |
| } |
| |
| csAP_D_ON = |
| GetCircleFillAppStream(rcCenter, crBK) + |
| GetCircleBorderAppStream(rcCenter, fBorderWidth, crBorder, crLeftTop, |
| crRightBottom, nBorderStyle, dsBorder); |
| } else { |
| csAP_D_ON = |
| GetRectFillAppStream(rcWindow, crBackground - 0.25f) + |
| GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop, |
| crRightBottom, nBorderStyle, dsBorder); |
| } |
| |
| ByteString csAP_D_OFF = csAP_D_ON; |
| |
| ByteString app_stream = GetRadioButtonAppStream(rcClient, nStyle, crText); |
| csAP_N_ON += app_stream; |
| csAP_D_ON += app_stream; |
| |
| Write("N", csAP_N_ON, pControl->GetCheckedAPState()); |
| Write("N", csAP_N_OFF, "Off"); |
| |
| Write("D", csAP_D_ON, pControl->GetCheckedAPState()); |
| Write("D", csAP_D_OFF, "Off"); |
| |
| ByteString csAS = widget_->GetAppState(); |
| if (csAS.IsEmpty()) |
| widget_->SetAppStateOff(); |
| } |
| |
| void CPDFSDK_AppStream::SetAsComboBox(absl::optional<WideString> sValue) { |
| CPDF_FormControl* pControl = widget_->GetFormControl(); |
| CPDF_FormField* pField = pControl->GetField(); |
| fxcrt::ostringstream sBody; |
| |
| CFX_FloatRect rcClient = widget_->GetClientRect(); |
| CFX_FloatRect rcButton = rcClient; |
| rcButton.left = rcButton.right - 13; |
| rcButton.Normalize(); |
| |
| // Font map must outlive |pEdit|. |
| CPDF_BAFontMap font_map(widget_->GetPDFPage()->GetDocument(), |
| widget_->GetPDFAnnot()->GetMutableAnnotDict(), "N"); |
| |
| auto pEdit = std::make_unique<CPWL_EditImpl>(); |
| pEdit->EnableRefresh(false); |
| pEdit->SetFontMap(&font_map); |
| |
| CFX_FloatRect rcEdit = rcClient; |
| rcEdit.right = rcButton.left; |
| rcEdit.Normalize(); |
| |
| pEdit->SetPlateRect(rcEdit); |
| pEdit->SetAlignmentV(1); |
| |
| float fFontSize = widget_->GetFontSize(); |
| if (FXSYS_IsFloatZero(fFontSize)) |
| pEdit->SetAutoFontSize(true); |
| else |
| pEdit->SetFontSize(fFontSize); |
| |
| pEdit->Initialize(); |
| if (sValue.has_value()) { |
| pEdit->SetText(sValue.value()); |
| } else { |
| int32_t nCurSel = pField->GetSelectedIndex(0); |
| if (nCurSel < 0) { |
| pEdit->SetText(pField->GetValue()); |
| } else { |
| pEdit->SetText(pField->GetOptionLabel(nCurSel)); |
| } |
| } |
| pEdit->Paint(); |
| |
| CFX_FloatRect rcContent = pEdit->GetContentRect(); |
| ByteString sEdit = GetEditAppStream(pEdit.get(), CFX_PointF(), true, 0); |
| if (sEdit.GetLength() > 0) { |
| sBody << "/Tx "; |
| AutoClosedCommand bmc(&sBody, kMarkedSequenceBeginOperator, |
| kMarkedSequenceEndOperator); |
| AutoClosedQCommand q(&sBody); |
| |
| if (rcContent.Width() > rcEdit.Width() || |
| rcContent.Height() > rcEdit.Height()) { |
| WriteAppendRect(sBody, rcEdit); |
| sBody << kSetNonZeroWindingClipOperator << "\n" |
| << kEndPathNoFillOrStrokeOperator << "\n"; |
| } |
| |
| CFX_Color crText = widget_->GetTextPWLColor(); |
| AutoClosedCommand bt(&sBody, kTextBeginOperator, kTextEndOperator); |
| sBody << GetFillColorAppStream(crText) << sEdit; |
| } |
| |
| sBody << GetDropButtonAppStream(rcButton); |
| Write("N", |
| GetBackgroundAppStream() + GetBorderAppStream() + ByteString(sBody), |
| ByteString()); |
| } |
| |
| void CPDFSDK_AppStream::SetAsListBox() { |
| CPDF_FormControl* pControl = widget_->GetFormControl(); |
| CPDF_FormField* pField = pControl->GetField(); |
| CFX_FloatRect rcClient = widget_->GetClientRect(); |
| fxcrt::ostringstream sBody; |
| |
| // Font map must outlive |pEdit|. |
| CPDF_BAFontMap font_map(widget_->GetPDFPage()->GetDocument(), |
| widget_->GetPDFAnnot()->GetMutableAnnotDict(), "N"); |
| |
| auto pEdit = std::make_unique<CPWL_EditImpl>(); |
| pEdit->EnableRefresh(false); |
| pEdit->SetFontMap(&font_map); |
| pEdit->SetPlateRect(CFX_FloatRect(rcClient.left, 0.0f, rcClient.right, 0.0f)); |
| |
| float fFontSize = widget_->GetFontSize(); |
| pEdit->SetFontSize(FXSYS_IsFloatZero(fFontSize) ? 12.0f : fFontSize); |
| pEdit->Initialize(); |
| |
| fxcrt::ostringstream sList; |
| float fy = rcClient.top; |
| |
| int32_t nTop = pField->GetTopVisibleIndex(); |
| int32_t nCount = pField->CountOptions(); |
| int32_t nSelCount = pField->CountSelectedItems(); |
| |
| for (int32_t i = nTop; i < nCount; ++i) { |
| bool bSelected = false; |
| for (int32_t j = 0; j < nSelCount; ++j) { |
| if (pField->GetSelectedIndex(j) == i) { |
| bSelected = true; |
| break; |
| } |
| } |
| |
| pEdit->SetText(pField->GetOptionLabel(i)); |
| pEdit->Paint(); |
| |
| CFX_FloatRect rcContent = pEdit->GetContentRect(); |
| float fItemHeight = rcContent.Height(); |
| |
| if (bSelected) { |
| CFX_FloatRect rcItem = |
| CFX_FloatRect(rcClient.left, fy - fItemHeight, rcClient.right, fy); |
| { |
| AutoClosedQCommand q(&sList); |
| sList << GetFillColorAppStream(CFX_Color( |
| CFX_Color::Type::kRGB, 0, 51.0f / 255.0f, 113.0f / 255.0f)); |
| WriteAppendRect(sList, rcItem); |
| sList << kFillOperator << "\n"; |
| } |
| |
| AutoClosedCommand bt(&sList, kTextBeginOperator, kTextEndOperator); |
| sList << GetFillColorAppStream(CFX_Color(CFX_Color::Type::kGray, 1)) |
| << GetEditAppStream(pEdit.get(), CFX_PointF(0.0f, fy), true, 0); |
| } else { |
| CFX_Color crText = widget_->GetTextPWLColor(); |
| |
| AutoClosedCommand bt(&sList, kTextBeginOperator, kTextEndOperator); |
| sList << GetFillColorAppStream(crText) |
| << GetEditAppStream(pEdit.get(), CFX_PointF(0.0f, fy), true, 0); |
| } |
| |
| fy -= fItemHeight; |
| } |
| |
| if (sList.tellp() > 0) { |
| sBody << "/Tx "; |
| AutoClosedCommand bmc(&sBody, kMarkedSequenceBeginOperator, |
| kMarkedSequenceEndOperator); |
| AutoClosedQCommand q(&sBody); |
| |
| WriteAppendRect(sBody, rcClient); |
| sBody << kSetNonZeroWindingClipOperator << "\n" |
| << kEndPathNoFillOrStrokeOperator << "\n" |
| << sList.str(); |
| } |
| Write("N", |
| GetBackgroundAppStream() + GetBorderAppStream() + ByteString(sBody), |
| ByteString()); |
| } |
| |
| void CPDFSDK_AppStream::SetAsTextField(absl::optional<WideString> sValue) { |
| CPDF_FormControl* pControl = widget_->GetFormControl(); |
| CPDF_FormField* pField = pControl->GetField(); |
| fxcrt::ostringstream sBody; |
| fxcrt::ostringstream sLines; |
| |
| // Font map must outlive |pEdit|. |
| CPDF_BAFontMap font_map(widget_->GetPDFPage()->GetDocument(), |
| widget_->GetPDFAnnot()->GetMutableAnnotDict(), "N"); |
| |
| auto pEdit = std::make_unique<CPWL_EditImpl>(); |
| pEdit->EnableRefresh(false); |
| pEdit->SetFontMap(&font_map); |
| |
| CFX_FloatRect rcClient = widget_->GetClientRect(); |
| pEdit->SetPlateRect(rcClient); |
| pEdit->SetAlignmentH(pControl->GetControlAlignment()); |
| |
| uint32_t dwFieldFlags = pField->GetFieldFlags(); |
| bool bMultiLine = dwFieldFlags & pdfium::form_flags::kTextMultiline; |
| if (bMultiLine) { |
| pEdit->SetMultiLine(true); |
| pEdit->SetAutoReturn(true); |
| } else { |
| pEdit->SetAlignmentV(1); |
| } |
| |
| uint16_t subWord = 0; |
| if (dwFieldFlags & pdfium::form_flags::kTextPassword) { |
| subWord = '*'; |
| pEdit->SetPasswordChar(subWord); |
| } |
| |
| int nMaxLen = pField->GetMaxLen(); |
| bool bCharArray = dwFieldFlags & pdfium::form_flags::kTextComb; |
| float fFontSize = widget_->GetFontSize(); |
| |
| #ifdef PDF_ENABLE_XFA |
| if (!sValue.has_value() && widget_->GetMixXFAWidget()) |
| sValue = widget_->GetValue(); |
| #endif // PDF_ENABLE_XFA |
| |
| if (nMaxLen > 0) { |
| if (bCharArray) { |
| pEdit->SetCharArray(nMaxLen); |
| if (FXSYS_IsFloatZero(fFontSize)) { |
| fFontSize = CPWL_Edit::GetCharArrayAutoFontSize( |
| font_map.GetPDFFont(0).Get(), rcClient, nMaxLen); |
| } |
| } else { |
| if (sValue.has_value()) |
| nMaxLen = pdfium::base::checked_cast<int>(sValue.value().GetLength()); |
| pEdit->SetLimitChar(nMaxLen); |
| } |
| } |
| |
| if (FXSYS_IsFloatZero(fFontSize)) |
| pEdit->SetAutoFontSize(true); |
| else |
| pEdit->SetFontSize(fFontSize); |
| |
| pEdit->Initialize(); |
| pEdit->SetText(sValue.value_or(pField->GetValue())); |
| pEdit->Paint(); |
| |
| CFX_FloatRect rcContent = pEdit->GetContentRect(); |
| ByteString sEdit = |
| GetEditAppStream(pEdit.get(), CFX_PointF(), !bCharArray, subWord); |
| |
| if (sEdit.GetLength() > 0) { |
| sBody << "/Tx "; |
| AutoClosedCommand bmc(&sBody, kMarkedSequenceBeginOperator, |
| kMarkedSequenceEndOperator); |
| AutoClosedQCommand q(&sBody); |
| |
| if (rcContent.Width() > rcClient.Width() || |
| rcContent.Height() > rcClient.Height()) { |
| WriteAppendRect(sBody, rcClient); |
| sBody << kSetNonZeroWindingClipOperator << "\n" |
| << kEndPathNoFillOrStrokeOperator << "\n"; |
| } |
| CFX_Color crText = widget_->GetTextPWLColor(); |
| |
| AutoClosedCommand bt(&sBody, kTextBeginOperator, kTextEndOperator); |
| sBody << GetFillColorAppStream(crText) << sEdit; |
| } |
| |
| if (bCharArray) { |
| switch (widget_->GetBorderStyle()) { |
| case BorderStyle::kSolid: { |
| ByteString sColor = |
| GetStrokeColorAppStream(widget_->GetBorderPWLColor()); |
| if (sColor.GetLength() > 0) { |
| AutoClosedQCommand q(&sLines); |
| sLines << widget_->GetBorderWidth() << " " << kSetLineWidthOperator |
| << "\n" |
| << GetStrokeColorAppStream(widget_->GetBorderPWLColor()) |
| << " 2 " << kSetLineCapStyleOperator << " 0 " |
| << kSetLineJoinStyleOperator << "\n"; |
| |
| const float width = rcClient.right - rcClient.left; |
| for (int32_t i = 1; i < nMaxLen; ++i) { |
| const float left = rcClient.left + (width / nMaxLen) * i; |
| WriteMove(sLines, {left, rcClient.bottom}); |
| WriteLine(sLines, {left, rcClient.top}); |
| sLines << kStrokeOperator << "\n"; |
| } |
| } |
| break; |
| } |
| case BorderStyle::kDash: { |
| ByteString sColor = |
| GetStrokeColorAppStream(widget_->GetBorderPWLColor()); |
| if (sColor.GetLength() > 0) { |
| CPWL_Dash dsBorder = CPWL_Dash(3, 3, 0); |
| AutoClosedQCommand q(&sLines); |
| sLines << widget_->GetBorderWidth() << " " << kSetLineWidthOperator |
| << "\n" |
| << GetStrokeColorAppStream(widget_->GetBorderPWLColor()) << "[" |
| << dsBorder.nDash << " " << dsBorder.nGap << "] " |
| << dsBorder.nPhase << " " << kSetDashOperator << "\n"; |
| |
| const float width = rcClient.right - rcClient.left; |
| for (int32_t i = 1; i < nMaxLen; ++i) { |
| const float left = rcClient.left + (width / nMaxLen) * i; |
| WriteMove(sLines, {left, rcClient.bottom}); |
| WriteLine(sLines, {left, rcClient.top}); |
| sLines << kStrokeOperator << "\n"; |
| } |
| } |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| |
| Write("N", |
| GetBackgroundAppStream() + GetBorderAppStream() + ByteString(sLines) + |
| ByteString(sBody), |
| ByteString()); |
| } |
| |
| void CPDFSDK_AppStream::AddImage(const ByteString& sAPType, |
| CPDF_Stream* pImage) { |
| RetainPtr<CPDF_Stream> pStream = dict_->GetMutableStreamFor(sAPType); |
| RetainPtr<CPDF_Dictionary> pStreamDict = pStream->GetMutableDict(); |
| ByteString sImageAlias = "IMG"; |
| |
| RetainPtr<const CPDF_Dictionary> pImageDict = pImage->GetDict(); |
| if (pImageDict) |
| sImageAlias = pImageDict->GetByteStringFor("Name"); |
| |
| RetainPtr<CPDF_Dictionary> pStreamResList = |
| pStreamDict->GetOrCreateDictFor("Resources"); |
| auto pXObject = pStreamResList->SetNewFor<CPDF_Dictionary>("XObject"); |
| pXObject->SetNewFor<CPDF_Reference>(sImageAlias, |
| widget_->GetPageView()->GetPDFDocument(), |
| pImage->GetObjNum()); |
| } |
| |
| void CPDFSDK_AppStream::Write(const ByteString& sAPType, |
| const ByteString& sContents, |
| const ByteString& sAPState) { |
| RetainPtr<CPDF_Dictionary> pParentDict; |
| ByteString key; |
| if (sAPState.IsEmpty()) { |
| pParentDict = dict_; |
| key = sAPType; |
| } else { |
| pParentDict = dict_->GetOrCreateDictFor(sAPType); |
| key = sAPState; |
| } |
| |
| RetainPtr<CPDF_Dictionary> pOrigStreamDict; |
| |
| // If `pStream` is created by CreateModifiedAPStream(), then it is safe to |
| // edit, as it is not shared. |
| RetainPtr<CPDF_Stream> pStream = pParentDict->GetMutableStreamFor(key); |
| CPDF_Document* doc = widget_->GetPageView()->GetPDFDocument(); |
| if (!doc->IsModifiedAPStream(pStream.Get())) { |
| if (pStream) |
| pOrigStreamDict = pStream->GetMutableDict(); |
| pStream.Reset(doc->CreateModifiedAPStream()); |
| pParentDict->SetNewFor<CPDF_Reference>(key, doc, pStream->GetObjNum()); |
| } |
| |
| RetainPtr<CPDF_Dictionary> pStreamDict = pStream->GetMutableDict(); |
| if (!pStreamDict) { |
| pStreamDict = doc->New<CPDF_Dictionary>(); |
| pStreamDict->SetNewFor<CPDF_Name>("Type", "XObject"); |
| pStreamDict->SetNewFor<CPDF_Name>("Subtype", "Form"); |
| pStreamDict->SetNewFor<CPDF_Number>("FormType", 1); |
| |
| if (pOrigStreamDict) { |
| RetainPtr<const CPDF_Dictionary> pResources = |
| pOrigStreamDict->GetDictFor("Resources"); |
| if (pResources) |
| pStreamDict->SetFor("Resources", pResources->Clone()); |
| } |
| |
| pStream->InitStreamWithEmptyData(pStreamDict); |
| } |
| pStreamDict->SetMatrixFor("Matrix", widget_->GetMatrix()); |
| pStreamDict->SetRectFor("BBox", widget_->GetRotatedRect()); |
| pStream->SetDataAndRemoveFilter(sContents.raw_span()); |
| } |
| |
| void CPDFSDK_AppStream::Remove(ByteStringView sAPType) { |
| dict_->RemoveFor(sAPType); |
| } |
| |
| ByteString CPDFSDK_AppStream::GetBackgroundAppStream() const { |
| CFX_Color crBackground = widget_->GetFillPWLColor(); |
| if (crBackground.nColorType != CFX_Color::Type::kTransparent) |
| return GetRectFillAppStream(widget_->GetRotatedRect(), crBackground); |
| |
| return ByteString(); |
| } |
| |
| ByteString CPDFSDK_AppStream::GetBorderAppStream() const { |
| CFX_FloatRect rcWindow = widget_->GetRotatedRect(); |
| CFX_Color crBorder = widget_->GetBorderPWLColor(); |
| CFX_Color crBackground = widget_->GetFillPWLColor(); |
| CFX_Color crLeftTop; |
| CFX_Color crRightBottom; |
| |
| float fBorderWidth = static_cast<float>(widget_->GetBorderWidth()); |
| CPWL_Dash dsBorder(3, 0, 0); |
| |
| BorderStyle nBorderStyle = widget_->GetBorderStyle(); |
| switch (nBorderStyle) { |
| case BorderStyle::kDash: |
| dsBorder = CPWL_Dash(3, 3, 0); |
| break; |
| case BorderStyle::kBeveled: |
| fBorderWidth *= 2; |
| crLeftTop = CFX_Color(CFX_Color::Type::kGray, 1); |
| crRightBottom = crBackground / 2.0f; |
| break; |
| case BorderStyle::kInset: |
| fBorderWidth *= 2; |
| crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0.5); |
| crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.75); |
| break; |
| default: |
| break; |
| } |
| |
| return GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop, |
| crRightBottom, nBorderStyle, dsBorder); |
| } |