// 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/threads.h" #include "flutter/lib/ui/ui_dart_state.h" #include "flutter/sky/engine/core/rendering/RenderInline.h" #include "flutter/sky/engine/core/rendering/RenderParagraph.h" #include "flutter/sky/engine/core/rendering/RenderText.h" #include "flutter/sky/engine/core/rendering/style/RenderStyle.h" #include "flutter/sky/engine/platform/text/LocaleToScriptMapping.h" #include "lib/ftl/tasks/task_runner.h" #include "lib/tonic/converter/dart_converter.h" #include "lib/tonic/dart_args.h" #include "lib/tonic/dart_binding_macros.h" #include "lib/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 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; // ParagraphStyle const int psTextAlignIndex = 1; const int psFontWeightIndex = 2; const int psFontStyleIndex = 3; const int psMaxLinesIndex = 4; const int psFontFamilyIndex = 5; const int psFontSizeIndex = 6; const int psLineHeightIndex = 7; const int psEllipsisIndex = 8; const int psTextAlignMask = 1 << psTextAlignIndex; 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; float getComputedSizeFromSpecifiedSize(float specifiedSize) { if (specifiedSize < std::numeric_limits::epsilon()) return 0.0f; return specifiedSize; } void createFontForDocument(RenderStyle* style) { FontDescription fontDescription = FontDescription(); fontDescription.setScript( localeToScriptCodeForFontSelection(style->locale())); // Using 14px default to match Material Design English Body1: // http://www.google.com/design/spec/style/typography.html#typography-typeface const float defaultFontSize = 14.0; fontDescription.setSpecifiedSize(defaultFontSize); fontDescription.setComputedSize(defaultFontSize); FontOrientation fontOrientation = Horizontal; NonCJKGlyphOrientation glyphOrientation = NonCJKGlyphOrientationVerticalRight; fontDescription.setOrientation(fontOrientation); fontDescription.setNonCJKGlyphOrientation(glyphOrientation); style->setFontDescription(fontDescription); style->font().update(UIDartState::Current()->font_selector()); } PassRefPtr decodeParagraphStyle( RenderStyle* parentStyle, tonic::Int32List& encoded, const std::string& fontFamily, double fontSize, double lineHeight, const std::string& ellipsis) { FTL_DCHECK(encoded.num_elements() == 5); RefPtr style = RenderStyle::create(); style->inheritFrom(parentStyle); style->setDisplay(PARAGRAPH); int32_t mask = encoded[0]; if (mask & psTextAlignMask) style->setTextAlign(static_cast(encoded[psTextAlignIndex])); if (mask & (psFontWeightMask | psFontStyleMask | psFontFamilyMask | psFontSizeMask)) { FontDescription fontDescription = style->fontDescription(); if (mask & psFontWeightMask) fontDescription.setWeight( static_cast(encoded[psFontWeightIndex])); if (mask & psFontStyleMask) fontDescription.setStyle( static_cast(encoded[psFontStyleIndex])); if (mask & psFontFamilyMask) { FontFamily family; family.setFamily(String::fromUTF8(fontFamily)); fontDescription.setFamily(family); } if (mask & psFontSizeMask) { fontDescription.setSpecifiedSize(fontSize); fontDescription.setIsAbsoluteSize(true); fontDescription.setComputedSize( getComputedSizeFromSpecifiedSize(fontSize)); } style->setFontDescription(fontDescription); style->font().update(UIDartState::Current()->font_selector()); } if (mask & psLineHeightMask) style->setLineHeight(Length(lineHeight * 100.0, Percent)); if (mask & psMaxLinesMask) style->setMaxLines(encoded[psMaxLinesIndex]); if (mask & psEllipsisMask) style->setEllipsis(AtomicString::fromUTF8(ellipsis.c_str())); return style.release(); } Color getColorFromARGB(int argb) { return Color((argb & 0x00FF0000) >> 16, (argb & 0x0000FF00) >> 8, (argb & 0x000000FF) >> 0, (argb & 0xFF000000) >> 24); } } // 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, 6, true}, FOR_EACH_BINDING(DART_REGISTER_NATIVE)}); } ftl::RefPtr ParagraphBuilder::create( tonic::Int32List& encoded, const std::string& fontFamily, double fontSize, double lineHeight, const std::string& ellipsis) { return ftl::MakeRefCounted( encoded, fontFamily, fontSize, lineHeight, ellipsis); } ParagraphBuilder::ParagraphBuilder(tonic::Int32List& encoded, const std::string& fontFamily, double fontSize, double lineHeight, const std::string& ellipsis) { createRenderView(); RefPtr paragraphStyle = decodeParagraphStyle( m_renderView->style(), encoded, fontFamily, fontSize, lineHeight, ellipsis); encoded.Release(); m_renderParagraph = new RenderParagraph(); m_renderParagraph->setStyle(paragraphStyle.release()); m_currentRenderObject = m_renderParagraph; m_renderView->addChild(m_currentRenderObject); } ParagraphBuilder::~ParagraphBuilder() { if (m_renderView) { RenderView* renderView = m_renderView.leakPtr(); Threads::UI()->PostTask( [renderView]() { renderView->destroy(); }); } } void ParagraphBuilder::pushStyle(tonic::Int32List& encoded, const std::string& fontFamily, double fontSize, double letterSpacing, double wordSpacing, double height) { FTL_DCHECK(encoded.num_elements() == 8); RefPtr style = RenderStyle::create(); style->inheritFrom(m_currentRenderObject->style()); int32_t mask = encoded[0]; if (mask & tsColorMask) style->setColor(getColorFromARGB(encoded[tsColorIndex])); if (mask & tsTextDecorationMask) { style->setTextDecoration( static_cast(encoded[tsTextDecorationIndex])); style->applyTextDecorations(); } if (mask & tsTextDecorationColorMask) style->setTextDecorationColor( StyleColor(getColorFromARGB(encoded[tsTextDecorationColorIndex]))); if (mask & tsTextDecorationStyleMask) style->setTextDecorationStyle( static_cast(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)) { FontDescription fontDescription = style->fontDescription(); if (mask & tsFontWeightMask) fontDescription.setWeight( static_cast(encoded[tsFontWeightIndex])); if (mask & tsFontStyleMask) fontDescription.setStyle( static_cast(encoded[tsFontStyleIndex])); if (mask & tsFontFamilyMask) { FontFamily family; family.setFamily(String::fromUTF8(fontFamily)); fontDescription.setFamily(family); } if (mask & tsFontSizeMask) { fontDescription.setSpecifiedSize(fontSize); fontDescription.setIsAbsoluteSize(true); fontDescription.setComputedSize( getComputedSizeFromSpecifiedSize(fontSize)); } if (mask & tsLetterSpacingMask) fontDescription.setLetterSpacing(letterSpacing); if (mask & tsWordSpacingMask) fontDescription.setWordSpacing(wordSpacing); style->setFontDescription(fontDescription); style->font().update(UIDartState::Current()->font_selector()); } if (mask & tsHeightMask) { style->setLineHeight(Length(height * 100.0, Percent)); } encoded.Release(); RenderObject* span = new RenderInline(); span->setStyle(style.release()); m_currentRenderObject->addChild(span); m_currentRenderObject = span; } void ParagraphBuilder::pop() { if (m_currentRenderObject) m_currentRenderObject = m_currentRenderObject->parent(); } void ParagraphBuilder::addText(const std::string& text) { if (!m_currentRenderObject) return; RenderText* renderText = new RenderText(String::fromUTF8(text).impl()); RefPtr style = RenderStyle::create(); style->inheritFrom(m_currentRenderObject->style()); renderText->setStyle(style.release()); m_currentRenderObject->addChild(renderText); } ftl::RefPtr ParagraphBuilder::build() { m_currentRenderObject = nullptr; return Paragraph::create(m_renderView.release()); } void ParagraphBuilder::createRenderView() { RefPtr style = RenderStyle::create(); style->setRTLOrdering(LogicalOrder); style->setZIndex(0); style->setUserModify(READ_ONLY); createFontForDocument(style.get()); m_renderView = adoptPtr(new RenderView()); m_renderView->setStyle(style.release()); } } // namespace blink