/src/skia/modules/skparagraph/include/TextStyle.h
Line | Count | Source (jump to first uncovered line) |
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 | 0 | static inline bool nearlyZero(SkScalar x, SkScalar tolerance = SK_ScalarNearlyZero) { |
25 | 0 | if (SkIsFinite(x)) { |
26 | 0 | return SkScalarNearlyZero(x, tolerance); |
27 | 0 | } |
28 | 0 | return false; |
29 | 0 | } Unexecuted instantiation: FuzzSkParagraph.cpp:skia::textlayout::nearlyZero(float, float) Unexecuted instantiation: FontCollection.cpp:skia::textlayout::nearlyZero(float, float) Unexecuted instantiation: ParagraphBuilderImpl.cpp:skia::textlayout::nearlyZero(float, float) Unexecuted instantiation: ParagraphCache.cpp:skia::textlayout::nearlyZero(float, float) Unexecuted instantiation: ParagraphImpl.cpp:skia::textlayout::nearlyZero(float, float) Unexecuted instantiation: ParagraphStyle.cpp:skia::textlayout::nearlyZero(float, float) Unexecuted instantiation: Run.cpp:skia::textlayout::nearlyZero(float, float) Unexecuted instantiation: TextLine.cpp:skia::textlayout::nearlyZero(float, float) Unexecuted instantiation: TextStyle.cpp:skia::textlayout::nearlyZero(float, float) Unexecuted instantiation: TextWrapper.cpp:skia::textlayout::nearlyZero(float, float) Unexecuted instantiation: Decorations.cpp:skia::textlayout::nearlyZero(float, float) Unexecuted instantiation: OneLineShaper.cpp:skia::textlayout::nearlyZero(float, float) |
30 | | |
31 | 0 | static inline bool nearlyEqual(SkScalar x, SkScalar y, SkScalar tolerance = SK_ScalarNearlyZero) { |
32 | 0 | if (SkIsFinite(x, y)) { |
33 | 0 | return SkScalarNearlyEqual(x, y, tolerance); |
34 | 0 | } |
35 | | // Inf == Inf, anything else is false |
36 | 0 | return x == y; |
37 | 0 | } Unexecuted instantiation: FuzzSkParagraph.cpp:skia::textlayout::nearlyEqual(float, float, float) Unexecuted instantiation: FontCollection.cpp:skia::textlayout::nearlyEqual(float, float, float) Unexecuted instantiation: ParagraphBuilderImpl.cpp:skia::textlayout::nearlyEqual(float, float, float) Unexecuted instantiation: ParagraphCache.cpp:skia::textlayout::nearlyEqual(float, float, float) Unexecuted instantiation: ParagraphImpl.cpp:skia::textlayout::nearlyEqual(float, float, float) Unexecuted instantiation: ParagraphStyle.cpp:skia::textlayout::nearlyEqual(float, float, float) Unexecuted instantiation: Run.cpp:skia::textlayout::nearlyEqual(float, float, float) Unexecuted instantiation: TextLine.cpp:skia::textlayout::nearlyEqual(float, float, float) Unexecuted instantiation: TextStyle.cpp:skia::textlayout::nearlyEqual(float, float, float) Unexecuted instantiation: TextWrapper.cpp:skia::textlayout::nearlyEqual(float, float, float) Unexecuted instantiation: Decorations.cpp:skia::textlayout::nearlyEqual(float, float, float) Unexecuted instantiation: OneLineShaper.cpp:skia::textlayout::nearlyEqual(float, float, float) |
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 | 0 | bool operator==(const Decoration& other) const { |
78 | 0 | return this->fType == other.fType && |
79 | 0 | this->fMode == other.fMode && |
80 | 0 | this->fColor == other.fColor && |
81 | 0 | this->fStyle == other.fStyle && |
82 | 0 | this->fThicknessMultiplier == other.fThicknessMultiplier; |
83 | 0 | } |
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 { |
116 | 0 | FontFeature(SkString name, int value) : fName(std::move(name)), fValue(value) {} |
117 | 0 | bool operator==(const FontFeature& that) const { |
118 | 0 | return fName == that.fName && fValue == that.fValue; |
119 | 0 | } |
120 | | SkString fName; |
121 | | int fValue; |
122 | | }; |
123 | | |
124 | | struct PlaceholderStyle { |
125 | 0 | PlaceholderStyle() = default; |
126 | | 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 | 0 | , 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 | | class TextStyle { |
152 | | public: |
153 | 0 | TextStyle() = default; |
154 | 0 | TextStyle(const TextStyle& other) = default; |
155 | 0 | TextStyle& operator=(const TextStyle& other) = default; |
156 | | |
157 | | TextStyle cloneForPlaceholder(); |
158 | | |
159 | | bool equals(const TextStyle& other) const; |
160 | | bool equalsByFonts(const TextStyle& that) const; |
161 | | bool matchOneAttribute(StyleType styleType, const TextStyle& other) const; |
162 | 0 | bool operator==(const TextStyle& rhs) const { return this->equals(rhs); } |
163 | | |
164 | | // Colors |
165 | 0 | SkColor getColor() const { return fColor; } |
166 | 0 | void setColor(SkColor color) { fColor = color; } |
167 | | |
168 | 0 | bool hasForeground() const { return fHasForeground; } |
169 | 0 | SkPaint getForeground() const { |
170 | 0 | const SkPaint* paint = std::get_if<SkPaint>(&fForeground); |
171 | 0 | return paint ? *paint : SkPaint(); |
172 | 0 | } |
173 | 0 | ParagraphPainter::SkPaintOrID getForegroundPaintOrID() const { |
174 | 0 | return fForeground; |
175 | 0 | } |
176 | 0 | void setForegroundPaint(SkPaint paint) { |
177 | 0 | fHasForeground = true; |
178 | 0 | fForeground = std::move(paint); |
179 | 0 | } |
180 | | // DEPRECATED: prefer `setForegroundPaint`. |
181 | 0 | void setForegroundColor(SkPaint paint) { setForegroundPaint(std::move(paint)); } |
182 | | |
183 | | // Set the foreground to a paint ID. This is intended for use by clients |
184 | | // that implement a custom ParagraphPainter that can not accept an SkPaint. |
185 | 0 | void setForegroundPaintID(ParagraphPainter::PaintID paintID) { |
186 | 0 | fHasForeground = true; |
187 | 0 | fForeground = paintID; |
188 | 0 | } |
189 | 0 | void clearForegroundColor() { fHasForeground = false; } |
190 | | |
191 | 0 | bool hasBackground() const { return fHasBackground; } |
192 | 0 | SkPaint getBackground() const { |
193 | 0 | const SkPaint* paint = std::get_if<SkPaint>(&fBackground); |
194 | 0 | return paint ? *paint : SkPaint(); |
195 | 0 | } |
196 | 0 | ParagraphPainter::SkPaintOrID getBackgroundPaintOrID() const { |
197 | 0 | return fBackground; |
198 | 0 | } |
199 | 0 | void setBackgroundPaint(SkPaint paint) { |
200 | 0 | fHasBackground = true; |
201 | 0 | fBackground = std::move(paint); |
202 | 0 | } |
203 | | // DEPRECATED: prefer `setBackgroundPaint`. |
204 | 0 | void setBackgroundColor(SkPaint paint) { setBackgroundPaint(std::move(paint)); } |
205 | 0 | void setBackgroundPaintID(ParagraphPainter::PaintID paintID) { |
206 | 0 | fHasBackground = true; |
207 | 0 | fBackground = paintID; |
208 | 0 | } |
209 | 0 | void clearBackgroundColor() { fHasBackground = false; } |
210 | | |
211 | | // Decorations |
212 | 0 | Decoration getDecoration() const { return fDecoration; } |
213 | 0 | TextDecoration getDecorationType() const { return fDecoration.fType; } |
214 | 0 | TextDecorationMode getDecorationMode() const { return fDecoration.fMode; } |
215 | 0 | SkColor getDecorationColor() const { return fDecoration.fColor; } |
216 | 0 | TextDecorationStyle getDecorationStyle() const { return fDecoration.fStyle; } |
217 | 0 | SkScalar getDecorationThicknessMultiplier() const { |
218 | 0 | return fDecoration.fThicknessMultiplier; |
219 | 0 | } |
220 | 0 | void setDecoration(TextDecoration decoration) { fDecoration.fType = decoration; } |
221 | 0 | void setDecorationMode(TextDecorationMode mode) { fDecoration.fMode = mode; } |
222 | 0 | void setDecorationStyle(TextDecorationStyle style) { fDecoration.fStyle = style; } |
223 | 0 | void setDecorationColor(SkColor color) { fDecoration.fColor = color; } |
224 | 0 | void setDecorationThicknessMultiplier(SkScalar m) { fDecoration.fThicknessMultiplier = m; } |
225 | | |
226 | | // Weight/Width/Slant |
227 | 0 | SkFontStyle getFontStyle() const { return fFontStyle; } |
228 | 0 | void setFontStyle(SkFontStyle fontStyle) { fFontStyle = fontStyle; } |
229 | | |
230 | | // Shadows |
231 | 0 | size_t getShadowNumber() const { return fTextShadows.size(); } |
232 | 0 | std::vector<TextShadow> getShadows() const { return fTextShadows; } |
233 | 0 | void addShadow(TextShadow shadow) { fTextShadows.emplace_back(shadow); } |
234 | 0 | void resetShadows() { fTextShadows.clear(); } |
235 | | |
236 | | // Font features |
237 | 0 | size_t getFontFeatureNumber() const { return fFontFeatures.size(); } |
238 | 0 | std::vector<FontFeature> getFontFeatures() const { return fFontFeatures; } |
239 | | void addFontFeature(const SkString& fontFeature, int value) |
240 | 0 | { fFontFeatures.emplace_back(fontFeature, value); } |
241 | 0 | void resetFontFeatures() { fFontFeatures.clear(); } |
242 | | |
243 | | // Font arguments |
244 | 0 | const std::optional<FontArguments>& getFontArguments() const { return fFontArguments; } |
245 | | // The contents of the SkFontArguments will be copied into the TextStyle, |
246 | | // and the SkFontArguments can be safely deleted after setFontArguments returns. |
247 | | void setFontArguments(const std::optional<SkFontArguments>& args); |
248 | | |
249 | 0 | SkScalar getFontSize() const { return fFontSize; } |
250 | 0 | void setFontSize(SkScalar size) { fFontSize = size; } |
251 | | |
252 | 0 | const std::vector<SkString>& getFontFamilies() const { return fFontFamilies; } |
253 | 0 | void setFontFamilies(std::vector<SkString> families) { |
254 | 0 | fFontFamilies = std::move(families); |
255 | 0 | } |
256 | | |
257 | 0 | SkScalar getBaselineShift() const { return fBaselineShift; } |
258 | 0 | void setBaselineShift(SkScalar baselineShift) { fBaselineShift = baselineShift; } |
259 | | |
260 | 0 | void setHeight(SkScalar height) { fHeight = height; } |
261 | 0 | SkScalar getHeight() const { return fHeightOverride ? fHeight : 0; } |
262 | | |
263 | 0 | void setHeightOverride(bool heightOverride) { fHeightOverride = heightOverride; } |
264 | 0 | bool getHeightOverride() const { return fHeightOverride; } |
265 | | |
266 | 0 | void setHalfLeading(bool halfLeading) { fHalfLeading = halfLeading; } |
267 | 0 | bool getHalfLeading() const { return fHalfLeading; } |
268 | | |
269 | 0 | void setLetterSpacing(SkScalar letterSpacing) { fLetterSpacing = letterSpacing; } |
270 | 0 | SkScalar getLetterSpacing() const { return fLetterSpacing; } |
271 | | |
272 | 0 | void setWordSpacing(SkScalar wordSpacing) { fWordSpacing = wordSpacing; } |
273 | 0 | SkScalar getWordSpacing() const { return fWordSpacing; } |
274 | | |
275 | 0 | SkTypeface* getTypeface() const { return fTypeface.get(); } |
276 | 0 | sk_sp<SkTypeface> refTypeface() const { return fTypeface; } |
277 | 0 | void setTypeface(sk_sp<SkTypeface> typeface) { fTypeface = std::move(typeface); } |
278 | | |
279 | 0 | SkString getLocale() const { return fLocale; } |
280 | 0 | void setLocale(const SkString& locale) { fLocale = locale; } |
281 | | |
282 | 0 | TextBaseline getTextBaseline() const { return fTextBaseline; } |
283 | 0 | void setTextBaseline(TextBaseline baseline) { fTextBaseline = baseline; } |
284 | | |
285 | | void getFontMetrics(SkFontMetrics* metrics) const; |
286 | | |
287 | 0 | bool isPlaceholder() const { return fIsPlaceholder; } |
288 | 0 | void setPlaceholder() { fIsPlaceholder = true; } |
289 | | |
290 | | private: |
291 | | static const std::vector<SkString>* kDefaultFontFamilies; |
292 | | |
293 | | Decoration fDecoration = { |
294 | | TextDecoration::kNoDecoration, |
295 | | // TODO: switch back to kGaps when (if) switching flutter to skparagraph |
296 | | TextDecorationMode::kThrough, |
297 | | // It does not make sense to draw a transparent object, so we use this as a default |
298 | | // value to indicate no decoration color was set. |
299 | | SK_ColorTRANSPARENT, TextDecorationStyle::kSolid, |
300 | | // Thickness is applied as a multiplier to the default thickness of the font. |
301 | | 1.0f}; |
302 | | |
303 | | SkFontStyle fFontStyle; |
304 | | |
305 | | std::vector<SkString> fFontFamilies = *kDefaultFontFamilies; |
306 | | |
307 | | SkScalar fFontSize = 14.0; |
308 | | SkScalar fHeight = 1.0; |
309 | | bool fHeightOverride = false; |
310 | | SkScalar fBaselineShift = 0.0f; |
311 | | // true: half leading. |
312 | | // false: scale ascent/descent with fHeight. |
313 | | bool fHalfLeading = false; |
314 | | SkString fLocale = {}; |
315 | | SkScalar fLetterSpacing = 0.0; |
316 | | SkScalar fWordSpacing = 0.0; |
317 | | |
318 | | TextBaseline fTextBaseline = TextBaseline::kAlphabetic; |
319 | | |
320 | | SkColor fColor = SK_ColorWHITE; |
321 | | bool fHasBackground = false; |
322 | | ParagraphPainter::SkPaintOrID fBackground; |
323 | | bool fHasForeground = false; |
324 | | ParagraphPainter::SkPaintOrID fForeground; |
325 | | |
326 | | std::vector<TextShadow> fTextShadows; |
327 | | |
328 | | sk_sp<SkTypeface> fTypeface; |
329 | | bool fIsPlaceholder = false; |
330 | | |
331 | | std::vector<FontFeature> fFontFeatures; |
332 | | |
333 | | std::optional<FontArguments> fFontArguments; |
334 | | }; |
335 | | |
336 | | typedef size_t TextIndex; |
337 | | typedef SkRange<size_t> TextRange; |
338 | | const SkRange<size_t> EMPTY_TEXT = EMPTY_RANGE; |
339 | | |
340 | | struct Block { |
341 | 0 | Block() = default; |
342 | 0 | Block(size_t start, size_t end, const TextStyle& style) : fRange(start, end), fStyle(style) {} |
343 | 0 | Block(TextRange textRange, const TextStyle& style) : fRange(textRange), fStyle(style) {} |
344 | | |
345 | 0 | void add(TextRange tail) { |
346 | 0 | SkASSERT(fRange.end == tail.start); |
347 | 0 | fRange = TextRange(fRange.start, fRange.start + fRange.width() + tail.width()); |
348 | 0 | } |
349 | | |
350 | | TextRange fRange = EMPTY_RANGE; |
351 | | TextStyle fStyle; |
352 | | }; |
353 | | |
354 | | |
355 | | typedef size_t BlockIndex; |
356 | | typedef SkRange<size_t> BlockRange; |
357 | | const size_t EMPTY_BLOCK = EMPTY_INDEX; |
358 | | const SkRange<size_t> EMPTY_BLOCKS = EMPTY_RANGE; |
359 | | |
360 | | struct Placeholder { |
361 | | Placeholder() = default; |
362 | | Placeholder(size_t start, size_t end, const PlaceholderStyle& style, const TextStyle& textStyle, |
363 | | BlockRange blocksBefore, TextRange textBefore) |
364 | | : fRange(start, end) |
365 | | , fStyle(style) |
366 | | , fTextStyle(textStyle) |
367 | | , fBlocksBefore(blocksBefore) |
368 | 0 | , fTextBefore(textBefore) {} |
369 | | |
370 | | TextRange fRange = EMPTY_RANGE; |
371 | | PlaceholderStyle fStyle; |
372 | | TextStyle fTextStyle; |
373 | | BlockRange fBlocksBefore; |
374 | | TextRange fTextBefore; |
375 | | }; |
376 | | |
377 | | } // namespace textlayout |
378 | | } // namespace skia |
379 | | |
380 | | #endif // TextStyle_DEFINED |