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"
23#include "third_party/skia/include/core/SkColor.h"
24#include "third_party/tonic/converter/dart_converter.h"
25#include "third_party/tonic/dart_args.h"
26#include "third_party/tonic/dart_binding_macros.h"
27#include "third_party/tonic/dart_library_natives.h"
28#include "third_party/tonic/typed_data/dart_byte_data.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
144IMPLEMENT_WRAPPERTYPEINFO(ui, ParagraphBuilder);
145
146void ParagraphBuilder::Create(Dart_Handle wrapper,
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,
155 bool applyRoundingHack) {
156 UIDartState::ThrowIfUIOperationsProhibited();
157 auto res = fml::MakeRefCounted<ParagraphBuilder>(
158 args&: encoded_handle, args&: strutData, args: fontFamily, args: strutFontFamilies, args&: fontSize,
159 args&: height, args: ellipsis, args: locale, args&: applyRoundingHack);
160 res->AssociateWithDartWrapper(wrappable: wrapper);
161}
162
163// returns true if there is a font family defined. Font family is the only
164// parameter passed directly.
165void decodeStrut(Dart_Handle strut_data,
166 const std::vector<std::string>& strut_font_families,
167 txt::ParagraphStyle& paragraph_style) { // NOLINT
168 if (strut_data == Dart_Null()) {
169 return;
170 }
171
172 tonic::DartByteData byte_data(strut_data);
173 if (byte_data.length_in_bytes() == 0) {
174 return;
175 }
176 paragraph_style.strut_enabled = true;
177
178 const uint8_t* uint8_data = static_cast<const uint8_t*>(byte_data.data());
179 uint8_t mask = uint8_data[0];
180
181 // Data is stored in order of increasing size, eg, 8 bit ints will be before
182 // any 32 bit ints. In addition, the order of decoding is the same order
183 // as it is encoded, and the order is used to maintain consistency.
184 size_t byte_count = 1;
185 if (mask & kSFontWeightMask) {
186 paragraph_style.strut_font_weight =
187 static_cast<txt::FontWeight>(uint8_data[byte_count++]);
188 }
189 if (mask & kSFontStyleMask) {
190 paragraph_style.strut_font_style =
191 static_cast<txt::FontStyle>(uint8_data[byte_count++]);
192 }
193
194 paragraph_style.strut_half_leading = mask & kSLeadingDistributionMask;
195
196 std::vector<float> float_data;
197 float_data.resize(sz: (byte_data.length_in_bytes() - byte_count) / 4);
198 memcpy(dest: float_data.data(),
199 src: static_cast<const char*>(byte_data.data()) + byte_count,
200 n: byte_data.length_in_bytes() - byte_count);
201 size_t float_count = 0;
202 if (mask & kSFontSizeMask) {
203 paragraph_style.strut_font_size = float_data[float_count++];
204 }
205 if (mask & kSHeightMask) {
206 paragraph_style.strut_height = float_data[float_count++];
207 paragraph_style.strut_has_height_override = true;
208 }
209 if (mask & kSLeadingMask) {
210 paragraph_style.strut_leading = float_data[float_count++];
211 }
212
213 // The boolean is stored as the last bit in the bitmask, as null
214 // and false have the same behavior.
215 paragraph_style.force_strut_height = mask & kSForceStrutHeightMask;
216
217 if (mask & kSFontFamilyMask) {
218 paragraph_style.strut_font_families = strut_font_families;
219 } else {
220 // Provide an empty font name so that the platform default font will be
221 // used.
222 paragraph_style.strut_font_families.push_back(x: "");
223 }
224}
225
226ParagraphBuilder::ParagraphBuilder(
227 Dart_Handle encoded_data,
228 Dart_Handle strutData,
229 const std::string& fontFamily,
230 const std::vector<std::string>& strutFontFamilies,
231 double fontSize,
232 double height,
233 const std::u16string& ellipsis,
234 const std::string& locale,
235 bool applyRoundingHack) {
236 int32_t mask = 0;
237 txt::ParagraphStyle style;
238 {
239 tonic::Int32List encoded(encoded_data);
240
241 mask = encoded[0];
242
243 if (mask & kPSTextAlignMask) {
244 style.text_align =
245 static_cast<txt::TextAlign>(encoded[kPSTextAlignIndex]);
246 }
247
248 if (mask & kPSTextDirectionMask) {
249 style.text_direction =
250 static_cast<txt::TextDirection>(encoded[kPSTextDirectionIndex]);
251 }
252
253 if (mask & kPSFontWeightMask) {
254 style.font_weight =
255 static_cast<txt::FontWeight>(encoded[kPSFontWeightIndex]);
256 }
257
258 if (mask & kPSFontStyleMask) {
259 style.font_style =
260 static_cast<txt::FontStyle>(encoded[kPSFontStyleIndex]);
261 }
262
263 if (mask & kPSFontFamilyMask) {
264 style.font_family = fontFamily;
265 }
266
267 if (mask & kPSFontSizeMask) {
268 style.font_size = fontSize;
269 }
270
271 if (mask & kPSHeightMask) {
272 style.height = height;
273 style.has_height_override = true;
274 }
275
276 if (mask & kPSTextHeightBehaviorMask) {
277 style.text_height_behavior = encoded[kPSTextHeightBehaviorIndex];
278 }
279
280 if (mask & kPSMaxLinesMask) {
281 style.max_lines = encoded[kPSMaxLinesIndex];
282 }
283 }
284
285 if (mask & kPSStrutStyleMask) {
286 decodeStrut(strut_data: strutData, strut_font_families: strutFontFamilies, paragraph_style&: style);
287 }
288
289 if (mask & kPSEllipsisMask) {
290 style.ellipsis = ellipsis;
291 }
292
293 if (mask & kPSLocaleMask) {
294 style.locale = locale;
295 }
296 style.apply_rounding_hack = applyRoundingHack;
297
298 FontCollection& font_collection = UIDartState::Current()
299 ->platform_configuration()
300 ->client()
301 ->GetFontCollection();
302
303 m_paragraphBuilder = txt::ParagraphBuilder::CreateSkiaBuilder(
304 style, font_collection: font_collection.GetFontCollection());
305}
306
307ParagraphBuilder::~ParagraphBuilder() = default;
308
309void decodeTextShadows(
310 Dart_Handle shadows_data,
311 std::vector<txt::TextShadow>& decoded_shadows) { // NOLINT
312 decoded_shadows.clear();
313
314 tonic::DartByteData byte_data(shadows_data);
315 FML_CHECK(byte_data.length_in_bytes() % kBytesPerShadow == 0);
316
317 const uint32_t* uint_data = static_cast<const uint32_t*>(byte_data.data());
318 const float* float_data = static_cast<const float*>(byte_data.data());
319
320 size_t shadow_count = byte_data.length_in_bytes() / kBytesPerShadow;
321 size_t shadow_count_offset = 0;
322 for (size_t shadow_index = 0; shadow_index < shadow_count; ++shadow_index) {
323 shadow_count_offset = shadow_index * kShadowPropertiesCount;
324 SkColor color =
325 uint_data[shadow_count_offset + kColorOffset] ^ kColorDefault;
326 decoded_shadows.emplace_back(
327 args&: color,
328 args: SkPoint::Make(x: float_data[shadow_count_offset + kXOffset],
329 y: float_data[shadow_count_offset + kYOffset]),
330 args: float_data[shadow_count_offset + kBlurOffset]);
331 }
332}
333
334void decodeFontFeatures(Dart_Handle font_features_data,
335 txt::FontFeatures& font_features) { // NOLINT
336 tonic::DartByteData byte_data(font_features_data);
337 FML_CHECK(byte_data.length_in_bytes() % kBytesPerFontFeature == 0);
338
339 size_t feature_count = byte_data.length_in_bytes() / kBytesPerFontFeature;
340 for (size_t feature_index = 0; feature_index < feature_count;
341 ++feature_index) {
342 size_t feature_offset = feature_index * kBytesPerFontFeature;
343 const char* feature_bytes =
344 static_cast<const char*>(byte_data.data()) + feature_offset;
345 std::string tag(feature_bytes, kFontFeatureTagLength);
346 int32_t value = *(reinterpret_cast<const int32_t*>(feature_bytes +
347 kFontFeatureTagLength));
348 font_features.SetFeature(tag, value);
349 }
350}
351
352void decodeFontVariations(Dart_Handle font_variations_data,
353 txt::FontVariations& font_variations) { // NOLINT
354 tonic::DartByteData byte_data(font_variations_data);
355 FML_CHECK(byte_data.length_in_bytes() % kBytesPerFontVariation == 0);
356
357 size_t variation_count = byte_data.length_in_bytes() / kBytesPerFontVariation;
358 for (size_t variation_index = 0; variation_index < variation_count;
359 ++variation_index) {
360 size_t variation_offset = variation_index * kBytesPerFontVariation;
361 const char* variation_bytes =
362 static_cast<const char*>(byte_data.data()) + variation_offset;
363 std::string tag(variation_bytes, kFontVariationTagLength);
364 float value = *(reinterpret_cast<const float*>(variation_bytes +
365 kFontVariationTagLength));
366 font_variations.SetAxisValue(tag, value);
367 }
368}
369
370void ParagraphBuilder::pushStyle(const tonic::Int32List& encoded,
371 const std::vector<std::string>& fontFamilies,
372 double fontSize,
373 double letterSpacing,
374 double wordSpacing,
375 double height,
376 double decorationThickness,
377 const std::string& locale,
378 Dart_Handle background_objects,
379 Dart_Handle background_data,
380 Dart_Handle foreground_objects,
381 Dart_Handle foreground_data,
382 Dart_Handle shadows_data,
383 Dart_Handle font_features_data,
384 Dart_Handle font_variations_data) {
385 FML_DCHECK(encoded.num_elements() == 9);
386
387 int32_t mask = encoded[0];
388
389 // Set to use the properties of the previous style if the property is not
390 // explicitly given.
391 txt::TextStyle style = m_paragraphBuilder->PeekStyle();
392
393 style.half_leading = mask & kTSLeadingDistributionMask;
394 // Only change the style property from the previous value if a new explicitly
395 // set value is available
396 if (mask & kTSColorMask) {
397 style.color = encoded[kTSColorIndex];
398 }
399
400 if (mask & kTSTextDecorationMask) {
401 style.decoration =
402 static_cast<txt::TextDecoration>(encoded[kTSTextDecorationIndex]);
403 }
404
405 if (mask & kTSTextDecorationColorMask) {
406 style.decoration_color = encoded[kTSTextDecorationColorIndex];
407 }
408
409 if (mask & kTSTextDecorationStyleMask) {
410 style.decoration_style = static_cast<txt::TextDecorationStyle>(
411 encoded[kTSTextDecorationStyleIndex]);
412 }
413
414 if (mask & kTSTextDecorationThicknessMask) {
415 style.decoration_thickness_multiplier = decorationThickness;
416 }
417
418 if (mask & kTSTextBaselineMask) {
419 // TODO(abarth): Implement TextBaseline. The CSS version of this
420 // property wasn't wired up either.
421 }
422
423 if (mask & (kTSFontWeightMask | kTSFontStyleMask | kTSFontSizeMask |
424 kTSLetterSpacingMask | kTSWordSpacingMask)) {
425 if (mask & kTSFontWeightMask) {
426 style.font_weight =
427 static_cast<txt::FontWeight>(encoded[kTSFontWeightIndex]);
428 }
429
430 if (mask & kTSFontStyleMask) {
431 style.font_style =
432 static_cast<txt::FontStyle>(encoded[kTSFontStyleIndex]);
433 }
434
435 if (mask & kTSFontSizeMask) {
436 style.font_size = fontSize;
437 }
438
439 if (mask & kTSLetterSpacingMask) {
440 style.letter_spacing = letterSpacing;
441 }
442
443 if (mask & kTSWordSpacingMask) {
444 style.word_spacing = wordSpacing;
445 }
446 }
447
448 if (mask & kTSHeightMask) {
449 style.height = height;
450 style.has_height_override = true;
451 }
452
453 if (mask & kTSLocaleMask) {
454 style.locale = locale;
455 }
456
457 if (mask & kTSBackgroundMask) {
458 Paint background(background_objects, background_data);
459 if (background.isNotNull()) {
460 DlPaint dl_paint;
461 background.toDlPaint(paint&: dl_paint);
462 style.background = dl_paint;
463 }
464 }
465
466 if (mask & kTSForegroundMask) {
467 Paint foreground(foreground_objects, foreground_data);
468 if (foreground.isNotNull()) {
469 DlPaint dl_paint;
470 foreground.toDlPaint(paint&: dl_paint);
471 style.foreground = dl_paint;
472 }
473 }
474
475 if (mask & kTSTextShadowsMask) {
476 decodeTextShadows(shadows_data, decoded_shadows&: style.text_shadows);
477 }
478
479 if (mask & kTSFontFamilyMask) {
480 // The child style's font families override the parent's font families.
481 // If the child's fonts are not available, then the font collection will
482 // use the system fallback fonts (not the parent's fonts).
483 style.font_families = fontFamilies;
484 }
485
486 if (mask & kTSFontFeaturesMask) {
487 decodeFontFeatures(font_features_data, font_features&: style.font_features);
488 }
489
490 if (mask & kTSFontVariationsMask) {
491 decodeFontVariations(font_variations_data, font_variations&: style.font_variations);
492 }
493
494 m_paragraphBuilder->PushStyle(style);
495}
496
497void ParagraphBuilder::pop() {
498 m_paragraphBuilder->Pop();
499}
500
501Dart_Handle ParagraphBuilder::addText(const std::u16string& text) {
502 if (text.empty()) {
503 return Dart_Null();
504 }
505
506 // Use ICU to validate the UTF-16 input. Calling u_strToUTF8 with a null
507 // output buffer will return U_BUFFER_OVERFLOW_ERROR if the input is well
508 // formed.
509 const UChar* text_ptr = reinterpret_cast<const UChar*>(text.data());
510 UErrorCode error_code = U_ZERO_ERROR;
511 u_strToUTF8(dest: nullptr, destCapacity: 0, pDestLength: nullptr, src: text_ptr, srcLength: text.size(), pErrorCode: &error_code);
512 if (error_code != U_BUFFER_OVERFLOW_ERROR) {
513 return tonic::ToDart(val: "string is not well-formed UTF-16");
514 }
515
516 m_paragraphBuilder->AddText(text);
517
518 return Dart_Null();
519}
520
521void ParagraphBuilder::addPlaceholder(double width,
522 double height,
523 unsigned alignment,
524 double baseline_offset,
525 unsigned baseline) {
526 txt::PlaceholderRun placeholder_run(
527 width, height, static_cast<txt::PlaceholderAlignment>(alignment),
528 static_cast<txt::TextBaseline>(baseline), baseline_offset);
529
530 m_paragraphBuilder->AddPlaceholder(span&: placeholder_run);
531}
532
533void ParagraphBuilder::build(Dart_Handle paragraph_handle) {
534 Paragraph::Create(paragraph_handle, txt_paragraph: m_paragraphBuilder->Build());
535 m_paragraphBuilder.reset();
536 ClearDartWrapper();
537}
538
539} // namespace flutter
540

source code of flutter_engine/flutter/lib/ui/text/paragraph_builder.cc