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 #ifdef ENABLE_TEXT_ENHANCE 18 #include "drawing.h" 19 #endif // ENABLE_TEXT_ENHANCE 20 // TODO: Make it external so the other platforms (Android) could use it 21 #define DEFAULT_FONT_FAMILY "sans-serif" 22 23 namespace skia { 24 namespace textlayout { 25 #ifdef ENABLE_TEXT_ENHANCE 26 const SkScalar TEXT_BADGE_FONT_SIZE_SCALE = 0.65; 27 const SkScalar SUPERSCRIPT_BASELINE_SHIFT_SCALE = -0.7; 28 const SkScalar SUBSCRIPT_BASELINE_SHIFT_SCALE = 0.2; 29 #endif 30 31 static inline bool nearlyZero(SkScalar x, SkScalar tolerance = SK_ScalarNearlyZero) { 32 if (SkIsFinite(x)) { 33 return SkScalarNearlyZero(x, tolerance); 34 } 35 return false; 36 } 37 38 static inline bool nearlyEqual(SkScalar x, SkScalar y, SkScalar tolerance = SK_ScalarNearlyZero) { 39 #ifdef ENABLE_TEXT_ENHANCE 40 if (SkIsNaN(x) && SkIsNaN(y)) { 41 // Generally NaN has no equality, but it will break the invariant of the hashtable 42 // in ParagraphCache, resulting in errors. This fix is only a backstop for 43 // this condition, other functions may still be unreliable in the presence of NaN. 44 return true; 45 } 46 #endif 47 if (SkIsFinite(x, y)) { 48 return SkScalarNearlyEqual(x, y, tolerance); 49 } 50 // Inf == Inf, anything else is false 51 return x == y; 52 } 53 54 // Multiple decorations can be applied at once. Ex: Underline and overline is 55 // (0x1 | 0x2) 56 enum TextDecoration { 57 kNoDecoration = 0x0, 58 kUnderline = 0x1, 59 kOverline = 0x2, 60 kLineThrough = 0x4, 61 }; 62 constexpr TextDecoration AllTextDecorations[] = { 63 kNoDecoration, 64 kUnderline, 65 kOverline, 66 kLineThrough, 67 }; 68 69 enum TextDecorationStyle { kSolid, kDouble, kDotted, kDashed, kWavy }; 70 71 enum TextDecorationMode { kGaps, kThrough }; 72 73 enum StyleType { 74 kNone, 75 kAllAttributes, 76 kFont, 77 kForeground, 78 kBackground, 79 kShadow, 80 kDecorations, 81 kLetterSpacing, 82 kWordSpacing 83 }; 84 85 struct Decoration { 86 TextDecoration fType; 87 TextDecorationMode fMode; 88 SkColor fColor; 89 TextDecorationStyle fStyle; 90 SkScalar fThicknessMultiplier; 91 92 bool operator==(const Decoration& other) const { 93 return this->fType == other.fType && 94 this->fMode == other.fMode && 95 this->fColor == other.fColor && 96 this->fStyle == other.fStyle && 97 this->fThicknessMultiplier == other.fThicknessMultiplier; 98 } 99 }; 100 101 /// Where to vertically align the placeholder relative to the surrounding text. 102 enum class PlaceholderAlignment { 103 /// Match the baseline of the placeholder with the baseline. 104 kBaseline, 105 106 /// Align the bottom edge of the placeholder with the baseline such that the 107 /// placeholder sits on top of the baseline. 108 kAboveBaseline, 109 110 /// Align the top edge of the placeholder with the baseline specified in 111 /// such that the placeholder hangs below the baseline. 112 kBelowBaseline, 113 114 /// Align the top edge of the placeholder with the top edge of the font. 115 /// When the placeholder is very tall, the extra space will hang from 116 /// the top and extend through the bottom of the line. 117 kTop, 118 119 /// Align the bottom edge of the placeholder with the top edge of the font. 120 /// When the placeholder is very tall, the extra space will rise from 121 /// the bottom and extend through the top of the line. 122 kBottom, 123 124 /// Align the middle of the placeholder with the middle of the text. When the 125 /// placeholder is very tall, the extra space will grow equally from 126 /// the top and bottom of the line. 127 kMiddle, 128 129 #ifdef ENABLE_TEXT_ENHANCE 130 // Follow text vertically aligned 131 kFollow, 132 #endif 133 }; 134 135 struct FontFeature { FontFeatureFontFeature136 FontFeature(SkString name, int value) : fName(std::move(name)), fValue(value) {} 137 bool operator==(const FontFeature& that) const { 138 return fName == that.fName && fValue == that.fValue; 139 } 140 SkString fName; 141 int fValue; 142 }; 143 144 struct PlaceholderStyle { 145 PlaceholderStyle() = default; PlaceholderStylePlaceholderStyle146 PlaceholderStyle(SkScalar width, SkScalar height, PlaceholderAlignment alignment, 147 TextBaseline baseline, SkScalar offset) 148 : fWidth(width) 149 , fHeight(height) 150 , fAlignment(alignment) 151 , fBaseline(baseline) 152 , fBaselineOffset(offset) {} 153 154 bool equals(const PlaceholderStyle&) const; 155 156 SkScalar fWidth = 0; 157 SkScalar fHeight = 0; 158 PlaceholderAlignment fAlignment = PlaceholderAlignment::kBaseline; 159 TextBaseline fBaseline = TextBaseline::kAlphabetic; 160 // Distance from the top edge of the rect to the baseline position. This 161 // baseline will be aligned against the alphabetic baseline of the surrounding 162 // text. 163 // 164 // Positive values drop the baseline lower (positions the rect higher) and 165 // small or negative values will cause the rect to be positioned underneath 166 // the line. When baseline == height, the bottom edge of the rect will rest on 167 // the alphabetic baseline. 168 SkScalar fBaselineOffset = 0; 169 }; 170 171 #ifdef ENABLE_TEXT_ENHANCE 172 struct RectStyle { 173 SkColor color{0}; 174 SkScalar leftTopRadius{0.0f}; 175 SkScalar rightTopRadius{0.0f}; 176 SkScalar rightBottomRadius{0.0f}; 177 SkScalar leftBottomRadius{0.0f}; 178 179 bool operator ==(const RectStyle& rhs) const { 180 return color == rhs.color && 181 leftTopRadius == rhs.leftTopRadius && 182 rightTopRadius == rhs.rightTopRadius && 183 rightBottomRadius == rhs.rightBottomRadius && 184 leftBottomRadius == rhs.leftBottomRadius; 185 } 186 187 bool operator !=(const RectStyle& rhs) const { 188 return !(color == rhs.color && 189 leftTopRadius == rhs.leftTopRadius && 190 rightTopRadius == rhs.rightTopRadius && 191 rightBottomRadius == rhs.rightBottomRadius && 192 leftBottomRadius == rhs.leftBottomRadius); 193 } 194 }; 195 196 enum class TextBadgeType { 197 BADGE_NONE, 198 SUPERSCRIPT, 199 SUBSCRIPT, 200 }; 201 #endif 202 203 class TextStyle { 204 public: 205 TextStyle() = default; 206 TextStyle(const TextStyle& other) = default; 207 TextStyle& operator=(const TextStyle& other) = default; 208 209 TextStyle cloneForPlaceholder(); 210 211 bool equals(const TextStyle& other) const; 212 bool equalsByFonts(const TextStyle& that) const; 213 bool matchOneAttribute(StyleType styleType, const TextStyle& other) const; 214 bool operator==(const TextStyle& rhs) const { return this->equals(rhs); } 215 216 // Colors getColor()217 SkColor getColor() const { return fColor; } setColor(SkColor color)218 void setColor(SkColor color) { fColor = color; } 219 hasForeground()220 bool hasForeground() const { return fHasForeground; } getForeground()221 SkPaint getForeground() const { 222 const SkPaint* paint = std::get_if<SkPaint>(&fForeground); 223 return paint ? *paint : SkPaint(); 224 } getForegroundPaintOrID()225 ParagraphPainter::SkPaintOrID getForegroundPaintOrID() const { 226 return fForeground; 227 } setForegroundPaint(SkPaint paint)228 void setForegroundPaint(SkPaint paint) { 229 fHasForeground = true; 230 fForeground = std::move(paint); 231 } 232 // DEPRECATED: prefer `setForegroundPaint`. setForegroundColor(SkPaint paint)233 void setForegroundColor(SkPaint paint) { setForegroundPaint(std::move(paint)); } 234 235 // Set the foreground to a paint ID. This is intended for use by clients 236 // that implement a custom ParagraphPainter that can not accept an SkPaint. setForegroundPaintID(ParagraphPainter::PaintID paintID)237 void setForegroundPaintID(ParagraphPainter::PaintID paintID) { 238 fHasForeground = true; 239 fForeground = paintID; 240 } clearForegroundColor()241 void clearForegroundColor() { fHasForeground = false; } 242 hasBackground()243 bool hasBackground() const { return fHasBackground; } getBackground()244 SkPaint getBackground() const { 245 const SkPaint* paint = std::get_if<SkPaint>(&fBackground); 246 return paint ? *paint : SkPaint(); 247 } getBackgroundPaintOrID()248 ParagraphPainter::SkPaintOrID getBackgroundPaintOrID() const { 249 return fBackground; 250 } setBackgroundPaint(SkPaint paint)251 void setBackgroundPaint(SkPaint paint) { 252 fHasBackground = true; 253 fBackground = std::move(paint); 254 } 255 // DEPRECATED: prefer `setBackgroundPaint`. setBackgroundColor(SkPaint paint)256 void setBackgroundColor(SkPaint paint) { setBackgroundPaint(std::move(paint)); } setBackgroundPaintID(ParagraphPainter::PaintID paintID)257 void setBackgroundPaintID(ParagraphPainter::PaintID paintID) { 258 fHasBackground = true; 259 fBackground = paintID; 260 } clearBackgroundColor()261 void clearBackgroundColor() { fHasBackground = false; } 262 263 // Decorations getDecoration()264 Decoration getDecoration() const { return fDecoration; } getDecorationType()265 TextDecoration getDecorationType() const { return fDecoration.fType; } getDecorationMode()266 TextDecorationMode getDecorationMode() const { return fDecoration.fMode; } getDecorationColor()267 SkColor getDecorationColor() const { return fDecoration.fColor; } getDecorationStyle()268 TextDecorationStyle getDecorationStyle() const { return fDecoration.fStyle; } getDecorationThicknessMultiplier()269 SkScalar getDecorationThicknessMultiplier() const { 270 return fDecoration.fThicknessMultiplier; 271 } setDecoration(TextDecoration decoration)272 void setDecoration(TextDecoration decoration) { fDecoration.fType = decoration; } setDecorationMode(TextDecorationMode mode)273 void setDecorationMode(TextDecorationMode mode) { fDecoration.fMode = mode; } setDecorationStyle(TextDecorationStyle style)274 void setDecorationStyle(TextDecorationStyle style) { fDecoration.fStyle = style; } setDecorationColor(SkColor color)275 void setDecorationColor(SkColor color) { fDecoration.fColor = color; } setDecorationThicknessMultiplier(SkScalar m)276 void setDecorationThicknessMultiplier(SkScalar m) { fDecoration.fThicknessMultiplier = m; } 277 278 // Weight/Width/Slant 279 #ifdef ENABLE_TEXT_ENHANCE getFontStyle()280 RSFontStyle getFontStyle() const { return fFontStyle; } setFontStyle(RSFontStyle fontStyle)281 void setFontStyle(RSFontStyle fontStyle) { fFontStyle = fontStyle; } 282 #else getFontStyle()283 SkFontStyle getFontStyle() const { return fFontStyle; } setFontStyle(SkFontStyle fontStyle)284 void setFontStyle(SkFontStyle fontStyle) { fFontStyle = fontStyle; } 285 #endif 286 287 // Shadows getShadowNumber()288 size_t getShadowNumber() const { return fTextShadows.size(); } getShadows()289 std::vector<TextShadow> getShadows() const { return fTextShadows; } addShadow(TextShadow shadow)290 void addShadow(TextShadow shadow) { fTextShadows.emplace_back(shadow); } resetShadows()291 void resetShadows() { fTextShadows.clear(); } 292 293 // Font features getFontFeatureNumber()294 size_t getFontFeatureNumber() const { return fFontFeatures.size(); } getFontFeatures()295 std::vector<FontFeature> getFontFeatures() const { return fFontFeatures; } addFontFeature(const SkString & fontFeature,int value)296 void addFontFeature(const SkString& fontFeature, int value) 297 { fFontFeatures.emplace_back(fontFeature, value); } resetFontFeatures()298 void resetFontFeatures() { fFontFeatures.clear(); } 299 300 // Font arguments getFontArguments()301 const std::optional<FontArguments>& getFontArguments() const { return fFontArguments; } 302 // The contents of the SkFontArguments will be copied into the TextStyle, 303 // and the SkFontArguments can be safely deleted after setFontArguments returns. 304 void setFontArguments(const std::optional<SkFontArguments>& args); 305 getFontSize()306 SkScalar getFontSize() const { return fFontSize; } setFontSize(SkScalar size)307 void setFontSize(SkScalar size) { fFontSize = size; } 308 getFontFamilies()309 const std::vector<SkString>& getFontFamilies() const { return fFontFamilies; } 310 311 #ifdef ENABLE_TEXT_ENHANCE 312 void setFontFamilies(std::vector<SkString> families); 313 getBaselineShift()314 SkScalar getBaselineShift() const { return fBaselineShift; } getVerticalAlignShift()315 SkScalar getVerticalAlignShift() const { return fVerticalAlignShift; }; getTotalVerticalShift()316 SkScalar getTotalVerticalShift() const { return fBaselineShift + getBadgeBaseLineShift(); } setVerticalAlignShift(SkScalar shift)317 void setVerticalAlignShift(SkScalar shift) { fVerticalAlignShift = shift; } 318 #else setFontFamilies(std::vector<SkString> families)319 void setFontFamilies(std::vector<SkString> families) { 320 fFontFamilies = std::move(families); 321 } 322 getBaselineShift()323 SkScalar getBaselineShift() const { return fBaselineShift; } 324 #endif 325 setBaselineShift(SkScalar baselineShift)326 void setBaselineShift(SkScalar baselineShift) { fBaselineShift = baselineShift; } 327 setHeight(SkScalar height)328 void setHeight(SkScalar height) { fHeight = height; } getHeight()329 SkScalar getHeight() const { return fHeightOverride ? fHeight : 0; } 330 setHeightOverride(bool heightOverride)331 void setHeightOverride(bool heightOverride) { fHeightOverride = heightOverride; } getHeightOverride()332 bool getHeightOverride() const { return fHeightOverride; } 333 setHalfLeading(bool halfLeading)334 void setHalfLeading(bool halfLeading) { fHalfLeading = halfLeading; } getHalfLeading()335 bool getHalfLeading() const { return fHalfLeading; } 336 setLetterSpacing(SkScalar letterSpacing)337 void setLetterSpacing(SkScalar letterSpacing) { fLetterSpacing = letterSpacing; } getLetterSpacing()338 SkScalar getLetterSpacing() const { return fLetterSpacing; } 339 setWordSpacing(SkScalar wordSpacing)340 void setWordSpacing(SkScalar wordSpacing) { fWordSpacing = wordSpacing; } getWordSpacing()341 SkScalar getWordSpacing() const { return fWordSpacing; } 342 343 #ifdef ENABLE_TEXT_ENHANCE getTypeface()344 RSTypeface* getTypeface() const { return fTypeface.get(); } refTypeface()345 std::shared_ptr<RSTypeface> refTypeface() const { return fTypeface; } setTypeface(std::shared_ptr<RSTypeface> typeface)346 void setTypeface(std::shared_ptr<RSTypeface> typeface) { fTypeface = std::move(typeface); } 347 #else getTypeface()348 SkTypeface* getTypeface() const { return fTypeface.get(); } refTypeface()349 sk_sp<SkTypeface> refTypeface() const { return fTypeface; } setTypeface(sk_sp<SkTypeface> typeface)350 void setTypeface(sk_sp<SkTypeface> typeface) { fTypeface = std::move(typeface); } 351 #endif 352 getLocale()353 SkString getLocale() const { return fLocale; } setLocale(const SkString & locale)354 void setLocale(const SkString& locale) { fLocale = locale; } 355 getTextBaseline()356 TextBaseline getTextBaseline() const { return fTextBaseline; } setTextBaseline(TextBaseline baseline)357 void setTextBaseline(TextBaseline baseline) { fTextBaseline = baseline; } 358 359 #ifdef ENABLE_TEXT_ENHANCE 360 void getFontMetrics(RSFontMetrics* metrics) const; 361 #else 362 void getFontMetrics(SkFontMetrics* metrics) const; 363 #endif 364 isPlaceholder()365 bool isPlaceholder() const { return fIsPlaceholder; } setPlaceholder()366 void setPlaceholder() { fIsPlaceholder = true; } 367 368 #ifdef ENABLE_TEXT_ENHANCE getStyleId()369 int getStyleId() const { return fStyleId; } setStyleId(int styleId)370 void setStyleId(int styleId) { fStyleId = static_cast<SkColor>(styleId); } getTextStyleUid()371 size_t getTextStyleUid() const { return fTextStyleUid; } setTextStyleUid(size_t textStyleUid)372 void setTextStyleUid(size_t textStyleUid) { fTextStyleUid = textStyleUid; } getBackgroundRect()373 RectStyle getBackgroundRect() const { return fBackgroundRect; } setBackgroundRect(RectStyle rect)374 void setBackgroundRect(RectStyle rect) { fBackgroundRect = rect; } isCustomSymbol()375 bool isCustomSymbol() const { return fIsCustomSymbol; } 376 setCustomSymbol(bool state)377 void setCustomSymbol(bool state) { fIsCustomSymbol = state; } 378 getTextBadgeType()379 TextBadgeType getTextBadgeType() const { return fBadgeType; } 380 setTextBadgeType(TextBadgeType badgeType)381 void setTextBadgeType(TextBadgeType badgeType) { fBadgeType = badgeType; } 382 383 SkScalar getBadgeBaseLineShift() const; 384 385 SkScalar getCorrectFontSize() const; 386 #endif 387 private: 388 static const std::vector<SkString>* kDefaultFontFamilies; 389 390 Decoration fDecoration = { 391 TextDecoration::kNoDecoration, 392 #ifdef ENABLE_TEXT_ENHANCE 393 TextDecorationMode::kGaps, 394 #else 395 // TODO: switch back to kGaps when (if) switching flutter to skparagraph 396 TextDecorationMode::kThrough, 397 #endif 398 // It does not make sense to draw a transparent object, so we use this as a default 399 // value to indicate no decoration color was set. 400 SK_ColorTRANSPARENT, TextDecorationStyle::kSolid, 401 // Thickness is applied as a multiplier to the default thickness of the font. 402 1.0f}; 403 404 #ifdef ENABLE_TEXT_ENHANCE 405 RSFontStyle fFontStyle; 406 #else 407 SkFontStyle fFontStyle; 408 #endif 409 410 std::vector<SkString> fFontFamilies = *kDefaultFontFamilies; 411 412 SkScalar fFontSize = 14.0; 413 SkScalar fHeight = 1.0; 414 bool fHeightOverride = false; 415 SkScalar fBaselineShift = 0.0f; 416 // true: half leading. 417 // false: scale ascent/descent with fHeight. 418 bool fHalfLeading = false; 419 SkString fLocale = {}; 420 SkScalar fLetterSpacing = 0.0; 421 SkScalar fWordSpacing = 0.0; 422 #ifdef ENABLE_TEXT_ENHANCE 423 RectStyle fBackgroundRect = {0, 0.0f, 0.0f, 0.0f, 0.0f}; 424 SkColor fStyleId = {0}; 425 size_t fTextStyleUid{0}; 426 SkScalar fVerticalAlignShift{0.0f}; 427 #endif 428 429 TextBaseline fTextBaseline = TextBaseline::kAlphabetic; 430 431 SkColor fColor = SK_ColorWHITE; 432 bool fHasBackground = false; 433 ParagraphPainter::SkPaintOrID fBackground; 434 bool fHasForeground = false; 435 ParagraphPainter::SkPaintOrID fForeground; 436 437 std::vector<TextShadow> fTextShadows; 438 439 #ifdef ENABLE_TEXT_ENHANCE 440 bool fIsCustomSymbol{false}; 441 std::shared_ptr<RSTypeface> fTypeface; 442 #else 443 sk_sp<SkTypeface> fTypeface; 444 #endif 445 bool fIsPlaceholder = false; 446 447 std::vector<FontFeature> fFontFeatures; 448 449 std::optional<FontArguments> fFontArguments; 450 451 #ifdef ENABLE_TEXT_ENHANCE 452 TextBadgeType fBadgeType{TextBadgeType::BADGE_NONE}; 453 #endif 454 }; 455 456 typedef size_t TextIndex; 457 typedef SkRange<size_t> TextRange; 458 const SkRange<size_t> EMPTY_TEXT = EMPTY_RANGE; 459 460 struct Block { 461 Block() = default; BlockBlock462 Block(size_t start, size_t end, const TextStyle& style) : fRange(start, end), fStyle(style) {} BlockBlock463 Block(TextRange textRange, const TextStyle& style) : fRange(textRange), fStyle(style) {} 464 addBlock465 void add(TextRange tail) { 466 SkASSERT(fRange.end == tail.start); 467 fRange = TextRange(fRange.start, fRange.start + fRange.width() + tail.width()); 468 } 469 470 TextRange fRange = EMPTY_RANGE; 471 TextStyle fStyle; 472 }; 473 474 475 typedef size_t BlockIndex; 476 typedef SkRange<size_t> BlockRange; 477 const size_t EMPTY_BLOCK = EMPTY_INDEX; 478 const SkRange<size_t> EMPTY_BLOCKS = EMPTY_RANGE; 479 480 struct Placeholder { 481 Placeholder() = default; PlaceholderPlaceholder482 Placeholder(size_t start, size_t end, const PlaceholderStyle& style, const TextStyle& textStyle, 483 BlockRange blocksBefore, TextRange textBefore) 484 : fRange(start, end) 485 , fStyle(style) 486 , fTextStyle(textStyle) 487 , fBlocksBefore(blocksBefore) 488 , fTextBefore(textBefore) {} 489 490 TextRange fRange = EMPTY_RANGE; 491 PlaceholderStyle fStyle; 492 TextStyle fTextStyle; 493 BlockRange fBlocksBefore; 494 TextRange fTextBefore; 495 }; 496 497 } // namespace textlayout 498 } // namespace skia 499 500 #endif // TextStyle_DEFINED 501