Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
paragraph_builder.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "flutter/lib/ui/text/paragraph_builder.h"
6
7#include <cstring>
8
9#include "flutter/common/settings.h"
10#include "flutter/common/task_runners.h"
11#include "flutter/fml/logging.h"
12#include "flutter/fml/task_runner.h"
13#include "flutter/lib/ui/text/font_collection.h"
14#include "flutter/lib/ui/ui_dart_state.h"
15#include "flutter/lib/ui/window/platform_configuration.h"
16#include "flutter/third_party/txt/src/txt/font_style.h"
17#include "flutter/third_party/txt/src/txt/font_weight.h"
18#include "flutter/third_party/txt/src/txt/paragraph_style.h"
19#include "flutter/third_party/txt/src/txt/text_baseline.h"
20#include "flutter/third_party/txt/src/txt/text_decoration.h"
21#include "flutter/third_party/txt/src/txt/text_style.h"
22#include "third_party/icu/source/common/unicode/ustring.h"
29
30namespace flutter {
31namespace {
32
33// TextStyle
34
35const int kTSLeadingDistributionIndex = 0;
36const int kTSColorIndex = 1;
37const int kTSTextDecorationIndex = 2;
38const int kTSTextDecorationColorIndex = 3;
39const int kTSTextDecorationStyleIndex = 4;
40const int kTSFontWeightIndex = 5;
41const int kTSFontStyleIndex = 6;
42const int kTSTextBaselineIndex = 7;
43const int kTSTextDecorationThicknessIndex = 8;
44const int kTSFontFamilyIndex = 9;
45const int kTSFontSizeIndex = 10;
46const int kTSLetterSpacingIndex = 11;
47const int kTSWordSpacingIndex = 12;
48const int kTSHeightIndex = 13;
49const int kTSLocaleIndex = 14;
50const int kTSBackgroundIndex = 15;
51const int kTSForegroundIndex = 16;
52const int kTSTextShadowsIndex = 17;
53const int kTSFontFeaturesIndex = 18;
54const int kTSFontVariationsIndex = 19;
55
56const int kTSLeadingDistributionMask = 1 << kTSLeadingDistributionIndex;
57const int kTSColorMask = 1 << kTSColorIndex;
58const int kTSTextDecorationMask = 1 << kTSTextDecorationIndex;
59const int kTSTextDecorationColorMask = 1 << kTSTextDecorationColorIndex;
60const int kTSTextDecorationStyleMask = 1 << kTSTextDecorationStyleIndex;
61const int kTSTextDecorationThicknessMask = 1 << kTSTextDecorationThicknessIndex;
62const int kTSFontWeightMask = 1 << kTSFontWeightIndex;
63const int kTSFontStyleMask = 1 << kTSFontStyleIndex;
64const int kTSTextBaselineMask = 1 << kTSTextBaselineIndex;
65const int kTSFontFamilyMask = 1 << kTSFontFamilyIndex;
66const int kTSFontSizeMask = 1 << kTSFontSizeIndex;
67const int kTSLetterSpacingMask = 1 << kTSLetterSpacingIndex;
68const int kTSWordSpacingMask = 1 << kTSWordSpacingIndex;
69const int kTSHeightMask = 1 << kTSHeightIndex;
70const int kTSLocaleMask = 1 << kTSLocaleIndex;
71const int kTSBackgroundMask = 1 << kTSBackgroundIndex;
72const int kTSForegroundMask = 1 << kTSForegroundIndex;
73const int kTSTextShadowsMask = 1 << kTSTextShadowsIndex;
74const int kTSFontFeaturesMask = 1 << kTSFontFeaturesIndex;
75const int kTSFontVariationsMask = 1 << kTSFontVariationsIndex;
76
77// ParagraphStyle
78
79const int kPSTextAlignIndex = 1;
80const int kPSTextDirectionIndex = 2;
81const int kPSFontWeightIndex = 3;
82const int kPSFontStyleIndex = 4;
83const int kPSMaxLinesIndex = 5;
84const int kPSTextHeightBehaviorIndex = 6;
85const int kPSFontFamilyIndex = 7;
86const int kPSFontSizeIndex = 8;
87const int kPSHeightIndex = 9;
88const int kPSStrutStyleIndex = 10;
89const int kPSEllipsisIndex = 11;
90const int kPSLocaleIndex = 12;
91
92const int kPSTextAlignMask = 1 << kPSTextAlignIndex;
93const int kPSTextDirectionMask = 1 << kPSTextDirectionIndex;
94const int kPSFontWeightMask = 1 << kPSFontWeightIndex;
95const int kPSFontStyleMask = 1 << kPSFontStyleIndex;
96const int kPSMaxLinesMask = 1 << kPSMaxLinesIndex;
97const int kPSFontFamilyMask = 1 << kPSFontFamilyIndex;
98const int kPSFontSizeMask = 1 << kPSFontSizeIndex;
99const int kPSHeightMask = 1 << kPSHeightIndex;
100const int kPSTextHeightBehaviorMask = 1 << kPSTextHeightBehaviorIndex;
101const int kPSStrutStyleMask = 1 << kPSStrutStyleIndex;
102const int kPSEllipsisMask = 1 << kPSEllipsisIndex;
103const int kPSLocaleMask = 1 << kPSLocaleIndex;
104
105// TextShadows decoding
106
107constexpr uint32_t kColorDefault = 0xFF000000;
108constexpr uint32_t kBytesPerShadow = 16;
109constexpr uint32_t kShadowPropertiesCount = 4;
110constexpr uint32_t kColorOffset = 0;
111constexpr uint32_t kXOffset = 1;
112constexpr uint32_t kYOffset = 2;
113constexpr uint32_t kBlurOffset = 3;
114
115// FontFeature decoding
116constexpr uint32_t kBytesPerFontFeature = 8;
117constexpr uint32_t kFontFeatureTagLength = 4;
118
119// FontVariation decoding
120constexpr uint32_t kBytesPerFontVariation = 8;
121constexpr uint32_t kFontVariationTagLength = 4;
122
123// Strut decoding
124const int kSFontWeightIndex = 0;
125const int kSFontStyleIndex = 1;
126const int kSFontFamilyIndex = 2;
127const int kSLeadingDistributionIndex = 3;
128const int kSFontSizeIndex = 4;
129const int kSHeightIndex = 5;
130const int kSLeadingIndex = 6;
131const int kSForceStrutHeightIndex = 7;
132
133const int kSFontWeightMask = 1 << kSFontWeightIndex;
134const int kSFontStyleMask = 1 << kSFontStyleIndex;
135const int kSFontFamilyMask = 1 << kSFontFamilyIndex;
136const int kSLeadingDistributionMask = 1 << kSLeadingDistributionIndex;
137const int kSFontSizeMask = 1 << kSFontSizeIndex;
138const int kSHeightMask = 1 << kSHeightIndex;
139const int kSLeadingMask = 1 << kSLeadingIndex;
140const int kSForceStrutHeightMask = 1 << kSForceStrutHeightIndex;
141
142} // namespace
143
145
147 Dart_Handle encoded_handle,
148 Dart_Handle strutData,
149 const std::string& fontFamily,
150 const std::vector<std::string>& strutFontFamilies,
151 double fontSize,
152 double height,
153 const std::u16string& ellipsis,
154 const std::string& locale) {
156 auto res = fml::MakeRefCounted<ParagraphBuilder>(
157 encoded_handle, strutData, fontFamily, strutFontFamilies, fontSize,
158 height, ellipsis, locale);
159 res->AssociateWithDartWrapper(wrapper);
160}
161
162// returns true if there is a font family defined. Font family is the only
163// parameter passed directly.
164void decodeStrut(Dart_Handle strut_data,
165 const std::vector<std::string>& strut_font_families,
166 txt::ParagraphStyle& paragraph_style) { // NOLINT
167 if (strut_data == Dart_Null()) {
168 return;
169 }
170
171 tonic::DartByteData byte_data(strut_data);
172 if (byte_data.length_in_bytes() == 0) {
173 return;
174 }
175 paragraph_style.strut_enabled = true;
176
177 const uint8_t* uint8_data = static_cast<const uint8_t*>(byte_data.data());
178 uint8_t mask = uint8_data[0];
179
180 // Data is stored in order of increasing size, eg, 8 bit ints will be before
181 // any 32 bit ints. In addition, the order of decoding is the same order
182 // as it is encoded, and the order is used to maintain consistency.
183 size_t byte_count = 1;
184 if (mask & kSFontWeightMask) {
185 paragraph_style.strut_font_weight =
186 static_cast<txt::FontWeight>(uint8_data[byte_count++]);
187 }
188 if (mask & kSFontStyleMask) {
189 paragraph_style.strut_font_style =
190 static_cast<txt::FontStyle>(uint8_data[byte_count++]);
191 }
192
193 paragraph_style.strut_half_leading = mask & kSLeadingDistributionMask;
194
195 std::vector<float> float_data;
196 float_data.resize((byte_data.length_in_bytes() - byte_count) / 4);
197 memcpy(float_data.data(),
198 static_cast<const char*>(byte_data.data()) + byte_count,
199 byte_data.length_in_bytes() - byte_count);
200 size_t float_count = 0;
201 if (mask & kSFontSizeMask) {
202 paragraph_style.strut_font_size = float_data[float_count++];
203 }
204 if (mask & kSHeightMask) {
205 paragraph_style.strut_height = float_data[float_count++];
206 paragraph_style.strut_has_height_override = true;
207 }
208 if (mask & kSLeadingMask) {
209 paragraph_style.strut_leading = float_data[float_count++];
210 }
211
212 // The boolean is stored as the last bit in the bitmask, as null
213 // and false have the same behavior.
214 paragraph_style.force_strut_height = mask & kSForceStrutHeightMask;
215
216 if (mask & kSFontFamilyMask) {
217 paragraph_style.strut_font_families = strut_font_families;
218 } else {
219 // Provide an empty font name so that the platform default font will be
220 // used.
221 paragraph_style.strut_font_families.push_back("");
222 }
223}
224
225ParagraphBuilder::ParagraphBuilder(
226 Dart_Handle encoded_data,
227 Dart_Handle strutData,
228 const std::string& fontFamily,
229 const std::vector<std::string>& strutFontFamilies,
230 double fontSize,
231 double height,
232 const std::u16string& ellipsis,
233 const std::string& locale) {
234 int32_t mask = 0;
236 {
237 tonic::Int32List encoded(encoded_data);
238
239 mask = encoded[0];
240
241 if (mask & kPSTextAlignMask) {
242 style.text_align =
243 static_cast<txt::TextAlign>(encoded[kPSTextAlignIndex]);
244 }
245
246 if (mask & kPSTextDirectionMask) {
247 style.text_direction =
248 static_cast<txt::TextDirection>(encoded[kPSTextDirectionIndex]);
249 }
250
251 if (mask & kPSFontWeightMask) {
252 style.font_weight =
253 static_cast<txt::FontWeight>(encoded[kPSFontWeightIndex]);
254 }
255
256 if (mask & kPSFontStyleMask) {
257 style.font_style =
258 static_cast<txt::FontStyle>(encoded[kPSFontStyleIndex]);
259 }
260
261 if (mask & kPSFontFamilyMask) {
262 style.font_family = fontFamily;
263 }
264
265 if (mask & kPSFontSizeMask) {
266 style.font_size = fontSize;
267 }
268
269 if (mask & kPSHeightMask) {
270 style.height = height;
271 style.has_height_override = true;
272 }
273
274 if (mask & kPSTextHeightBehaviorMask) {
275 style.text_height_behavior = encoded[kPSTextHeightBehaviorIndex];
276 }
277
278 if (mask & kPSMaxLinesMask) {
279 style.max_lines = encoded[kPSMaxLinesIndex];
280 }
281 }
282
283 if (mask & kPSStrutStyleMask) {
284 decodeStrut(strutData, strutFontFamilies, style);
285 }
286
287 if (mask & kPSEllipsisMask) {
288 style.ellipsis = ellipsis;
289 }
290
291 if (mask & kPSLocaleMask) {
292 style.locale = locale;
293 }
294
295 FontCollection& font_collection = UIDartState::Current()
297 ->client()
299
300 auto impeller_enabled = UIDartState::Current()->IsImpellerEnabled();
301 m_paragraph_builder_ = txt::ParagraphBuilder::CreateSkiaBuilder(
302 style, font_collection.GetFontCollection(), impeller_enabled);
303}
304
306
308 Dart_Handle shadows_data,
309 std::vector<txt::TextShadow>& decoded_shadows) { // NOLINT
310 decoded_shadows.clear();
311
312 tonic::DartByteData byte_data(shadows_data);
313 FML_CHECK(byte_data.length_in_bytes() % kBytesPerShadow == 0);
314
315 const uint32_t* uint_data = static_cast<const uint32_t*>(byte_data.data());
316 const float* float_data = static_cast<const float*>(byte_data.data());
317
318 size_t shadow_count = byte_data.length_in_bytes() / kBytesPerShadow;
319 size_t shadow_count_offset = 0;
320 for (size_t shadow_index = 0; shadow_index < shadow_count; ++shadow_index) {
321 shadow_count_offset = shadow_index * kShadowPropertiesCount;
322 SkColor color =
323 uint_data[shadow_count_offset + kColorOffset] ^ kColorDefault;
324 decoded_shadows.emplace_back(
325 color,
326 SkPoint::Make(float_data[shadow_count_offset + kXOffset],
327 float_data[shadow_count_offset + kYOffset]),
328 float_data[shadow_count_offset + kBlurOffset]);
329 }
330}
331
332void decodeFontFeatures(Dart_Handle font_features_data,
333 txt::FontFeatures& font_features) { // NOLINT
334 tonic::DartByteData byte_data(font_features_data);
335 FML_CHECK(byte_data.length_in_bytes() % kBytesPerFontFeature == 0);
336
337 size_t feature_count = byte_data.length_in_bytes() / kBytesPerFontFeature;
338 for (size_t feature_index = 0; feature_index < feature_count;
339 ++feature_index) {
340 size_t feature_offset = feature_index * kBytesPerFontFeature;
341 const char* feature_bytes =
342 static_cast<const char*>(byte_data.data()) + feature_offset;
343 std::string tag(feature_bytes, kFontFeatureTagLength);
344 int32_t value = *(reinterpret_cast<const int32_t*>(feature_bytes +
345 kFontFeatureTagLength));
346 font_features.SetFeature(tag, value);
347 }
348}
349
350void decodeFontVariations(Dart_Handle font_variations_data,
351 txt::FontVariations& font_variations) { // NOLINT
352 tonic::DartByteData byte_data(font_variations_data);
353 FML_CHECK(byte_data.length_in_bytes() % kBytesPerFontVariation == 0);
354
355 size_t variation_count = byte_data.length_in_bytes() / kBytesPerFontVariation;
356 for (size_t variation_index = 0; variation_index < variation_count;
357 ++variation_index) {
358 size_t variation_offset = variation_index * kBytesPerFontVariation;
359 const char* variation_bytes =
360 static_cast<const char*>(byte_data.data()) + variation_offset;
361 std::string tag(variation_bytes, kFontVariationTagLength);
362 float value = *(reinterpret_cast<const float*>(variation_bytes +
363 kFontVariationTagLength));
364 font_variations.SetAxisValue(tag, value);
365 }
366}
367
368void ParagraphBuilder::pushStyle(const tonic::Int32List& encoded,
369 const std::vector<std::string>& fontFamilies,
370 double fontSize,
371 double letterSpacing,
372 double wordSpacing,
373 double height,
374 double decorationThickness,
375 const std::string& locale,
376 Dart_Handle background_objects,
377 Dart_Handle background_data,
378 Dart_Handle foreground_objects,
379 Dart_Handle foreground_data,
380 Dart_Handle shadows_data,
381 Dart_Handle font_features_data,
382 Dart_Handle font_variations_data) {
383 FML_DCHECK(encoded.num_elements() == 9);
384
385 int32_t mask = encoded[0];
386
387 // Set to use the properties of the previous style if the property is not
388 // explicitly given.
389 txt::TextStyle style = m_paragraph_builder_->PeekStyle();
390
391 style.half_leading = mask & kTSLeadingDistributionMask;
392 // Only change the style property from the previous value if a new explicitly
393 // set value is available
394 if (mask & kTSColorMask) {
395 style.color = encoded[kTSColorIndex];
396 }
397
398 if (mask & kTSTextDecorationMask) {
399 style.decoration =
400 static_cast<txt::TextDecoration>(encoded[kTSTextDecorationIndex]);
401 }
402
403 if (mask & kTSTextDecorationColorMask) {
404 style.decoration_color = encoded[kTSTextDecorationColorIndex];
405 }
406
407 if (mask & kTSTextDecorationStyleMask) {
408 style.decoration_style = static_cast<txt::TextDecorationStyle>(
409 encoded[kTSTextDecorationStyleIndex]);
410 }
411
412 if (mask & kTSTextDecorationThicknessMask) {
413 style.decoration_thickness_multiplier = decorationThickness;
414 }
415
416 if (mask & kTSTextBaselineMask) {
417 // TODO(abarth): Implement TextBaseline. The CSS version of this
418 // property wasn't wired up either.
419 }
420
421 if (mask & (kTSFontWeightMask | kTSFontStyleMask | kTSFontSizeMask |
422 kTSLetterSpacingMask | kTSWordSpacingMask)) {
423 if (mask & kTSFontWeightMask) {
424 style.font_weight =
425 static_cast<txt::FontWeight>(encoded[kTSFontWeightIndex]);
426 }
427
428 if (mask & kTSFontStyleMask) {
429 style.font_style =
430 static_cast<txt::FontStyle>(encoded[kTSFontStyleIndex]);
431 }
432
433 if (mask & kTSFontSizeMask) {
434 style.font_size = fontSize;
435 }
436
437 if (mask & kTSLetterSpacingMask) {
438 style.letter_spacing = letterSpacing;
439 }
440
441 if (mask & kTSWordSpacingMask) {
442 style.word_spacing = wordSpacing;
443 }
444 }
445
446 if (mask & kTSHeightMask) {
447 style.height = height;
448 style.has_height_override = true;
449 }
450
451 if (mask & kTSLocaleMask) {
452 style.locale = locale;
453 }
454
455 if (mask & kTSBackgroundMask) {
456 Paint background(background_objects, background_data);
457 if (background.isNotNull()) {
458 DlPaint dl_paint;
459 background.toDlPaint(dl_paint);
460 style.background = dl_paint;
461 }
462 }
463
464 if (mask & kTSForegroundMask) {
465 Paint foreground(foreground_objects, foreground_data);
466 if (foreground.isNotNull()) {
467 DlPaint dl_paint;
468 foreground.toDlPaint(dl_paint);
469 style.foreground = dl_paint;
470 }
471 }
472
473 if (mask & kTSTextShadowsMask) {
474 decodeTextShadows(shadows_data, style.text_shadows);
475 }
476
477 if (mask & kTSFontFamilyMask) {
478 // The child style's font families override the parent's font families.
479 // If the child's fonts are not available, then the font collection will
480 // use the system fallback fonts (not the parent's fonts).
481 style.font_families = fontFamilies;
482 }
483
484 if (mask & kTSFontFeaturesMask) {
485 decodeFontFeatures(font_features_data, style.font_features);
486 }
487
488 if (mask & kTSFontVariationsMask) {
489 decodeFontVariations(font_variations_data, style.font_variations);
490 }
491
492 m_paragraph_builder_->PushStyle(style);
493}
494
496 m_paragraph_builder_->Pop();
497}
498
500 if (text.empty()) {
501 return Dart_Null();
502 }
503
504 // Use ICU to validate the UTF-16 input. Calling u_strToUTF8 with a null
505 // output buffer will return U_BUFFER_OVERFLOW_ERROR if the input is well
506 // formed.
507 const UChar* text_ptr = reinterpret_cast<const UChar*>(text.data());
508 UErrorCode error_code = U_ZERO_ERROR;
509 u_strToUTF8(nullptr, 0, nullptr, text_ptr, text.size(), &error_code);
510 if (error_code != U_BUFFER_OVERFLOW_ERROR) {
511 return tonic::ToDart("string is not well-formed UTF-16");
512 }
513
514 m_paragraph_builder_->AddText(text);
515
516 return Dart_Null();
517}
518
520 double height,
521 unsigned alignment,
522 double baseline_offset,
523 unsigned baseline) {
524 txt::PlaceholderRun placeholder_run(
525 width, height, static_cast<txt::PlaceholderAlignment>(alignment),
526 static_cast<txt::TextBaseline>(baseline), baseline_offset);
527
528 m_paragraph_builder_->AddPlaceholder(placeholder_run);
529}
530
531void ParagraphBuilder::build(Dart_Handle paragraph_handle) {
532 Paragraph::Create(paragraph_handle, m_paragraph_builder_->Build());
533 m_paragraph_builder_.reset();
535}
536
537} // namespace flutter
SkColor4f color
uint32_t SkColor
Definition SkColor.h:37
void toDlPaint(DlPaint &paint) const
Definition paint.cc:197
bool isNotNull() const
Definition paint.h:26
static void Create(Dart_Handle wrapper, Dart_Handle encoded_handle, Dart_Handle strutData, const std::string &fontFamily, const std::vector< std::string > &strutFontFamilies, double fontSize, double height, const std::u16string &ellipsis, const std::string &locale)
void build(Dart_Handle paragraph_handle)
void pushStyle(const tonic::Int32List &encoded, const std::vector< std::string > &fontFamilies, double fontSize, double letterSpacing, double wordSpacing, double height, double decorationThickness, const std::string &locale, Dart_Handle background_objects, Dart_Handle background_data, Dart_Handle foreground_objects, Dart_Handle foreground_data, Dart_Handle shadows_data, Dart_Handle font_features_data, Dart_Handle font_variations_data)
void addPlaceholder(double width, double height, unsigned alignment, double baseline_offset, unsigned baseline)
Dart_Handle addText(const std::u16string &text)
static void Create(Dart_Handle paragraph_handle, std::unique_ptr< txt::Paragraph > txt_paragraph)
Definition paragraph.h:20
virtual FontCollection & GetFontCollection()=0
Returns the current collection of fonts available on the platform.
PlatformConfigurationClient * client() const
Access to the platform configuration client (which typically is implemented by the RuntimeController)...
PlatformConfiguration * platform_configuration() const
bool IsImpellerEnabled() const
Whether Impeller is enabled for this application.
static UIDartState * Current()
static void ThrowIfUIOperationsProhibited()
const void * data() const
size_t length_in_bytes() const
void SetFeature(std::string tag, int value)
void SetAxisValue(std::string tag, float value)
static std::unique_ptr< ParagraphBuilder > CreateSkiaBuilder(const ParagraphStyle &style, std::shared_ptr< FontCollection > font_collection, const bool impeller_enabled)
Creates a |ParagraphBuilder| based on Skia's text layout module.
std::u16string ellipsis
TextDirection text_direction
FontWeight strut_font_weight
std::vector< std::string > strut_font_families
std::vector< std::string > font_families
Definition text_style.h:52
std::optional< flutter::DlPaint > foreground
Definition text_style.h:60
TextDecorationStyle decoration_style
Definition text_style.h:43
FontStyle font_style
Definition text_style.h:47
SkColor decoration_color
Definition text_style.h:42
double word_spacing
Definition text_style.h:55
FontFeatures font_features
Definition text_style.h:64
FontVariations font_variations
Definition text_style.h:65
bool has_height_override
Definition text_style.h:57
std::vector< TextShadow > text_shadows
Definition text_style.h:63
double font_size
Definition text_style.h:53
std::string locale
Definition text_style.h:58
SkColor color
Definition text_style.h:38
double decoration_thickness_multiplier
Definition text_style.h:45
FontWeight font_weight
Definition text_style.h:46
std::optional< flutter::DlPaint > background
Definition text_style.h:59
double letter_spacing
Definition text_style.h:54
struct _Dart_Handle * Dart_Handle
Definition dart_api.h:258
DART_EXPORT Dart_Handle Dart_Null(void)
#define IMPLEMENT_WRAPPERTYPEINFO(LibraryName, ClassName)
uint8_t value
#define FML_CHECK(condition)
Definition logging.h:85
#define FML_DCHECK(condition)
Definition logging.h:103
std::u16string text
constexpr uint32_t kColorDefault
Definition paint.cc:48
void decodeStrut(Dart_Handle strut_data, const std::vector< std::string > &strut_font_families, txt::ParagraphStyle &paragraph_style)
void decodeFontVariations(Dart_Handle font_variations_data, txt::FontVariations &font_variations)
void decodeTextShadows(Dart_Handle shadows_data, std::vector< txt::TextShadow > &decoded_shadows)
void decodeFontFeatures(Dart_Handle font_features_data, txt::FontFeatures &font_features)
Dart_Handle ToDart(const T &object)
PlaceholderAlignment
Where to vertically align the placeholder relative to the surrounding text.
FontWeight
Definition font_weight.h:22
FontStyle
Definition font_style.h:22
TextDecorationStyle
int32_t height
int32_t width
static constexpr SkPoint Make(float x, float y)