blob: 15ce9c8167e0cbe5acf268a4617e3b9491e0d7a9 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/lib/ui/text/paragraph_builder.h"
#include "flutter/common/settings.h"
#include "flutter/common/task_runners.h"
#include "flutter/fml/task_runner.h"
#include "flutter/lib/ui/text/font_collection.h"
#include "flutter/lib/ui/ui_dart_state.h"
#include "flutter/lib/ui/window/window.h"
#include "flutter/third_party/txt/src/txt/font_style.h"
#include "flutter/third_party/txt/src/txt/font_weight.h"
#include "flutter/third_party/txt/src/txt/paragraph_style.h"
#include "flutter/third_party/txt/src/txt/text_decoration.h"
#include "flutter/third_party/txt/src/txt/text_style.h"
#include "third_party/icu/source/common/unicode/ustring.h"
#include "third_party/tonic/converter/dart_converter.h"
#include "third_party/tonic/dart_args.h"
#include "third_party/tonic/dart_binding_macros.h"
#include "third_party/tonic/dart_library_natives.h"
namespace blink {
namespace {
// TextStyle
const int tsColorIndex = 1;
const int tsTextDecorationIndex = 2;
const int tsTextDecorationColorIndex = 3;
const int tsTextDecorationStyleIndex = 4;
const int tsFontWeightIndex = 5;
const int tsFontStyleIndex = 6;
const int tsTextBaselineIndex = 7;
const int tsFontFamilyIndex = 8;
const int tsFontSizeIndex = 9;
const int tsLetterSpacingIndex = 10;
const int tsWordSpacingIndex = 11;
const int tsHeightIndex = 12;
const int tsLocaleIndex = 13;
const int tsBackgroundIndex = 14;
const int tsForegroundIndex = 15;
const int tsColorMask = 1 << tsColorIndex;
const int tsTextDecorationMask = 1 << tsTextDecorationIndex;
const int tsTextDecorationColorMask = 1 << tsTextDecorationColorIndex;
const int tsTextDecorationStyleMask = 1 << tsTextDecorationStyleIndex;
const int tsFontWeightMask = 1 << tsFontWeightIndex;
const int tsFontStyleMask = 1 << tsFontStyleIndex;
const int tsTextBaselineMask = 1 << tsTextBaselineIndex;
const int tsFontFamilyMask = 1 << tsFontFamilyIndex;
const int tsFontSizeMask = 1 << tsFontSizeIndex;
const int tsLetterSpacingMask = 1 << tsLetterSpacingIndex;
const int tsWordSpacingMask = 1 << tsWordSpacingIndex;
const int tsHeightMask = 1 << tsHeightIndex;
const int tsLocaleMask = 1 << tsLocaleIndex;
const int tsBackgroundMask = 1 << tsBackgroundIndex;
const int tsForegroundMask = 1 << tsForegroundIndex;
// ParagraphStyle
const int psTextAlignIndex = 1;
const int psTextDirectionIndex = 2;
const int psFontWeightIndex = 3;
const int psFontStyleIndex = 4;
const int psMaxLinesIndex = 5;
const int psFontFamilyIndex = 6;
const int psFontSizeIndex = 7;
const int psLineHeightIndex = 8;
const int psEllipsisIndex = 9;
const int psLocaleIndex = 10;
const int psTextAlignMask = 1 << psTextAlignIndex;
const int psTextDirectionMask = 1 << psTextDirectionIndex;
const int psFontWeightMask = 1 << psFontWeightIndex;
const int psFontStyleMask = 1 << psFontStyleIndex;
const int psMaxLinesMask = 1 << psMaxLinesIndex;
const int psFontFamilyMask = 1 << psFontFamilyIndex;
const int psFontSizeMask = 1 << psFontSizeIndex;
const int psLineHeightMask = 1 << psLineHeightIndex;
const int psEllipsisMask = 1 << psEllipsisIndex;
const int psLocaleMask = 1 << psLocaleIndex;
} // namespace
static void ParagraphBuilder_constructor(Dart_NativeArguments args) {
DartCallConstructor(&ParagraphBuilder::create, args);
}
IMPLEMENT_WRAPPERTYPEINFO(ui, ParagraphBuilder);
#define FOR_EACH_BINDING(V) \
V(ParagraphBuilder, pushStyle) \
V(ParagraphBuilder, pop) \
V(ParagraphBuilder, addText) \
V(ParagraphBuilder, build)
FOR_EACH_BINDING(DART_NATIVE_CALLBACK)
void ParagraphBuilder::RegisterNatives(tonic::DartLibraryNatives* natives) {
natives->Register(
{{"ParagraphBuilder_constructor", ParagraphBuilder_constructor, 7, true},
FOR_EACH_BINDING(DART_REGISTER_NATIVE)});
}
fml::RefPtr<ParagraphBuilder> ParagraphBuilder::create(
tonic::Int32List& encoded,
const std::string& fontFamily,
double fontSize,
double lineHeight,
const std::u16string& ellipsis,
const std::string& locale) {
return fml::MakeRefCounted<ParagraphBuilder>(encoded, fontFamily, fontSize,
lineHeight, ellipsis, locale);
}
ParagraphBuilder::ParagraphBuilder(tonic::Int32List& encoded,
const std::string& fontFamily,
double fontSize,
double lineHeight,
const std::u16string& ellipsis,
const std::string& locale) {
int32_t mask = encoded[0];
txt::ParagraphStyle style;
if (mask & psTextAlignMask)
style.text_align = txt::TextAlign(encoded[psTextAlignIndex]);
if (mask & psTextDirectionMask)
style.text_direction = txt::TextDirection(encoded[psTextDirectionIndex]);
if (mask & psFontWeightMask)
style.font_weight =
static_cast<txt::FontWeight>(encoded[psFontWeightIndex]);
if (mask & psFontStyleMask)
style.font_style = static_cast<txt::FontStyle>(encoded[psFontStyleIndex]);
if (mask & psFontFamilyMask)
style.font_family = fontFamily;
if (mask & psFontSizeMask)
style.font_size = fontSize;
if (mask & psLineHeightMask)
style.line_height = lineHeight;
if (mask & psMaxLinesMask)
style.max_lines = encoded[psMaxLinesIndex];
if (mask & psEllipsisMask) {
style.ellipsis = ellipsis;
}
if (mask & psLocaleMask) {
style.locale = locale;
}
FontCollection& font_collection =
UIDartState::Current()->window()->client()->GetFontCollection();
m_paragraphBuilder = std::make_unique<txt::ParagraphBuilder>(
style, font_collection.GetFontCollection());
} // namespace blink
ParagraphBuilder::~ParagraphBuilder() = default;
void ParagraphBuilder::pushStyle(tonic::Int32List& encoded,
const std::string& fontFamily,
double fontSize,
double letterSpacing,
double wordSpacing,
double height,
const std::string& locale,
Dart_Handle background_objects,
Dart_Handle background_data,
Dart_Handle foreground_objects,
Dart_Handle foreground_data) {
FML_DCHECK(encoded.num_elements() == 8);
int32_t mask = encoded[0];
// Set to use the properties of the previous style if the property is not
// explicitly given.
txt::TextStyle style = m_paragraphBuilder->PeekStyle();
if (mask & tsColorMask)
style.color = encoded[tsColorIndex];
if (mask & tsTextDecorationMask) {
style.decoration =
static_cast<txt::TextDecoration>(encoded[tsTextDecorationIndex]);
}
if (mask & tsTextDecorationColorMask)
style.decoration_color = encoded[tsTextDecorationColorIndex];
if (mask & tsTextDecorationStyleMask)
style.decoration_style = static_cast<txt::TextDecorationStyle>(
encoded[tsTextDecorationStyleIndex]);
if (mask & tsTextBaselineMask) {
// TODO(abarth): Implement TextBaseline. The CSS version of this
// property wasn't wired up either.
}
if (mask & (tsFontWeightMask | tsFontStyleMask | tsFontFamilyMask |
tsFontSizeMask | tsLetterSpacingMask | tsWordSpacingMask)) {
if (mask & tsFontWeightMask)
style.font_weight =
static_cast<txt::FontWeight>(encoded[tsFontWeightIndex]);
if (mask & tsFontStyleMask)
style.font_style = static_cast<txt::FontStyle>(encoded[tsFontStyleIndex]);
if (mask & tsFontFamilyMask)
style.font_family = fontFamily;
if (mask & tsFontSizeMask)
style.font_size = fontSize;
if (mask & tsLetterSpacingMask)
style.letter_spacing = letterSpacing;
if (mask & tsWordSpacingMask)
style.word_spacing = wordSpacing;
}
if (mask & tsHeightMask) {
style.height = height;
}
if (mask & tsLocaleMask) {
style.locale = locale;
}
if (mask & tsBackgroundMask) {
Paint background(background_objects, background_data);
if (background.paint()) {
style.has_background = true;
style.background = *background.paint();
}
}
if (mask & tsForegroundMask) {
Paint foreground(foreground_objects, foreground_data);
if (foreground.paint()) {
style.has_foreground = true;
style.foreground = *foreground.paint();
}
}
m_paragraphBuilder->PushStyle(style);
}
void ParagraphBuilder::pop() {
m_paragraphBuilder->Pop();
}
Dart_Handle ParagraphBuilder::addText(const std::u16string& text) {
if (text.empty())
return Dart_Null();
// Use ICU to validate the UTF-16 input. Calling u_strToUTF8 with a null
// output buffer will return U_BUFFER_OVERFLOW_ERROR if the input is well
// formed.
const UChar* text_ptr = reinterpret_cast<const UChar*>(text.data());
UErrorCode error_code = U_ZERO_ERROR;
u_strToUTF8(nullptr, 0, nullptr, text_ptr, text.size(), &error_code);
if (error_code != U_BUFFER_OVERFLOW_ERROR)
return tonic::ToDart("string is not well-formed UTF-16");
m_paragraphBuilder->AddText(text);
return Dart_Null();
}
fml::RefPtr<Paragraph> ParagraphBuilder::build() {
return Paragraph::Create(m_paragraphBuilder->Build());
}
} // namespace blink