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