• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "flutter/common/settings.h"
8 #include "flutter/common/task_runners.h"
9 #include "flutter/fml/logging.h"
10 #include "flutter/fml/task_runner.h"
11 #include "flutter/lib/ui/text/font_collection.h"
12 #include "flutter/lib/ui/ui_dart_state.h"
13 #include "flutter/lib/ui/window/window.h"
14 #include "flutter/third_party/txt/src/txt/font_style.h"
15 #include "flutter/third_party/txt/src/txt/font_weight.h"
16 #include "flutter/third_party/txt/src/txt/paragraph_style.h"
17 #include "flutter/third_party/txt/src/txt/text_baseline.h"
18 #include "flutter/third_party/txt/src/txt/text_decoration.h"
19 #include "flutter/third_party/txt/src/txt/text_style.h"
20 #include "third_party/icu/source/common/unicode/ustring.h"
21 #include "third_party/skia/include/core/SkColor.h"
22 
23 namespace flutter {
24 namespace {
25 
26 // TextStyle
27 
28 const int tsColorIndex = 1;
29 const int tsTextDecorationIndex = 2;
30 const int tsTextDecorationColorIndex = 3;
31 const int tsTextDecorationStyleIndex = 4;
32 const int tsFontWeightIndex = 5;
33 const int tsFontStyleIndex = 6;
34 const int tsTextBaselineIndex = 7;
35 const int tsTextDecorationThicknessIndex = 8;
36 const int tsFontFamilyIndex = 9;
37 const int tsFontSizeIndex = 10;
38 const int tsLetterSpacingIndex = 11;
39 const int tsWordSpacingIndex = 12;
40 const int tsHeightIndex = 13;
41 const int tsLocaleIndex = 14;
42 const int tsBackgroundIndex = 15;
43 const int tsForegroundIndex = 16;
44 const int tsTextShadowsIndex = 17;
45 const int tsFontFeaturesIndex = 18;
46 
47 const int tsColorMask = 1 << tsColorIndex;
48 const int tsTextDecorationMask = 1 << tsTextDecorationIndex;
49 const int tsTextDecorationColorMask = 1 << tsTextDecorationColorIndex;
50 const int tsTextDecorationStyleMask = 1 << tsTextDecorationStyleIndex;
51 const int tsTextDecorationThicknessMask = 1 << tsTextDecorationThicknessIndex;
52 const int tsFontWeightMask = 1 << tsFontWeightIndex;
53 const int tsFontStyleMask = 1 << tsFontStyleIndex;
54 const int tsTextBaselineMask = 1 << tsTextBaselineIndex;
55 const int tsFontFamilyMask = 1 << tsFontFamilyIndex;
56 const int tsFontSizeMask = 1 << tsFontSizeIndex;
57 const int tsLetterSpacingMask = 1 << tsLetterSpacingIndex;
58 const int tsWordSpacingMask = 1 << tsWordSpacingIndex;
59 const int tsHeightMask = 1 << tsHeightIndex;
60 const int tsLocaleMask = 1 << tsLocaleIndex;
61 const int tsBackgroundMask = 1 << tsBackgroundIndex;
62 const int tsForegroundMask = 1 << tsForegroundIndex;
63 const int tsTextShadowsMask = 1 << tsTextShadowsIndex;
64 const int tsFontFeaturesMask = 1 << tsFontFeaturesIndex;
65 
66 // ParagraphStyle
67 
68 const int psTextAlignIndex = 1;
69 const int psTextDirectionIndex = 2;
70 const int psFontWeightIndex = 3;
71 const int psFontStyleIndex = 4;
72 const int psMaxLinesIndex = 5;
73 const int psFontFamilyIndex = 6;
74 const int psFontSizeIndex = 7;
75 const int psHeightIndex = 8;
76 const int psStrutStyleIndex = 9;
77 const int psEllipsisIndex = 10;
78 const int psLocaleIndex = 11;
79 
80 const int psTextAlignMask = 1 << psTextAlignIndex;
81 const int psTextDirectionMask = 1 << psTextDirectionIndex;
82 const int psFontWeightMask = 1 << psFontWeightIndex;
83 const int psFontStyleMask = 1 << psFontStyleIndex;
84 const int psMaxLinesMask = 1 << psMaxLinesIndex;
85 const int psFontFamilyMask = 1 << psFontFamilyIndex;
86 const int psFontSizeMask = 1 << psFontSizeIndex;
87 const int psHeightMask = 1 << psHeightIndex;
88 const int psStrutStyleMask = 1 << psStrutStyleIndex;
89 const int psEllipsisMask = 1 << psEllipsisIndex;
90 const int psLocaleMask = 1 << psLocaleIndex;
91 
92 // TextShadows decoding
93 
94 constexpr uint32_t kColorDefault = 0xFF000000;
95 constexpr uint32_t kBytesPerShadow = 16;
96 constexpr uint32_t kShadowPropertiesCount = 4;
97 constexpr uint32_t kColorOffset = 0;
98 constexpr uint32_t kXOffset = 1;
99 constexpr uint32_t kYOffset = 2;
100 constexpr uint32_t kBlurOffset = 3;
101 
102 // FontFeature decoding
103 constexpr uint32_t kBytesPerFontFeature = 8;
104 constexpr uint32_t kFontFeatureTagLength = 4;
105 
106 // Strut decoding
107 const int sFontWeightIndex = 0;
108 const int sFontStyleIndex = 1;
109 const int sFontFamilyIndex = 2;
110 const int sFontSizeIndex = 3;
111 const int sHeightIndex = 4;
112 const int sLeadingIndex = 5;
113 const int sForceStrutHeightIndex = 6;
114 
115 const int sFontWeightMask = 1 << sFontWeightIndex;
116 const int sFontStyleMask = 1 << sFontStyleIndex;
117 const int sFontFamilyMask = 1 << sFontFamilyIndex;
118 const int sFontSizeMask = 1 << sFontSizeIndex;
119 const int sHeightMask = 1 << sHeightIndex;
120 const int sLeadingMask = 1 << sLeadingIndex;
121 const int sForceStrutHeightMask = 1 << sForceStrutHeightIndex;
122 
123 }  // namespace
124 
125 IMPLEMENT_WRAPPERTYPEINFO(ui, ParagraphBuilder);
126 
127 #define FOR_EACH_BINDING(V)           \
128   V(ParagraphBuilder, pushStyle)      \
129   V(ParagraphBuilder, pop)            \
130   V(ParagraphBuilder, addText)        \
131   V(ParagraphBuilder, addPlaceholder) \
132   V(ParagraphBuilder, build)
133 
FOR_EACH_BINDING(DART_NATIVE_CALLBACK)134 FOR_EACH_BINDING(DART_NATIVE_CALLBACK)
135 
136 void ParagraphBuilder::RegisterNatives(tonic::DartLibraryNatives* natives) {
137 }
138 
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)139 fml::RefPtr<ParagraphBuilder> ParagraphBuilder::create(
140     tonic::Int32List& encoded,
141     Dart_Handle strutData,
142     const std::string& fontFamily,
143     const std::vector<std::string>& strutFontFamilies,
144     double fontSize,
145     double height,
146     const std::u16string& ellipsis,
147     const std::string& locale) {
148   return fml::MakeRefCounted<ParagraphBuilder>(encoded, strutData, fontFamily,
149                                                strutFontFamilies, fontSize,
150                                                height, ellipsis, locale);
151 }
152 
153 // returns true if there is a font family defined. Font family is the only
154 // parameter passed directly.
decodeStrut(Dart_Handle strut_data,const std::vector<std::string> & strut_font_families,txt::ParagraphStyle & paragraph_style)155 void decodeStrut(Dart_Handle strut_data,
156                  const std::vector<std::string>& strut_font_families,
157                  txt::ParagraphStyle& paragraph_style) {
158   return ;
159 }
160 
ParagraphBuilder(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)161 ParagraphBuilder::ParagraphBuilder(
162     tonic::Int32List& encoded,
163     Dart_Handle strutData,
164     const std::string& fontFamily,
165     const std::vector<std::string>& strutFontFamilies,
166     double fontSize,
167     double height,
168     const std::u16string& ellipsis,
169     const std::string& locale) {
170   int32_t mask = encoded[0];
171   txt::ParagraphStyle style;
172 
173   if (mask & psTextAlignMask) {
174     style.text_align = txt::TextAlign(encoded[psTextAlignIndex]);
175   }
176 
177   if (mask & psTextDirectionMask) {
178     style.text_direction = txt::TextDirection(encoded[psTextDirectionIndex]);
179   }
180 
181   if (mask & psFontWeightMask) {
182     style.font_weight =
183         static_cast<txt::FontWeight>(encoded[psFontWeightIndex]);
184   }
185 
186   if (mask & psFontStyleMask) {
187     style.font_style = static_cast<txt::FontStyle>(encoded[psFontStyleIndex]);
188   }
189 
190   if (mask & psFontFamilyMask) {
191     style.font_family = fontFamily;
192   }
193 
194   if (mask & psFontSizeMask) {
195     style.font_size = fontSize;
196   }
197 
198   if (mask & psHeightMask) {
199     style.height = height;
200     style.has_height_override = true;
201   }
202 
203   if (mask & psStrutStyleMask) {
204     decodeStrut(strutData, strutFontFamilies, style);
205   }
206 
207   if (mask & psMaxLinesMask) {
208     style.max_lines = encoded[psMaxLinesIndex];
209   }
210 
211   if (mask & psEllipsisMask) {
212     style.ellipsis = ellipsis;
213   }
214 
215   if (mask & psLocaleMask) {
216     style.locale = locale;
217   }
218 
219   FontCollection& font_collection =
220       UIDartState::Current()->window()->client()->GetFontCollection();
221 
222 #if FLUTTER_ENABLE_SKSHAPER
223 #define FLUTTER_PARAGRAPH_BUILDER txt::ParagraphBuilder::CreateSkiaBuilder
224 #else
225 #define FLUTTER_PARAGRAPH_BUILDER txt::ParagraphBuilder::CreateTxtBuilder
226 #endif
227 
228   m_paragraphBuilder =
229       FLUTTER_PARAGRAPH_BUILDER(style, font_collection.GetFontCollection());
230 }
231 
232 ParagraphBuilder::~ParagraphBuilder() = default;
233 
decodeTextShadows(Dart_Handle shadows_data,std::vector<txt::TextShadow> & decoded_shadows)234 void decodeTextShadows(Dart_Handle shadows_data,
235                        std::vector<txt::TextShadow>& decoded_shadows) {
236   decoded_shadows.clear();
237 
238   tonic::DartByteData byte_data(shadows_data);
239   FML_CHECK(byte_data.length_in_bytes() % kBytesPerShadow == 0);
240 
241   const uint32_t* uint_data = static_cast<const uint32_t*>(byte_data.data());
242   const float* float_data = static_cast<const float*>(byte_data.data());
243 
244   size_t shadow_count = byte_data.length_in_bytes() / kBytesPerShadow;
245   size_t shadow_count_offset = 0;
246   for (size_t shadow_index = 0; shadow_index < shadow_count; ++shadow_index) {
247     shadow_count_offset = shadow_index * kShadowPropertiesCount;
248     SkColor color =
249         uint_data[shadow_count_offset + kColorOffset] ^ kColorDefault;
250     decoded_shadows.emplace_back(
251         color,
252         SkPoint::Make(float_data[shadow_count_offset + kXOffset],
253                       float_data[shadow_count_offset + kYOffset]),
254         float_data[shadow_count_offset + kBlurOffset]);
255   }
256 }
257 
decodeFontFeatures(Dart_Handle font_features_data,txt::FontFeatures & font_features)258 void decodeFontFeatures(Dart_Handle font_features_data,
259                         txt::FontFeatures& font_features) {
260   tonic::DartByteData byte_data(font_features_data);
261   FML_CHECK(byte_data.length_in_bytes() % kBytesPerFontFeature == 0);
262 
263   size_t feature_count = byte_data.length_in_bytes() / kBytesPerFontFeature;
264   for (size_t feature_index = 0; feature_index < feature_count;
265        ++feature_index) {
266     size_t feature_offset = feature_index * kBytesPerFontFeature;
267     const char* feature_bytes =
268         static_cast<const char*>(byte_data.data()) + feature_offset;
269     std::string tag(feature_bytes, kFontFeatureTagLength);
270     int32_t value = *(reinterpret_cast<const int32_t*>(feature_bytes +
271                                                        kFontFeatureTagLength));
272     font_features.SetFeature(tag, value);
273   }
274 }
275 
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)276 void ParagraphBuilder::pushStyle(tonic::Int32List& encoded,
277                                  const std::vector<std::string>& fontFamilies,
278                                  double fontSize,
279                                  double letterSpacing,
280                                  double wordSpacing,
281                                  double height,
282                                  double decorationThickness,
283                                  const std::string& locale,
284                                  Dart_Handle background_objects,
285                                  Dart_Handle background_data,
286                                  Dart_Handle foreground_objects,
287                                  Dart_Handle foreground_data,
288                                  Dart_Handle shadows_data,
289                                  Dart_Handle font_features_data) {
290   FML_DCHECK(encoded.size() == 8);
291 
292   int32_t mask = encoded[0];
293 
294   // Set to use the properties of the previous style if the property is not
295   // explicitly given.
296   txt::TextStyle style = m_paragraphBuilder->PeekStyle();
297 
298   // Only change the style property from the previous value if a new explicitly
299   // set value is available
300   if (mask & tsColorMask) {
301     style.color = encoded[tsColorIndex];
302   }
303 
304   if (mask & tsTextDecorationMask) {
305     style.decoration =
306         static_cast<txt::TextDecoration>(encoded[tsTextDecorationIndex]);
307   }
308 
309   if (mask & tsTextDecorationColorMask) {
310     style.decoration_color = encoded[tsTextDecorationColorIndex];
311   }
312 
313   if (mask & tsTextDecorationStyleMask) {
314     style.decoration_style = static_cast<txt::TextDecorationStyle>(
315         encoded[tsTextDecorationStyleIndex]);
316   }
317 
318   if (mask & tsTextDecorationThicknessMask) {
319     style.decoration_thickness_multiplier = decorationThickness;
320   }
321 
322   if (mask & tsTextBaselineMask) {
323     // TODO(abarth): Implement TextBaseline. The CSS version of this
324     // property wasn't wired up either.
325   }
326 
327   if (mask & (tsFontWeightMask | tsFontStyleMask | tsFontSizeMask |
328               tsLetterSpacingMask | tsWordSpacingMask)) {
329     if (mask & tsFontWeightMask)
330       style.font_weight =
331           static_cast<txt::FontWeight>(encoded[tsFontWeightIndex]);
332 
333     if (mask & tsFontStyleMask)
334       style.font_style = static_cast<txt::FontStyle>(encoded[tsFontStyleIndex]);
335 
336     if (mask & tsFontSizeMask)
337       style.font_size = fontSize;
338 
339     if (mask & tsLetterSpacingMask)
340       style.letter_spacing = letterSpacing;
341 
342     if (mask & tsWordSpacingMask)
343       style.word_spacing = wordSpacing;
344   }
345 
346   if (mask & tsHeightMask) {
347     style.height = height;
348     style.has_height_override = true;
349   }
350 
351   if (mask & tsLocaleMask) {
352     style.locale = locale;
353   }
354 
355   if (mask & tsBackgroundMask) {
356     Paint background(background_objects, background_data);
357     if (background.paint()) {
358       style.has_background = true;
359       style.background = *background.paint();
360     }
361   }
362 
363   if (mask & tsForegroundMask) {
364     Paint foreground(foreground_objects, foreground_data);
365     if (foreground.paint()) {
366       style.has_foreground = true;
367       style.foreground = *foreground.paint();
368     }
369   }
370 
371   if (mask & tsTextShadowsMask) {
372     decodeTextShadows(shadows_data, style.text_shadows);
373   }
374 
375   if (mask & tsFontFamilyMask) {
376     // The child style's font families override the parent's font families.
377     // If the child's fonts are not available, then the font collection will
378     // use the system fallback fonts (not the parent's fonts).
379     style.font_families = fontFamilies;
380   }
381 
382   if (mask & tsFontFeaturesMask) {
383     decodeFontFeatures(font_features_data, style.font_features);
384   }
385 
386   m_paragraphBuilder->PushStyle(style);
387 }
388 
pop()389 void ParagraphBuilder::pop() {
390   m_paragraphBuilder->Pop();
391 }
392 
addText(const std::u16string & text)393 Dart_Handle ParagraphBuilder::addText(const std::u16string& text) {
394   if (text.empty())
395     return nullptr;
396 
397   // Use ICU to validate the UTF-16 input.  Calling u_strToUTF8 with a null
398   // output buffer will return U_BUFFER_OVERFLOW_ERROR if the input is well
399   // formed.
400   const UChar* text_ptr = reinterpret_cast<const UChar*>(text.data());
401   UErrorCode error_code = U_ZERO_ERROR;
402   u_strToUTF8(nullptr, 0, nullptr, text_ptr, text.size(), &error_code);
403   if (error_code != U_BUFFER_OVERFLOW_ERROR)
404     return reinterpret_cast<Dart_Handle>("string is not well-formed UTF-16");
405 
406   m_paragraphBuilder->AddText(text);
407 
408   return nullptr;
409 }
410 
addPlaceholder(double width,double height,unsigned alignment,double baseline_offset,unsigned baseline)411 Dart_Handle ParagraphBuilder::addPlaceholder(double width,
412                                              double height,
413                                              unsigned alignment,
414                                              double baseline_offset,
415                                              unsigned baseline) {
416   txt::PlaceholderRun placeholder_run(
417       width, height, static_cast<txt::PlaceholderAlignment>(alignment),
418       static_cast<txt::TextBaseline>(baseline), baseline_offset);
419 
420   m_paragraphBuilder->AddPlaceholder(placeholder_run);
421 
422   return nullptr;
423 }
424 
build()425 fml::RefPtr<Paragraph> ParagraphBuilder::build() {
426   return Paragraph::Create(m_paragraphBuilder->Build());
427 }
428 
429 }  // namespace flutter
430