// Copyright 2019 Google LLC. #ifndef TextStyle_DEFINED #define TextStyle_DEFINED #include #include "include/core/SkColor.h" #include "include/core/SkFont.h" #include "include/core/SkFontMetrics.h" #include "include/core/SkFontStyle.h" #include "include/core/SkPaint.h" #include "include/core/SkScalar.h" #include "modules/skparagraph/include/DartTypes.h" #include "modules/skparagraph/include/TextShadow.h" // TODO: Make it external so the other platforms (Android) could use it #define DEFAULT_FONT_FAMILY "sans-serif" namespace skia { namespace textlayout { static inline bool nearlyZero(SkScalar x, SkScalar tolerance = SK_ScalarNearlyZero) { if (SkScalarIsFinite(x)) { return SkScalarNearlyZero(x, tolerance); } return false; } static inline bool nearlyEqual(SkScalar x, SkScalar y, SkScalar tolerance = SK_ScalarNearlyZero) { if (SkScalarIsFinite(x) && SkScalarIsFinite(x)) { return SkScalarNearlyEqual(x, y, tolerance); } // Inf == Inf, anything else is false return x == y; } // Multiple decorations can be applied at once. Ex: Underline and overline is // (0x1 | 0x2) enum TextDecoration { kNoDecoration = 0x0, kUnderline = 0x1, kOverline = 0x2, kLineThrough = 0x4, }; constexpr TextDecoration AllTextDecorations[] = { kNoDecoration, kUnderline, kOverline, kLineThrough, }; enum TextDecorationStyle { kSolid, kDouble, kDotted, kDashed, kWavy }; enum TextDecorationMode { kGaps, kThrough }; enum StyleType { kNone, kAllAttributes, kFont, kForeground, kBackground, kShadow, kDecorations, kLetterSpacing, kWordSpacing }; struct Decoration { TextDecoration fType; TextDecorationMode fMode; SkColor fColor; TextDecorationStyle fStyle; SkScalar fThicknessMultiplier; bool operator==(const Decoration& other) const { return this->fType == other.fType && this->fMode == other.fMode && this->fColor == other.fColor && this->fStyle == other.fStyle && this->fThicknessMultiplier == other.fThicknessMultiplier; } }; /// Where to vertically align the placeholder relative to the surrounding text. enum class PlaceholderAlignment { /// Match the baseline of the placeholder with the baseline. kBaseline, /// Align the bottom edge of the placeholder with the baseline such that the /// placeholder sits on top of the baseline. kAboveBaseline, /// Align the top edge of the placeholder with the baseline specified in /// such that the placeholder hangs below the baseline. kBelowBaseline, /// Align the top edge of the placeholder with the top edge of the font. /// When the placeholder is very tall, the extra space will hang from /// the top and extend through the bottom of the line. kTop, /// Align the bottom edge of the placeholder with the top edge of the font. /// When the placeholder is very tall, the extra space will rise from /// the bottom and extend through the top of the line. kBottom, /// Align the middle of the placeholder with the middle of the text. When the /// placeholder is very tall, the extra space will grow equally from /// the top and bottom of the line. kMiddle, }; struct FontFeature { FontFeature(const SkString name, int value) : fName(name), fValue(value) {} bool operator==(const FontFeature& that) const { return fName == that.fName && fValue == that.fValue; } SkString fName; int fValue; }; struct PlaceholderStyle { PlaceholderStyle() = default; PlaceholderStyle(SkScalar width, SkScalar height, PlaceholderAlignment alignment, TextBaseline baseline, SkScalar offset) : fWidth(width) , fHeight(height) , fAlignment(alignment) , fBaseline(baseline) , fBaselineOffset(offset) {} bool equals(const PlaceholderStyle&) const; SkScalar fWidth = 0; SkScalar fHeight = 0; PlaceholderAlignment fAlignment = PlaceholderAlignment::kBaseline; TextBaseline fBaseline = TextBaseline::kAlphabetic; // Distance from the top edge of the rect to the baseline position. This // baseline will be aligned against the alphabetic baseline of the surrounding // text. // // Positive values drop the baseline lower (positions the rect higher) and // small or negative values will cause the rect to be positioned underneath // the line. When baseline == height, the bottom edge of the rect will rest on // the alphabetic baseline. SkScalar fBaselineOffset = 0; }; class TextStyle { public: TextStyle() = default; TextStyle(const TextStyle& other, bool placeholder); bool equals(const TextStyle& other) const; bool equalsByFonts(const TextStyle& that) const; bool matchOneAttribute(StyleType styleType, const TextStyle& other) const; bool operator==(const TextStyle& rhs) const { return this->equals(rhs); } // Colors SkColor getColor() const { return fColor; } void setColor(SkColor color) { fColor = color; } bool hasForeground() const { return fHasForeground; } SkPaint getForeground() const { return fForeground; } void setForegroundColor(SkPaint paint) { fHasForeground = true; fForeground = std::move(paint); } void clearForegroundColor() { fHasForeground = false; } bool hasBackground() const { return fHasBackground; } SkPaint getBackground() const { return fBackground; } void setBackgroundColor(SkPaint paint) { fHasBackground = true; fBackground = std::move(paint); } void clearBackgroundColor() { fHasBackground = false; } // Decorations Decoration getDecoration() const { return fDecoration; } TextDecoration getDecorationType() const { return fDecoration.fType; } TextDecorationMode getDecorationMode() const { return fDecoration.fMode; } SkColor getDecorationColor() const { return fDecoration.fColor; } TextDecorationStyle getDecorationStyle() const { return fDecoration.fStyle; } SkScalar getDecorationThicknessMultiplier() const { return fDecoration.fThicknessMultiplier; } void setDecoration(TextDecoration decoration) { fDecoration.fType = decoration; } void setDecorationMode(TextDecorationMode mode) { fDecoration.fMode = mode; } void setDecorationStyle(TextDecorationStyle style) { fDecoration.fStyle = style; } void setDecorationColor(SkColor color) { fDecoration.fColor = color; } void setDecorationThicknessMultiplier(SkScalar m) { fDecoration.fThicknessMultiplier = m; } // Weight/Width/Slant SkFontStyle getFontStyle() const { return fFontStyle; } void setFontStyle(SkFontStyle fontStyle) { fFontStyle = fontStyle; } // Shadows size_t getShadowNumber() const { return fTextShadows.size(); } std::vector getShadows() const { return fTextShadows; } void addShadow(TextShadow shadow) { fTextShadows.emplace_back(shadow); } void resetShadows() { fTextShadows.clear(); } // Font features size_t getFontFeatureNumber() const { return fFontFeatures.size(); } std::vector getFontFeatures() const { return fFontFeatures; } void addFontFeature(const SkString& fontFeature, int value) { fFontFeatures.emplace_back(fontFeature, value); } void resetFontFeatures() { fFontFeatures.clear(); } SkScalar getFontSize() const { return fFontSize; } void setFontSize(SkScalar size) { fFontSize = size; } const std::vector& getFontFamilies() const { return fFontFamilies; } void setFontFamilies(std::vector families) { fFontFamilies = std::move(families); } void setHeight(SkScalar height) { fHeight = height; } SkScalar getHeight() const { return fHeightOverride ? fHeight : 0; } void setHeightOverride(bool heightOverride) { fHeightOverride = heightOverride; } bool getHeightOverride() const { return fHeightOverride; } void setLetterSpacing(SkScalar letterSpacing) { fLetterSpacing = letterSpacing; } SkScalar getLetterSpacing() const { return fLetterSpacing; } void setWordSpacing(SkScalar wordSpacing) { fWordSpacing = wordSpacing; } SkScalar getWordSpacing() const { return fWordSpacing; } SkTypeface* getTypeface() const { return fTypeface.get(); } sk_sp refTypeface() const { return fTypeface; } void setTypeface(sk_sp typeface) { fTypeface = std::move(typeface); } SkString getLocale() const { return fLocale; } void setLocale(const SkString& locale) { fLocale = locale; } TextBaseline getTextBaseline() const { return fTextBaseline; } void setTextBaseline(TextBaseline baseline) { fTextBaseline = baseline; } void getFontMetrics(SkFontMetrics* metrics) const; bool isPlaceholder() const { return fIsPlaceholder; } void setPlaceholder() { fIsPlaceholder = true; } private: Decoration fDecoration = { TextDecoration::kNoDecoration, // TODO: switch back to kGaps when (if) switching flutter to skparagraph TextDecorationMode::kThrough, // It does not make sense to draw a transparent object, so we use this as a default // value to indicate no decoration color was set. SK_ColorTRANSPARENT, TextDecorationStyle::kSolid, // Thickness is applied as a multiplier to the default thickness of the font. 1.0f}; SkFontStyle fFontStyle; std::vector fFontFamilies = { SkString(DEFAULT_FONT_FAMILY) }; SkScalar fFontSize = 14.0; SkScalar fHeight = 1.0; bool fHeightOverride = false; SkString fLocale = {}; SkScalar fLetterSpacing = 0.0; SkScalar fWordSpacing = 0.0; TextBaseline fTextBaseline = TextBaseline::kAlphabetic; SkColor fColor = SK_ColorWHITE; bool fHasBackground = false; SkPaint fBackground; bool fHasForeground = false; SkPaint fForeground; std::vector fTextShadows; sk_sp fTypeface; bool fIsPlaceholder = false; std::vector fFontFeatures; }; typedef size_t TextIndex; typedef SkRange TextRange; const SkRange EMPTY_TEXT = EMPTY_RANGE; struct Block { Block() = default; Block(size_t start, size_t end, const TextStyle& style) : fRange(start, end), fStyle(style) {} Block(TextRange textRange, const TextStyle& style) : fRange(textRange), fStyle(style) {} void add(TextRange tail) { SkASSERT(fRange.end == tail.start); fRange = TextRange(fRange.start, fRange.start + fRange.width() + tail.width()); } TextRange fRange = EMPTY_RANGE; TextStyle fStyle; }; typedef size_t BlockIndex; typedef SkRange BlockRange; const size_t EMPTY_BLOCK = EMPTY_INDEX; const SkRange EMPTY_BLOCKS = EMPTY_RANGE; struct Placeholder { Placeholder() = default; Placeholder(size_t start, size_t end, const PlaceholderStyle& style, const TextStyle& textStyle, BlockRange blocksBefore, TextRange textBefore) : fRange(start, end) , fStyle(style) , fTextStyle(textStyle) , fBlocksBefore(blocksBefore) , fTextBefore(textBefore) {} TextRange fRange = EMPTY_RANGE; PlaceholderStyle fStyle; TextStyle fTextStyle; BlockRange fBlocksBefore; TextRange fTextBefore; }; } // namespace textlayout } // namespace skia #endif // TextStyle_DEFINED