1 // Copyright 2019 Google LLC. 2 #ifndef TextStyle_DEFINED 3 #define TextStyle_DEFINED 4 5 #include <optional> 6 #include <vector> 7 #include "include/core/SkColor.h" 8 #include "include/core/SkFont.h" 9 #include "include/core/SkFontMetrics.h" 10 #include "include/core/SkFontStyle.h" 11 #include "include/core/SkPaint.h" 12 #include "include/core/SkScalar.h" 13 #include "modules/skparagraph/include/DartTypes.h" 14 #include "modules/skparagraph/include/FontArguments.h" 15 #include "modules/skparagraph/include/ParagraphPainter.h" 16 #include "modules/skparagraph/include/TextShadow.h" 17 18 // TODO: Make it external so the other platforms (Android) could use it 19 #define DEFAULT_FONT_FAMILY "sans-serif" 20 21 namespace skia { 22 namespace textlayout { 23 24 static inline bool nearlyZero(SkScalar x, SkScalar tolerance = SK_ScalarNearlyZero) { 25 if (SkScalarIsFinite(x)) { 26 return SkScalarNearlyZero(x, tolerance); 27 } 28 return false; 29 } 30 31 static inline bool nearlyEqual(SkScalar x, SkScalar y, SkScalar tolerance = SK_ScalarNearlyZero) { 32 if (SkScalarIsFinite(x) && SkScalarIsFinite(x)) { 33 return SkScalarNearlyEqual(x, y, tolerance); 34 } 35 // Inf == Inf, anything else is false 36 return x == y; 37 } 38 39 // Multiple decorations can be applied at once. Ex: Underline and overline is 40 // (0x1 | 0x2) 41 enum TextDecoration { 42 kNoDecoration = 0x0, 43 kUnderline = 0x1, 44 kOverline = 0x2, 45 kLineThrough = 0x4, 46 }; 47 constexpr TextDecoration AllTextDecorations[] = { 48 kNoDecoration, 49 kUnderline, 50 kOverline, 51 kLineThrough, 52 }; 53 54 enum TextDecorationStyle { kSolid, kDouble, kDotted, kDashed, kWavy }; 55 56 enum TextDecorationMode { kGaps, kThrough }; 57 58 enum StyleType { 59 kNone, 60 kAllAttributes, 61 kFont, 62 kForeground, 63 kBackground, 64 kShadow, 65 kDecorations, 66 kLetterSpacing, 67 kWordSpacing 68 }; 69 70 struct Decoration { 71 TextDecoration fType; 72 TextDecorationMode fMode; 73 SkColor fColor; 74 TextDecorationStyle fStyle; 75 SkScalar fThicknessMultiplier; 76 77 bool operator==(const Decoration& other) const { 78 return this->fType == other.fType && 79 this->fMode == other.fMode && 80 this->fColor == other.fColor && 81 this->fStyle == other.fStyle && 82 this->fThicknessMultiplier == other.fThicknessMultiplier; 83 } 84 }; 85 86 /// Where to vertically align the placeholder relative to the surrounding text. 87 enum class PlaceholderAlignment { 88 /// Match the baseline of the placeholder with the baseline. 89 kBaseline, 90 91 /// Align the bottom edge of the placeholder with the baseline such that the 92 /// placeholder sits on top of the baseline. 93 kAboveBaseline, 94 95 /// Align the top edge of the placeholder with the baseline specified in 96 /// such that the placeholder hangs below the baseline. 97 kBelowBaseline, 98 99 /// Align the top edge of the placeholder with the top edge of the font. 100 /// When the placeholder is very tall, the extra space will hang from 101 /// the top and extend through the bottom of the line. 102 kTop, 103 104 /// Align the bottom edge of the placeholder with the top edge of the font. 105 /// When the placeholder is very tall, the extra space will rise from 106 /// the bottom and extend through the top of the line. 107 kBottom, 108 109 /// Align the middle of the placeholder with the middle of the text. When the 110 /// placeholder is very tall, the extra space will grow equally from 111 /// the top and bottom of the line. 112 kMiddle, 113 }; 114 115 struct FontFeature { FontFeatureFontFeature116 FontFeature(const SkString name, int value) : fName(name), fValue(value) {} 117 bool operator==(const FontFeature& that) const { 118 return fName == that.fName && fValue == that.fValue; 119 } 120 SkString fName; 121 int fValue; 122 }; 123 124 struct PlaceholderStyle { 125 PlaceholderStyle() = default; PlaceholderStylePlaceholderStyle126 PlaceholderStyle(SkScalar width, SkScalar height, PlaceholderAlignment alignment, 127 TextBaseline baseline, SkScalar offset) 128 : fWidth(width) 129 , fHeight(height) 130 , fAlignment(alignment) 131 , fBaseline(baseline) 132 , fBaselineOffset(offset) {} 133 134 bool equals(const PlaceholderStyle&) const; 135 136 SkScalar fWidth = 0; 137 SkScalar fHeight = 0; 138 PlaceholderAlignment fAlignment = PlaceholderAlignment::kBaseline; 139 TextBaseline fBaseline = TextBaseline::kAlphabetic; 140 // Distance from the top edge of the rect to the baseline position. This 141 // baseline will be aligned against the alphabetic baseline of the surrounding 142 // text. 143 // 144 // Positive values drop the baseline lower (positions the rect higher) and 145 // small or negative values will cause the rect to be positioned underneath 146 // the line. When baseline == height, the bottom edge of the rect will rest on 147 // the alphabetic baseline. 148 SkScalar fBaselineOffset = 0; 149 }; 150 151 struct RectStyle { 152 SkColor color = 0; 153 SkScalar leftTopRadius = 0.0f; 154 SkScalar rightTopRadius = 0.0f; 155 SkScalar rightBottomRadius = 0.0f; 156 SkScalar leftBottomRadius = 0.0f; 157 }; 158 159 class TextStyle { 160 public: 161 TextStyle() = default; 162 TextStyle(const TextStyle& other) = default; 163 TextStyle& operator=(const TextStyle& other) = default; 164 165 TextStyle cloneForPlaceholder(); 166 167 bool equals(const TextStyle& other) const; 168 bool equalsByFonts(const TextStyle& that) const; 169 bool matchOneAttribute(StyleType styleType, const TextStyle& other) const; 170 bool operator==(const TextStyle& rhs) const { return this->equals(rhs); } 171 172 // Colors getColor()173 SkColor getColor() const { return fColor; } setColor(SkColor color)174 void setColor(SkColor color) { fColor = color; } 175 hasForeground()176 bool hasForeground() const { return fHasForeground; } getForeground()177 SkPaint getForeground() const { 178 const SkPaint* paint = std::get_if<SkPaint>(&fForeground); 179 return paint ? *paint : SkPaint(); 180 } getForegroundPaintOrID()181 ParagraphPainter::SkPaintOrID getForegroundPaintOrID() const { 182 return fForeground; 183 } setForegroundPaint(SkPaint paint)184 void setForegroundPaint(SkPaint paint) { 185 fHasForeground = true; 186 fForeground = std::move(paint); 187 } 188 // DEPRECATED: prefer `setForegroundPaint`. setForegroundColor(SkPaint paint)189 void setForegroundColor(SkPaint paint) { setForegroundPaint(paint); } 190 // Set the foreground to a paint ID. This is intended for use by clients 191 // that implement a custom ParagraphPainter that can not accept an SkPaint. setForegroundPaintID(ParagraphPainter::PaintID paintID)192 void setForegroundPaintID(ParagraphPainter::PaintID paintID) { 193 fHasForeground = true; 194 fForeground = paintID; 195 } clearForegroundColor()196 void clearForegroundColor() { fHasForeground = false; } 197 hasBackground()198 bool hasBackground() const { return fHasBackground; } getBackground()199 SkPaint getBackground() const { 200 const SkPaint* paint = std::get_if<SkPaint>(&fBackground); 201 return paint ? *paint : SkPaint(); 202 } getBackgroundPaintOrID()203 ParagraphPainter::SkPaintOrID getBackgroundPaintOrID() const { 204 return fBackground; 205 } setBackgroundPaint(SkPaint paint)206 void setBackgroundPaint(SkPaint paint) { 207 fHasBackground = true; 208 fBackground = std::move(paint); 209 } 210 // DEPRECATED: prefer `setBackgroundPaint`. setBackgroundColor(SkPaint paint)211 void setBackgroundColor(SkPaint paint) { setBackgroundPaint(paint); } setBackgroundPaintID(ParagraphPainter::PaintID paintID)212 void setBackgroundPaintID(ParagraphPainter::PaintID paintID) { 213 fHasBackground = true; 214 fBackground = paintID; 215 } clearBackgroundColor()216 void clearBackgroundColor() { fHasBackground = false; } 217 218 // Decorations getDecoration()219 Decoration getDecoration() const { return fDecoration; } getDecorationType()220 TextDecoration getDecorationType() const { return fDecoration.fType; } getDecorationMode()221 TextDecorationMode getDecorationMode() const { return fDecoration.fMode; } getDecorationColor()222 SkColor getDecorationColor() const { return fDecoration.fColor; } getDecorationStyle()223 TextDecorationStyle getDecorationStyle() const { return fDecoration.fStyle; } getDecorationThicknessMultiplier()224 SkScalar getDecorationThicknessMultiplier() const { 225 return fDecoration.fThicknessMultiplier; 226 } setDecoration(TextDecoration decoration)227 void setDecoration(TextDecoration decoration) { fDecoration.fType = decoration; } setDecorationMode(TextDecorationMode mode)228 void setDecorationMode(TextDecorationMode mode) { fDecoration.fMode = mode; } setDecorationStyle(TextDecorationStyle style)229 void setDecorationStyle(TextDecorationStyle style) { fDecoration.fStyle = style; } setDecorationColor(SkColor color)230 void setDecorationColor(SkColor color) { fDecoration.fColor = color; } setDecorationThicknessMultiplier(SkScalar m)231 void setDecorationThicknessMultiplier(SkScalar m) { fDecoration.fThicknessMultiplier = m; } 232 233 // Weight/Width/Slant getFontStyle()234 SkFontStyle getFontStyle() const { return fFontStyle; } setFontStyle(SkFontStyle fontStyle)235 void setFontStyle(SkFontStyle fontStyle) { fFontStyle = fontStyle; } 236 237 // Shadows getShadowNumber()238 size_t getShadowNumber() const { return fTextShadows.size(); } getShadows()239 std::vector<TextShadow> getShadows() const { return fTextShadows; } addShadow(TextShadow shadow)240 void addShadow(TextShadow shadow) { fTextShadows.emplace_back(shadow); } resetShadows()241 void resetShadows() { fTextShadows.clear(); } 242 243 // Font features getFontFeatureNumber()244 size_t getFontFeatureNumber() const { return fFontFeatures.size(); } getFontFeatures()245 std::vector<FontFeature> getFontFeatures() const { return fFontFeatures; } addFontFeature(const SkString & fontFeature,int value)246 void addFontFeature(const SkString& fontFeature, int value) 247 { fFontFeatures.emplace_back(fontFeature, value); } resetFontFeatures()248 void resetFontFeatures() { fFontFeatures.clear(); } 249 250 // Font arguments getFontArguments()251 const std::optional<FontArguments>& getFontArguments() const { return fFontArguments; } 252 // The contents of the SkFontArguments will be copied into the TextStyle, 253 // and the SkFontArguments can be safely deleted after setFontArguments returns. 254 void setFontArguments(const std::optional<SkFontArguments>& args); 255 getFontSize()256 SkScalar getFontSize() const { return fFontSize; } setFontSize(SkScalar size)257 void setFontSize(SkScalar size) { fFontSize = size; } 258 getFontFamilies()259 const std::vector<SkString>& getFontFamilies() const { return fFontFamilies; } setFontFamilies(std::vector<SkString> families)260 void setFontFamilies(std::vector<SkString> families) { 261 fFontFamilies = std::move(families); 262 } 263 getBaselineShift()264 SkScalar getBaselineShift() const { return fBaselineShift; } setBaselineShift(SkScalar baselineShift)265 void setBaselineShift(SkScalar baselineShift) { fBaselineShift = baselineShift; } 266 setHeight(SkScalar height)267 void setHeight(SkScalar height) { fHeight = height; } getHeight()268 SkScalar getHeight() const { return fHeightOverride ? fHeight : 0; } 269 setHeightOverride(bool heightOverride)270 void setHeightOverride(bool heightOverride) { fHeightOverride = heightOverride; } getHeightOverride()271 bool getHeightOverride() const { return fHeightOverride; } 272 setHalfLeading(bool halfLeading)273 void setHalfLeading(bool halfLeading) { fHalfLeading = halfLeading; } getHalfLeading()274 bool getHalfLeading() const { return fHalfLeading; } 275 setLetterSpacing(SkScalar letterSpacing)276 void setLetterSpacing(SkScalar letterSpacing) { fLetterSpacing = letterSpacing; } getLetterSpacing()277 SkScalar getLetterSpacing() const { return fLetterSpacing; } 278 setWordSpacing(SkScalar wordSpacing)279 void setWordSpacing(SkScalar wordSpacing) { fWordSpacing = wordSpacing; } getWordSpacing()280 SkScalar getWordSpacing() const { return fWordSpacing; } 281 getTypeface()282 SkTypeface* getTypeface() const { return fTypeface.get(); } refTypeface()283 sk_sp<SkTypeface> refTypeface() const { return fTypeface; } setTypeface(sk_sp<SkTypeface> typeface)284 void setTypeface(sk_sp<SkTypeface> typeface) { fTypeface = std::move(typeface); } 285 getLocale()286 SkString getLocale() const { return fLocale; } setLocale(const SkString & locale)287 void setLocale(const SkString& locale) { fLocale = locale; } 288 getTextBaseline()289 TextBaseline getTextBaseline() const { return fTextBaseline; } setTextBaseline(TextBaseline baseline)290 void setTextBaseline(TextBaseline baseline) { fTextBaseline = baseline; } 291 292 void getFontMetrics(SkFontMetrics* metrics) const; 293 isPlaceholder()294 bool isPlaceholder() const { return fIsPlaceholder; } setPlaceholder()295 void setPlaceholder() { fIsPlaceholder = true; } 296 getStyleId()297 int getStyleId() const { return fStyleId; } setStyleId(int styleId)298 void setStyleId(int styleId) { fStyleId = styleId; } 299 getBackgroundRect()300 RectStyle getBackgroundRect() const { return fBackgroundRect; } setBackgroundRect(RectStyle rect)301 void setBackgroundRect(RectStyle rect) { fBackgroundRect = rect; } 302 303 private: 304 static const std::vector<SkString>* kDefaultFontFamilies; 305 306 Decoration fDecoration = { 307 TextDecoration::kNoDecoration, 308 // TODO: switch back to kGaps when (if) switching flutter to skparagraph 309 TextDecorationMode::kThrough, 310 // It does not make sense to draw a transparent object, so we use this as a default 311 // value to indicate no decoration color was set. 312 SK_ColorTRANSPARENT, TextDecorationStyle::kSolid, 313 // Thickness is applied as a multiplier to the default thickness of the font. 314 1.0f}; 315 316 SkFontStyle fFontStyle; 317 318 std::vector<SkString> fFontFamilies = *kDefaultFontFamilies; 319 320 SkScalar fFontSize = 14.0; 321 SkScalar fHeight = 1.0; 322 bool fHeightOverride = false; 323 SkScalar fBaselineShift = 0.0f; 324 // true: half leading. 325 // false: scale ascent/descent with fHeight. 326 bool fHalfLeading = false; 327 SkString fLocale = {}; 328 RectStyle fBackgroundRect = {0, 0.0f, 0.0f, 0.0f, 0.0f}; 329 SkColor fStyleId = 0; 330 SkScalar fLetterSpacing = 0.0; 331 SkScalar fWordSpacing = 0.0; 332 333 TextBaseline fTextBaseline = TextBaseline::kAlphabetic; 334 335 SkColor fColor = SK_ColorWHITE; 336 bool fHasBackground = false; 337 ParagraphPainter::SkPaintOrID fBackground; 338 bool fHasForeground = false; 339 ParagraphPainter::SkPaintOrID fForeground; 340 341 std::vector<TextShadow> fTextShadows; 342 343 sk_sp<SkTypeface> fTypeface; 344 bool fIsPlaceholder = false; 345 346 std::vector<FontFeature> fFontFeatures; 347 348 std::optional<FontArguments> fFontArguments; 349 }; 350 351 typedef size_t TextIndex; 352 typedef SkRange<size_t> TextRange; 353 const SkRange<size_t> EMPTY_TEXT = EMPTY_RANGE; 354 355 struct Block { 356 Block() = default; BlockBlock357 Block(size_t start, size_t end, const TextStyle& style) : fRange(start, end), fStyle(style) {} BlockBlock358 Block(TextRange textRange, const TextStyle& style) : fRange(textRange), fStyle(style) {} 359 addBlock360 void add(TextRange tail) { 361 SkASSERT(fRange.end == tail.start); 362 fRange = TextRange(fRange.start, fRange.start + fRange.width() + tail.width()); 363 } 364 365 TextRange fRange = EMPTY_RANGE; 366 TextStyle fStyle; 367 }; 368 369 370 typedef size_t BlockIndex; 371 typedef SkRange<size_t> BlockRange; 372 const size_t EMPTY_BLOCK = EMPTY_INDEX; 373 const SkRange<size_t> EMPTY_BLOCKS = EMPTY_RANGE; 374 375 struct Placeholder { 376 Placeholder() = default; PlaceholderPlaceholder377 Placeholder(size_t start, size_t end, const PlaceholderStyle& style, const TextStyle& textStyle, 378 BlockRange blocksBefore, TextRange textBefore) 379 : fRange(start, end) 380 , fStyle(style) 381 , fTextStyle(textStyle) 382 , fBlocksBefore(blocksBefore) 383 , fTextBefore(textBefore) {} 384 385 TextRange fRange = EMPTY_RANGE; 386 PlaceholderStyle fStyle; 387 TextStyle fTextStyle; 388 BlockRange fBlocksBefore; 389 TextRange fTextBefore; 390 }; 391 392 } // namespace textlayout 393 } // namespace skia 394 395 #endif // TextStyle_DEFINED 396