From 656f6eec4881d0305fc1322a0531cadd816287dc Mon Sep 17 00:00:00 2001 From: abdullah Date: Thu, 25 Feb 2021 14:00:05 +0200 Subject: [PATCH] Add text wrapping hyphen mode support related to https://review.tizen.org/gerrit/c/platform/core/uifw/dali-adaptor/+/254278 using namespace Dali; using namespace Dali::Toolkit; class SimpleApp : public ConnectionTracker { public: SimpleApp(Application& application) : mApplication(application) { mApplication.InitSignal().Connect(this, &SimpleApp::Create); } void Create(Application& application) { Window window = mApplication.GetWindow(); mEditor = TextEditor::New(); mEditor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER); mEditor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER); mEditor.SetProperty(Actor::Property::POSITION, Vector3(0.f, 0.0f, 0.f)); mEditor.SetProperty(Actor::Property::SIZE, Vector2(150, 300.0f)); window.SetBackgroundColor(Vector4(0.04f, 0.345f, 0.392f, 1.0f)); mEditor.SetProperty(TextEditor::Property::POINT_SIZE, 26.f); mEditor.SetProperty(TextEditor::Property::TEXT, "Experimental!!!!"); mEditor.SetProperty(TextEditor::Property::LINE_WRAP_MODE, Text::LineWrap::HYPHENATION); window.Add(mEditor); } private: Application& mApplication; TextEditor mEditor; }; int DALI_EXPORT_API main(int argc, char** argv) { Application application = Application::New(&argc, &argv); SimpleApp test(application); application.MainLoop(); return 0; } Change-Id: I736a8d251d1f7fb364b7bfb26520767dfd019ccb --- .../src/dali-toolkit-internal/CMakeLists.txt | 1 + .../dali-toolkit-test-utils/toolkit-text-utils.cpp | 42 ++- .../dali-toolkit-test-utils/toolkit-text-utils.h | 5 +- .../utc-Dali-BidirectionalSupport.cpp | 11 +- .../utc-Dali-LogicalModel.cpp | 17 +- .../dali-toolkit-internal/utc-Dali-Text-Cursor.cpp | 14 +- .../utc-Dali-Text-Hyphen-Wrapping.cpp | 307 +++++++++++++++++++++ .../dali-toolkit-internal/utc-Dali-Text-Layout.cpp | 8 +- .../utc-Dali-Text-Shaping.cpp | 5 +- .../dali-toolkit-internal/utc-Dali-VisualModel.cpp | 8 +- .../src/dali-toolkit/utc-Dali-TextEditor.cpp | 49 ++++ .../src/dali-toolkit/utc-Dali-TextLabel.cpp | 50 +++- .../devel-api/text/text-enumerations-devel.h | 13 +- dali-toolkit/devel-api/text/text-utils-devel.cpp | 2 +- dali-toolkit/internal/file.list | 1 + dali-toolkit/internal/text/hyphenator.cpp | 69 +++++ dali-toolkit/internal/text/hyphenator.h | 53 ++++ .../internal/text/layouts/layout-engine.cpp | 78 +++++- .../text/rendering/atlas/text-atlas-renderer.cpp | 58 +++- .../internal/text/rendering/text-typesetter.cpp | 54 +++- .../internal/text/rendering/view-model.cpp | 15 + dali-toolkit/internal/text/rendering/view-model.h | 15 + .../internal/text/text-controller-impl.cpp | 36 +++ dali-toolkit/internal/text/text-controller.cpp | 16 +- dali-toolkit/internal/text/text-model-interface.h | 21 ++ dali-toolkit/internal/text/text-model.cpp | 15 + dali-toolkit/internal/text/text-model.h | 15 + dali-toolkit/internal/text/text-view-interface.h | 20 ++ dali-toolkit/internal/text/text-view.cpp | 29 ++ dali-toolkit/internal/text/text-view.h | 15 + dali-toolkit/internal/text/visual-model-impl.h | 8 + 31 files changed, 980 insertions(+), 70 deletions(-) create mode 100755 automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Hyphen-Wrapping.cpp create mode 100644 dali-toolkit/internal/text/hyphenator.cpp create mode 100644 dali-toolkit/internal/text/hyphenator.h diff --git a/automated-tests/src/dali-toolkit-internal/CMakeLists.txt b/automated-tests/src/dali-toolkit-internal/CMakeLists.txt index 1162266..f20e494 100755 --- a/automated-tests/src/dali-toolkit-internal/CMakeLists.txt +++ b/automated-tests/src/dali-toolkit-internal/CMakeLists.txt @@ -36,6 +36,7 @@ SET(TC_SOURCES utc-Dali-Visuals-internal.cpp utc-Dali-VisualModel.cpp utc-Dali-VisualUrl.cpp + utc-Dali-Text-Hyphen-Wrapping.cpp ) IF(ELDBUS_AVAILABLE) diff --git a/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/toolkit-text-utils.cpp b/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/toolkit-text-utils.cpp index 1e745b1..0a426b0 100755 --- a/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/toolkit-text-utils.cpp +++ b/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/toolkit-text-utils.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 Samsung Electronics Co., Ltd. + * Copyright (c) 2021 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,6 +32,7 @@ #include #include #include +#include namespace Dali { @@ -100,7 +101,8 @@ void CreateTextModel( const std::string& text, Size& layoutSize, ModelPtr& textModel, MetricsPtr& metrics, - bool markupProcessorEnabled ) + bool markupProcessorEnabled, + LineWrap::Mode wrapMode ) { textModel = Model::New(); ///< Pointer to the text's model. LogicalModelPtr logicalModel = textModel->mLogicalModel; @@ -154,6 +156,41 @@ void CreateTextModel( const std::string& text, return; } + textModel->mLineWrapMode = wrapMode; + + if(textModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) || + textModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::MIXED)) + { + CharacterIndex end = characterCount; + LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin(); + + for(CharacterIndex index = 0; index < end; index++) + { + CharacterIndex wordEnd = index; + while((*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_ALLOW_BREAK) && (*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_MUST_BREAK)) + { + wordEnd++; + } + + if((wordEnd + 1) == end) // add last char + { + wordEnd++; + } + + Vector hyphens = GetWordHyphens(utf32Characters.Begin() + index, wordEnd - index, nullptr); + + for(CharacterIndex i = 0; i < (wordEnd - index); i++) + { + if(hyphens[i]) + { + *(lineBreakInfoBuffer + index + i) = TextAbstraction::LINE_HYPHENATION_BREAK; + } + } + + index = wordEnd; + } + } + // 3) Set the script info. MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get(); @@ -284,7 +321,6 @@ void CreateTextModel( const std::string& text, // Set the layout parameters. textModel->mHorizontalAlignment = Text::HorizontalAlignment::BEGIN; - textModel->mLineWrapMode = LineWrap::WORD; textModel->mIgnoreSpacesAfterText = true; textModel->mMatchSystemLanguageDirection = false; Layout::Parameters layoutParameters( textArea, diff --git a/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/toolkit-text-utils.h b/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/toolkit-text-utils.h index eb0dd40..6e30bbb 100644 --- a/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/toolkit-text-utils.h +++ b/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/toolkit-text-utils.h @@ -2,7 +2,7 @@ #define DALI_TOOLKIT_TEXT_UTILS_H /* - * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * Copyright (c) 2021 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -64,7 +64,8 @@ void CreateTextModel( const std::string& text, Size& layoutSize, ModelPtr& textModel, MetricsPtr& metrics, - bool markupProcessorEnabled ); + bool markupProcessorEnabled, + LineWrap::Mode wrapMode ); /** * @brief Configures the text @p controller similarly to the one configured by the text-label. diff --git a/automated-tests/src/dali-toolkit-internal/utc-Dali-BidirectionalSupport.cpp b/automated-tests/src/dali-toolkit-internal/utc-Dali-BidirectionalSupport.cpp index e056f73..de772f3 100644 --- a/automated-tests/src/dali-toolkit-internal/utc-Dali-BidirectionalSupport.cpp +++ b/automated-tests/src/dali-toolkit-internal/utc-Dali-BidirectionalSupport.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 Samsung Electronics Co., Ltd. + * Copyright (c) 2021 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -111,7 +111,8 @@ bool SetBidirectionalInfoTest( const SetBidirectionalInfoData& data ) layoutSize, textModel, metrics, - false ); + false, + LineWrap::WORD ); LogicalModelPtr logicalModel = textModel->mLogicalModel; VisualModelPtr visualModel = textModel->mVisualModel; @@ -196,7 +197,8 @@ bool GetMirroredTextTest( const GetMirroredTextData& data ) layoutSize, textModel, metrics, - false ); + false, + LineWrap::WORD ); LogicalModelPtr logicalModel = textModel->mLogicalModel; VisualModelPtr visualModel = textModel->mVisualModel; @@ -272,7 +274,8 @@ bool GetCharactersDirectionTest( const GetCharactersDirectionData& data ) layoutSize, textModel, metrics, - data.markupProcessorEnabled ); + data.markupProcessorEnabled, + LineWrap::WORD ); LogicalModelPtr logicalModel = textModel->mLogicalModel; VisualModelPtr visualModel = textModel->mVisualModel; diff --git a/automated-tests/src/dali-toolkit-internal/utc-Dali-LogicalModel.cpp b/automated-tests/src/dali-toolkit-internal/utc-Dali-LogicalModel.cpp index 7cbbe90..8515172 100755 --- a/automated-tests/src/dali-toolkit-internal/utc-Dali-LogicalModel.cpp +++ b/automated-tests/src/dali-toolkit-internal/utc-Dali-LogicalModel.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * Copyright (c) 2021 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -118,7 +118,8 @@ bool CreateParagraphTest( const CreateParagraphData& data ) layoutSize, textModel, metrics, - false ); + false, + LineWrap::WORD ); LogicalModelPtr logicalModel = textModel->mLogicalModel; VisualModelPtr visualModel = textModel->mVisualModel; @@ -180,7 +181,8 @@ bool FindParagraphTest( const FindParagraphData& data ) layoutSize, textModel, metrics, - false ); + false, + LineWrap::WORD ); LogicalModelPtr logicalModel = textModel->mLogicalModel; VisualModelPtr visualModel = textModel->mVisualModel; @@ -231,7 +233,8 @@ bool FetchBidirectionalLineInfoTest( const FetchBidirectionalLineInfoData& data layoutSize, textModel, metrics, - false ); + false, + LineWrap::WORD ); LogicalModelPtr logicalModel = textModel->mLogicalModel; VisualModelPtr visualModel = textModel->mVisualModel; @@ -277,7 +280,8 @@ bool GetLogicalCharacterIndexTest( const GetLogicalCharacterIndexData& data ) layoutSize, textModel, metrics, - false ); + false, + LineWrap::WORD ); LogicalModelPtr logicalModel = textModel->mLogicalModel; VisualModelPtr visualModel = textModel->mVisualModel; @@ -340,7 +344,8 @@ bool GetLogicalCursorIndexTest( const GetLogicalCursorIndexData& data ) layoutSize, textModel, metrics, - false ); + false, + LineWrap::WORD ); LogicalModelPtr logicalModel = textModel->mLogicalModel; VisualModelPtr visualModel = textModel->mVisualModel; diff --git a/automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Cursor.cpp b/automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Cursor.cpp index 2ab84e7..78d7cf8 100755 --- a/automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Cursor.cpp +++ b/automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Cursor.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * Copyright (c) 2021 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -111,7 +111,8 @@ bool GetClosestLineTest( const GetClosestLineData& data ) layoutSize, textModel, metrics, - false ); + false, + LineWrap::WORD ); LogicalModelPtr logicalModel = textModel->mLogicalModel; VisualModelPtr visualModel = textModel->mVisualModel; @@ -157,7 +158,8 @@ bool GetClosestCursorIndexTest( const GetClosestCursorIndexData& data ) layoutSize, textModel, metrics, - false ); + false, + LineWrap::WORD ); LogicalModelPtr logicalModel = textModel->mLogicalModel; VisualModelPtr visualModel = textModel->mVisualModel; @@ -207,7 +209,8 @@ bool GetCursorPositionTest( const GetCursorPositionData& data ) layoutSize, textModel, metrics, - false ); + false, + LineWrap::WORD ); LogicalModelPtr logicalModel = textModel->mLogicalModel; VisualModelPtr visualModel = textModel->mVisualModel; @@ -260,7 +263,8 @@ bool FindSelectionIndicesTest( const FindSelectionIndicesData& data ) layoutSize, textModel, metrics, - false ); + false, + LineWrap::WORD ); LogicalModelPtr logicalModel = textModel->mLogicalModel; VisualModelPtr visualModel = textModel->mVisualModel; diff --git a/automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Hyphen-Wrapping.cpp b/automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Hyphen-Wrapping.cpp new file mode 100755 index 0000000..ccc8463 --- /dev/null +++ b/automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Hyphen-Wrapping.cpp @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace Dali; +using namespace Toolkit; +using namespace Text; + + +namespace +{ + +const std::string DEFAULT_FONT_DIR( "/resources/fonts" ); + +struct LayoutTextData +{ + std::string text; + Size textArea; + unsigned int numberOfFonts; + FontDescriptionRun *fontDescriptions; + unsigned int numberOfLines; + LineRun* lines; + Layout::Engine::Type layout; + unsigned int startIndex; + unsigned int numberOfGlyphs; + Text::LineWrap::Mode wrapMode; +}; + +void Print( const LineRun& line ) +{ + std::cout << " glyph run, index : " << line.glyphRun.glyphIndex << ", num glyphs : " << line.glyphRun.numberOfGlyphs << std::endl; + std::cout << " character run, index : " << line.characterRun.characterIndex << ", num chars : " << line.characterRun.numberOfCharacters << std::endl; +} + +bool LayoutTextTest( const LayoutTextData& data ) +{ + // Load some fonts. + TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get(); + fontClient.SetDpi( 96u, 96u ); + + char* pathNamePtr = get_current_dir_name(); + const std::string pathName( pathNamePtr ); + free( pathNamePtr ); + + fontClient.GetFontId( pathName + DEFAULT_FONT_DIR + "/tizen/TizenSansRegular.ttf" ); + + // 1) Create the model. + ModelPtr textModel; + MetricsPtr metrics; + Size layoutSize; + + Vector fontDescriptionRuns; + if( 0u != data.numberOfFonts ) + { + fontDescriptionRuns.Insert( fontDescriptionRuns.End(), + data.fontDescriptions, + data.fontDescriptions + data.numberOfFonts ); + } + + LayoutOptions options; + options.align = false; + CreateTextModel( data.text, + data.textArea, + fontDescriptionRuns, + options, + layoutSize, + textModel, + metrics, + false, + data.wrapMode ); + + Vector& lines = textModel->mVisualModel->mLines; + + // 4) Compare the results. + + if( lines.Count() != data.numberOfLines ) + { + std::cout << " Different number of lines : " << lines.Count() << ", expected : " << data.numberOfLines << std::endl; + return false; + } + + for( unsigned int index = 0u; index < data.numberOfLines; ++index ) + { + const LineRun& line = *( lines.Begin() + index ); + const LineRun& expectedLine = *( data.lines + index ); + + if( line.characterRun.characterIndex != expectedLine.characterRun.characterIndex ) + { + std::cout << " Different line info for line : " << index << std::endl; + Print( line ); + std::cout << " expected" << std::endl; + Print( expectedLine ); + return false; + } + if( line.characterRun.numberOfCharacters != expectedLine.characterRun.numberOfCharacters ) + { + std::cout << " Different line info for line : " << index << std::endl; + Print( line ); + std::cout << " expected" << std::endl; + Print( expectedLine ); + return false; + } + } + + return true; +} + +} // namespace + + +int UtcDaliTextHyphenWrapping(void) +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliTextHyphenWrapping"); + + // Layout some lines of left to right text. + + const std::string fontFamily( "TizenSans" ); + + // Set a known font description + FontDescriptionRun fontDescriptionRun1; + fontDescriptionRun1.characterRun.characterIndex = 0u; + fontDescriptionRun1.characterRun.numberOfCharacters = 13u; + fontDescriptionRun1.familyLength = fontFamily.size(); + fontDescriptionRun1.familyName = new char[fontDescriptionRun1.familyLength]; + memcpy( fontDescriptionRun1.familyName, fontFamily.c_str(), fontDescriptionRun1.familyLength ); + fontDescriptionRun1.familyDefined = true; + fontDescriptionRun1.weightDefined = false; + fontDescriptionRun1.widthDefined = false; + fontDescriptionRun1.slantDefined = false; + fontDescriptionRun1.sizeDefined = false; + + Vector fontDescriptionRuns; + fontDescriptionRuns.PushBack( fontDescriptionRun1 ); + Size textArea(65.0f, 200.f); + + LineRun line1 = + { + { 0u, 5u }, + { 0u, 5u }, + 0.f, + 0.f, + 0.f, + 0.f, + 0.f, + 0.f, + false, + false + }; + LineRun line2 = + { + { 5u, 8u }, + { 5u, 8u }, + 0.f, + 0.f, + 0.f, + 0.f, + 0.f, + 0.f, + false, + false + }; + + Vector lines; + lines.PushBack( line1 ); + lines.PushBack( line2 ); + + LayoutTextData data = + { + "Hi Experiment", + textArea, + 1u, + fontDescriptionRuns.Begin(), + 2u, + lines.Begin(), + Layout::Engine::MULTI_LINE_BOX, + 0u, + 13u, + (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION + }; + + if( !LayoutTextTest( data ) ) + { + tet_result(TET_FAIL); + } + + tet_result(TET_PASS); + END_TEST; +} + +int UtcDaliTextMixedWrapping(void) +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliTextMixedWrapping"); + + // Layout some lines of left to right text. + + const std::string fontFamily( "DejaVuSans" ); + + // Set a known font description + FontDescriptionRun fontDescriptionRun1; + fontDescriptionRun1.characterRun.characterIndex = 0u; + fontDescriptionRun1.characterRun.numberOfCharacters = 13u; + fontDescriptionRun1.familyLength = fontFamily.size(); + fontDescriptionRun1.familyName = new char[fontDescriptionRun1.familyLength]; + memcpy( fontDescriptionRun1.familyName, fontFamily.c_str(), fontDescriptionRun1.familyLength ); + fontDescriptionRun1.familyDefined = true; + fontDescriptionRun1.weightDefined = false; + fontDescriptionRun1.widthDefined = false; + fontDescriptionRun1.slantDefined = false; + fontDescriptionRun1.sizeDefined = false; + + Vector fontDescriptionRuns; + fontDescriptionRuns.PushBack( fontDescriptionRun1 ); + Size textArea(72.0f, 200.f); + + LineRun line1 = + { + { 0u, 3u }, + { 0u, 3u }, + 0.f, + 0.f, + 0.f, + 0.f, + 0.f, + 0.f, + false, + false + }; + LineRun line2 = + { + { 3u, 6u }, + { 3u, 6u }, + 0.f, + 0.f, + 0.f, + 0.f, + 0.f, + 0.f, + false, + false + }; + LineRun line3 = + { + { 9u, 4u }, + { 9u, 4u }, + 0.f, + 0.f, + 0.f, + 0.f, + 0.f, + 0.f, + false, + false + }; + + Vector lines; + lines.PushBack( line1 ); + lines.PushBack( line2 ); + lines.PushBack( line3 ); + + LayoutTextData data = + { + "Hi Experiment", + textArea, + 1u, + fontDescriptionRuns.Begin(), + 3u, + lines.Begin(), + Layout::Engine::MULTI_LINE_BOX, + 0u, + 13u, + (Text::LineWrap::Mode)DevelText::LineWrap::MIXED + }; + + if( !LayoutTextTest( data ) ) + { + tet_result(TET_FAIL); + } + + tet_result(TET_PASS); + END_TEST; +} \ No newline at end of file diff --git a/automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Layout.cpp b/automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Layout.cpp index 05226d4..8bdecaa 100755 --- a/automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Layout.cpp +++ b/automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Layout.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * Copyright (c) 2021 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -108,7 +108,8 @@ bool LayoutTextTest( const LayoutTextData& data ) layoutSize, textModel, metrics, - false ); + false, + LineWrap::WORD ); LogicalModelPtr logicalModel = textModel->mLogicalModel; VisualModelPtr visualModel = textModel->mVisualModel; @@ -363,7 +364,8 @@ bool AlignTest( const AlignData& data ) layoutSize, textModel, metrics, - false ); + false, + LineWrap::WORD ); LogicalModelPtr logicalModel = textModel->mLogicalModel; VisualModelPtr visualModel = textModel->mVisualModel; diff --git a/automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Shaping.cpp b/automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Shaping.cpp index a7534f9..52c8808 100755 --- a/automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Shaping.cpp +++ b/automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Shaping.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * Copyright (c) 2021 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -140,7 +140,8 @@ bool ShapeInfoTest( const ShapeInfoData& data ) layoutSize, textModel, metrics, - false ); + false, + LineWrap::WORD ); LogicalModelPtr logicalModel = textModel->mLogicalModel; VisualModelPtr visualModel = textModel->mVisualModel; diff --git a/automated-tests/src/dali-toolkit-internal/utc-Dali-VisualModel.cpp b/automated-tests/src/dali-toolkit-internal/utc-Dali-VisualModel.cpp index df247e0..43157ff 100644 --- a/automated-tests/src/dali-toolkit-internal/utc-Dali-VisualModel.cpp +++ b/automated-tests/src/dali-toolkit-internal/utc-Dali-VisualModel.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * Copyright (c) 2021 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -79,7 +79,8 @@ bool SetGlyphsPerCharacterTest( const SetGlyphsPerCharacterData& data ) layoutSize, textModel, metrics, - false ); + false, + LineWrap::WORD ); LogicalModelPtr logicalModel = textModel->mLogicalModel; VisualModelPtr visualModel = textModel->mVisualModel; @@ -163,7 +164,8 @@ bool SetCharacterToGlyphTest( const SetCharacterToGlyphData& data ) layoutSize, textModel, metrics, - false ); + false, + LineWrap::WORD ); LogicalModelPtr logicalModel = textModel->mLogicalModel; VisualModelPtr visualModel = textModel->mVisualModel; diff --git a/automated-tests/src/dali-toolkit/utc-Dali-TextEditor.cpp b/automated-tests/src/dali-toolkit/utc-Dali-TextEditor.cpp index 3bd9ee6..626432e 100644 --- a/automated-tests/src/dali-toolkit/utc-Dali-TextEditor.cpp +++ b/automated-tests/src/dali-toolkit/utc-Dali-TextEditor.cpp @@ -28,6 +28,7 @@ #include #include #include +#include using namespace Dali; using namespace Toolkit; @@ -3640,4 +3641,52 @@ int UtcDaliTextEditorAtlasLimitationIsEnabledPerformanceCases(void) } END_TEST; +} + +int UtcDaliTextEditorHyphenWrapMode(void) +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliTextEditorHyphenWrapMode "); + + int lineCount =0; + TextEditor textEditor = TextEditor::New(); + + textEditor.SetProperty( Actor::Property::SIZE, Vector2( 150.0f, 300.f ) ); + + application.GetScene().Add( textEditor ); + application.SendNotification(); + application.Render(); + + textEditor.SetProperty( TextEditor::Property::TEXT, "Hi Experimen" ); + textEditor.SetProperty(TextEditor::Property::LINE_WRAP_MODE, DevelText::LineWrap::HYPHENATION); + DALI_TEST_EQUALS( textEditor.GetProperty< int >( TextEditor::Property::LINE_WRAP_MODE ), static_cast< int >( DevelText::LineWrap::HYPHENATION ), TEST_LOCATION ); + + application.SendNotification(); + application.Render(); + + lineCount = textEditor.GetProperty( TextEditor::Property::LINE_COUNT ); + /* + text will be : + Hi Exp- + erimen + */ + DALI_TEST_EQUALS( lineCount, 2, TEST_LOCATION ); + + textEditor.SetProperty( TextEditor::Property::TEXT, "Hi Experimen" ); + textEditor.SetProperty(TextEditor::Property::LINE_WRAP_MODE, DevelText::LineWrap::MIXED); + DALI_TEST_EQUALS( textEditor.GetProperty< int >( TextEditor::Property::LINE_WRAP_MODE ), static_cast< int >( DevelText::LineWrap::MIXED ), TEST_LOCATION ); + + application.SendNotification(); + application.Render(); + + lineCount = textEditor.GetProperty( TextEditor::Property::LINE_COUNT ); + /* + text will be : + Hi + Experi- + men + */ + DALI_TEST_EQUALS( lineCount, 3, TEST_LOCATION ); + + END_TEST; } \ No newline at end of file diff --git a/automated-tests/src/dali-toolkit/utc-Dali-TextLabel.cpp b/automated-tests/src/dali-toolkit/utc-Dali-TextLabel.cpp index d8a7c18..ac7403b 100644 --- a/automated-tests/src/dali-toolkit/utc-Dali-TextLabel.cpp +++ b/automated-tests/src/dali-toolkit/utc-Dali-TextLabel.cpp @@ -1836,4 +1836,52 @@ int UtcDaliTextLabelAtlasLimitationIsEnabledForLargeFontPointSize(void) DALI_TEST_GREATER( lessThanHeight, static_cast(naturalSize.height), TEST_LOCATION ); END_TEST; -} \ No newline at end of file +} + +int UtcDaliTextLabelHyphenWrapMode(void) +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliTextLabelHyphenWrapMode "); + + int lineCount =0; + TextLabel label = TextLabel::New(); + label.SetProperty( Actor::Property::SIZE, Vector2( 150.0f, 300.f )); + label.SetProperty( TextLabel::Property::POINT_SIZE, 12.f ); + label.SetProperty( TextLabel::Property::MULTI_LINE, true); + application.GetScene().Add( label ); + application.SendNotification(); + application.Render(); + + label.SetProperty( TextLabel::Property::TEXT, "Hi Experimen" ); + label.SetProperty(TextLabel::Property::LINE_WRAP_MODE,DevelText::LineWrap::HYPHENATION); + DALI_TEST_EQUALS( label.GetProperty< int >( TextLabel::Property::LINE_WRAP_MODE ), static_cast< int >( DevelText::LineWrap::HYPHENATION ), TEST_LOCATION ); + + application.SendNotification(); + application.Render(); + + lineCount = label.GetProperty( TextLabel::Property::LINE_COUNT ); + /* + text will be : + Hi Exp- + erimen + */ + DALI_TEST_EQUALS( lineCount, 2, TEST_LOCATION ); + + label.SetProperty( TextLabel::Property::TEXT, "Hi Experimen" ); + label.SetProperty(TextLabel::Property::LINE_WRAP_MODE,DevelText::LineWrap::MIXED); + DALI_TEST_EQUALS( label.GetProperty< int >( TextLabel::Property::LINE_WRAP_MODE ), static_cast< int >( DevelText::LineWrap::MIXED ), TEST_LOCATION ); + + application.SendNotification(); + application.Render(); + + lineCount = label.GetProperty( TextLabel::Property::LINE_COUNT ); + /* + text will be : + Hi + Experi- + men + */ + DALI_TEST_EQUALS( lineCount, 3, TEST_LOCATION ); + + END_TEST; +} diff --git a/dali-toolkit/devel-api/text/text-enumerations-devel.h b/dali-toolkit/devel-api/text/text-enumerations-devel.h index 0163cc6..a74af43 100644 --- a/dali-toolkit/devel-api/text/text-enumerations-devel.h +++ b/dali-toolkit/devel-api/text/text-enumerations-devel.h @@ -2,7 +2,7 @@ #define DALI_TOOLKIT_TEXT_ENUMERATIONS_DEVEL_H /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * Copyright (c) 2021 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,6 +44,17 @@ enum Type }; } // namespace VerticalLineAlignment +namespace LineWrap +{ +enum Mode +{ + WORD, + CHARACTER, + HYPHENATION, // HYPHENATION mode will add hyphen and move part of the word to the next line. + MIXED // MIXEd mode will apply WORD mode, if failed try HYPHENATION mode, if failed try CHARACTER. +}; + +} // namespace LineWrap } // namespace DevelText diff --git a/dali-toolkit/devel-api/text/text-utils-devel.cpp b/dali-toolkit/devel-api/text/text-utils-devel.cpp index 787cc0f..a4dbce4 100644 --- a/dali-toolkit/devel-api/text/text-utils-devel.cpp +++ b/dali-toolkit/devel-api/text/text-utils-devel.cpp @@ -1030,7 +1030,7 @@ Size LayoutText(const RendererParameters& textParameters, TextAbstraction::TextR // Resize the vector of positions to have the same size than the vector of glyphs. rendererParameters.positions.Resize(numberOfGlyphs); - textModel->mLineWrapMode = LineWrap::WORD; + textModel->mLineWrapMode = Text::LineWrap::WORD; textModel->mIgnoreSpacesAfterText = false; textModel->mMatchSystemLanguageDirection = false; Text::Layout::Parameters layoutParameters(internalDataModel.textLayoutArea, diff --git a/dali-toolkit/internal/file.list b/dali-toolkit/internal/file.list index 35ac87b..395dfc8 100644 --- a/dali-toolkit/internal/file.list +++ b/dali-toolkit/internal/file.list @@ -142,6 +142,7 @@ SET( toolkit_src_files ${toolkit_src_dir}/text/property-string-parser.cpp ${toolkit_src_dir}/text/segmentation.cpp ${toolkit_src_dir}/text/shaper.cpp + ${toolkit_src_dir}/text/hyphenator.cpp ${toolkit_src_dir}/text/text-enumerations-impl.cpp ${toolkit_src_dir}/text/text-controller.cpp ${toolkit_src_dir}/text/text-controller-event-handler.cpp diff --git a/dali-toolkit/internal/text/hyphenator.cpp b/dali-toolkit/internal/text/hyphenator.cpp new file mode 100644 index 0000000..66f99a3 --- /dev/null +++ b/dali-toolkit/internal/text/hyphenator.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// CLASS HEADER +#include + +// EXTERNAL INCLUDES +#include + +// INTERNAL INCLUDES +#include + +#include // for strcmp + +namespace Dali +{ +namespace Toolkit +{ +namespace Text +{ +const char* UTF8 = "UTF-8"; + +Vector GetWordHyphens(const Character* word, + Length wordSize, + const char* lang) +{ + Vector hyphens; + + if(0u == wordSize || word == nullptr) + { + // Nothing to do if there are no characters. + return hyphens; + } + + TextAbstraction::Hyphenation hyphenation = TextAbstraction::Hyphenation::Get(); + + // first get the needed encoding + std::string text; + if(strcmp(hyphenation.GetDictionaryEncoding(lang), UTF8) == 0) + { + Utf32ToUtf8(word, wordSize, text); + } + else + { + text = std::string((char*)word, (size_t)(wordSize * sizeof(Character))); + } + + return hyphenation.GetWordHyphens(text.c_str(), (int)text.length(), lang); +} + +} // namespace Text + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/text/hyphenator.h b/dali-toolkit/internal/text/hyphenator.h new file mode 100644 index 0000000..eef2246 --- /dev/null +++ b/dali-toolkit/internal/text/hyphenator.h @@ -0,0 +1,53 @@ +#ifndef DALI_TOOLKIT_TEXT_HYPHENATOR_H +#define DALI_TOOLKIT_TEXT_HYPHENATOR_H + +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// EXTERNAL INCLUDES +#include + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ +namespace Toolkit +{ +namespace Text +{ +/** + * Gets a vector booleans that indicates possible hyphen locations. + * + * @param[in] word the word to get possible hyphens for. + * @param[in] wordSize the word size in bytes. + * @param[in] lang the language for the word + * + * @return vector of boolean, true if possible to hyphenate at this character position. + */ +Vector GetWordHyphens(const Character* word, + Length wordSize, + const char* lang); + +} // namespace Text + +} // namespace Toolkit + +} // namespace Dali + +#endif // DALI_TOOLKIT_TEXT_HYPHENATOR_H diff --git a/dali-toolkit/internal/text/layouts/layout-engine.cpp b/dali-toolkit/internal/text/layouts/layout-engine.cpp index fcf5f5e..25f83d1 100644 --- a/dali-toolkit/internal/text/layouts/layout-engine.cpp +++ b/dali-toolkit/internal/text/layouts/layout-engine.cpp @@ -44,11 +44,12 @@ namespace Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_LAYOUT"); #endif -const float MAX_FLOAT = std::numeric_limits::max(); -const CharacterDirection LTR = false; -const CharacterDirection RTL = !LTR; -const float LINE_SPACING = 0.f; -const float MIN_LINE_SIZE = 0.f; +const float MAX_FLOAT = std::numeric_limits::max(); +const CharacterDirection LTR = false; +const CharacterDirection RTL = !LTR; +const float LINE_SPACING = 0.f; +const float MIN_LINE_SIZE = 0.f; +const Character HYPHEN_UNICODE = 0x002D; inline bool isEmptyLineAtLast(const Vector& lines, const Vector::Iterator& line) { @@ -448,7 +449,11 @@ struct Engine::Impl const Length totalNumberOfGlyphs = parameters.textModel->mVisualModel->mGlyphs.Count(); const bool isMultiline = mLayout == MULTI_LINE_BOX; - const bool isWordLaidOut = parameters.textModel->mLineWrapMode == Text::LineWrap::WORD; + const bool isWordLaidOut = parameters.textModel->mLineWrapMode == Text::LineWrap::WORD || + (parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) || + (parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED); + const bool isHyphenMode = parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION; + const bool isMixedMode = parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED; // The last glyph to be laid-out. const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs; @@ -486,7 +491,10 @@ struct Engine::Impl FontId lastFontId = glyphMetrics.fontId; UpdateLineHeight(glyphMetrics, tmpLineLayout); - bool oneWordLaidOut = false; + bool oneWordLaidOut = false; + bool oneHyphenLaidOut = false; + GlyphIndex hyphenIndex = 0; + GlyphInfo hyphenGlyph; for(GlyphIndex glyphIndex = lineLayout.glyphIndex; glyphIndex < lastGlyphOfParagraphPlusOne;) @@ -565,7 +573,15 @@ struct Engine::Impl (tmpLineLayout.length > parameters.boundingBox.width)) { // Current word does not fit in the box's width. - if(!oneWordLaidOut || completelyFill) + if(((oneHyphenLaidOut && isHyphenMode) || + (!oneWordLaidOut && isMixedMode && oneHyphenLaidOut)) && + !completelyFill) + { + parameters.textModel->mVisualModel->mHyphen.glyph.PushBack(hyphenGlyph); + parameters.textModel->mVisualModel->mHyphen.index.PushBack(hyphenIndex + 1); + } + + if((!oneWordLaidOut && !oneHyphenLaidOut) || completelyFill) { DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Break the word by character\n"); @@ -608,7 +624,7 @@ struct Engine::Impl (TextAbstraction::LINE_MUST_BREAK == lineBreakInfo)) { LineLayout currentLineLayout = lineLayout; - + oneHyphenLaidOut = false; // Must break the line. Update the line layout and return. MergeLineLayout(lineLayout, tmpLineLayout); @@ -631,7 +647,8 @@ struct Engine::Impl if(isMultiline && (TextAbstraction::LINE_ALLOW_BREAK == lineBreakInfo)) { - oneWordLaidOut = isWordLaidOut; + oneHyphenLaidOut = false; + oneWordLaidOut = isWordLaidOut; DALI_LOG_INFO(gLogFilter, Debug::Verbose, " One word laid-out\n"); // Current glyph is the last one of the current word. @@ -641,6 +658,33 @@ struct Engine::Impl tmpLineLayout.Clear(); } + if(isMultiline && + ((isHyphenMode || (!oneWordLaidOut && isMixedMode))) && + (TextAbstraction::LINE_HYPHENATION_BREAK == lineBreakInfo)) + { + hyphenGlyph = GlyphInfo(); + hyphenGlyph.fontId = glyphsBuffer[glyphIndex].fontId; + + TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get(); + hyphenGlyph.index = fontClient.GetGlyphIndex(hyphenGlyph.fontId, HYPHEN_UNICODE); + + mMetrics->GetGlyphMetrics(&hyphenGlyph, 1); + + if((tmpLineLayout.length + hyphenGlyph.width) <= parameters.boundingBox.width) + { + hyphenIndex = glyphIndex; + oneHyphenLaidOut = true; + + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " One hyphen laid-out\n"); + + // Current glyph is the last one of the current word hyphen. + // Add the temporal layout to the current one. + MergeLineLayout(lineLayout, tmpLineLayout); + + tmpLineLayout.Clear(); + } + } + glyphIndex += numberOfGLyphsInGroup; } @@ -1059,6 +1103,9 @@ struct Engine::Impl DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->LayoutText\n"); DALI_LOG_INFO(gLogFilter, Debug::Verbose, " box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height); + layoutParameters.textModel->mVisualModel->mHyphen.glyph.Clear(); + layoutParameters.textModel->mVisualModel->mHyphen.index.Clear(); + Vector& lines = layoutParameters.textModel->mVisualModel->mLines; if(0u == layoutParameters.numberOfGlyphs) @@ -1271,6 +1318,17 @@ struct Engine::Impl if(ellipsis) { + //clear hyphen from ellipsis line + const Length* hyphenIndices = layoutParameters.textModel->mVisualModel->mHyphen.index.Begin(); + Length hyphensCount = layoutParameters.textModel->mVisualModel->mHyphen.glyph.Size(); + + while(hyphenIndices && hyphensCount > 0 && hyphenIndices[hyphensCount - 1] >= layout.glyphIndex) + { + layoutParameters.textModel->mVisualModel->mHyphen.index.Remove(layoutParameters.textModel->mVisualModel->mHyphen.index.Begin() + hyphensCount - 1); + layoutParameters.textModel->mVisualModel->mHyphen.glyph.Remove(layoutParameters.textModel->mVisualModel->mHyphen.glyph.Begin() + hyphensCount - 1); + hyphensCount--; + } + // No more lines to layout. break; } diff --git a/dali-toolkit/internal/text/rendering/atlas/text-atlas-renderer.cpp b/dali-toolkit/internal/text/rendering/atlas/text-atlas-renderer.cpp index 1b1fc35..1023dc4 100644 --- a/dali-toolkit/internal/text/rendering/atlas/text-atlas-renderer.cpp +++ b/dali-toolkit/internal/text/rendering/atlas/text-atlas-renderer.cpp @@ -419,16 +419,19 @@ struct AtlasRenderer::Impl Vector extents; mDepth = depth; - const Vector2& textSize(view.GetLayoutSize()); - const Vector2 halfTextSize(textSize * 0.5f); - const Vector2& shadowOffset(view.GetShadowOffset()); - const Vector4& shadowColor(view.GetShadowColor()); - const bool underlineEnabled = view.IsUnderlineEnabled(); - const Vector4& underlineColor(view.GetUnderlineColor()); - const float underlineHeight = view.GetUnderlineHeight(); - const uint16_t outlineWidth = view.GetOutlineWidth(); - const Vector4& outlineColor(view.GetOutlineColor()); - const bool isOutline = 0u != outlineWidth; + const Vector2& textSize(view.GetLayoutSize()); + const Vector2 halfTextSize(textSize * 0.5f); + const Vector2& shadowOffset(view.GetShadowOffset()); + const Vector4& shadowColor(view.GetShadowColor()); + const bool underlineEnabled = view.IsUnderlineEnabled(); + const Vector4& underlineColor(view.GetUnderlineColor()); + const float underlineHeight = view.GetUnderlineHeight(); + const uint16_t outlineWidth = view.GetOutlineWidth(); + const Vector4& outlineColor(view.GetOutlineColor()); + const bool isOutline = 0u != outlineWidth; + const GlyphInfo* hyphens = view.GetHyphens(); + const Length* hyphenIndices = view.GetHyphenIndices(); + const Length hyphensCount = view.GetHyphensCount(); const bool useDefaultColor = (NULL == colorsBuffer); @@ -460,6 +463,7 @@ struct AtlasRenderer::Impl const GlyphInfo* const glyphsBuffer = glyphs.Begin(); const Vector2* const positionsBuffer = positions.Begin(); const Vector2 lineOffsetPosition(minLineOffset, 0.f); + uint32_t hyphenIndex = 0; //For septated underlined chunks. (this is for Markup case) uint32_t underlineChunkId = 0u; // give id for each chunk. @@ -467,9 +471,20 @@ struct AtlasRenderer::Impl for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i) { - const GlyphInfo& glyph = *(glyphsBuffer + i); - const bool isGlyphUnderlined = underlineEnabled || IsGlyphUnderlined(i, underlineRuns); - thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || isGlyphUnderlined; + GlyphInfo glyph; + bool addHyphen = ((hyphenIndex < hyphensCount) && hyphenIndices && (i == hyphenIndices[hyphenIndex])); + if(addHyphen && hyphens) + { + glyph = hyphens[hyphenIndex]; + i--; + } + else + { + glyph = *(glyphsBuffer + i); + } + + const bool isGlyphUnderlined = underlineEnabled || IsGlyphUnderlined(i, underlineRuns); + thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || isGlyphUnderlined; // No operation for white space if(glyph.width && glyph.height) @@ -529,8 +544,16 @@ struct AtlasRenderer::Impl } // Move the origin (0,0) of the mesh to the center of the actor - const Vector2& temp = *(positionsBuffer + i); - const Vector2 position = Vector2(roundf(temp.x), temp.y) - halfTextSize - lineOffsetPosition; // roundf() avoids pixel alignment issues. + Vector2 position = *(positionsBuffer + i); + + if(addHyphen) + { + GlyphInfo tempInfo = *(glyphsBuffer + i); + position.x = position.x + tempInfo.advance - tempInfo.xBearing + glyph.xBearing; + position.y += tempInfo.yBearing - glyph.yBearing; + } + + position = Vector2(roundf(position.x), position.y) - halfTextSize - lineOffsetPosition; // roundf() avoids pixel alignment issues. if(0u != slot.mImageId) // invalid slot id, glyph has failed to be added to atlas { @@ -588,6 +611,11 @@ struct AtlasRenderer::Impl //Keep status of underlined for previous glyph to check consecutive indices isPreUnderlined = isGlyphUnderlined; } + + if(addHyphen) + { + hyphenIndex++; + } } // glyphs // Now remove references for the old text diff --git a/dali-toolkit/internal/text/rendering/text-typesetter.cpp b/dali-toolkit/internal/text/rendering/text-typesetter.cpp index 60888db..40d96b8 100644 --- a/dali-toolkit/internal/text/rendering/text-typesetter.cpp +++ b/dali-toolkit/internal/text/rendering/text-typesetter.cpp @@ -618,6 +618,9 @@ Devel::PixelBuffer Typesetter::CreateImageBuffer(const unsigned int bufferWidth, const Vector2* const positionBuffer = mModel->GetLayout(); const Vector4* const colorsBuffer = mModel->GetColors(); const ColorIndex* const colorIndexBuffer = mModel->GetColorIndices(); + const GlyphInfo* hyphens = mModel->GetHyphens(); + const Length* hyphenIndices = mModel->GetHyphenIndices(); + const Length hyphensCount = mModel->GetHyphensCount(); // Whether to use the default color. const bool useDefaultColor = (NULL == colorsBuffer); @@ -643,7 +646,8 @@ Devel::PixelBuffer Typesetter::CreateImageBuffer(const unsigned int bufferWidth, } // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs. - TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get(); + TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get(); + Length hyphenIndex = 0; // Traverses the lines of the text. for(LineIndex lineIndex = 0u; lineIndex < modelNumberOfLines; ++lineIndex) @@ -708,6 +712,7 @@ Devel::PixelBuffer Typesetter::CreateImageBuffer(const unsigned int bufferWidth, float lineExtentLeft = bufferWidth; float lineExtentRight = 0.0f; float baseline = 0.0f; + bool addHyphen = false; // Traverses the glyphs of the line. const GlyphIndex endGlyphIndex = std::min(numberOfGlyphs, line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs); @@ -720,7 +725,17 @@ Devel::PixelBuffer Typesetter::CreateImageBuffer(const unsigned int bufferWidth, } // Retrieve the glyph's info. - const GlyphInfo* const glyphInfo = glyphsBuffer + glyphIndex; + const GlyphInfo* glyphInfo; + + if(addHyphen && hyphens) + { + glyphInfo = hyphens + hyphenIndex; + hyphenIndex++; + } + else + { + glyphInfo = glyphsBuffer + glyphIndex; + } if((glyphInfo->width < Math::MACHINE_EPSILON_1000) || (glyphInfo->height < Math::MACHINE_EPSILON_1000)) @@ -740,21 +755,29 @@ Devel::PixelBuffer Typesetter::CreateImageBuffer(const unsigned int bufferWidth, } // underline // Retrieves the glyph's position. - const Vector2* const position = positionBuffer + glyphIndex; - if(baseline < position->y + glyphInfo->yBearing) + Vector2 position = *(positionBuffer + glyphIndex); + + if(addHyphen) + { + GlyphInfo tempInfo = *(glyphsBuffer + glyphIndex); + position.x = position.x + tempInfo.advance - tempInfo.xBearing + glyphInfo->xBearing; + position.y = -glyphInfo->yBearing; + } + + if(baseline < position.y + glyphInfo->yBearing) { - baseline = position->y + glyphInfo->yBearing; + baseline = position.y + glyphInfo->yBearing; } // Calculate the positions of leftmost and rightmost glyphs in the current line - if(position->x < lineExtentLeft) + if(position.x < lineExtentLeft) { - lineExtentLeft = position->x; + lineExtentLeft = position.x; } - if(position->x + glyphInfo->width > lineExtentRight) + if(position.x + glyphInfo->width > lineExtentRight) { - lineExtentRight = position->x + glyphInfo->width; + lineExtentRight = position.x + glyphInfo->width; } // Retrieves the glyph's color. @@ -812,7 +835,7 @@ Devel::PixelBuffer Typesetter::CreateImageBuffer(const unsigned int bufferWidth, // Set the buffer of the glyph's bitmap into the final bitmap's buffer TypesetGlyph(glyphData, - position, + &position, &color, style, pixelFormat); @@ -828,6 +851,17 @@ Devel::PixelBuffer Typesetter::CreateImageBuffer(const unsigned int bufferWidth, delete[] glyphData.glyphBitmap.buffer; glyphData.glyphBitmap.buffer = NULL; } + + while((hyphenIndex < hyphensCount) && (glyphIndex > hyphenIndices[hyphenIndex])) + { + hyphenIndex++; + } + + addHyphen = ((hyphenIndex < hyphensCount) && hyphenIndices && ((glyphIndex + 1) == hyphenIndices[hyphenIndex])); + if(addHyphen) + { + glyphIndex--; + } } // Draw the underline from the leftmost glyph to the rightmost glyph diff --git a/dali-toolkit/internal/text/rendering/view-model.cpp b/dali-toolkit/internal/text/rendering/view-model.cpp index 2ad03c5..f695cd2 100644 --- a/dali-toolkit/internal/text/rendering/view-model.cpp +++ b/dali-toolkit/internal/text/rendering/view-model.cpp @@ -230,6 +230,21 @@ bool ViewModel::IsMarkupProcessorEnabled() const return mModel->IsMarkupProcessorEnabled(); } +const GlyphInfo* ViewModel::GetHyphens() const +{ + return mModel->GetHyphens(); +} + +const Length* ViewModel::GetHyphenIndices() const +{ + return mModel->GetHyphenIndices(); +} + +Length ViewModel::GetHyphensCount() const +{ + return mModel->GetHyphensCount(); +} + void ViewModel::ElideGlyphs() { mIsTextElided = false; diff --git a/dali-toolkit/internal/text/rendering/view-model.h b/dali-toolkit/internal/text/rendering/view-model.h index 1ea38ee..9272956 100644 --- a/dali-toolkit/internal/text/rendering/view-model.h +++ b/dali-toolkit/internal/text/rendering/view-model.h @@ -216,6 +216,21 @@ public: bool IsMarkupProcessorEnabled() const override; /** + * @copydoc ModelInterface::GetHyphens() + */ + const GlyphInfo* GetHyphens() const override; + + /** + * @copydoc ModelInterface::GetHyphens() + */ + const Length* GetHyphenIndices() const override; + + /** + * @copydoc ModelInterface::GetHyphens() + */ + Length GetHyphensCount() const override; + + /** * @brief Does the text elide. * * It stores a copy of the visible glyphs and removes as many glyphs as needed diff --git a/dali-toolkit/internal/text/text-controller-impl.cpp b/dali-toolkit/internal/text/text-controller-impl.cpp index 2faaee1..417eaed 100644 --- a/dali-toolkit/internal/text/text-controller-impl.cpp +++ b/dali-toolkit/internal/text/text-controller-impl.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,8 @@ #include #include +#include + using namespace Dali; namespace @@ -661,6 +664,39 @@ bool Controller::Impl::UpdateModel(OperationsMask operationsRequired) requestedNumberOfCharacters, lineBreakInfo); + if(mModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) || + mModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::MIXED)) + { + CharacterIndex end = startIndex + requestedNumberOfCharacters; + LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin(); + + for(CharacterIndex index = startIndex; index < end; index++) + { + CharacterIndex wordEnd = index; + while((*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_ALLOW_BREAK) && (*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_MUST_BREAK)) + { + wordEnd++; + } + + if((wordEnd + 1) == end) // add last char + { + wordEnd++; + } + + Vector hyphens = GetWordHyphens(utf32Characters.Begin() + index, wordEnd - index, nullptr); + + for(CharacterIndex i = 0; i < (wordEnd - index); i++) + { + if(hyphens[i]) + { + *(lineBreakInfoBuffer + index + i) = TextAbstraction::LINE_HYPHENATION_BREAK; + } + } + + index = wordEnd; + } + } + // Create the paragraph info. mModel->mLogicalModel->CreateParagraphInfo(startIndex, requestedNumberOfCharacters); diff --git a/dali-toolkit/internal/text/text-controller.cpp b/dali-toolkit/internal/text/text-controller.cpp index 99c0264..6866ff4 100644 --- a/dali-toolkit/internal/text/text-controller.cpp +++ b/dali-toolkit/internal/text/text-controller.cpp @@ -25,6 +25,7 @@ #include // INTERNAL INCLUDES +#include #include #include #include @@ -405,15 +406,22 @@ void Controller::SetLineWrapMode(Text::LineWrap::Mode lineWrapMode) { if(lineWrapMode != mImpl->mModel->mLineWrapMode) { - // Set the text wrap mode. - mImpl->mModel->mLineWrapMode = lineWrapMode; - // Update Text layout for applying wrap mode - mImpl->mOperationsPending = static_cast(mImpl->mOperationsPending | + mImpl->mOperationsPending = static_cast(mImpl->mOperationsPending | ALIGN | LAYOUT | UPDATE_LAYOUT_SIZE | REORDER); + + if((mImpl->mModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) || (lineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) || + (mImpl->mModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED) || (lineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED)) // hyphen is treated as line break + { + mImpl->mOperationsPending = static_cast(mImpl->mOperationsPending | GET_LINE_BREAKS); + } + + // Set the text wrap mode. + mImpl->mModel->mLineWrapMode = lineWrapMode; + mImpl->mTextUpdateInfo.mCharacterIndex = 0u; mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters; mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = mImpl->mModel->mLogicalModel->mText.Count(); diff --git a/dali-toolkit/internal/text/text-model-interface.h b/dali-toolkit/internal/text/text-model-interface.h index 5c3fa1f..c116f8f 100644 --- a/dali-toolkit/internal/text/text-model-interface.h +++ b/dali-toolkit/internal/text/text-model-interface.h @@ -272,6 +272,27 @@ public: * @return The markup-processor state. */ virtual bool IsMarkupProcessorEnabled() const = 0; + + /** + * @brief Returns the hyphens glyph info. + * + * @return hyphens glyph info. + */ + virtual const GlyphInfo* GetHyphens() const = 0; + + /** + * @brief Returns the indices of the hyphen in the text. + * + * @return the hyphen indices. + */ + virtual const Length* GetHyphenIndices() const = 0; + + /** + * @brief Returns number of hyphens to add in text. + * + * @return number of hyphens. + */ + virtual Length GetHyphensCount() const = 0; }; } // namespace Text diff --git a/dali-toolkit/internal/text/text-model.cpp b/dali-toolkit/internal/text/text-model.cpp index 42a5795..948044e 100644 --- a/dali-toolkit/internal/text/text-model.cpp +++ b/dali-toolkit/internal/text/text-model.cpp @@ -189,6 +189,21 @@ bool Model::IsMarkupProcessorEnabled() const return mVisualModel->IsMarkupProcessorEnabled(); } +const GlyphInfo* Model::GetHyphens() const +{ + return mVisualModel->mHyphen.glyph.Begin(); +} + +const Length* Model::GetHyphenIndices() const +{ + return mVisualModel->mHyphen.index.Begin(); +} + +Length Model::GetHyphensCount() const +{ + return mVisualModel->mHyphen.glyph.Size(); +} + Model::Model() : mLogicalModel(), mVisualModel(), diff --git a/dali-toolkit/internal/text/text-model.h b/dali-toolkit/internal/text/text-model.h index 705adeb..4faa1bc 100644 --- a/dali-toolkit/internal/text/text-model.h +++ b/dali-toolkit/internal/text/text-model.h @@ -212,6 +212,21 @@ public: */ bool IsMarkupProcessorEnabled() const override; + /** + * @copydoc ModelInterface::GetHyphens() + */ + const GlyphInfo* GetHyphens() const override; + + /** + * @copydoc ModelInterface::GetHyphens() + */ + const Length* GetHyphenIndices() const override; + + /** + * @copydoc ModelInterface::GetHyphens() + */ + Length GetHyphensCount() const override; + private: // Private contructors & copy operator. /** * @brief Private constructor. diff --git a/dali-toolkit/internal/text/text-view-interface.h b/dali-toolkit/internal/text/text-view-interface.h index 1a04135..63f7fd4 100644 --- a/dali-toolkit/internal/text/text-view-interface.h +++ b/dali-toolkit/internal/text/text-view-interface.h @@ -159,6 +159,26 @@ public: virtual bool IsUnderlineEnabled() const = 0; /** + * @brief Returns the hyphens glyph info. + * + * @return hyphens glyph info. + */ + virtual const GlyphInfo* GetHyphens() const = 0; + + /** + * @brief Returns the indices of the hyphen in the text. + * + * @return the hyphen indices. + */ + virtual const Length* GetHyphenIndices() const = 0; + + /** + * @brief Returns number of hyphens to add in text. + * + * @return number of hyphens. + */ + virtual Length GetHyphensCount() const = 0; + /** * @brief Retrieves the underline height override * * @return Returns the override height for an underline, 0 indicates that adaptor will determine the height diff --git a/dali-toolkit/internal/text/text-view.cpp b/dali-toolkit/internal/text/text-view.cpp index 83ad28f..2f74699 100644 --- a/dali-toolkit/internal/text/text-view.cpp +++ b/dali-toolkit/internal/text/text-view.cpp @@ -388,6 +388,35 @@ bool View::IsUnderlineEnabled() const return false; } +const GlyphInfo* View::GetHyphens() const +{ + if(mImpl->mVisualModel) + { + return mImpl->mVisualModel->mHyphen.glyph.Begin(); + } + + return nullptr; +} + +const Length* View::GetHyphenIndices() const +{ + if(mImpl->mVisualModel) + { + return mImpl->mVisualModel->mHyphen.index.Begin(); + } + + return nullptr; +} + +Length View::GetHyphensCount() const +{ + if(mImpl->mVisualModel) + { + return mImpl->mVisualModel->mHyphen.glyph.Size(); + } + + return 0; +} float View::GetUnderlineHeight() const { if(mImpl->mVisualModel) diff --git a/dali-toolkit/internal/text/text-view.h b/dali-toolkit/internal/text/text-view.h index cc82820..3790fd0 100644 --- a/dali-toolkit/internal/text/text-view.h +++ b/dali-toolkit/internal/text/text-view.h @@ -121,6 +121,21 @@ public: bool IsUnderlineEnabled() const override; /** + * @copydoc Dali::Toolkit::Text::ViewInterface::GetHyphens() + */ + const GlyphInfo* GetHyphens() const override; + + /** + * @copydoc Dali::Toolkit::Text::ViewInterface::GetHyphens() + */ + const Length* GetHyphenIndices() const override; + + /** + * @copydoc Dali::Toolkit::Text::ViewInterface::GetHyphens() + */ + Length GetHyphensCount() const override; + + /** * @copydoc Dali::Toolkit::Text::ViewInterface::GetUnderlineHeight() */ float GetUnderlineHeight() const override; diff --git a/dali-toolkit/internal/text/visual-model-impl.h b/dali-toolkit/internal/text/visual-model-impl.h index bf3ead2..c355c3c 100644 --- a/dali-toolkit/internal/text/visual-model-impl.h +++ b/dali-toolkit/internal/text/visual-model-impl.h @@ -35,6 +35,13 @@ namespace Toolkit { namespace Text { +struct HyphenInfo +{ + Vector glyph; + Vector position; + Vector index; +}; + class VisualModel; typedef IntrusivePtr VisualModelPtr; @@ -422,6 +429,7 @@ public: bool mUnderlineColorSet : 1; ///< Has the underline color been explicitly set? bool mBackgroundEnabled : 1; ///< Background enabled flag bool mMarkupProcessorEnabled : 1; ///< Markup-processor enabled flag + HyphenInfo mHyphen; ///< Contains hyphen glyph info & the character index to draw hyphen after. }; } // namespace Text -- 2.7.4