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