Flutter Engine
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"
23 #include "third_party/skia/include/core/SkColor.h"
29 
30 namespace flutter {
31 namespace {
32 
33 // TextStyle
34 
35 const int tsColorIndex = 1;
36 const int tsTextDecorationIndex = 2;
37 const int tsTextDecorationColorIndex = 3;
38 const int tsTextDecorationStyleIndex = 4;
39 const int tsFontWeightIndex = 5;
40 const int tsFontStyleIndex = 6;
41 const int tsTextBaselineIndex = 7;
42 const int tsTextDecorationThicknessIndex = 8;
43 const int tsFontFamilyIndex = 9;
44 const int tsFontSizeIndex = 10;
45 const int tsLetterSpacingIndex = 11;
46 const int tsWordSpacingIndex = 12;
47 const int tsHeightIndex = 13;
48 const int tsLocaleIndex = 14;
49 const int tsBackgroundIndex = 15;
50 const int tsForegroundIndex = 16;
51 const int tsTextShadowsIndex = 17;
52 const int tsFontFeaturesIndex = 18;
53 
54 const int tsColorMask = 1 << tsColorIndex;
55 const int tsTextDecorationMask = 1 << tsTextDecorationIndex;
56 const int tsTextDecorationColorMask = 1 << tsTextDecorationColorIndex;
57 const int tsTextDecorationStyleMask = 1 << tsTextDecorationStyleIndex;
58 const int tsTextDecorationThicknessMask = 1 << tsTextDecorationThicknessIndex;
59 const int tsFontWeightMask = 1 << tsFontWeightIndex;
60 const int tsFontStyleMask = 1 << tsFontStyleIndex;
61 const int tsTextBaselineMask = 1 << tsTextBaselineIndex;
62 const int tsFontFamilyMask = 1 << tsFontFamilyIndex;
63 const int tsFontSizeMask = 1 << tsFontSizeIndex;
64 const int tsLetterSpacingMask = 1 << tsLetterSpacingIndex;
65 const int tsWordSpacingMask = 1 << tsWordSpacingIndex;
66 const int tsHeightMask = 1 << tsHeightIndex;
67 const int tsLocaleMask = 1 << tsLocaleIndex;
68 const int tsBackgroundMask = 1 << tsBackgroundIndex;
69 const int tsForegroundMask = 1 << tsForegroundIndex;
70 const int tsTextShadowsMask = 1 << tsTextShadowsIndex;
71 const int tsFontFeaturesMask = 1 << tsFontFeaturesIndex;
72 
73 // ParagraphStyle
74 
75 const int psTextAlignIndex = 1;
76 const int psTextDirectionIndex = 2;
77 const int psFontWeightIndex = 3;
78 const int psFontStyleIndex = 4;
79 const int psMaxLinesIndex = 5;
80 const int psTextHeightBehaviorIndex = 6;
81 const int psFontFamilyIndex = 7;
82 const int psFontSizeIndex = 8;
83 const int psHeightIndex = 9;
84 const int psStrutStyleIndex = 10;
85 const int psEllipsisIndex = 11;
86 const int psLocaleIndex = 12;
87 
88 const int psTextAlignMask = 1 << psTextAlignIndex;
89 const int psTextDirectionMask = 1 << psTextDirectionIndex;
90 const int psFontWeightMask = 1 << psFontWeightIndex;
91 const int psFontStyleMask = 1 << psFontStyleIndex;
92 const int psMaxLinesMask = 1 << psMaxLinesIndex;
93 const int psFontFamilyMask = 1 << psFontFamilyIndex;
94 const int psFontSizeMask = 1 << psFontSizeIndex;
95 const int psHeightMask = 1 << psHeightIndex;
96 const int psTextHeightBehaviorMask = 1 << psTextHeightBehaviorIndex;
97 const int psStrutStyleMask = 1 << psStrutStyleIndex;
98 const int psEllipsisMask = 1 << psEllipsisIndex;
99 const int psLocaleMask = 1 << psLocaleIndex;
100 
101 // TextShadows decoding
102 
103 constexpr uint32_t kColorDefault = 0xFF000000;
104 constexpr uint32_t kBytesPerShadow = 16;
105 constexpr uint32_t kShadowPropertiesCount = 4;
106 constexpr uint32_t kColorOffset = 0;
107 constexpr uint32_t kXOffset = 1;
108 constexpr uint32_t kYOffset = 2;
109 constexpr uint32_t kBlurOffset = 3;
110 
111 // FontFeature decoding
112 constexpr uint32_t kBytesPerFontFeature = 8;
113 constexpr uint32_t kFontFeatureTagLength = 4;
114 
115 // Strut decoding
116 const int sFontWeightIndex = 0;
117 const int sFontStyleIndex = 1;
118 const int sFontFamilyIndex = 2;
119 const int sFontSizeIndex = 3;
120 const int sHeightIndex = 4;
121 const int sLeadingIndex = 5;
122 const int sForceStrutHeightIndex = 6;
123 
124 const int sFontWeightMask = 1 << sFontWeightIndex;
125 const int sFontStyleMask = 1 << sFontStyleIndex;
126 const int sFontFamilyMask = 1 << sFontFamilyIndex;
127 const int sFontSizeMask = 1 << sFontSizeIndex;
128 const int sHeightMask = 1 << sHeightIndex;
129 const int sLeadingMask = 1 << sLeadingIndex;
130 const int sForceStrutHeightMask = 1 << sForceStrutHeightIndex;
131 
132 } // namespace
133 
134 static void ParagraphBuilder_constructor(Dart_NativeArguments args) {
137 }
138 
140 
141 #define FOR_EACH_BINDING(V) \
142  V(ParagraphBuilder, pushStyle) \
143  V(ParagraphBuilder, pop) \
144  V(ParagraphBuilder, addText) \
145  V(ParagraphBuilder, addPlaceholder) \
146  V(ParagraphBuilder, build)
147 
149 
150 void ParagraphBuilder::RegisterNatives(tonic::DartLibraryNatives* natives) {
151  natives->Register(
152  {{"ParagraphBuilder_constructor", ParagraphBuilder_constructor, 9, true},
154 }
155 
157  tonic::Int32List& encoded,
158  Dart_Handle strutData,
159  const std::string& fontFamily,
160  const std::vector<std::string>& strutFontFamilies,
161  double fontSize,
162  double height,
163  const std::u16string& ellipsis,
164  const std::string& locale) {
165  return fml::MakeRefCounted<ParagraphBuilder>(encoded, strutData, fontFamily,
166  strutFontFamilies, fontSize,
167  height, ellipsis, locale);
168 }
169 
170 // returns true if there is a font family defined. Font family is the only
171 // parameter passed directly.
172 void decodeStrut(Dart_Handle strut_data,
173  const std::vector<std::string>& strut_font_families,
174  txt::ParagraphStyle& paragraph_style) { // NOLINT
175  if (strut_data == Dart_Null()) {
176  return;
177  }
178 
179  tonic::DartByteData byte_data(strut_data);
180  if (byte_data.length_in_bytes() == 0) {
181  return;
182  }
183  paragraph_style.strut_enabled = true;
184 
185  const uint8_t* uint8_data = static_cast<const uint8_t*>(byte_data.data());
186  uint8_t mask = uint8_data[0];
187 
188  // Data is stored in order of increasing size, eg, 8 bit ints will be before
189  // any 32 bit ints. In addition, the order of decoding is the same order
190  // as it is encoded, and the order is used to maintain consistency.
191  size_t byte_count = 1;
192  if (mask & sFontWeightMask) {
193  paragraph_style.strut_font_weight =
194  static_cast<txt::FontWeight>(uint8_data[byte_count++]);
195  }
196  if (mask & sFontStyleMask) {
197  paragraph_style.strut_font_style =
198  static_cast<txt::FontStyle>(uint8_data[byte_count++]);
199  }
200 
201  std::vector<float> float_data;
202  float_data.resize((byte_data.length_in_bytes() - byte_count) / 4);
203  memcpy(float_data.data(),
204  static_cast<const char*>(byte_data.data()) + byte_count,
205  byte_data.length_in_bytes() - byte_count);
206  size_t float_count = 0;
207  if (mask & sFontSizeMask) {
208  paragraph_style.strut_font_size = float_data[float_count++];
209  }
210  if (mask & sHeightMask) {
211  paragraph_style.strut_height = float_data[float_count++];
212  paragraph_style.strut_has_height_override = true;
213  }
214  if (mask & sLeadingMask) {
215  paragraph_style.strut_leading = float_data[float_count++];
216  }
217  if (mask & sForceStrutHeightMask) {
218  // The boolean is stored as the last bit in the bitmask.
219  paragraph_style.force_strut_height = (mask & 1 << 7) != 0;
220  }
221 
222  if (mask & sFontFamilyMask) {
223  paragraph_style.strut_font_families = strut_font_families;
224  } else {
225  // Provide an empty font name so that the platform default font will be
226  // used.
227  paragraph_style.strut_font_families.push_back("");
228  }
229 }
230 
231 ParagraphBuilder::ParagraphBuilder(
232  tonic::Int32List& encoded,
233  Dart_Handle strutData,
234  const std::string& fontFamily,
235  const std::vector<std::string>& strutFontFamilies,
236  double fontSize,
237  double height,
238  const std::u16string& ellipsis,
239  const std::string& locale) {
240  int32_t mask = encoded[0];
241  txt::ParagraphStyle style;
242 
243  if (mask & psTextAlignMask) {
244  style.text_align = txt::TextAlign(encoded[psTextAlignIndex]);
245  }
246 
247  if (mask & psTextDirectionMask) {
248  style.text_direction = txt::TextDirection(encoded[psTextDirectionIndex]);
249  }
250 
251  if (mask & psFontWeightMask) {
252  style.font_weight =
253  static_cast<txt::FontWeight>(encoded[psFontWeightIndex]);
254  }
255 
256  if (mask & psFontStyleMask) {
257  style.font_style = static_cast<txt::FontStyle>(encoded[psFontStyleIndex]);
258  }
259 
260  if (mask & psFontFamilyMask) {
261  style.font_family = fontFamily;
262  }
263 
264  if (mask & psFontSizeMask) {
265  style.font_size = fontSize;
266  }
267 
268  if (mask & psHeightMask) {
269  style.height = height;
270  style.has_height_override = true;
271  }
272 
273  if (mask & psTextHeightBehaviorMask) {
274  style.text_height_behavior = encoded[psTextHeightBehaviorIndex];
275  }
276 
277  if (mask & psStrutStyleMask) {
278  decodeStrut(strutData, strutFontFamilies, style);
279  }
280 
281  if (mask & psMaxLinesMask) {
282  style.max_lines = encoded[psMaxLinesIndex];
283  }
284 
285  if (mask & psEllipsisMask) {
286  style.ellipsis = ellipsis;
287  }
288 
289  if (mask & psLocaleMask) {
290  style.locale = locale;
291  }
292 
293  FontCollection& font_collection = UIDartState::Current()
295  ->client()
296  ->GetFontCollection();
297 
298 #if FLUTTER_ENABLE_SKSHAPER
299 #define FLUTTER_PARAGRAPH_BUILDER txt::ParagraphBuilder::CreateSkiaBuilder
300 #else
301 #define FLUTTER_PARAGRAPH_BUILDER txt::ParagraphBuilder::CreateTxtBuilder
302 #endif
303 
304  m_paragraphBuilder =
305  FLUTTER_PARAGRAPH_BUILDER(style, font_collection.GetFontCollection());
306 }
307 
309 
311  Dart_Handle shadows_data,
312  std::vector<txt::TextShadow>& decoded_shadows) { // NOLINT
313  decoded_shadows.clear();
314 
315  tonic::DartByteData byte_data(shadows_data);
316  FML_CHECK(byte_data.length_in_bytes() % kBytesPerShadow == 0);
317 
318  const uint32_t* uint_data = static_cast<const uint32_t*>(byte_data.data());
319  const float* float_data = static_cast<const float*>(byte_data.data());
320 
321  size_t shadow_count = byte_data.length_in_bytes() / kBytesPerShadow;
322  size_t shadow_count_offset = 0;
323  for (size_t shadow_index = 0; shadow_index < shadow_count; ++shadow_index) {
324  shadow_count_offset = shadow_index * kShadowPropertiesCount;
325  SkColor color =
326  uint_data[shadow_count_offset + kColorOffset] ^ kColorDefault;
327  decoded_shadows.emplace_back(
328  color,
329  SkPoint::Make(float_data[shadow_count_offset + kXOffset],
330  float_data[shadow_count_offset + kYOffset]),
331  float_data[shadow_count_offset + kBlurOffset]);
332  }
333 }
334 
335 void decodeFontFeatures(Dart_Handle font_features_data,
336  txt::FontFeatures& font_features) { // NOLINT
337  tonic::DartByteData byte_data(font_features_data);
338  FML_CHECK(byte_data.length_in_bytes() % kBytesPerFontFeature == 0);
339 
340  size_t feature_count = byte_data.length_in_bytes() / kBytesPerFontFeature;
341  for (size_t feature_index = 0; feature_index < feature_count;
342  ++feature_index) {
343  size_t feature_offset = feature_index * kBytesPerFontFeature;
344  const char* feature_bytes =
345  static_cast<const char*>(byte_data.data()) + feature_offset;
346  std::string tag(feature_bytes, kFontFeatureTagLength);
347  int32_t value = *(reinterpret_cast<const int32_t*>(feature_bytes +
348  kFontFeatureTagLength));
349  font_features.SetFeature(tag, value);
350  }
351 }
352 
353 void ParagraphBuilder::pushStyle(tonic::Int32List& encoded,
354  const std::vector<std::string>& fontFamilies,
355  double fontSize,
356  double letterSpacing,
357  double wordSpacing,
358  double height,
359  double decorationThickness,
360  const std::string& locale,
361  Dart_Handle background_objects,
362  Dart_Handle background_data,
363  Dart_Handle foreground_objects,
364  Dart_Handle foreground_data,
365  Dart_Handle shadows_data,
366  Dart_Handle font_features_data) {
367  FML_DCHECK(encoded.num_elements() == 8);
368 
369  int32_t mask = encoded[0];
370 
371  // Set to use the properties of the previous style if the property is not
372  // explicitly given.
373  txt::TextStyle style = m_paragraphBuilder->PeekStyle();
374 
375  // Only change the style property from the previous value if a new explicitly
376  // set value is available
377  if (mask & tsColorMask) {
378  style.color = encoded[tsColorIndex];
379  }
380 
381  if (mask & tsTextDecorationMask) {
382  style.decoration =
383  static_cast<txt::TextDecoration>(encoded[tsTextDecorationIndex]);
384  }
385 
386  if (mask & tsTextDecorationColorMask) {
387  style.decoration_color = encoded[tsTextDecorationColorIndex];
388  }
389 
390  if (mask & tsTextDecorationStyleMask) {
391  style.decoration_style = static_cast<txt::TextDecorationStyle>(
392  encoded[tsTextDecorationStyleIndex]);
393  }
394 
395  if (mask & tsTextDecorationThicknessMask) {
396  style.decoration_thickness_multiplier = decorationThickness;
397  }
398 
399  if (mask & tsTextBaselineMask) {
400  // TODO(abarth): Implement TextBaseline. The CSS version of this
401  // property wasn't wired up either.
402  }
403 
404  if (mask & (tsFontWeightMask | tsFontStyleMask | tsFontSizeMask |
405  tsLetterSpacingMask | tsWordSpacingMask)) {
406  if (mask & tsFontWeightMask) {
407  style.font_weight =
408  static_cast<txt::FontWeight>(encoded[tsFontWeightIndex]);
409  }
410 
411  if (mask & tsFontStyleMask) {
412  style.font_style = static_cast<txt::FontStyle>(encoded[tsFontStyleIndex]);
413  }
414 
415  if (mask & tsFontSizeMask) {
416  style.font_size = fontSize;
417  }
418 
419  if (mask & tsLetterSpacingMask) {
420  style.letter_spacing = letterSpacing;
421  }
422 
423  if (mask & tsWordSpacingMask) {
424  style.word_spacing = wordSpacing;
425  }
426  }
427 
428  if (mask & tsHeightMask) {
429  style.height = height;
430  style.has_height_override = true;
431  }
432 
433  if (mask & tsLocaleMask) {
434  style.locale = locale;
435  }
436 
437  if (mask & tsBackgroundMask) {
438  Paint background(background_objects, background_data);
439  if (background.paint()) {
440  style.has_background = true;
441  style.background = *background.paint();
442  }
443  }
444 
445  if (mask & tsForegroundMask) {
446  Paint foreground(foreground_objects, foreground_data);
447  if (foreground.paint()) {
448  style.has_foreground = true;
449  style.foreground = *foreground.paint();
450  }
451  }
452 
453  if (mask & tsTextShadowsMask) {
454  decodeTextShadows(shadows_data, style.text_shadows);
455  }
456 
457  if (mask & tsFontFamilyMask) {
458  // The child style's font families override the parent's font families.
459  // If the child's fonts are not available, then the font collection will
460  // use the system fallback fonts (not the parent's fonts).
461  style.font_families = fontFamilies;
462  }
463 
464  if (mask & tsFontFeaturesMask) {
465  decodeFontFeatures(font_features_data, style.font_features);
466  }
467 
468  m_paragraphBuilder->PushStyle(style);
469 }
470 
472  m_paragraphBuilder->Pop();
473 }
474 
475 Dart_Handle ParagraphBuilder::addText(const std::u16string& text) {
476  if (text.empty()) {
477  return Dart_Null();
478  }
479 
480  // Use ICU to validate the UTF-16 input. Calling u_strToUTF8 with a null
481  // output buffer will return U_BUFFER_OVERFLOW_ERROR if the input is well
482  // formed.
483  const UChar* text_ptr = reinterpret_cast<const UChar*>(text.data());
484  UErrorCode error_code = U_ZERO_ERROR;
485  u_strToUTF8(nullptr, 0, nullptr, text_ptr, text.size(), &error_code);
486  if (error_code != U_BUFFER_OVERFLOW_ERROR) {
487  return tonic::ToDart("string is not well-formed UTF-16");
488  }
489 
490  m_paragraphBuilder->AddText(text);
491 
492  return Dart_Null();
493 }
494 
496  double height,
497  unsigned alignment,
498  double baseline_offset,
499  unsigned baseline) {
500  txt::PlaceholderRun placeholder_run(
501  width, height, static_cast<txt::PlaceholderAlignment>(alignment),
502  static_cast<txt::TextBaseline>(baseline), baseline_offset);
503 
504  m_paragraphBuilder->AddPlaceholder(placeholder_run);
505 
506  return Dart_Null();
507 }
508 
509 void ParagraphBuilder::build(Dart_Handle paragraph_handle) {
510  Paragraph::Create(paragraph_handle, m_paragraphBuilder->Build());
511 }
512 
513 } // namespace flutter
PlatformConfigurationClient * client() const
Access to the platform configuration client (which typically is implemented by the RuntimeController)...
constexpr uint32_t kColorDefault
Definition: paint.cc:45
#define FOR_EACH_BINDING(V)
G_BEGIN_DECLS FlValue * args
bool has_background
Definition: text_style.h:56
FontStyle
Definition: font_style.h:22
virtual FontCollection & GetFontCollection()=0
Returns the current collection of fonts available on the platform.
std::vector< std::string > strut_font_families
const void * data() const
Dart_Handle addPlaceholder(double width, double height, unsigned alignment, double baseline_offset, unsigned baseline)
#define FLUTTER_PARAGRAPH_BUILDER
std::vector< TextShadow > text_shadows
Definition: text_style.h:62
TextDecorationStyle
#define FML_DCHECK(condition)
Definition: logging.h:86
void build(Dart_Handle paragraph_handle)
void DartCallConstructor(Sig func, Dart_NativeArguments args)
Definition: dart_args.h:218
bool has_height_override
Definition: text_style.h:54
double height
Definition: text_style.h:53
double decoration_thickness_multiplier
Definition: text_style.h:43
FontStyle font_style
Definition: text_style.h:45
FontWeight strut_font_weight
FontWeight
Definition: font_weight.h:22
double letter_spacing
Definition: text_style.h:51
std::string locale
Definition: text_style.h:55
double font_size
Definition: text_style.h:50
std::u16string ellipsis
std::shared_ptr< txt::FontCollection > GetFontCollection() const
const SkPaint * paint() const
Definition: paint.h:18
#define DART_NATIVE_CALLBACK(CLASS, METHOD)
void decodeTextShadows(Dart_Handle shadows_data, std::vector< txt::TextShadow > &decoded_shadows)
std::string font_family
FontStyle strut_font_style
FontFeatures font_features
Definition: text_style.h:63
double word_spacing
Definition: text_style.h:52
uint8_t value
SkColor decoration_color
Definition: text_style.h:40
IMPLEMENT_WRAPPERTYPEINFO(ui, Scene)
size_t length_in_bytes() const
void pushStyle(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)
TextDecorationStyle decoration_style
Definition: text_style.h:41
std::vector< std::string > font_families
Definition: text_style.h:49
int32_t height
SkColor color
Definition: text_style.h:36
bool has_foreground
Definition: text_style.h:58
int32_t width
static void ThrowIfUIOperationsProhibited()
PlatformConfiguration * platform_configuration() const
Definition: ui_dart_state.h:48
void SetFeature(std::string tag, int value)
SkPaint foreground
Definition: text_style.h:59
void decodeFontFeatures(Dart_Handle font_features_data, txt::FontFeatures &font_features)
static fml::RefPtr< ParagraphBuilder > create(tonic::Int32List &encoded, 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)
FontWeight font_weight
Definition: text_style.h:44
static void Create(Dart_Handle paragraph_handle, std::unique_ptr< txt::Paragraph > txt_paragraph)
Definition: paragraph.h:26
#define FML_CHECK(condition)
Definition: logging.h:68
SkPaint background
Definition: text_style.h:57
#define DART_REGISTER_NATIVE(CLASS, METHOD)
Dart_Handle addText(const std::u16string &text)
TextDirection text_direction
Dart_Handle ToDart(const T &object)
static void ParagraphBuilder_constructor(Dart_NativeArguments args)
void decodeStrut(Dart_Handle strut_data, const std::vector< std::string > &strut_font_families, txt::ParagraphStyle &paragraph_style)
TextDecoration
static UIDartState * Current()