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