| 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 | |
| 30 | namespace flutter { |
| 31 | namespace { |
| 32 | |
| 33 | // TextStyle |
| 34 | |
| 35 | const int kTSLeadingDistributionIndex = 0; |
| 36 | const int kTSColorIndex = 1; |
| 37 | const int kTSTextDecorationIndex = 2; |
| 38 | const int kTSTextDecorationColorIndex = 3; |
| 39 | const int kTSTextDecorationStyleIndex = 4; |
| 40 | const int kTSFontWeightIndex = 5; |
| 41 | const int kTSFontStyleIndex = 6; |
| 42 | const int kTSTextBaselineIndex = 7; |
| 43 | const int kTSTextDecorationThicknessIndex = 8; |
| 44 | const int kTSFontFamilyIndex = 9; |
| 45 | const int kTSFontSizeIndex = 10; |
| 46 | const int kTSLetterSpacingIndex = 11; |
| 47 | const int kTSWordSpacingIndex = 12; |
| 48 | const int kTSHeightIndex = 13; |
| 49 | const int kTSLocaleIndex = 14; |
| 50 | const int kTSBackgroundIndex = 15; |
| 51 | const int kTSForegroundIndex = 16; |
| 52 | const int kTSTextShadowsIndex = 17; |
| 53 | const int kTSFontFeaturesIndex = 18; |
| 54 | const int kTSFontVariationsIndex = 19; |
| 55 | |
| 56 | const int kTSLeadingDistributionMask = 1 << kTSLeadingDistributionIndex; |
| 57 | const int kTSColorMask = 1 << kTSColorIndex; |
| 58 | const int kTSTextDecorationMask = 1 << kTSTextDecorationIndex; |
| 59 | const int kTSTextDecorationColorMask = 1 << kTSTextDecorationColorIndex; |
| 60 | const int kTSTextDecorationStyleMask = 1 << kTSTextDecorationStyleIndex; |
| 61 | const int kTSTextDecorationThicknessMask = 1 << kTSTextDecorationThicknessIndex; |
| 62 | const int kTSFontWeightMask = 1 << kTSFontWeightIndex; |
| 63 | const int kTSFontStyleMask = 1 << kTSFontStyleIndex; |
| 64 | const int kTSTextBaselineMask = 1 << kTSTextBaselineIndex; |
| 65 | const int kTSFontFamilyMask = 1 << kTSFontFamilyIndex; |
| 66 | const int kTSFontSizeMask = 1 << kTSFontSizeIndex; |
| 67 | const int kTSLetterSpacingMask = 1 << kTSLetterSpacingIndex; |
| 68 | const int kTSWordSpacingMask = 1 << kTSWordSpacingIndex; |
| 69 | const int kTSHeightMask = 1 << kTSHeightIndex; |
| 70 | const int kTSLocaleMask = 1 << kTSLocaleIndex; |
| 71 | const int kTSBackgroundMask = 1 << kTSBackgroundIndex; |
| 72 | const int kTSForegroundMask = 1 << kTSForegroundIndex; |
| 73 | const int kTSTextShadowsMask = 1 << kTSTextShadowsIndex; |
| 74 | const int kTSFontFeaturesMask = 1 << kTSFontFeaturesIndex; |
| 75 | const int kTSFontVariationsMask = 1 << kTSFontVariationsIndex; |
| 76 | |
| 77 | // ParagraphStyle |
| 78 | |
| 79 | const int kPSTextAlignIndex = 1; |
| 80 | const int kPSTextDirectionIndex = 2; |
| 81 | const int kPSFontWeightIndex = 3; |
| 82 | const int kPSFontStyleIndex = 4; |
| 83 | const int kPSMaxLinesIndex = 5; |
| 84 | const int kPSTextHeightBehaviorIndex = 6; |
| 85 | const int kPSFontFamilyIndex = 7; |
| 86 | const int kPSFontSizeIndex = 8; |
| 87 | const int kPSHeightIndex = 9; |
| 88 | const int kPSStrutStyleIndex = 10; |
| 89 | const int kPSEllipsisIndex = 11; |
| 90 | const int kPSLocaleIndex = 12; |
| 91 | |
| 92 | const int kPSTextAlignMask = 1 << kPSTextAlignIndex; |
| 93 | const int kPSTextDirectionMask = 1 << kPSTextDirectionIndex; |
| 94 | const int kPSFontWeightMask = 1 << kPSFontWeightIndex; |
| 95 | const int kPSFontStyleMask = 1 << kPSFontStyleIndex; |
| 96 | const int kPSMaxLinesMask = 1 << kPSMaxLinesIndex; |
| 97 | const int kPSFontFamilyMask = 1 << kPSFontFamilyIndex; |
| 98 | const int kPSFontSizeMask = 1 << kPSFontSizeIndex; |
| 99 | const int kPSHeightMask = 1 << kPSHeightIndex; |
| 100 | const int kPSTextHeightBehaviorMask = 1 << kPSTextHeightBehaviorIndex; |
| 101 | const int kPSStrutStyleMask = 1 << kPSStrutStyleIndex; |
| 102 | const int kPSEllipsisMask = 1 << kPSEllipsisIndex; |
| 103 | const int kPSLocaleMask = 1 << kPSLocaleIndex; |
| 104 | |
| 105 | // TextShadows decoding |
| 106 | |
| 107 | constexpr uint32_t kColorDefault = 0xFF000000; |
| 108 | constexpr uint32_t kBytesPerShadow = 16; |
| 109 | constexpr uint32_t kShadowPropertiesCount = 4; |
| 110 | constexpr uint32_t kColorOffset = 0; |
| 111 | constexpr uint32_t kXOffset = 1; |
| 112 | constexpr uint32_t kYOffset = 2; |
| 113 | constexpr uint32_t kBlurOffset = 3; |
| 114 | |
| 115 | // FontFeature decoding |
| 116 | constexpr uint32_t kBytesPerFontFeature = 8; |
| 117 | constexpr uint32_t kFontFeatureTagLength = 4; |
| 118 | |
| 119 | // FontVariation decoding |
| 120 | constexpr uint32_t kBytesPerFontVariation = 8; |
| 121 | constexpr uint32_t kFontVariationTagLength = 4; |
| 122 | |
| 123 | // Strut decoding |
| 124 | const int kSFontWeightIndex = 0; |
| 125 | const int kSFontStyleIndex = 1; |
| 126 | const int kSFontFamilyIndex = 2; |
| 127 | const int kSLeadingDistributionIndex = 3; |
| 128 | const int kSFontSizeIndex = 4; |
| 129 | const int kSHeightIndex = 5; |
| 130 | const int kSLeadingIndex = 6; |
| 131 | const int kSForceStrutHeightIndex = 7; |
| 132 | |
| 133 | const int kSFontWeightMask = 1 << kSFontWeightIndex; |
| 134 | const int kSFontStyleMask = 1 << kSFontStyleIndex; |
| 135 | const int kSFontFamilyMask = 1 << kSFontFamilyIndex; |
| 136 | const int kSLeadingDistributionMask = 1 << kSLeadingDistributionIndex; |
| 137 | const int kSFontSizeMask = 1 << kSFontSizeIndex; |
| 138 | const int kSHeightMask = 1 << kSHeightIndex; |
| 139 | const int kSLeadingMask = 1 << kSLeadingIndex; |
| 140 | const int kSForceStrutHeightMask = 1 << kSForceStrutHeightIndex; |
| 141 | |
| 142 | } // namespace |
| 143 | |
| 144 | IMPLEMENT_WRAPPERTYPEINFO(ui, ParagraphBuilder); |
| 145 | |
| 146 | void 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. |
| 165 | void 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 | |
| 226 | ParagraphBuilder::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 | |
| 307 | ParagraphBuilder::~ParagraphBuilder() = default; |
| 308 | |
| 309 | void 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 | |
| 334 | void 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 | |
| 352 | void 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 | |
| 370 | void 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 | |
| 497 | void ParagraphBuilder::pop() { |
| 498 | m_paragraphBuilder->Pop(); |
| 499 | } |
| 500 | |
| 501 | Dart_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 | |
| 521 | void 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 | |
| 533 | void 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 | |