diff options
Diffstat (limited to 'fpdfsdk/pwl/cpwl_appstream.cpp')
-rw-r--r-- | fpdfsdk/pwl/cpwl_appstream.cpp | 1992 |
1 files changed, 1992 insertions, 0 deletions
diff --git a/fpdfsdk/pwl/cpwl_appstream.cpp b/fpdfsdk/pwl/cpwl_appstream.cpp new file mode 100644 index 0000000000..fe401639da --- /dev/null +++ b/fpdfsdk/pwl/cpwl_appstream.cpp @@ -0,0 +1,1992 @@ +// Copyright 2017 PDFium Authors. All rights reserved. +// 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/pwl/cpwl_appstream.h" + +#include <utility> + +#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/fpdfdoc/cpvt_word.h" +#include "fpdfsdk/cpdfsdk_formfillenvironment.h" +#include "fpdfsdk/cpdfsdk_interform.h" +#include "fpdfsdk/cpdfsdk_pageview.h" +#include "fpdfsdk/cpdfsdk_widget.h" +#include "fpdfsdk/formfiller/cba_fontmap.h" +#include "fpdfsdk/pwl/cpwl_edit.h" +#include "fpdfsdk/pwl/cpwl_edit_impl.h" +#include "fpdfsdk/pwl/cpwl_icon.h" +#include "fpdfsdk/pwl/cpwl_wnd.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 kSetCharacterSpacingOperator[] = "Tc"; +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 kSetTextScaleHorizontalOperator[] = "Tz"; +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(std::ostringstream* stream, + CFX_ByteString open, + CFX_ByteString close) + : stream_(stream), close_(close) { + *stream_ << open << "\n"; + } + + virtual ~AutoClosedCommand() { *stream_ << close_ << "\n"; } + + private: + std::ostringstream* stream_; + CFX_ByteString close_; +}; + +class AutoClosedQCommand : public AutoClosedCommand { + public: + explicit AutoClosedQCommand(std::ostringstream* stream) + : AutoClosedCommand(stream, kStateSaveOperator, kStateRestoreOperator) {} + ~AutoClosedQCommand() override {} +}; + +CFX_ByteString GetColorAppStream(const CFX_Color& color, + const bool& bFillOrStroke) { + std::ostringstream sColorStream; + + switch (color.nColorType) { + case COLORTYPE_RGB: + sColorStream << color.fColor1 << " " << color.fColor2 << " " + << color.fColor3 << " " + << (bFillOrStroke ? kSetRGBOperator : kSetRGBStrokedOperator) + << "\n"; + break; + case COLORTYPE_GRAY: + sColorStream << color.fColor1 << " " + << (bFillOrStroke ? kSetGrayOperator + : kSetGrayStrokedOperator) + << "\n"; + break; + case COLORTYPE_CMYK: + sColorStream << color.fColor1 << " " << color.fColor2 << " " + << color.fColor3 << " " << color.fColor4 << " " + << (bFillOrStroke ? kSetCMYKOperator + : kSetCMKYStrokedOperator) + << "\n"; + break; + } + + return CFX_ByteString(sColorStream); +} + +CFX_ByteString GetAP_Check(const CFX_FloatRect& crBBox) { + const float fWidth = crBBox.right - crBBox.left; + const float fHeight = crBBox.top - crBBox.bottom; + + 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 < FX_ArraySize(pts); ++i) { + for (size_t j = 0; j < FX_ArraySize(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; + } + } + + std::ostringstream csAP; + csAP << pts[0][0].x << " " << pts[0][0].y << " " << kMoveToOperator << "\n"; + + for (size_t i = 0; i < FX_ArraySize(pts); ++i) { + size_t nNext = i < FX_ArraySize(pts) - 1 ? i + 1 : 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 - pts[nNext][0].x; + float py2 = pts[i][2].y - pts[nNext][0].y; + + csAP << pts[i][0].x + px1 * FX_BEZIER << " " + << pts[i][0].y + py1 * FX_BEZIER << " " + << pts[nNext][0].x + px2 * FX_BEZIER << " " + << pts[nNext][0].y + py2 * FX_BEZIER << " " << pts[nNext][0].x << " " + << pts[nNext][0].y << " " << kCurveToOperator << "\n"; + } + + return CFX_ByteString(csAP); +} + +CFX_ByteString GetAP_Circle(const CFX_FloatRect& crBBox) { + std::ostringstream csAP; + + float fWidth = crBBox.right - crBBox.left; + float fHeight = crBBox.top - crBBox.bottom; + + 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); + + csAP << pt1.x << " " << pt1.y << " " << kMoveToOperator << "\n"; + + float px = pt2.x - pt1.x; + float py = pt2.y - pt1.y; + + csAP << pt1.x << " " << pt1.y + py * FX_BEZIER << " " + << pt2.x - px * FX_BEZIER << " " << pt2.y << " " << pt2.x << " " << pt2.y + << " " << kCurveToOperator << "\n"; + + px = pt3.x - pt2.x; + py = pt2.y - pt3.y; + + csAP << pt2.x + px * FX_BEZIER << " " << pt2.y << " " << pt3.x << " " + << pt3.y + py * FX_BEZIER << " " << pt3.x << " " << pt3.y << " " + << kCurveToOperator << "\n"; + + px = pt3.x - pt4.x; + py = pt3.y - pt4.y; + + csAP << pt3.x << " " << pt3.y - py * FX_BEZIER << " " + << pt4.x + px * FX_BEZIER << " " << pt4.y << " " << pt4.x << " " << pt4.y + << " " << kCurveToOperator << "\n"; + + px = pt4.x - pt1.x; + py = pt1.y - pt4.y; + + csAP << pt4.x - px * FX_BEZIER << " " << pt4.y << " " << pt1.x << " " + << pt1.y - py * FX_BEZIER << " " << pt1.x << " " << pt1.y << " " + << kCurveToOperator << "\n"; + + return CFX_ByteString(csAP); +} + +CFX_ByteString GetAP_Cross(const CFX_FloatRect& crBBox) { + std::ostringstream csAP; + + csAP << crBBox.left << " " << crBBox.top << " " << kMoveToOperator << "\n"; + csAP << crBBox.right << " " << crBBox.bottom << " " << kLineToOperator + << "\n"; + csAP << crBBox.left << " " << crBBox.bottom << " " << kMoveToOperator << "\n"; + csAP << crBBox.right << " " << crBBox.top << " " << kLineToOperator << "\n"; + + return CFX_ByteString(csAP); +} + +CFX_ByteString GetAP_Diamond(const CFX_FloatRect& crBBox) { + std::ostringstream csAP; + + float fWidth = crBBox.right - crBBox.left; + float fHeight = crBBox.top - crBBox.bottom; + + 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); + + csAP << pt1.x << " " << pt1.y << " " << kMoveToOperator << "\n"; + csAP << pt2.x << " " << pt2.y << " " << kLineToOperator << "\n"; + csAP << pt3.x << " " << pt3.y << " " << kLineToOperator << "\n"; + csAP << pt4.x << " " << pt4.y << " " << kLineToOperator << "\n"; + csAP << pt1.x << " " << pt1.y << " " << kLineToOperator << "\n"; + + return CFX_ByteString(csAP); +} + +CFX_ByteString GetAP_Square(const CFX_FloatRect& crBBox) { + std::ostringstream csAP; + + csAP << crBBox.left << " " << crBBox.top << " " << kMoveToOperator << "\n"; + csAP << crBBox.right << " " << crBBox.top << " " << kLineToOperator << "\n"; + csAP << crBBox.right << " " << crBBox.bottom << " " << kLineToOperator + << "\n"; + csAP << crBBox.left << " " << crBBox.bottom << " " << kLineToOperator << "\n"; + csAP << crBBox.left << " " << crBBox.top << " " << kLineToOperator << "\n"; + + return CFX_ByteString(csAP); +} + +CFX_ByteString GetAP_Star(const CFX_FloatRect& crBBox) { + std::ostringstream csAP; + + float fRadius = (crBBox.top - crBBox.bottom) / (1 + (float)cos(FX_PI / 5.0f)); + CFX_PointF ptCenter = CFX_PointF((crBBox.left + crBBox.right) / 2.0f, + (crBBox.top + crBBox.bottom) / 2.0f); + + float px[5]; + float py[5]; + float fAngel = FX_PI / 10.0f; + for (int32_t i = 0; i < 5; i++) { + px[i] = ptCenter.x + fRadius * (float)cos(fAngel); + py[i] = ptCenter.y + fRadius * (float)sin(fAngel); + fAngel += FX_PI * 2 / 5.0f; + } + + csAP << px[0] << " " << py[0] << " " << kMoveToOperator << "\n"; + + int32_t nNext = 0; + for (int32_t j = 0; j < 5; j++) { + nNext += 2; + if (nNext >= 5) + nNext -= 5; + csAP << px[nNext] << " " << py[nNext] << " " << kLineToOperator << "\n"; + } + + return CFX_ByteString(csAP); +} + +CFX_ByteString GetAP_HalfCircle(const CFX_FloatRect& crBBox, float fRotate) { + std::ostringstream csAP; + + float fWidth = crBBox.right - crBBox.left; + float fHeight = crBBox.top - crBBox.bottom; + + CFX_PointF pt1(-fWidth / 2, 0); + CFX_PointF pt2(0, fHeight / 2); + CFX_PointF pt3(fWidth / 2, 0); + + float px; + float py; + + csAP << cos(fRotate) << " " << sin(fRotate) << " " << -sin(fRotate) << " " + << cos(fRotate) << " " << crBBox.left + fWidth / 2 << " " + << crBBox.bottom + fHeight / 2 << " " << kConcatMatrixOperator << "\n"; + + csAP << pt1.x << " " << pt1.y << " " << kMoveToOperator << "\n"; + + px = pt2.x - pt1.x; + py = pt2.y - pt1.y; + + csAP << pt1.x << " " << pt1.y + py * FX_BEZIER << " " + << pt2.x - px * FX_BEZIER << " " << pt2.y << " " << pt2.x << " " << pt2.y + << " " << kCurveToOperator << "\n"; + + px = pt3.x - pt2.x; + py = pt2.y - pt3.y; + + csAP << pt2.x + px * FX_BEZIER << " " << pt2.y << " " << pt3.x << " " + << pt3.y + py * FX_BEZIER << " " << pt3.x << " " << pt3.y << " " + << kCurveToOperator << "\n"; + + return CFX_ByteString(csAP); +} + +CFX_ByteString GetAppStream_Check(const CFX_FloatRect& rcBBox, + const CFX_Color& crText) { + std::ostringstream sAP; + { + AutoClosedQCommand q(&sAP); + sAP << GetColorAppStream(crText, true) << GetAP_Check(rcBBox) + << kFillOperator << "\n"; + } + return CFX_ByteString(sAP); +} + +CFX_ByteString GetAppStream_Circle(const CFX_FloatRect& rcBBox, + const CFX_Color& crText) { + std::ostringstream sAP; + { + AutoClosedQCommand q(&sAP); + sAP << GetColorAppStream(crText, true) << GetAP_Circle(rcBBox) + << kFillOperator << "\n"; + } + return CFX_ByteString(sAP); +} + +CFX_ByteString GetAppStream_Cross(const CFX_FloatRect& rcBBox, + const CFX_Color& crText) { + std::ostringstream sAP; + { + AutoClosedQCommand q(&sAP); + sAP << GetColorAppStream(crText, false) << GetAP_Cross(rcBBox) + << kStrokeOperator << "\n"; + } + return CFX_ByteString(sAP); +} + +CFX_ByteString GetAppStream_Diamond(const CFX_FloatRect& rcBBox, + const CFX_Color& crText) { + std::ostringstream sAP; + { + AutoClosedQCommand q(&sAP); + sAP << "1 " << kSetLineWidthOperator << "\n" + << GetColorAppStream(crText, true) << GetAP_Diamond(rcBBox) + << kFillOperator << "\n"; + } + return CFX_ByteString(sAP); +} + +CFX_ByteString GetAppStream_Square(const CFX_FloatRect& rcBBox, + const CFX_Color& crText) { + std::ostringstream sAP; + { + AutoClosedQCommand q(&sAP); + sAP << GetColorAppStream(crText, true) << GetAP_Square(rcBBox) + << kFillOperator << "\n"; + } + return CFX_ByteString(sAP); +} + +CFX_ByteString GetAppStream_Star(const CFX_FloatRect& rcBBox, + const CFX_Color& crText) { + std::ostringstream sAP; + { + AutoClosedQCommand q(&sAP); + sAP << GetColorAppStream(crText, true) << GetAP_Star(rcBBox) + << kFillOperator << "\n"; + } + return CFX_ByteString(sAP); +} + +CFX_ByteString GetCircleFillAppStream(const CFX_FloatRect& rect, + const CFX_Color& color) { + std::ostringstream sAppStream; + CFX_ByteString sColor = GetColorAppStream(color, true); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q(&sAppStream); + sAppStream << sColor << GetAP_Circle(rect) << kFillOperator << "\n"; + } + return CFX_ByteString(sAppStream); +} + +CFX_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) { + std::ostringstream sAppStream; + CFX_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::SOLID: + case BorderStyle::UNDERLINE: { + sColor = GetColorAppStream(color, false); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q2(&sAppStream); + sAppStream << fWidth << " " << kSetLineWidthOperator << "\n" + << sColor << GetAP_Circle(rect_by_2) << " " + << kStrokeOperator << "\n"; + } + } break; + case BorderStyle::DASH: { + sColor = GetColorAppStream(color, false); + 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::BEVELED: { + sColor = GetColorAppStream(color, false); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q2(&sAppStream); + sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n" + << sColor << GetAP_Circle(rect) << " " << kStrokeOperator + << "\n"; + } + + sColor = GetColorAppStream(crLeftTop, false); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q2(&sAppStream); + sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n" + << sColor << GetAP_HalfCircle(rect_by_75, FX_PI / 4.0f) + << " " << kStrokeOperator << "\n"; + } + + sColor = GetColorAppStream(crRightBottom, false); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q2(&sAppStream); + sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n" + << sColor << GetAP_HalfCircle(rect_by_75, FX_PI * 5 / 4.0f) + << " " << kStrokeOperator << "\n"; + } + } break; + case BorderStyle::INSET: { + sColor = GetColorAppStream(color, false); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q2(&sAppStream); + sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n" + << sColor << GetAP_Circle(rect) << " " << kStrokeOperator + << "\n"; + } + + sColor = GetColorAppStream(crLeftTop, false); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q2(&sAppStream); + sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n" + << sColor << GetAP_HalfCircle(rect_by_75, FX_PI / 4.0f) + << " " << kStrokeOperator << "\n"; + } + + sColor = GetColorAppStream(crRightBottom, false); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q2(&sAppStream); + sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n" + << sColor << GetAP_HalfCircle(rect_by_75, FX_PI * 5 / 4.0f) + << " " << kStrokeOperator << "\n"; + } + } break; + } + } + return CFX_ByteString(sAppStream); +} + +CFX_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.Scale(2.0f / 3.0f); + return GetAppStream_Circle(rcCenter, crText); + case CheckStyle::kCross: + return GetAppStream_Cross(rcCenter, crText); + case CheckStyle::kDiamond: + rcCenter.Scale(2.0f / 3.0f); + return GetAppStream_Diamond(rcCenter, crText); + case CheckStyle::kSquare: + rcCenter.Scale(2.0f / 3.0f); + return GetAppStream_Square(rcCenter, crText); + case CheckStyle::kStar: + rcCenter.Scale(2.0f / 3.0f); + return GetAppStream_Star(rcCenter, crText); + } +} + +CFX_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.Scale(1.0f / 2.0f); + return GetAppStream_Circle(rcCenter, crText); + case CheckStyle::kCross: + return GetAppStream_Cross(rcCenter, crText); + case CheckStyle::kDiamond: + rcCenter.Scale(2.0f / 3.0f); + return GetAppStream_Diamond(rcCenter, crText); + case CheckStyle::kSquare: + rcCenter.Scale(2.0f / 3.0f); + return GetAppStream_Square(rcCenter, crText); + case CheckStyle::kStar: + rcCenter.Scale(2.0f / 3.0f); + return GetAppStream_Star(rcCenter, crText); + } +} + +CFX_ByteString GetFontSetString(IPVT_FontMap* pFontMap, + int32_t nFontIndex, + float fFontSize) { + if (!pFontMap) + return CFX_ByteString(); + + CFX_ByteString sFontAlias = pFontMap->GetPDFFontAlias(nFontIndex); + if (sFontAlias.GetLength() <= 0 || fFontSize <= 0) + return CFX_ByteString(); + + std::ostringstream sRet; + sRet << "/" << sFontAlias << " " << fFontSize << " " + << kSetTextFontAndSizeOperator << "\n"; + return CFX_ByteString(sRet); +} + +CFX_ByteString GetWordRenderString(const CFX_ByteString& strWords) { + if (strWords.GetLength() > 0) { + return PDF_EncodeString(strWords, false) + " " + kShowTextOperator + "\n"; + } + return CFX_ByteString(); +} + +CFX_ByteString GetEditAppStream(CPWL_EditImpl* pEdit, + const CFX_PointF& ptOffset, + bool bContinuous, + uint16_t SubWord) { + CPWL_EditImpl_Iterator* pIterator = pEdit->GetIterator(); + pIterator->SetAt(0); + + std::ostringstream sEditStream; + std::ostringstream sWords; + int32_t nCurFontIndex = -1; + CFX_PointF ptOld; + CFX_PointF ptNew; + CPVT_WordPlace oldplace; + + while (pIterator->NextWord()) { + CPVT_WordPlace place = pIterator->GetAt(); + if (bContinuous) { + if (place.LineCmp(oldplace) != 0) { + if (sWords.tellp() > 0) { + sEditStream << GetWordRenderString(CFX_ByteString(sWords)); + sWords.str(""); + } + + 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) { + 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.tellp() > 0) { + sEditStream << GetWordRenderString(CFX_ByteString(sWords)); + sWords.str(""); + } + 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) { + 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)); + } + } + } + + if (sWords.tellp() > 0) { + sEditStream << GetWordRenderString(CFX_ByteString(sWords)); + sWords.str(""); + } + + std::ostringstream sAppStream; + if (sEditStream.tellp() > 0) { + int32_t nHorzScale = pEdit->GetHorzScale(); + if (nHorzScale != 100) { + sAppStream << nHorzScale << " " << kSetTextScaleHorizontalOperator + << "\n"; + } + + float fCharSpace = pEdit->GetCharSpace(); + if (!IsFloatZero(fCharSpace)) { + sAppStream << fCharSpace << " " << kSetCharacterSpacingOperator << "\n"; + } + + sAppStream << sEditStream.str(); + } + + return CFX_ByteString(sAppStream); +} + +CFX_ByteString GenerateIconAppStream(CPDF_IconFit& fit, + CPDF_Stream* pIconStream, + const CFX_FloatRect& rcIcon) { + if (rcIcon.IsEmpty() || !pIconStream) + return CFX_ByteString(); + + CPWL_Icon icon; + PWL_CREATEPARAM cp; + cp.dwFlags = PWS_VISIBLE; + icon.Create(cp); + icon.SetIconFit(&fit); + icon.SetPDFStream(pIconStream); + icon.Move(rcIcon, false, false); + + CFX_ByteString sAlias = icon.GetImageAlias(); + if (sAlias.GetLength() <= 0) + return CFX_ByteString(); + + CFX_FloatRect rcPlate = icon.GetClientRect(); + CFX_Matrix mt = icon.GetImageMatrix().GetInverse(); + + float fHScale; + float fVScale; + std::tie(fHScale, fVScale) = icon.GetScale(); + + float fx; + float fy; + std::tie(fx, fy) = icon.GetImageOffset(); + + std::ostringstream str; + { + AutoClosedQCommand q(&str); + str << rcPlate.left << " " << rcPlate.bottom << " " + << rcPlate.right - rcPlate.left << " " << rcPlate.top - rcPlate.bottom + << " " << kAppendRectOperator << " " << kSetNonZeroWindingClipOperator + << " " << kEndPathNoFillOrStrokeOperator << "\n"; + + str << fHScale << " 0 0 " << fVScale << " " << rcPlate.left + fx << " " + << rcPlate.bottom + fy << " " << kConcatMatrixOperator << "\n"; + str << mt.a << " " << mt.b << " " << mt.c << " " << mt.d << " " << mt.e + << " " << mt.f << " " << kConcatMatrixOperator << "\n"; + + str << "0 " << kSetGrayOperator << " 0 " << kSetGrayStrokedOperator << " 1 " + << kSetLineWidthOperator << " /" << sAlias << " " + << kInvokeNamedXObjectOperator << "\n"; + } + icon.Destroy(); + + return CFX_ByteString(str); +} + +CFX_ByteString GetPushButtonAppStream(const CFX_FloatRect& rcBBox, + IPVT_FontMap* pFontMap, + CPDF_Stream* pIconStream, + CPDF_IconFit& IconFit, + const CFX_WideString& sLabel, + const CFX_Color& crText, + float fFontSize, + ButtonStyle nLayOut) { + const float fAutoFontScale = 1.0f / 3.0f; + + auto pEdit = pdfium::MakeUnique<CPWL_EditImpl>(); + pEdit->SetFontMap(pFontMap); + pEdit->SetAlignmentH(1, true); + pEdit->SetAlignmentV(1, true); + pEdit->SetMultiLine(false, true); + pEdit->SetAutoReturn(false, true); + if (IsFloatZero(fFontSize)) + pEdit->SetAutoFontSize(true, true); + else + pEdit->SetFontSize(fFontSize); + + pEdit->Initialize(); + pEdit->SetText(sLabel); + + 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 (IsFloatZero(fFontSize)) { + fHeight = rcBBox.top - rcBBox.bottom; + 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 (IsFloatZero(fFontSize)) { + fHeight = rcBBox.top - rcBBox.bottom; + 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 (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 (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; + } + + std::ostringstream sTemp; + sTemp << GenerateIconAppStream(IconFit, pIconStream, rcIcon); + + if (!rcLabel.IsEmpty()) { + pEdit->SetPlateRect(rcLabel); + CFX_ByteString sEdit = + GetEditAppStream(pEdit.get(), CFX_PointF(0.0f, 0.0f), true, 0); + if (sEdit.GetLength() > 0) { + AutoClosedCommand bt(&sTemp, kTextBeginOperator, kTextEndOperator); + sTemp << GetColorAppStream(crText, true) << sEdit; + } + } + + if (sTemp.tellp() <= 0) + return CFX_ByteString(); + + std::ostringstream sAppStream; + { + AutoClosedQCommand q(&sAppStream); + sAppStream << rcBBox.left << " " << rcBBox.bottom << " " + << rcBBox.right - rcBBox.left << " " + << rcBBox.top - rcBBox.bottom << " " << kAppendRectOperator + << " " << kSetNonZeroWindingClipOperator << " " + << kEndPathNoFillOrStrokeOperator << "\n"; + sAppStream << sTemp.str().c_str(); + } + return CFX_ByteString(sAppStream); +} + +CFX_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) { + std::ostringstream sAppStream; + CFX_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::SOLID: + sColor = GetColorAppStream(color, true); + if (sColor.GetLength() > 0) { + sAppStream << sColor; + sAppStream << fLeft << " " << fBottom << " " << fRight - fLeft << " " + << fTop - fBottom << " " << kAppendRectOperator << "\n"; + sAppStream << fLeft + fWidth << " " << fBottom + fWidth << " " + << fRight - fLeft - fWidth * 2 << " " + << fTop - fBottom - fWidth * 2 << " " + << kAppendRectOperator << "\n"; + sAppStream << kFillEvenOddOperator << "\n"; + } + break; + case BorderStyle::DASH: + sColor = GetColorAppStream(color, false); + if (sColor.GetLength() > 0) { + sAppStream << sColor; + sAppStream << fWidth << " " << kSetLineWidthOperator << " [" + << dash.nDash << " " << dash.nGap << "] " << dash.nPhase + << " " << kSetDashOperator << "\n"; + sAppStream << fLeft + fWidth / 2 << " " << fBottom + fWidth / 2 << " " + << kMoveToOperator << "\n"; + sAppStream << fLeft + fWidth / 2 << " " << fTop - fWidth / 2 << " " + << kLineToOperator << "\n"; + sAppStream << fRight - fWidth / 2 << " " << fTop - fWidth / 2 << " " + << kLineToOperator << "\n"; + sAppStream << fRight - fWidth / 2 << " " << fBottom + fWidth / 2 + << " " << kLineToOperator << "\n"; + sAppStream << fLeft + fWidth / 2 << " " << fBottom + fWidth / 2 << " " + << kLineToOperator << " " << kStrokeOperator << "\n"; + } + break; + case BorderStyle::BEVELED: + case BorderStyle::INSET: + sColor = GetColorAppStream(crLeftTop, true); + if (sColor.GetLength() > 0) { + sAppStream << sColor; + sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth << " " + << kMoveToOperator << "\n"; + sAppStream << fLeft + fHalfWidth << " " << fTop - fHalfWidth << " " + << kLineToOperator << "\n"; + sAppStream << fRight - fHalfWidth << " " << fTop - fHalfWidth << " " + << kLineToOperator << "\n"; + sAppStream << fRight - fHalfWidth * 2 << " " << fTop - fHalfWidth * 2 + << " " << kLineToOperator << "\n"; + sAppStream << fLeft + fHalfWidth * 2 << " " << fTop - fHalfWidth * 2 + << " " << kLineToOperator << "\n"; + sAppStream << fLeft + fHalfWidth * 2 << " " + << fBottom + fHalfWidth * 2 << " " << kLineToOperator + << " " << kFillOperator << "\n"; + } + + sColor = GetColorAppStream(crRightBottom, true); + if (sColor.GetLength() > 0) { + sAppStream << sColor; + sAppStream << fRight - fHalfWidth << " " << fTop - fHalfWidth << " " + << kMoveToOperator << "\n"; + sAppStream << fRight - fHalfWidth << " " << fBottom + fHalfWidth + << " " << kLineToOperator << "\n"; + sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth << " " + << kLineToOperator << "\n"; + sAppStream << fLeft + fHalfWidth * 2 << " " + << fBottom + fHalfWidth * 2 << " " << kLineToOperator + << "\n"; + sAppStream << fRight - fHalfWidth * 2 << " " + << fBottom + fHalfWidth * 2 << " " << kLineToOperator + << "\n"; + sAppStream << fRight - fHalfWidth * 2 << " " << fTop - fHalfWidth * 2 + << " " << kLineToOperator << " " << kFillOperator << "\n"; + } + + sColor = GetColorAppStream(color, true); + if (sColor.GetLength() > 0) { + sAppStream << sColor; + sAppStream << fLeft << " " << fBottom << " " << fRight - fLeft << " " + << fTop - fBottom << " " << kAppendRectOperator << "\n"; + sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth << " " + << fRight - fLeft - fHalfWidth * 2 << " " + << fTop - fBottom - fHalfWidth * 2 << " " + << kAppendRectOperator << " " << kFillEvenOddOperator + << "\n"; + } + break; + case BorderStyle::UNDERLINE: + sColor = GetColorAppStream(color, false); + if (sColor.GetLength() > 0) { + sAppStream << sColor; + sAppStream << fWidth << " " << kSetLineWidthOperator << "\n"; + sAppStream << fLeft << " " << fBottom + fWidth / 2 << " " + << kMoveToOperator << "\n"; + sAppStream << fRight << " " << fBottom + fWidth / 2 << " " + << kLineToOperator << " " << kStrokeOperator << "\n"; + } + break; + } + } + + return CFX_ByteString(sAppStream); +} + +CFX_ByteString GetDropButtonAppStream(const CFX_FloatRect& rcBBox) { + if (rcBBox.IsEmpty()) + return CFX_ByteString(); + + std::ostringstream sAppStream; + { + AutoClosedQCommand q(&sAppStream); + sAppStream << GetColorAppStream(CFX_Color(COLORTYPE_RGB, 220.0f / 255.0f, + 220.0f / 255.0f, 220.0f / 255.0f), + true) + << rcBBox.left << " " << rcBBox.bottom << " " + << rcBBox.right - rcBBox.left << " " + << rcBBox.top - rcBBox.bottom << " " << kAppendRectOperator + << " " << kFillOperator << "\n"; + } + + { + AutoClosedQCommand q(&sAppStream); + sAppStream << GetBorderAppStreamInternal( + rcBBox, 2, CFX_Color(COLORTYPE_GRAY, 0), CFX_Color(COLORTYPE_GRAY, 1), + CFX_Color(COLORTYPE_GRAY, 0.5), BorderStyle::BEVELED, + CPWL_Dash(3, 0, 0)); + } + + CFX_PointF ptCenter = CFX_PointF((rcBBox.left + rcBBox.right) / 2, + (rcBBox.top + rcBBox.bottom) / 2); + if (IsFloatBigger(rcBBox.right - rcBBox.left, 6) && + IsFloatBigger(rcBBox.top - rcBBox.bottom, 6)) { + AutoClosedQCommand q(&sAppStream); + sAppStream << " 0 " << kSetGrayOperator << "\n" + << ptCenter.x - 3 << " " << ptCenter.y + 1.5f << " " + << kMoveToOperator << "\n" + << ptCenter.x + 3 << " " << ptCenter.y + 1.5f << " " + << kLineToOperator << "\n" + << ptCenter.x << " " << ptCenter.y - 1.5f << " " + << kLineToOperator << "\n" + << ptCenter.x - 3 << " " << ptCenter.y + 1.5f << " " + << kLineToOperator << " " << kFillOperator << "\n"; + } + + return CFX_ByteString(sAppStream); +} + +CFX_ByteString GetRectFillAppStream(const CFX_FloatRect& rect, + const CFX_Color& color) { + std::ostringstream sAppStream; + CFX_ByteString sColor = GetColorAppStream(color, true); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q(&sAppStream); + sAppStream << sColor << rect.left << " " << rect.bottom << " " + << rect.right - rect.left << " " << rect.top - rect.bottom << " " + << kAppendRectOperator << " " << kFillOperator << "\n"; + } + + return CFX_ByteString(sAppStream); +} + +} // namespace + +CPWL_AppStream::CPWL_AppStream(CPDFSDK_Widget* widget, CPDF_Dictionary* dict) + : widget_(widget), dict_(dict) {} + +CPWL_AppStream::~CPWL_AppStream() {} + +void CPWL_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; + CFX_Color crBorder; + int iColorType; + float fc[4]; + pControl->GetOriginalBackgroundColor(iColorType, fc); + if (iColorType > 0) + crBackground = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]); + + pControl->GetOriginalBorderColor(iColorType, fc); + if (iColorType > 0) + crBorder = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]); + + 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::DASH: + dsBorder = CPWL_Dash(3, 3, 0); + break; + case BorderStyle::BEVELED: + fBorderWidth *= 2; + crLeftTop = CFX_Color(COLORTYPE_GRAY, 1); + crRightBottom = crBackground / 2.0f; + break; + case BorderStyle::INSET: + fBorderWidth *= 2; + crLeftTop = CFX_Color(COLORTYPE_GRAY, 0.5); + crRightBottom = CFX_Color(COLORTYPE_GRAY, 0.75); + break; + default: + break; + } + + CFX_FloatRect rcClient = rcWindow.GetDeflated(fBorderWidth, fBorderWidth); + CFX_Color crText(COLORTYPE_GRAY, 0); + CFX_ByteString csNameTag; + CPDF_DefaultAppearance da = pControl->GetDefaultAppearance(); + if (da.HasColor()) { + da.GetColor(iColorType, fc); + crText = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]); + } + float fFontSize = 12.0f; + if (da.HasFont()) + csNameTag = da.GetFont(&fFontSize); + + CFX_WideString csWCaption; + CFX_WideString csNormalCaption; + CFX_WideString csRolloverCaption; + CFX_WideString csDownCaption; + if (pControl->HasMKEntry("CA")) + csNormalCaption = pControl->GetNormalCaption(); + + if (pControl->HasMKEntry("RC")) + csRolloverCaption = pControl->GetRolloverCaption(); + + if (pControl->HasMKEntry("AC")) + csDownCaption = pControl->GetDownCaption(); + + CPDF_Stream* pNormalIcon = nullptr; + CPDF_Stream* pRolloverIcon = nullptr; + CPDF_Stream* pDownIcon = nullptr; + if (pControl->HasMKEntry("I")) + pNormalIcon = pControl->GetNormalIcon(); + + if (pControl->HasMKEntry("RI")) + pRolloverIcon = pControl->GetRolloverIcon(); + + if (pControl->HasMKEntry("IX")) + pDownIcon = pControl->GetDownIcon(); + + if (pNormalIcon) { + if (CPDF_Dictionary* pImageDict = pNormalIcon->GetDict()) { + if (pImageDict->GetStringFor("Name").IsEmpty()) + pImageDict->SetNewFor<CPDF_String>("Name", "ImgA", false); + } + } + + if (pRolloverIcon) { + if (CPDF_Dictionary* pImageDict = pRolloverIcon->GetDict()) { + if (pImageDict->GetStringFor("Name").IsEmpty()) + pImageDict->SetNewFor<CPDF_String>("Name", "ImgB", false); + } + } + + if (pDownIcon) { + if (CPDF_Dictionary* pImageDict = pDownIcon->GetDict()) { + if (pImageDict->GetStringFor("Name").IsEmpty()) + pImageDict->SetNewFor<CPDF_String>("Name", "ImgC", false); + } + } + + CPDF_IconFit iconFit = pControl->GetIconFit(); + + CBA_FontMap font_map( + widget_.Get(), + widget_->GetInterForm()->GetFormFillEnv()->GetSysHandler()); + font_map.SetAPType("N"); + + CFX_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, ""); + if (pNormalIcon) + AddImage("N", pNormalIcon); + + CPDF_FormControl::HighlightingMode eHLM = pControl->GetHighlightingMode(); + if (eHLM == CPDF_FormControl::Push || eHLM == CPDF_FormControl::Toggle) { + if (csRolloverCaption.IsEmpty() && !pRolloverIcon) { + csRolloverCaption = csNormalCaption; + pRolloverIcon = pNormalIcon; + } + + font_map.SetAPType("R"); + + 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, ""); + if (pRolloverIcon) + AddImage("R", pRolloverIcon); + + if (csDownCaption.IsEmpty() && !pDownIcon) { + csDownCaption = csNormalCaption; + pDownIcon = pNormalIcon; + } + + switch (nBorderStyle) { + case BorderStyle::BEVELED: { + CFX_Color crTemp = crLeftTop; + crLeftTop = crRightBottom; + crRightBottom = crTemp; + break; + } + case BorderStyle::INSET: { + crLeftTop = CFX_Color(COLORTYPE_GRAY, 0); + crRightBottom = CFX_Color(COLORTYPE_GRAY, 1); + break; + } + default: + break; + } + + font_map.SetAPType("D"); + + 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, ""); + if (pDownIcon) + AddImage("D", pDownIcon); + } else { + Remove("D"); + Remove("R"); + } +} + +void CPWL_AppStream::SetAsCheckBox() { + CPDF_FormControl* pControl = widget_->GetFormControl(); + CFX_Color crBackground, crBorder, crText; + int iColorType; + float fc[4]; + + pControl->GetOriginalBackgroundColor(iColorType, fc); + if (iColorType > 0) + crBackground = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]); + + pControl->GetOriginalBorderColor(iColorType, fc); + if (iColorType > 0) + crBorder = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]); + + float fBorderWidth = static_cast<float>(widget_->GetBorderWidth()); + CPWL_Dash dsBorder(3, 0, 0); + CFX_Color crLeftTop, crRightBottom; + + BorderStyle nBorderStyle = widget_->GetBorderStyle(); + switch (nBorderStyle) { + case BorderStyle::DASH: + dsBorder = CPWL_Dash(3, 3, 0); + break; + case BorderStyle::BEVELED: + fBorderWidth *= 2; + crLeftTop = CFX_Color(COLORTYPE_GRAY, 1); + crRightBottom = crBackground / 2.0f; + break; + case BorderStyle::INSET: + fBorderWidth *= 2; + crLeftTop = CFX_Color(COLORTYPE_GRAY, 0.5); + crRightBottom = CFX_Color(COLORTYPE_GRAY, 0.75); + break; + default: + break; + } + + CFX_FloatRect rcWindow = widget_->GetRotatedRect(); + CFX_FloatRect rcClient = rcWindow.GetDeflated(fBorderWidth, fBorderWidth); + CPDF_DefaultAppearance da = pControl->GetDefaultAppearance(); + if (da.HasColor()) { + da.GetColor(iColorType, fc); + crText = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]); + } + + CheckStyle nStyle = CheckStyle::kCheck; + CFX_WideString csWCaption = pControl->GetNormalCaption(); + if (csWCaption.GetLength() > 0) { + switch (csWCaption[0]) { + case L'l': + nStyle = CheckStyle::kCircle; + break; + case L'8': + nStyle = CheckStyle::kCross; + break; + case L'u': + nStyle = CheckStyle::kDiamond; + break; + case L'n': + nStyle = CheckStyle::kSquare; + break; + case L'H': + nStyle = CheckStyle::kStar; + break; + case L'4': + default: + nStyle = CheckStyle::kCheck; + } + } + + CFX_ByteString csAP_N_ON = + GetRectFillAppStream(rcWindow, crBackground) + + GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop, + crRightBottom, nBorderStyle, dsBorder); + + CFX_ByteString csAP_N_OFF = csAP_N_ON; + + switch (nBorderStyle) { + case BorderStyle::BEVELED: { + CFX_Color crTemp = crLeftTop; + crLeftTop = crRightBottom; + crRightBottom = crTemp; + break; + } + case BorderStyle::INSET: { + crLeftTop = CFX_Color(COLORTYPE_GRAY, 0); + crRightBottom = CFX_Color(COLORTYPE_GRAY, 1); + break; + } + default: + break; + } + + CFX_ByteString csAP_D_ON = + GetRectFillAppStream(rcWindow, crBackground - 0.25f) + + GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop, + crRightBottom, nBorderStyle, dsBorder); + + CFX_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"); + + CFX_ByteString csAS = widget_->GetAppState(); + if (csAS.IsEmpty()) + widget_->SetAppState("Off"); +} + +void CPWL_AppStream::SetAsRadioButton() { + CPDF_FormControl* pControl = widget_->GetFormControl(); + CFX_Color crBackground; + CFX_Color crBorder; + CFX_Color crText; + int iColorType; + float fc[4]; + + pControl->GetOriginalBackgroundColor(iColorType, fc); + if (iColorType > 0) + crBackground = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]); + + pControl->GetOriginalBorderColor(iColorType, fc); + if (iColorType > 0) + crBorder = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]); + + 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::DASH: + dsBorder = CPWL_Dash(3, 3, 0); + break; + case BorderStyle::BEVELED: + fBorderWidth *= 2; + crLeftTop = CFX_Color(COLORTYPE_GRAY, 1); + crRightBottom = crBackground / 2.0f; + break; + case BorderStyle::INSET: + fBorderWidth *= 2; + crLeftTop = CFX_Color(COLORTYPE_GRAY, 0.5); + crRightBottom = CFX_Color(COLORTYPE_GRAY, 0.75); + break; + default: + break; + } + + CFX_FloatRect rcWindow = widget_->GetRotatedRect(); + CFX_FloatRect rcClient = rcWindow.GetDeflated(fBorderWidth, fBorderWidth); + CPDF_DefaultAppearance da = pControl->GetDefaultAppearance(); + if (da.HasColor()) { + da.GetColor(iColorType, fc); + crText = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]); + } + + CheckStyle nStyle = CheckStyle::kCircle; + CFX_WideString csWCaption = pControl->GetNormalCaption(); + if (csWCaption.GetLength() > 0) { + switch (csWCaption[0]) { + case L'8': + nStyle = CheckStyle::kCross; + break; + case L'u': + nStyle = CheckStyle::kDiamond; + break; + case L'n': + nStyle = CheckStyle::kSquare; + break; + case L'H': + nStyle = CheckStyle::kStar; + break; + case L'4': + nStyle = CheckStyle::kCheck; + break; + case L'l': + default: + nStyle = CheckStyle::kCircle; + } + } + + CFX_ByteString csAP_N_ON; + CFX_FloatRect rcCenter = rcWindow.GetCenterSquare().GetDeflated(1.0f, 1.0f); + if (nStyle == CheckStyle::kCircle) { + if (nBorderStyle == BorderStyle::BEVELED) { + crLeftTop = CFX_Color(COLORTYPE_GRAY, 1); + crRightBottom = crBackground - 0.25f; + } else if (nBorderStyle == BorderStyle::INSET) { + crLeftTop = CFX_Color(COLORTYPE_GRAY, 0.5f); + crRightBottom = CFX_Color(COLORTYPE_GRAY, 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); + } + + CFX_ByteString csAP_N_OFF = csAP_N_ON; + + switch (nBorderStyle) { + case BorderStyle::BEVELED: { + CFX_Color crTemp = crLeftTop; + crLeftTop = crRightBottom; + crRightBottom = crTemp; + break; + } + case BorderStyle::INSET: { + crLeftTop = CFX_Color(COLORTYPE_GRAY, 0); + crRightBottom = CFX_Color(COLORTYPE_GRAY, 1); + break; + } + default: + break; + } + + CFX_ByteString csAP_D_ON; + + if (nStyle == CheckStyle::kCircle) { + CFX_Color crBK = crBackground - 0.25f; + if (nBorderStyle == BorderStyle::BEVELED) { + crLeftTop = crBackground - 0.25f; + crRightBottom = CFX_Color(COLORTYPE_GRAY, 1); + crBK = crBackground; + } else if (nBorderStyle == BorderStyle::INSET) { + crLeftTop = CFX_Color(COLORTYPE_GRAY, 0); + crRightBottom = CFX_Color(COLORTYPE_GRAY, 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); + } + + CFX_ByteString csAP_D_OFF = csAP_D_ON; + + csAP_N_ON += GetRadioButtonAppStream(rcClient, nStyle, crText); + csAP_D_ON += GetRadioButtonAppStream(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"); + + CFX_ByteString csAS = widget_->GetAppState(); + if (csAS.IsEmpty()) + widget_->SetAppState("Off"); +} + +void CPWL_AppStream::SetAsComboBox(const CFX_WideString* sValue) { + CPDF_FormControl* pControl = widget_->GetFormControl(); + CPDF_FormField* pField = pControl->GetField(); + std::ostringstream sBody; + + CFX_FloatRect rcClient = widget_->GetClientRect(); + CFX_FloatRect rcButton = rcClient; + rcButton.left = rcButton.right - 13; + rcButton.Normalize(); + + auto pEdit = pdfium::MakeUnique<CPWL_EditImpl>(); + pEdit->EnableRefresh(false); + + CBA_FontMap font_map( + widget_.Get(), + widget_->GetInterForm()->GetFormFillEnv()->GetSysHandler()); + pEdit->SetFontMap(&font_map); + + CFX_FloatRect rcEdit = rcClient; + rcEdit.right = rcButton.left; + rcEdit.Normalize(); + + pEdit->SetPlateRect(rcEdit); + pEdit->SetAlignmentV(1, true); + + float fFontSize = widget_->GetFontSize(); + if (IsFloatZero(fFontSize)) + pEdit->SetAutoFontSize(true, true); + else + pEdit->SetFontSize(fFontSize); + + pEdit->Initialize(); + + if (sValue) { + pEdit->SetText(*sValue); + } else { + int32_t nCurSel = pField->GetSelectedIndex(0); + if (nCurSel < 0) + pEdit->SetText(pField->GetValue()); + else + pEdit->SetText(pField->GetOptionLabel(nCurSel)); + } + + CFX_FloatRect rcContent = pEdit->GetContentRect(); + CFX_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()) { + sBody << rcEdit.left << " " << rcEdit.bottom << " " << rcEdit.Width() + << " " << rcEdit.Height() << " " << kAppendRectOperator << "\n" + << kSetNonZeroWindingClipOperator << "\n" + << kEndPathNoFillOrStrokeOperator << "\n"; + } + + CFX_Color crText = widget_->GetTextPWLColor(); + AutoClosedCommand bt(&sBody, kTextBeginOperator, kTextEndOperator); + sBody << GetColorAppStream(crText, true) << sEdit; + } + + sBody << GetDropButtonAppStream(rcButton); + Write("N", + GetBackgroundAppStream() + GetBorderAppStream() + CFX_ByteString(sBody), + ""); +} + +void CPWL_AppStream::SetAsListBox() { + CPDF_FormControl* pControl = widget_->GetFormControl(); + CPDF_FormField* pField = pControl->GetField(); + CFX_FloatRect rcClient = widget_->GetClientRect(); + std::ostringstream sBody; + + auto pEdit = pdfium::MakeUnique<CPWL_EditImpl>(); + pEdit->EnableRefresh(false); + + CBA_FontMap font_map( + widget_.Get(), + widget_->GetInterForm()->GetFormFillEnv()->GetSysHandler()); + pEdit->SetFontMap(&font_map); + pEdit->SetPlateRect(CFX_FloatRect(rcClient.left, 0.0f, rcClient.right, 0.0f)); + + float fFontSize = widget_->GetFontSize(); + pEdit->SetFontSize(IsFloatZero(fFontSize) ? 12.0f : fFontSize); + pEdit->Initialize(); + + std::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)); + + 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 << GetColorAppStream(CFX_Color(COLORTYPE_RGB, 0, 51.0f / 255.0f, + 113.0f / 255.0f), + true) + << rcItem.left << " " << rcItem.bottom << " " << rcItem.Width() + << " " << rcItem.Height() << " " << kAppendRectOperator << " " + << kFillOperator << "\n"; + } + + AutoClosedCommand bt(&sList, kTextBeginOperator, kTextEndOperator); + sList << GetColorAppStream(CFX_Color(COLORTYPE_GRAY, 1), true) + << GetEditAppStream(pEdit.get(), CFX_PointF(0.0f, fy), true, 0); + } else { + CFX_Color crText = widget_->GetTextPWLColor(); + + AutoClosedCommand bt(&sList, kTextBeginOperator, kTextEndOperator); + sList << GetColorAppStream(crText, true) + << 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); + + sBody << rcClient.left << " " << rcClient.bottom << " " << rcClient.Width() + << " " << rcClient.Height() << " " << kAppendRectOperator << "\n" + << kSetNonZeroWindingClipOperator << "\n" + << kEndPathNoFillOrStrokeOperator << "\n" + << sList.str(); + } + Write("N", + GetBackgroundAppStream() + GetBorderAppStream() + CFX_ByteString(sBody), + ""); +} + +void CPWL_AppStream::SetAsTextField(const CFX_WideString* sValue) { + CPDF_FormControl* pControl = widget_->GetFormControl(); + CPDF_FormField* pField = pControl->GetField(); + std::ostringstream sBody; + std::ostringstream sLines; + + auto pEdit = pdfium::MakeUnique<CPWL_EditImpl>(); + pEdit->EnableRefresh(false); + + CBA_FontMap font_map( + widget_.Get(), + widget_->GetInterForm()->GetFormFillEnv()->GetSysHandler()); + pEdit->SetFontMap(&font_map); + + CFX_FloatRect rcClient = widget_->GetClientRect(); + pEdit->SetPlateRect(rcClient); + pEdit->SetAlignmentH(pControl->GetControlAlignment(), true); + + uint32_t dwFieldFlags = pField->GetFieldFlags(); + bool bMultiLine = (dwFieldFlags >> 12) & 1; + if (bMultiLine) { + pEdit->SetMultiLine(true, true); + pEdit->SetAutoReturn(true, true); + } else { + pEdit->SetAlignmentV(1, true); + } + + uint16_t subWord = 0; + if ((dwFieldFlags >> 13) & 1) { + subWord = '*'; + pEdit->SetPasswordChar(subWord, true); + } + + int nMaxLen = pField->GetMaxLen(); + bool bCharArray = (dwFieldFlags >> 24) & 1; + float fFontSize = widget_->GetFontSize(); + +#ifdef PDF_ENABLE_XFA + CFX_WideString sValueTmp; + if (!sValue && widget_->GetMixXFAWidget()) { + sValueTmp = widget_->GetValue(true); + sValue = &sValueTmp; + } +#endif // PDF_ENABLE_XFA + + if (nMaxLen > 0) { + if (bCharArray) { + pEdit->SetCharArray(nMaxLen); + + if (IsFloatZero(fFontSize)) { + fFontSize = CPWL_Edit::GetCharArrayAutoFontSize(font_map.GetPDFFont(0), + rcClient, nMaxLen); + } + } else { + if (sValue) + nMaxLen = sValue->GetLength(); + pEdit->SetLimitChar(nMaxLen); + } + } + + if (IsFloatZero(fFontSize)) + pEdit->SetAutoFontSize(true, true); + else + pEdit->SetFontSize(fFontSize); + + pEdit->Initialize(); + pEdit->SetText(sValue ? *sValue : pField->GetValue()); + + CFX_FloatRect rcContent = pEdit->GetContentRect(); + CFX_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()) { + sBody << rcClient.left << " " << rcClient.bottom << " " + << rcClient.Width() << " " << rcClient.Height() << " " + << kAppendRectOperator << "\n" + << kSetNonZeroWindingClipOperator << "\n" + << kEndPathNoFillOrStrokeOperator << "\n"; + } + CFX_Color crText = widget_->GetTextPWLColor(); + + AutoClosedCommand bt(&sBody, kTextBeginOperator, kTextEndOperator); + sBody << GetColorAppStream(crText, true) << sEdit; + } + + if (bCharArray) { + switch (widget_->GetBorderStyle()) { + case BorderStyle::SOLID: { + CFX_ByteString sColor = + GetColorAppStream(widget_->GetBorderPWLColor(), false); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q(&sLines); + sLines << widget_->GetBorderWidth() << " " << kSetLineWidthOperator + << "\n" + << GetColorAppStream(widget_->GetBorderPWLColor(), false) + << " 2 " << kSetLineCapStyleOperator << " 0 " + << kSetLineJoinStyleOperator << "\n"; + + for (int32_t i = 1; i < nMaxLen; ++i) { + sLines << rcClient.left + + ((rcClient.right - rcClient.left) / nMaxLen) * i + << " " << rcClient.bottom << " " << kMoveToOperator << "\n" + << rcClient.left + + ((rcClient.right - rcClient.left) / nMaxLen) * i + << " " << rcClient.top << " " << kLineToOperator << " " + << kStrokeOperator << "\n"; + } + } + break; + } + case BorderStyle::DASH: { + CFX_ByteString sColor = + GetColorAppStream(widget_->GetBorderPWLColor(), false); + if (sColor.GetLength() > 0) { + CPWL_Dash dsBorder = CPWL_Dash(3, 3, 0); + AutoClosedQCommand q(&sLines); + sLines << widget_->GetBorderWidth() << " " << kSetLineWidthOperator + << "\n" + << GetColorAppStream(widget_->GetBorderPWLColor(), false) + << "[" << dsBorder.nDash << " " << dsBorder.nGap << "] " + << dsBorder.nPhase << " " << kSetDashOperator << "\n"; + + for (int32_t i = 1; i < nMaxLen; ++i) { + sLines << rcClient.left + + ((rcClient.right - rcClient.left) / nMaxLen) * i + << " " << rcClient.bottom << " " << kMoveToOperator << "\n" + << rcClient.left + + ((rcClient.right - rcClient.left) / nMaxLen) * i + << " " << rcClient.top << " " << kLineToOperator << " " + << kStrokeOperator << "\n"; + } + } + break; + } + default: + break; + } + } + + Write("N", + GetBackgroundAppStream() + GetBorderAppStream() + + CFX_ByteString(sLines) + CFX_ByteString(sBody), + ""); +} + +void CPWL_AppStream::AddImage(const CFX_ByteString& sAPType, + CPDF_Stream* pImage) { + CPDF_Stream* pStream = dict_->GetStreamFor(sAPType); + CPDF_Dictionary* pStreamDict = pStream->GetDict(); + CFX_ByteString sImageAlias = "IMG"; + + if (CPDF_Dictionary* pImageDict = pImage->GetDict()) { + sImageAlias = pImageDict->GetStringFor("Name"); + if (sImageAlias.IsEmpty()) + sImageAlias = "IMG"; + } + + CPDF_Dictionary* pStreamResList = pStreamDict->GetDictFor("Resources"); + if (!pStreamResList) + pStreamResList = pStreamDict->SetNewFor<CPDF_Dictionary>("Resources"); + + CPDF_Dictionary* pXObject = + pStreamResList->SetNewFor<CPDF_Dictionary>("XObject"); + pXObject->SetNewFor<CPDF_Reference>(sImageAlias, + widget_->GetPageView()->GetPDFDocument(), + pImage->GetObjNum()); +} + +void CPWL_AppStream::Write(const CFX_ByteString& sAPType, + const CFX_ByteString& sContents, + const CFX_ByteString& sAPState) { + CPDF_Stream* pStream = nullptr; + CPDF_Dictionary* pParentDict = nullptr; + if (sAPState.IsEmpty()) { + pParentDict = dict_.Get(); + pStream = dict_->GetStreamFor(sAPType); + } else { + CPDF_Dictionary* pAPTypeDict = dict_->GetDictFor(sAPType); + if (!pAPTypeDict) + pAPTypeDict = dict_->SetNewFor<CPDF_Dictionary>(sAPType); + + pParentDict = pAPTypeDict; + pStream = pAPTypeDict->GetStreamFor(sAPState); + } + + if (!pStream) { + CPDF_Document* doc = widget_->GetPageView()->GetPDFDocument(); + pStream = doc->NewIndirect<CPDF_Stream>(); + pParentDict->SetNewFor<CPDF_Reference>(sAPType, doc, pStream->GetObjNum()); + } + + CPDF_Dictionary* pStreamDict = pStream->GetDict(); + if (!pStreamDict) { + auto pNewDict = pdfium::MakeUnique<CPDF_Dictionary>( + widget_->GetPDFAnnot()->GetDocument()->GetByteStringPool()); + pStreamDict = pNewDict.get(); + pStreamDict->SetNewFor<CPDF_Name>("Type", "XObject"); + pStreamDict->SetNewFor<CPDF_Name>("Subtype", "Form"); + pStreamDict->SetNewFor<CPDF_Number>("FormType", 1); + pStream->InitStream(nullptr, 0, std::move(pNewDict)); + } + pStreamDict->SetMatrixFor("Matrix", widget_->GetMatrix()); + pStreamDict->SetRectFor("BBox", widget_->GetRotatedRect()); + pStream->SetData((uint8_t*)(sContents.c_str()), sContents.GetLength()); +} + +void CPWL_AppStream::Remove(const CFX_ByteString& sAPType) { + dict_->RemoveFor(sAPType); +} + +CFX_ByteString CPWL_AppStream::GetBackgroundAppStream() const { + CFX_Color crBackground = widget_->GetFillPWLColor(); + if (crBackground.nColorType != COLORTYPE_TRANSPARENT) + return GetRectFillAppStream(widget_->GetRotatedRect(), crBackground); + + return CFX_ByteString(); +} + +CFX_ByteString CPWL_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::DASH: + dsBorder = CPWL_Dash(3, 3, 0); + break; + case BorderStyle::BEVELED: + fBorderWidth *= 2; + crLeftTop = CFX_Color(COLORTYPE_GRAY, 1); + crRightBottom = crBackground / 2.0f; + break; + case BorderStyle::INSET: + fBorderWidth *= 2; + crLeftTop = CFX_Color(COLORTYPE_GRAY, 0.5); + crRightBottom = CFX_Color(COLORTYPE_GRAY, 0.75); + break; + default: + break; + } + + return GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop, + crRightBottom, nBorderStyle, dsBorder); +} |