From: Victor Cebollada Date: Thu, 18 Feb 2016 09:45:31 +0000 (+0000) Subject: TextModel - Create the bidirectional info for a given range of characters inside... X-Git-Tag: dali_1.1.24~1 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=commitdiff_plain;h=eefe73d95db6e9be0b518c5b796e1f1a081cce12 TextModel - Create the bidirectional info for a given range of characters inside a text. Change-Id: I00d0c68d9d354d30e6fe3ad67de8d99f7c0651d4 Signed-off-by: Victor Cebollada --- diff --git a/automated-tests/src/dali-toolkit-internal/CMakeLists.txt b/automated-tests/src/dali-toolkit-internal/CMakeLists.txt index 1ce796d..7e21216 100644 --- a/automated-tests/src/dali-toolkit-internal/CMakeLists.txt +++ b/automated-tests/src/dali-toolkit-internal/CMakeLists.txt @@ -11,6 +11,8 @@ SET(TC_SOURCES utc-Dali-Text-CharacterSetConversion.cpp utc-Dali-Text-Segmentation.cpp utc-Dali-Text-MultiLanguage.cpp + utc-Dali-LogicalModel.cpp + utc-Dali-BidirectionalSupport.cpp ) # Append list of test harness files (Won't get parsed for test cases) @@ -26,6 +28,7 @@ LIST(APPEND TC_SOURCES ../dali-toolkit/dali-toolkit-test-utils/toolkit-singleton-service.cpp ../dali-toolkit/dali-toolkit-test-utils/toolkit-timer.cpp ../dali-toolkit/dali-toolkit-test-utils/toolkit-clipboard-event-notifier.cpp + ../dali-toolkit/dali-toolkit-test-utils/toolkit-text-model.cpp ../dali-toolkit/dali-toolkit-test-utils/dummy-control.cpp ../dali-toolkit/dali-toolkit-test-utils/dali-test-suite-utils.cpp ../dali-toolkit/dali-toolkit-test-utils/test-application.cpp diff --git a/automated-tests/src/dali-toolkit-internal/utc-Dali-BidirectionalSupport.cpp b/automated-tests/src/dali-toolkit-internal/utc-Dali-BidirectionalSupport.cpp new file mode 100644 index 0000000..92ee580 --- /dev/null +++ b/automated-tests/src/dali-toolkit-internal/utc-Dali-BidirectionalSupport.cpp @@ -0,0 +1,945 @@ +/* + * Copyright (c) 2016 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; + +// Tests the following functions. +// +// void SetBidirectionalInfo( const Vector& text, +// const Vector& scripts, +// const Vector& lineBreakInfo, +// CharacterIndex startIndex, +// Length numberOfCharacters, +// Vector& bidirectionalInfo ) +// void ReorderLines( const Vector& bidirectionalInfo, +// CharacterIndex startIndex, +// Length numberOfCharacters, +// Vector& lineRuns, +// Vector& lineInfoRuns ) +// bool GetMirroredText( const Vector& text, +// Vector& directions, +// const Vector& bidirectionalInfo, +// CharacterIndex startIndex, +// Length numberOfCharacters, +// Vector& mirroredText ) +// void GetCharactersDirection( const Vector& bidirectionalInfo, +// CharacterIndex startIndex, +// Length numberOfCharacters, +// Vector& directions ) + +////////////////////////////////////////////////////////// + +namespace +{ + +struct SetBidirectionalInfoData +{ + std::string description; ///< Description of the test. + std::string text; ///< Input text. + unsigned int startIndex; ///< The index from where the model is updated. + unsigned int numberOfCharacters; ///< The number of characters to update. + unsigned int numberOfParagraphs; ///< The expected number of bidirectional paragraphs. + unsigned int* indices; ///< The expected indices to the first character of each paragraph. + unsigned int* numberOfParagraphCharacters; ///< The expected number of characters of each paragraph. + bool* directions; ///< The expected direction of each paragraph. +}; + +struct BidiLineData +{ + unsigned int characterIndex; + unsigned int numberOfCharacters; + unsigned int* visualToLogical; +}; + +struct ReorderLinesData +{ + std::string description; ///< Description of the test. + std::string text; ///< Input text. + unsigned int startIndex; ///< The index from where the model is updated. + unsigned int numberOfCharacters; ///< The number of characters. + unsigned int numberOfLineInfo; ///< The number or reordered lines. + BidiLineData* bidiLineData; ///< The bidirectional line info. +}; + +struct GetMirroredTextData +{ + std::string description; ///< Description of the test. + std::string text; ///< Input text. + unsigned int startIndex; ///< The index from where the model is updated. + unsigned int numberOfCharacters; ///< The number of the characters. + std::string mirroredText; ///< The expected result. +}; + +struct GetCharactersDirectionData +{ + std::string description; ///< Description of the test. + std::string text; ///< Input text. + unsigned int startIndex; ///< The index from where the model is updated. + unsigned int numberOfCharacters; ///< The number of characters. + bool* directions; ///< The expected directions. +}; + +bool SetBidirectionalInfoTest( const SetBidirectionalInfoData& data ) +{ + // 1) Create the model. + LogicalModelPtr logicalModel = LogicalModel::New(); + VisualModelPtr visualModel = VisualModel::New(); + Size textArea(100.f, 60.f); + Size layoutSize; + + // Create the model. + CreateTextModel( data.text, + textArea, + layoutSize, + logicalModel, + visualModel ); + + // 2) Clear the bidirectional paragraph info data. + Vector& bidirectionalInfo = logicalModel->mBidirectionalParagraphInfo; + if( 0u != data.numberOfCharacters ) + { + ClearCharacterRuns( data.startIndex, + data.startIndex + data.numberOfCharacters - 1u, + bidirectionalInfo ); + } + + // 3) Call the SetBidirectionalInfo() function. + SetBidirectionalInfo( logicalModel->mText, + logicalModel->mScriptRuns, + logicalModel->mLineBreakInfo, + data.startIndex, + data.numberOfCharacters, + bidirectionalInfo ); + + // 4) Compare with the expected results. + TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get(); + + if( data.numberOfParagraphs != bidirectionalInfo.Count() ) + { + // Different number of expected bidirectional paragraphs. + std::cout << " Different number of bidi paragraphs : " << bidirectionalInfo.Count() << ", expected : " << data.numberOfParagraphs << std::endl; + return false; + } + + for( unsigned int index = 0u; index < data.numberOfParagraphs; ++index ) + { + const BidirectionalParagraphInfoRun& run = bidirectionalInfo[index]; + + const CharacterDirection direction = bidirectionalSupport.GetParagraphDirection( run.bidirectionalInfoIndex ); + if( direction != data.directions[index] ) + { + std::cout << " Different direction" << std::endl; + std::cout << " paragraph : " << index << std::endl; + std::cout << " index : " << run.characterRun.characterIndex << ", num chars : " << run.characterRun.numberOfCharacters << ", direction : " << direction << std::endl; + std::cout << " expected, index : " << data.indices[index] << ", num chars : " << data.numberOfParagraphCharacters[index] << ", direction : " << data.directions[index] << std::endl; + return false; + } + + if( run.characterRun.characterIndex != data.indices[index] ) + { + std::cout << " Different index" << std::endl; + std::cout << " paragraph : " << index << std::endl; + std::cout << " index : " << run.characterRun.characterIndex << ", num chars : " << run.characterRun.numberOfCharacters << ", direction : " << direction << std::endl; + std::cout << " expected, index : " << data.indices[index] << ", num chars : " << data.numberOfParagraphCharacters[index] << ", direction : " << data.directions[index] << std::endl; + return false; + } + if( run.characterRun.numberOfCharacters != data.numberOfParagraphCharacters[index] ) + { + std::cout << " Different number of characters" << std::endl; + std::cout << " paragraph : " << index << std::endl; + std::cout << " index : " << run.characterRun.characterIndex << ", num chars : " << run.characterRun.numberOfCharacters << ", direction : " << direction << std::endl; + std::cout << " expected, index : " << data.indices[index] << ", num chars : " << data.numberOfParagraphCharacters[index] << ", direction : " << data.directions[index] << std::endl; + return false; + } + } + + return true; +} + +/** + * @brief Frees previously allocated bidirectional resources. + * + * @param[in] bidirectionalLineInfo Bidirectional info per line. + * @param[in] startIndex Index to the first line with bidirectional info to be freed. + * @param[in] endIndex Index to the last line with bidirectional info to be freed. + */ +void FreeBidirectionalLineInfoResources( Vector bidirectionalLineInfo, + uint32_t startIndex, + uint32_t endIndex ) +{ + // Free the allocated memory used to store the conversion table in the bidirectional line info run. + for( Vector::Iterator it = bidirectionalLineInfo.Begin() + startIndex, + endIt = bidirectionalLineInfo.Begin() + endIndex; + it != endIt; + ++it ) + { + BidirectionalLineInfoRun& bidiLineInfo = *it; + + free( bidiLineInfo.visualToLogicalMap ); + } +} + +bool ReorderLinesTest( const ReorderLinesData& data ) +{ + // 1) Create the model. + LogicalModelPtr logicalModel = LogicalModel::New(); + VisualModelPtr visualModel = VisualModel::New(); + Size textArea(100.f, 300.f); + Size layoutSize; + + // Create the model. + CreateTextModel( data.text, + textArea, + layoutSize, + logicalModel, + visualModel ); + + // 2) Clear the bidirectional line info data. + uint32_t startRemoveIndex = logicalModel->mBidirectionalLineInfo.Count(); + uint32_t endRemoveIndex = startRemoveIndex; + ClearCharacterRuns( data.startIndex, + data.startIndex + data.numberOfCharacters - 1u, + logicalModel->mBidirectionalLineInfo, + startRemoveIndex, + endRemoveIndex ); + + // Index to the first index to be removed. + + FreeBidirectionalLineInfoResources( logicalModel->mBidirectionalLineInfo, startRemoveIndex, endRemoveIndex ); + BidirectionalLineInfoRun* bidiLineBuffer = logicalModel->mBidirectionalLineInfo.Begin(); + logicalModel->mBidirectionalLineInfo.Erase( bidiLineBuffer + startRemoveIndex, + bidiLineBuffer + endRemoveIndex ); + + // 3) Call the function ReorderLines() + + ReorderLines( logicalModel->mBidirectionalParagraphInfo, + data.startIndex, + data.numberOfCharacters, + visualModel->mLines, + logicalModel->mBidirectionalLineInfo ); + + // 4) Compare the results. + + if( data.numberOfLineInfo != logicalModel->mBidirectionalLineInfo.Count() ) + { + // Different number of bidirectional lines. + std::cout << " different number of bidi lines : " << logicalModel->mBidirectionalLineInfo.Count() << ", expected : " << data.numberOfLineInfo << std::endl; + for( unsigned int index = 0u; index < logicalModel->mBidirectionalLineInfo.Count(); ++index ) + { + const BidirectionalLineInfoRun& run = logicalModel->mBidirectionalLineInfo[index]; + const BidiLineData& bidiLineData = data.bidiLineData[index]; + + std::cout << " bidi line : " << index << ", index : " << run.characterRun.characterIndex << ", num chars : " << run.characterRun.numberOfCharacters << std::endl; + std::cout << " expected index : " << bidiLineData.characterIndex << ", num chars : " << bidiLineData.numberOfCharacters << std::endl; + } + return false; + } + + for( unsigned int index = 0u; index < data.numberOfLineInfo; ++index ) + { + const BidirectionalLineInfoRun& run = logicalModel->mBidirectionalLineInfo[index]; + const BidiLineData& bidiLineData = data.bidiLineData[index]; + + if( bidiLineData.characterIndex != run.characterRun.characterIndex ) + { + std::cout << " bidi line : " << index << ", index : " << run.characterRun.characterIndex << ", num chars : " << run.characterRun.numberOfCharacters << std::endl; + std::cout << " expected index : " << bidiLineData.characterIndex << ", num chars : " << bidiLineData.numberOfCharacters << std::endl; + return false; + } + if( bidiLineData.numberOfCharacters != run.characterRun.numberOfCharacters ) + { + std::cout << " bidi line : " << index << ", index : " << run.characterRun.characterIndex << ", num chars : " << run.characterRun.numberOfCharacters << std::endl; + std::cout << " expected index : " << bidiLineData.characterIndex << ", num chars : " << bidiLineData.numberOfCharacters << std::endl; + return false; + } + + for( unsigned int i = 0u; i < run.characterRun.numberOfCharacters; ++i ) + { + if( bidiLineData.visualToLogical[i] != run.visualToLogicalMap[i] ) + { + std::cout << " v2l : "; + for( unsigned int i = 0u; i < run.characterRun.numberOfCharacters; ++i ) + { + std::cout << run.visualToLogicalMap[i] << " "; + } + std::cout << std::endl; + std::cout << " expected v2l : "; + for( unsigned int i = 0u; i < run.characterRun.numberOfCharacters; ++i ) + { + std::cout << bidiLineData.visualToLogical[i] << " "; + } + std::cout << std::endl; + + return false; + } + } + } + + return true; +} + +bool GetMirroredTextTest( const GetMirroredTextData& data ) +{ + // 1) Create the model. + LogicalModelPtr logicalModel = LogicalModel::New(); + VisualModelPtr visualModel = VisualModel::New(); + Size textArea(100.f, 60.f); + Size layoutSize; + + // Create the model. + CreateTextModel( data.text, + textArea, + layoutSize, + logicalModel, + visualModel ); + + // 2) Call the GetMirroredText() function for the whole text + Vector mirroredText; + bool mirrored = false; + mirrored = GetMirroredText( logicalModel->mText, + logicalModel->mCharacterDirections, + logicalModel->mBidirectionalParagraphInfo, + 0u, + logicalModel->mText.Count(), + mirroredText ); + + // 3) Call the GetMirroredText() function for the given index + number of characters + mirrored = GetMirroredText( logicalModel->mText, + logicalModel->mCharacterDirections, + logicalModel->mBidirectionalParagraphInfo, + data.startIndex, + data.numberOfCharacters, + mirroredText ); + + // 4) Compare the results. + + // Convert to utf8 + std::string mirroredString; + Utf32ToUtf8( mirroredText.Begin(), + mirroredText.Count(), + mirroredString ); + + if( !mirrored && ( mirroredString != data.text ) ) + { + std::cout << " No mirrored text and mirroredString != data.text." << std::endl; + std::cout << " mirrored string : [" << mirroredString << "]" << std::endl; + std::cout << " text : [" << data.text << "]" << std::endl; + return false; + } + + if( mirrored && ( mirroredString == data.text ) ) + { + std::cout << " Mirrored text and mirroredString == data.text." << std::endl; + std::cout << " mirrored string : [" << mirroredString << "]" << std::endl; + std::cout << " text : [" << data.text << "]" << std::endl; + return false; + } + + if( mirrored && ( mirroredString != data.mirroredText ) ) + { + std::cout << " Mirrored text and mirroredString != data.mirroredText." << std::endl; + std::cout << " mirrored string : [" << mirroredString << "]" << std::endl; + std::cout << " text : [" << data.mirroredText << "]" << std::endl; + return false; + } + + return true; +} + +bool GetCharactersDirectionTest( const GetCharactersDirectionData& data ) +{ + // 1) Create the model. + LogicalModelPtr logicalModel = LogicalModel::New(); + VisualModelPtr visualModel = VisualModel::New(); + Size textArea(100.f, 60.f); + Size layoutSize; + + // Create the model. + CreateTextModel( data.text, + textArea, + layoutSize, + logicalModel, + visualModel ); + + Vector& bidirectionalInfo = logicalModel->mBidirectionalParagraphInfo; + + // 2) Clear the direction info data. + Vector& directions = logicalModel->mCharacterDirections; + + directions.Erase( directions.Begin() + data.startIndex, + directions.Begin() + data.startIndex + data.numberOfCharacters ); + + // 3) Call GetCharactersDirection() function. + + GetCharactersDirection( bidirectionalInfo, + logicalModel->mText.Count(), + data.startIndex, + data.numberOfCharacters, + directions ); + + for( unsigned int index = 0u; index < logicalModel->mText.Count(); ++index ) + { + if( data.directions[index] != directions[index] ) + { + return false; + } + } + + return true; +} + +} // namespace + +////////////////////////////////////////////////////////// + +int UtcDaliSetBidirectionalInfo(void) +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliSetBidirectionalInfo"); + + unsigned int indices01[] = {}; + unsigned int numberOfCharacters01[] = {}; + bool direction01[] = {}; + unsigned int indices02[] = {}; + unsigned int numberOfCharacters02[] = {}; + bool direction02[] = {}; + unsigned int indices03[] = { 17u, 48u }; + unsigned int numberOfCharacters03[] = { 14u, 14u }; + bool direction03[] = { true, true }; + unsigned int indices04[] = { 17u, 31u, 79u }; + unsigned int numberOfCharacters04[] = { 14u, 48u, 31u }; + bool direction04[] = { true, false, true }; + unsigned int indices05[] = { 17u, 41u, 117u }; + unsigned int numberOfCharacters05[] = { 24u, 76u, 49u }; + bool direction05[] = { true, false, true }; + unsigned int indices06[] = { 17u, 48u }; + unsigned int numberOfCharacters06[] = { 14u, 14u }; + bool direction06[] = { true, true }; + unsigned int indices07[] = { 17u, 31u, 79u }; + unsigned int numberOfCharacters07[] = { 14u, 48u, 31u }; + bool direction07[] = { true, false, true }; + unsigned int indices08[] = { 17u, 41u, 117u }; + unsigned int numberOfCharacters08[] = { 24u, 76u, 49u }; + bool direction08[] = { true, false, true }; + + struct SetBidirectionalInfoData data[] = + { + { + "Zero characters", + "", + 0u, + 0u, + 0u, + indices01, + numberOfCharacters01, + direction01 + }, + { + "Some left to right paragraphs", + "Hello world\ndemo\n\n", + 0u, + 18u, + 0u, + indices02, + numberOfCharacters02, + direction02 + }, + { + "A mix of left to right and right to left paragraphs.", + "Hello world demo\nمرحبا بالعالم\nhello world demo\nمرحبا بالعالم\nhello world demo", + 0u, + 78u, + 2u, + indices03, + numberOfCharacters03, + direction03 + }, + { + "A mix of left to right and right to left paragraphs. Paragraphs also contain a mix of bidirectional text.", + "Hello world demo\nمرحبا بالعالم\nhello world demo مرحبا بالعالم hello world demo\nمرحبا hello world demo بالعالم\nhello world demo", + 0u, + 126u, + 3u, + indices04, + numberOfCharacters04, + direction04 + }, + { + "A mix of left to right and right to left paragraphs. Paragraphs also contain a mix of bidirectional text and a mix of right to left scripts.", + "Hello world demo\nمرحبا שלום עולם بالعالم\nhello world שלום بالعالم עולם demo مرحبا שלום עולם بالعالم hello world demo\nمرحبا hello שלום بالعالم עולם world demo بالعالم\nhello world demo", + 0u, + 182u, + 3u, + indices05, + numberOfCharacters05, + direction05 + }, + { + "A mix of left to right and right to left paragraphs. Updates a left to right paragraph.", + "Hello world demo\nمرحبا بالعالم\nhello world demo\nمرحبا بالعالم\nhello world demo", + 31u, + 17u, + 2u, + indices06, + numberOfCharacters06, + direction06 + }, + { + "A mix of left to right and right to left paragraphs. Paragraphs also contain a mix of bidirectional text.", + "Hello world demo\nمرحبا بالعالم\nhello world demo مرحبا بالعالم hello world demo\nمرحبا hello world demo بالعالم\nhello world demo", + 0u, + 126u, + 3u, + indices07, + numberOfCharacters07, + direction07 + }, + { + "A mix of left to right and right to left paragraphs. Paragraphs also contain a mix of bidirectional text and a mix of right to left scripts. Updates initial paragraphs.", + "Hello world demo\nمرحبا שלום עולם بالعالم\nhello world שלום بالعالم עולם demo مرحبا שלום עולם بالعالم hello world demo\nمرحبا hello שלום بالعالم עולם world demo بالعالم\nhello world demo", + 0u, + 41u, + 3u, + indices08, + numberOfCharacters08, + direction08 + }, + { + "A mix of left to right and right to left paragraphs. Paragraphs also contain a mix of bidirectional text and a mix of right to left scripts. Updates mid paragraphs.", + "Hello world demo\nمرحبا שלום עולם بالعالم\nhello world שלום بالعالم עולם demo مرحبا שלום עולם بالعالم hello world demo\nمرحبا hello שלום بالعالم עולם world demo بالعالم\nhello world demo", + 41u, + 76u, + 3u, + indices08, + numberOfCharacters08, + direction08 + }, + { + "A mix of left to right and right to left paragraphs. Paragraphs also contain a mix of bidirectional text and a mix of right to left scripts. Updates from character 85", + "Hello world demo\nمرحبا שלום עולם بالعالم\nhello world שלום بالعالم עולם demo مرحبا שלום עולם بالعالم hello world demo\nمرحبا hello שלום بالعالم עולם world demo بالعالم\nhello world demo", + 117u, + 65u, + 3u, + indices08, + numberOfCharacters08, + direction08 + }, + }; + const unsigned int numberOfTests = 10u; + + for( unsigned int index = 0u; index < numberOfTests; ++index ) + { + if( !SetBidirectionalInfoTest( data[index] ) ) + { + tet_result(TET_FAIL); + } + } + + tet_result(TET_PASS); + END_TEST; +} + +int UtcDaliReorderLines(void) +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliSetBidirectionalInfo"); + + unsigned int visualToLogical0301[] = { 0u, 1u, 2u, 3u, 4u, 5u, 9u, 8u, 7u, 6u, 10u }; + unsigned int visualToLogical0302[] = { 3u, 2u, 1u, 0u, 4u, 5u, 6u, 7u, 8u, 9u, 10u }; + unsigned int visualToLogical0303[] = { 0u, 1u, 2u, 3u, 4u }; + unsigned int visualToLogical0304[] = { 12u, 11u, 10u, 9u, 8u, 7u, 6u, 5u, 4u, 3u, 2u, 1u, 0u }; + unsigned int visualToLogical0305[] = { 10u, 9u, 8u, 7u, 6u, 5u, 4u, 3u, 2u, 1u, 0u }; + unsigned int visualToLogical0306[] = { 9u, 8u, 7u, 6u, 5u, 4u, 3u, 2u, 1u, 0u }; + unsigned int visualToLogical0307[] = { 13u, 8u, 9u, 10u, 11u, 12u, 7u, 6u, 5u, 4u, 3u, 2u, 1u, 0u }; + unsigned int visualToLogical0308[] = { 5u, 0u, 1u, 2u, 3u, 4u }; + unsigned int visualToLogical0309[] = { 0u, 1u, 2u, 3u, 4u, 5u, 9u, 8u, 7u, 6u, 10u }; + unsigned int visualToLogical0310[] = { 3u, 2u, 1u, 0u, 4u, 5u, 6u, 7u, 8u, 9u, 10u }; + unsigned int visualToLogical0311[] = { 0u, 1u, 2u, 3u, 4u }; + unsigned int visualToLogical0312[] = { 12u, 11u, 10u, 9u, 8u, 7u, 6u, 5u, 4u, 3u, 2u, 1u, 0u }; + unsigned int visualToLogical0313[] = { 10u, 9u, 8u, 7u, 6u, 5u, 4u, 3u, 2u, 1u, 0u }; + unsigned int visualToLogical0314[] = { 9u, 8u, 7u, 6u, 5u, 4u, 3u, 2u, 1u, 0u }; + unsigned int visualToLogical0315[] = { 13u, 8u, 9u, 10u, 11u, 12u, 7u, 6u, 5u, 4u, 3u, 2u, 1u, 0u }; + unsigned int visualToLogical0316[] = { 0u, 1u, 2u, 3u, 4u }; + + BidiLineData bidiLine01[] = {}; + BidiLineData bidiLine02[] = {}; + BidiLineData bidiLine03[] = { + { + 17u, + 11u, + visualToLogical0301 + }, + { + 28u, + 11u, + visualToLogical0302 + }, + { + 39u, + 5u, + visualToLogical0303 + }, + { + 44u, + 13u, + visualToLogical0304 + }, + { + 57u, + 11u, + visualToLogical0305 + }, + { + 68u, + 10u, + visualToLogical0306 + }, + { + 78u, + 14u, + visualToLogical0307 + }, + { + 92u, + 6u, + visualToLogical0308 + }, + { + 115u, + 11u, + visualToLogical0309 + }, + { + 126u, + 11u, + visualToLogical0310 + }, + { + 137u, + 5u, + visualToLogical0311 + }, + { + 142u, + 13u, + visualToLogical0312 + }, + { + 155u, + 11u, + visualToLogical0313 + }, + { + 166u, + 10u, + visualToLogical0314 + }, + { + 176u, + 14u, + visualToLogical0315 + }, + { + 190u, + 5u, + visualToLogical0316 + }, + }; + + struct ReorderLinesData data[] = + { + { + "Zero characters.", + "", + 0u, + 0u, + 0u, + bidiLine01 + }, + { + "Left to right text only.", + "Hello world demo\nhello world demo\nhello world demo.", + 0u, + 51u, + 0u, + bidiLine02 + }, + { + "Bidirectional paragraphs.", + "Hello world demo\nhello שלום עולם world demo\nשלום بالعالم עולם مرحبا שלום עולם بالعالم hello world\nHello world demo\nhello שלום עולם world demo\nשלום بالعالم עולם مرحبا שלום עולם بالعالم hello world", + 0u, + 195u, + 16u, + bidiLine03 + }, + { + "Bidirectional paragraphs. Update initial paragraphs.", + "Hello world demo\nhello שלום עולם world demo\nשלום بالعالم עולם مرحبا שלום עולם بالعالم hello world\nHello world demo\nhello שלום עולם world demo\nשלום بالعالم עולם مرحبا שלום עולם بالعالم hello world", + 0u, + 44u, + 16u, + bidiLine03 + }, + { + "Bidirectional paragraphs. Update middle paragraphs.", + "Hello world demo\nhello שלום עולם world demo\nשלום بالعالم עולם مرحبا שלום עולם بالعالم hello world\nHello world demo\nhello שלום עולם world demo\nשלום بالعالم עולם مرحبا שלום עולם بالعالم hello world", + 44u, + 54u, + 16u, + bidiLine03 + }, + { + "Bidirectional paragraphs. Update final paragraphs.", + "Hello world demo\nhello שלום עולם world demo\nשלום بالعالم עולם مرحبا שלום עולם بالعالم hello world\nHello world demo\nhello שלום עולם world demo\nשלום بالعالم עולם مرحبا שלום עולם بالعالم hello world", + 142u, + 53u, + 16u, + bidiLine03 + }, + }; + const unsigned int numberOfTests = 6u; + + for( unsigned int index = 0u; index < numberOfTests; ++index ) + { + if( !ReorderLinesTest( data[index] ) ) + { + tet_result(TET_FAIL); + } + } + + tet_result(TET_PASS); + END_TEST; +} + +int UtcDaliGetMirroredText(void) +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliGetMirroredText"); + + struct GetMirroredTextData data[] = + { + { + "Zero characters.", + "", + 0u, + 0u, + "" + }, + { + "Left to right characters only.", + "Hello world\nhello world demo.", + 0u, + 29u, + "Hello world\nhello world demo." + }, + { + "Right to left characters but with no characters to mirror.", + "שלום עולם\nمرحبا بالعالم", + 0u, + 23u, + "שלום עולם\nمرحبا بالعالم" + }, + { + "Right to left characters with some characters to mirror.", + "שלום עולם\n(مرحبا بالعالم)", + 0u, + 25u, + "שלום עולם\n)مرحبا بالعالم(" + }, + { + "Right to left characters with some characters to mirror. Update last paragraph.", + "שלום עולם\n(مرحبا بالعالم)", + 10u, + 15u, + "שלום עולם\n)مرحبا بالعالم(" + }, + { + "Mix of bidirectional text. With more paragraphs.", + "Hello world demo\nhello world\nhello world (مرحبا بالعالم שלום) עולם\nשלום مرحبا بالعالم עולם (hello) مرحبا بالعالم world" + " مرحبا بالعالم שלום עולם hello world hello world\nبالعالم שלום (hello) world demo (עולם)\nשלום (مرحبا بالعالم עולם) (hello)", + 0u, + 239u, + "Hello world demo\nhello world\nhello world (مرحبا بالعالم שלום( עולם\nשלום مرحبا بالعالم עולם )hello( مرحبا بالعالم world" + " مرحبا بالعالم שלום עולם hello world hello world\nبالعالم שלום )hello) world demo )עולם(\nשלום )مرحبا بالعالم עולם( )hello(" + }, + { + "Mix of bidirectional text. With more paragraphs. Update middle paragraphs.", + "Hello world demo\nhello world\nhello world (مرحبا بالعالم שלום) עולם\nשלום مرحبا بالعالم עולם (hello) مرحبا بالعالم world" + " مرحبا بالعالم שלום עולם hello world hello world\nبالعالم שלום (hello) world demo (עולם)\nשלום (مرحبا بالعالم עולם) (hello)", + 29u, + 38u, + "Hello world demo\nhello world\nhello world (مرحبا بالعالم שלום( עולם\nשלום مرحبا بالعالم עולם (hello) مرحبا بالعالم world" + " مرحبا بالعالم שלום עולם hello world hello world\nبالعالم שלום (hello) world demo (עולם)\nשלום (مرحبا بالعالم עולם) (hello)" + }, + { + "Mix of bidirectional text. With more paragraphs. Update middle paragraphs (2).", + "Hello world demo\nhello world\nhello world (مرحبا بالعالم שלום) עולם\nשלום مرحبا بالعالم עולם (hello) مرحبا بالعالم world" + " مرحبا بالعالم שלום עולם hello world hello world\nبالعالم שלום (hello) world demo (עולם)\nשלום (مرحبا بالعالم עולם) (hello)", + 67u, + 100u, + "Hello world demo\nhello world\nhello world (مرحبا بالعالم שלום) עולם\nשלום مرحبا بالعالم עולם )hello( مرحبا بالعالم world" + " مرحبا بالعالم שלום עולם hello world hello world\nبالعالم שלום (hello) world demo (עולם)\nשלום (مرحبا بالعالم עולם) (hello)" + }, + }; + const unsigned int numberOfTests = 8u; + + for( unsigned int index = 0u; index < numberOfTests; ++index ) + { + if( !GetMirroredTextTest( data[index] ) ) + { + tet_result(TET_FAIL); + } + } + + tet_result(TET_PASS); + END_TEST; +} + +int UtcDaliGetCharactersDirection(void) +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliGetCharactersDirection"); + + bool directions01[] = {}; + bool directions02[] = { + false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false }; + bool directions03[] = { + true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true }; + bool directions04[] = { + false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, + false, false, false, false, true, true, true, true, true, true, + true, true, true, false, true, true, true, true, true, true, + true, true, true, true, false, false, false, false, false, false, + false, false, false, false, false }; + bool directions05[] = { + false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, + false, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, + true, true, true, true, false, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, false, + false, false, false, false, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, false, + false, false, false, false, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, false, + false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, + false, false, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, + false, false, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, + true, true, false, false, false, false, false }; + + struct GetCharactersDirectionData data[] = + { + { + "Zero characters", + "", + 0u, + 0u, + directions01 + }, + { + "Left to right characters only", + "Hello world\nhello world demo", + 0u, + 28u, + directions02 + }, + { + "Right to left characters only", + "שלום עולם\nשלום עולם", + 0u, + 19u, + directions03 + }, + { + "Mix of bidirectional text", + "Hello world\nhello world שלום עולם\nשלום עולם hello world", + 0u, + 55u, + directions04 + }, + { + "Mix of bidirectional text. With more paragraphs.", + "Hello world demo\nhello world\nhello world مرحبا بالعالم שלום עולם\nשלום مرحبا بالعالم עולם hello مرحبا بالعالم world" + " مرحبا بالعالم שלום עולם hello world hello world\nبالعالم שלום hello world demo עולם\nשלום مرحبا بالعالم עולם hello", + 0u, + 227u, + directions05 + }, + { + "Mix of bidirectional text. With more paragraphs. Update first paragraph.", + "Hello world demo\nhello world\nhello world مرحبا بالعالم שלום עולם\nשלום مرحبا بالعالم עולם hello مرحبا بالعالم world" + " مرحبا بالعالم שלום עולם hello world hello world\nبالعالم שלום hello world demo עולם\nשלום مرحبا بالعالم עולם hello", + 0u, + 17u, + directions05 + }, + { + "Mix of bidirectional text. With more paragraphs. Update from character 29", + "Hello world demo\nhello world\nhello world مرحبا بالعالم שלום עולם\nשלום مرحبا بالعالم עולם hello مرحبا بالعالم world" + " مرحبا بالعالم שלום עולם hello world hello world\nبالعالم שלום hello world demo עולם\nשלום مرحبا بالعالم עולם hello", + 29u, + 134u, + directions05 + }, + { + "Mix of bidirectional text. With more paragraphs. Update from character 163", + "Hello world demo\nhello world\nhello world مرحبا بالعالم שלום עולם\nשלום مرحبا بالعالم עולם hello مرحبا بالعالم world" + " مرحبا بالعالم שלום עולם hello world hello world\nبالعالم שלום hello world demo עולם\nשלום مرحبا بالعالم עולם hello", + 163u, + 35u, + directions05 + } + }; + const unsigned int numberOfTests = 8u; + + for( unsigned int index = 0u; index < numberOfTests; ++index ) + { + if( !GetCharactersDirectionTest( data[index] ) ) + { + tet_result(TET_FAIL); + } + } + + tet_result(TET_PASS); + END_TEST; +} diff --git a/automated-tests/src/dali-toolkit-internal/utc-Dali-LogicalModel.cpp b/automated-tests/src/dali-toolkit-internal/utc-Dali-LogicalModel.cpp new file mode 100644 index 0000000..b18e1e6 --- /dev/null +++ b/automated-tests/src/dali-toolkit-internal/utc-Dali-LogicalModel.cpp @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2016 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 + + +using namespace Dali; +using namespace Toolkit; +using namespace Text; + +// Tests the following functions. +// +// void SetVisualToLogicalMap( const BidirectionalLineInfoRun* const bidirectionalInfo, +// Length numberOfRuns, +// CharacterIndex startIndex ) + + +////////////////////////////////////////////////////////// + +namespace +{ + +struct SetVisualToLogicalMapData +{ + std::string description; ///< Description of the test. + std::string text; ///< Input text. + unsigned int startIndex; ///< The start index from where the visual to logical conversion table is set. + unsigned int numberOfCharacters; ///< The number of characters to set. + Size textArea; ///< The size of the area where the text is laid-out. + unsigned int expectedNumberOfCharacters; ///< The expected number of characters. + unsigned int* visualToLogical; ///< The expected visual to logical conversion table. +}; + +bool SetVisualToLogicalMapTest( const SetVisualToLogicalMapData& data ) +{ + std::cout << " testing : " << data.description << std::endl; + // Create the model. + LogicalModelPtr logicalModel = LogicalModel::New(); + VisualModelPtr visualModel = VisualModel::New(); + Size layoutSize; + + // Create the model with the whole text. + CreateTextModel( data.text, + data.textArea, + layoutSize, + logicalModel, + visualModel ); + + // Get the visual to logical map. + Vector& visualToLogicalMap = logicalModel->mVisualToLogicalMap; + + // Compare the results. + if( data.expectedNumberOfCharacters != visualToLogicalMap.Count() ) + { + std::cout << " differetn number of characters : " << visualToLogicalMap.Count() << ", expected : " << data.expectedNumberOfCharacters << std::endl; + return false; + } + + for( unsigned int index = 0u; index < data.expectedNumberOfCharacters; ++index ) + { + if( data.visualToLogical[index] != visualToLogicalMap[index] ) + { + std::cout << " different visualToLogical table : " << std::endl; + for( unsigned int i = 0; i < data.expectedNumberOfCharacters; ++i ) + { + std::cout << visualToLogicalMap[i] << " "; + } + std::cout << std::endl; + std::cout << " expected : " << std::endl; + for( unsigned int i = 0; i < data.expectedNumberOfCharacters; ++i ) + { + std::cout << data.visualToLogical[i] << " "; + } + std::cout << std::endl; + return false; + } + } + + return true; +} + +} // namespace + +////////////////////////////////////////////////////////// + +int UtcDaliSetVisualToLogicalMap(void) +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliSetVisualToLogicalMap"); + + unsigned int* visualToLogical01 = NULL; + unsigned int* visualToLogical02 = NULL; + unsigned int visualToLogical03[] = { 12u, 11u, 10u, 9u, 8u, 7u, 6u, 5u, 4u, 3u, 2u, 1u, 0u }; + unsigned int visualToLogical04[] = { 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 25u, 24u, 23u, 22u, 21u, 20u, 19u, 18u, 17u, 16u, 15u, 14u, 13u, }; + unsigned int visualToLogical05[] = { 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, 26u, 25u, 24u, 23u, 22u, 21u, 20u, 19u, 18u, 17u, 16u, 15u, 14u }; + unsigned int visualToLogical06[] = { 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 25u, 24u, 23u, 22u, 21u, 20u, 19u, 18u, 17u, 16u, 15u, 14u, 13u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, 54u, 53u, 52u, 51u, 50u, 49u, 48u, 47u, 46u, 45u, 44u, 43u, 42u, 41u, 40u, 67u, 66u, 55u, 56u, 57u, 58u, 59u, 60u, 61u, 62u, 63u, 64u, 65u, 81u, 80u, 79u, 78u, 77u, 76u, 75u, 74u, 73u, 72u, 71u, 70u, 69u, 68u, 95u, 94u, 93u, 92u, 91u, 90u, 89u, 88u, 87u, 86u, 85u, 84u, 83u, 82u, 96u, 97u, 98u, 99u, 100u, 101u, 102u, 103u, 104u, 105u, 106u }; + unsigned int visualToLogical07[] = { 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 25u, 24u, 23u, 22u, 21u, 20u, 19u, 18u, 17u, 16u, 15u, 14u, 13u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, 54u, 53u, 52u, 51u, 50u, 49u, 48u, 47u, 46u, 45u, 44u, 43u, 42u, 41u, 40u, 67u, 66u, 55u, 56u, 57u, 58u, 59u, 60u, 61u, 62u, 63u, 64u, 65u, 81u, 80u, 79u, 78u, 77u, 76u, 75u, 74u, 73u, 72u, 71u, 70u, 69u, 68u, 95u, 94u, 93u, 92u, 91u, 90u, 89u, 88u, 87u, 86u, 85u, 84u, 83u, 82u, 96u, 97u, 98u, 99u, 100u, 101u, 102u, 103u, 104u, 105u, 106u }; + unsigned int visualToLogical08[] = { 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 25u, 24u, 23u, 22u, 21u, 20u, 19u, 18u, 17u, 16u, 15u, 14u, 13u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, 54u, 53u, 52u, 51u, 50u, 49u, 48u, 47u, 46u, 45u, 44u, 43u, 42u, 41u, 40u, 67u, 66u, 55u, 56u, 57u, 58u, 59u, 60u, 61u, 62u, 63u, 64u, 65u, 81u, 80u, 79u, 78u, 77u, 76u, 75u, 74u, 73u, 72u, 71u, 70u, 69u, 68u, 95u, 94u, 93u, 92u, 91u, 90u, 89u, 88u, 87u, 86u, 85u, 84u, 83u, 82u, 96u, 97u, 98u, 99u, 100u, 101u, 102u, 103u, 104u, 105u, 106u }; + unsigned int visualToLogical09[] = { 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 25u, 24u, 23u, 22u, 21u, 20u, 19u, 18u, 17u, 16u, 15u, 14u, 13u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, 54u, 53u, 52u, 51u, 50u, 49u, 48u, 47u, 46u, 45u, 44u, 43u, 42u, 41u, 40u, 67u, 66u, 55u, 56u, 57u, 58u, 59u, 60u, 61u, 62u, 63u, 64u, 65u, 81u, 80u, 79u, 78u, 77u, 76u, 75u, 74u, 73u, 72u, 71u, 70u, 69u, 68u, 95u, 94u, 93u, 92u, 91u, 90u, 89u, 88u, 87u, 86u, 85u, 84u, 83u, 82u, 96u, 97u, 98u, 99u, 100u, 101u, 102u, 103u, 104u, 105u, 106u }; + + struct SetVisualToLogicalMapData data[] = + { + { + "Zero characters text", + "", + 0u, + 0u, + Size( 100.f, 300.f ), + 0u, + visualToLogical01 + }, + { + "Left to right text only", + "Hello world", + 0u, + 11u, + Size( 100.f, 300.f ), + 0u, + visualToLogical02 + }, + { + "Right to left text only", + "مرحبا بالعالم", + 0u, + 13u, + Size( 100.f, 300.f ), + 13u, + visualToLogical03 + }, + { + "Mix of left to right and right to left text.", + "Hello world, مرحبا بالعالم", + 0u, + 26u, + Size( 100.f, 300.f ), + 26u, + visualToLogical04 + }, + { + "Mix of left to right and right to left text.", + "Hello world, \nمرحبا بالعالم", + 0u, + 27u, + Size( 100.f, 300.f ), + 27u, + visualToLogical05 + }, + { + "Mix of left to right and right to left text.", + "Hello world, مرحبا بالعالم, hello world\nمرحبا بالعالم, hello world, مرحبا بالعالم\nمرحبا بالعالم\nhello world", + 0u, + 107u, + Size( 100.f, 300.f ), + 107u, + visualToLogical06 + }, + { + "Mix of left to right and right to left text. Updates from character index 5", + "Hello world, مرحبا بالعالم, hello world\nمرحبا بالعالم, hello world, مرحبا بالعالم\nمرحبا بالعالم\nhello world", + 5u, + 107u, + Size( 100.f, 300.f ), + 107u, + visualToLogical07 + }, + { + "Mix of left to right and right to left text. Updates from character index 39", + "Hello world, مرحبا بالعالم, hello world\nمرحبا بالعالم, hello world, مرحبا بالعالم\nمرحبا بالعالم\nhello world", + 39u, + 107u, + Size( 100.f, 300.f ), + 107u, + visualToLogical08 + }, + { + "Mix of left to right and right to left text. Updates from character index 70", + "Hello world, مرحبا بالعالم, hello world\nمرحبا بالعالم, hello world, مرحبا بالعالم\nمرحبا بالعالم\nhello world", + 70u, + 107u, + Size( 100.f, 300.f ), + 107u, + visualToLogical09 + } + }; + const unsigned int numberOfTests = 9u; + + for( unsigned int index = 0u; index < numberOfTests; ++index ) + { + if( !SetVisualToLogicalMapTest( data[index] ) ) + { + tet_result(TET_FAIL); + } + } + + tet_result(TET_PASS); + END_TEST; +} diff --git a/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-text-model.cpp b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-text-model.cpp new file mode 100644 index 0000000..10f910c --- /dev/null +++ b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-text-model.cpp @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2016 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 "toolkit-text-model.h" + +// EXTERNAL INCLUDES +#include + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Text +{ + +/** + * @brief Frees previously allocated bidirectional resources. + * + * @param[in] bidirectionalLineInfo Bidirectional info per line. + * @param[in] index Index to the first line with bidirectional info to be freed. + */ +void FreeBidirectionalLineInfoResources( Vector bidirectionalLineInfo, + uint32_t index ) +{ + // Free the allocated memory used to store the conversion table in the bidirectional line info run. + for( Vector::Iterator it = bidirectionalLineInfo.Begin() + index, + endIt = bidirectionalLineInfo.End(); + it != endIt; + ++it ) + { + BidirectionalLineInfoRun& bidiLineInfo = *it; + + free( bidiLineInfo.visualToLogicalMap ); + } +} + +/** + * @brief Clear all the model data except for LogicalModel::mText. + * + * @param[in] characterIndex Clear data starting from the index. + */ +void ClearModelData( CharacterIndex characterIndex, + LogicalModelPtr logicalModel, + VisualModelPtr visualModel ) +{ + // n.b. This does not Clear the mText from mLogicalModel + + // Frees previously allocated resources. + FreeBidirectionalLineInfoResources( logicalModel->mBidirectionalLineInfo, 0u ); + + logicalModel->mScriptRuns.Clear(); + logicalModel->mFontRuns.Clear(); + logicalModel->mWordBreakInfo.Clear(); + logicalModel->mBidirectionalParagraphInfo.Clear(); + logicalModel->mCharacterDirections.Clear(); + logicalModel->mBidirectionalLineInfo.Clear(); + logicalModel->mLogicalToVisualMap.Clear(); + logicalModel->mVisualToLogicalMap.Clear(); + visualModel->mGlyphs.Clear(); + visualModel->mGlyphsToCharacters.Clear(); + visualModel->mCharactersToGlyph.Clear(); + visualModel->mCharactersPerGlyph.Clear(); + visualModel->mGlyphsPerCharacter.Clear(); + visualModel->mGlyphPositions.Clear(); + visualModel->mLines.Clear(); + visualModel->mColorRuns.Clear(); + + visualModel->ClearCaches(); +} + +void CreateTextModel( const std::string& text, + const Size& textArea, + Size& layoutSize, + LogicalModelPtr logicalModel, + VisualModelPtr visualModel ) +{ + // 1) Convert to utf32 + Vector& utf32Characters = logicalModel->mText; + utf32Characters.Resize( text.size() ); + + const uint32_t numberOfCharacters = Utf8ToUtf32( reinterpret_cast( text.c_str() ), + text.size(), + &utf32Characters[0u] ); + utf32Characters.Resize( numberOfCharacters ); + + // 2) Set the break and paragraph info. + Vector& lineBreakInfo = logicalModel->mLineBreakInfo; + lineBreakInfo.Resize( numberOfCharacters ); + + SetLineBreakInfo( utf32Characters, lineBreakInfo ); + + if( 0u == numberOfCharacters ) + { + // Nothing else to do if the number of characters is zero. + return; + } + + // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines). + Vector& wordBreakInfo = logicalModel->mWordBreakInfo; + wordBreakInfo.Resize( numberOfCharacters ); + + SetWordBreakInfo( utf32Characters, + 0u, + numberOfCharacters, + wordBreakInfo ); + + // 3) Set the script info. + MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get(); + + Vector& scripts = logicalModel->mScriptRuns; + multilanguageSupport.SetScripts( utf32Characters, + 0u, + numberOfCharacters, + scripts ); + + // 4) Set the font info + Vector& fontDescriptionRuns = logicalModel->mFontDescriptionRuns; + Vector& validFonts = logicalModel->mFontRuns; + + // The default font id. + FontDefaults fontDefaults; + fontDefaults.mFontDescription.family = ""; + fontDefaults.familyDefined = true; + fontDefaults.mDefaultPointSize = 12.f; + fontDefaults.sizeDefined = true; + + TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get(); + fontClient.SetDpi( 96u, 96u ); + + const FontId defaultFontId = fontDefaults.GetFontId( fontClient ); + + // Validates the fonts. If there is a character with no assigned font it sets a default one. + // After this call, fonts are validated. + multilanguageSupport.ValidateFonts( utf32Characters, + scripts, + fontDescriptionRuns, + defaultFontId, + 0u, + numberOfCharacters, + validFonts ); + + // 5) Set the bidirectional info per paragraph. + Vector mirroredUtf32Characters; + bool textMirrored = false; + + // Reserve some space for the vector of paragraph's bidirectional info. + Vector& bidirectionalInfo = logicalModel->mBidirectionalParagraphInfo; + + // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts. + SetBidirectionalInfo( utf32Characters, + scripts, + lineBreakInfo, + 0u, + numberOfCharacters, + bidirectionalInfo ); + + // 6) Set character directions. + Vector& characterDirections = logicalModel->mCharacterDirections; + if( 0u != bidirectionalInfo.Count() ) + { + // Only set the character directions if there is right to left characters. + GetCharactersDirection( bidirectionalInfo, + numberOfCharacters, + 0u, + numberOfCharacters, + characterDirections ); + + + // This paragraph has right to left text. Some characters may need to be mirrored. + textMirrored = GetMirroredText( utf32Characters, + characterDirections, + bidirectionalInfo, + 0u, + numberOfCharacters, + mirroredUtf32Characters ); + } + else + { + // There is no right to left characters. Clear the directions vector. + characterDirections.Clear(); + } + + // 7) Shape the text. + + Vector& glyphs = visualModel->mGlyphs; + Vector& glyphsToCharactersMap = visualModel->mGlyphsToCharacters; + Vector& charactersPerGlyph = visualModel->mCharactersPerGlyph; + Vector newParagraphGlyphs; + + const Length currentNumberOfGlyphs = glyphs.Count(); + const Vector& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters; + + ShapeText( textToShape, + lineBreakInfo, + scripts, + validFonts, + glyphs, + glyphsToCharactersMap, + charactersPerGlyph, + newParagraphGlyphs ); + + // Create the 'number of glyphs' per character and the glyph to character conversion tables. + visualModel->CreateGlyphsPerCharacterTable( numberOfCharacters ); + visualModel->CreateCharacterToGlyphTable( numberOfCharacters ); + + const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs; + + // 8) Get the glyph metrics + MetricsPtr metrics = Metrics::New( fontClient ); + + const GlyphIndex glyphIndex = currentNumberOfGlyphs; + + GlyphInfo* glyphsBuffer = glyphs.Begin() + glyphIndex; + metrics->GetGlyphMetrics( glyphsBuffer, numberOfGlyphs ); + + // Update the width and advance of all new paragraph characters. + for( Vector::ConstIterator it = newParagraphGlyphs.Begin(), + endIt = newParagraphGlyphs.End(); + it != endIt; + ++it ) + { + const GlyphIndex index = *it; + GlyphInfo& glyph = *( glyphsBuffer + ( index - glyphIndex ) ); + + glyph.xBearing = 0.f; + glyph.width = 0.f; + glyph.advance = 0.f; + } + + // 9) Layout the text + LayoutEngine layoutEngine; + layoutEngine.SetMetrics( metrics ); + layoutEngine.SetLayout( LayoutEngine::MULTI_LINE_BOX ); + + // Set the layout parameters. + const Vector& charactersToGlyph = visualModel->mCharactersToGlyph; + const Vector& glyphsPerCharacter = visualModel->mGlyphsPerCharacter; + LayoutParameters layoutParameters( textArea, + utf32Characters.Begin(), + lineBreakInfo.Begin(), + wordBreakInfo.Begin(), + ( 0u != characterDirections.Count() ) ? characterDirections.Begin() : NULL, + glyphs.Count(), + glyphs.Begin(), + glyphsToCharactersMap.Begin(), + charactersPerGlyph.Begin() ); + + // Get the character to glyph conversion table and set into the layout. + layoutParameters.charactersToGlyphsBuffer = charactersToGlyph.Begin(); + // Get the glyphs per character table and set into the layout. + layoutParameters.glyphsPerCharacterBuffer = glyphsPerCharacter.Begin(); + + Vector& lines = visualModel->mLines; + + Vector& glyphPositions = visualModel->mGlyphPositions; + glyphPositions.Resize( numberOfGlyphs ); + + layoutParameters.isLastNewParagraph = TextAbstraction::IsNewParagraph( *( utf32Characters.Begin() + ( logicalModel->mText.Count() - 1u ) ) ); + + layoutEngine.LayoutText( layoutParameters, + glyphPositions, + lines, + layoutSize ); + + // 10) Reorder the lines + if( 0u != bidirectionalInfo.Count() ) + { + Vector& bidirectionalLineInfo = logicalModel->mBidirectionalLineInfo; + + // Get the lines + const Length numberOfLines = lines.Count(); + + // Reorder the lines. + bidirectionalLineInfo.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters. + ReorderLines( bidirectionalInfo, + 0u, + numberOfCharacters, + lines, + bidirectionalLineInfo ); + + // Set the bidirectional info per line into the layout parameters. + layoutParameters.lineBidirectionalInfoRunsBuffer = bidirectionalLineInfo.Begin(); + layoutParameters.numberOfBidirectionalInfoRuns = bidirectionalLineInfo.Count(); + + // Set the bidirectional info into the model. + logicalModel->SetVisualToLogicalMap( layoutParameters.lineBidirectionalInfoRunsBuffer, + layoutParameters.numberOfBidirectionalInfoRuns, + 0u, + numberOfCharacters ); + + // Re-layout the text. Reorder those lines with right to left characters. + layoutEngine.ReLayoutRightToLeftLines( layoutParameters, + glyphPositions ); + } +} + +} // namespace Text + +} // namespace Toolkit + +} // namespace Dali diff --git a/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-text-model.h b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-text-model.h new file mode 100644 index 0000000..02dbbcd --- /dev/null +++ b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-text-model.h @@ -0,0 +1,58 @@ +#ifndef __DALI_TOOLKIT_TEXT_MODEL_H__ +#define __DALI_TOOLKIT_TEXT_MODEL_H__ + +/* + * Copyright (c) 2016 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 + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Text +{ + +/** + * @brief Creates and fills all the vectors of a text model: characters in utf32, segmentation info, + * scripts, fonts, bidi info, glyphs, conversion tables, etc. + * + * @param[in] text The given text. + * @param[in] textArea The area where to layout the text. + * @param[out] layoutSize The laid-out size. + * @param[out] Pointer to a logical text model instance. + * @param[out] Pointer to a visual text model instance. + */ +void CreateTextModel( const std::string& text, + const Size& textArea, + Size& layoutSize, + LogicalModelPtr logicalModel, + VisualModelPtr visualModel ); + +} // namespace Text + +} // namespace Toolkit + +} // namespace Dali + +#endif // __DALI_TOOLKIT_TEXT_MODEL_H__ diff --git a/dali-toolkit/internal/text/bidirectional-support.cpp b/dali-toolkit/internal/text/bidirectional-support.cpp index c09c03c..57dc613 100644 --- a/dali-toolkit/internal/text/bidirectional-support.cpp +++ b/dali-toolkit/internal/text/bidirectional-support.cpp @@ -86,13 +86,32 @@ void GetLines( const BidirectionalParagraphInfoRun& paragraphInfo, void SetBidirectionalInfo( const Vector& text, const Vector& scripts, const Vector& lineBreakInfo, + CharacterIndex startIndex, + Length numberOfCharacters, Vector& bidirectionalInfo ) { + // Find where to insert the new paragraphs. + BidirectionalRunIndex bidiInfoIndex = 0u; + for( Vector::ConstIterator it = bidirectionalInfo.Begin(), + endIt = bidirectionalInfo.End(); + it != endIt; + ++it ) + { + const BidirectionalParagraphInfoRun& run = *it; + + if( startIndex < run.characterRun.characterIndex + run.characterRun.numberOfCharacters ) + { + // Found where to insert the bidi info. + break; + } + ++bidiInfoIndex; + } + // Traverse the script runs. If there is one with a right to left script, create the bidirectional info for the paragraph containing that script is needed. // From the bidirectional point of view, a paragraph is the piece of text between two LINE_MUST_BREAK. // Index pointing the first character of the current paragraph. - CharacterIndex paragraphCharacterIndex = 0u; + CharacterIndex paragraphCharacterIndex = startIndex; // Pointer to the text buffer. const Character* textBuffer = text.Begin(); @@ -100,75 +119,106 @@ void SetBidirectionalInfo( const Vector& text, // Pointer to the line break info buffer. const LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin(); - // The number of characters. - const Length numberOfCharacters = text.Count(); - // Handle to the bidirectional info module in text-abstraction. TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get(); + const CharacterIndex lastCharacter = startIndex + numberOfCharacters; + + bool hasRightToLeftScript = false; + for( Vector::ConstIterator it = scripts.Begin(), endIt = scripts.End(); it != endIt; ++it ) { const ScriptRun& scriptRun = *it; - const CharacterIndex lastScriptRunIndex = scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters; + const CharacterIndex lastScriptRunIndex = scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters - 1u; + + if( startIndex > lastScriptRunIndex ) + { + // Skip the run as it has already been processed. + continue; + } + + if( lastCharacter <= scriptRun.characterRun.characterIndex ) + { + // Do not get bidirectional info beyond startIndex + numberOfCharacters. + break; + } + + if( !hasRightToLeftScript && TextAbstraction::IsRightToLeftScript( scriptRun.script ) ) + { + // The script is right to left. + hasRightToLeftScript = true; + } - if( ( lastScriptRunIndex > paragraphCharacterIndex ) && // It isn't part of a previous paragraph. - TextAbstraction::IsRightToLeftScript( scriptRun.script ) ) // The script is right to left. + if( TextAbstraction::LINE_MUST_BREAK == *( lineBreakInfoBuffer + lastScriptRunIndex ) ) { - // Find the paragraphs which contains this script run. - // Consider: - // 1) Different paragraphs may contain this script run. - // ------||------------------- rtl sr ------------------------||------------------- - // --||----- p -----||------------------ p -------------||-------- p ------||------ - // - // 2) The paragraph which contains this script run may contain other right to left script runs. - // -----||--- rtl sr ---||---- ltr sr ----||---------- rtl sr -----------||-------- - // -----||---------------------------------- p -----------------------------------| - - while( lastScriptRunIndex > paragraphCharacterIndex ) + // A new paragraph has been found. + + if( hasRightToLeftScript ) { - // There is a paragraph which contains the current script. - - Length index = paragraphCharacterIndex; - while( ( index < numberOfCharacters ) && ( paragraphCharacterIndex < lastScriptRunIndex ) ) - { - if( TextAbstraction::LINE_MUST_BREAK == *( lineBreakInfoBuffer + index ) ) - { - if( index >= scriptRun.characterRun.characterIndex ) - { - // The Bidirectional run must have the same number of characters than the paragraph. - BidirectionalParagraphInfoRun bidirectionalRun; - bidirectionalRun.characterRun.characterIndex = paragraphCharacterIndex; - bidirectionalRun.characterRun.numberOfCharacters = ( index - paragraphCharacterIndex ) + 1u; // The must break character is part of the paragrah. - - // Create the bidirectional info for the whole paragraph and store the index to the table with this info in the run. - bidirectionalRun.bidirectionalInfoIndex = bidirectionalSupport.CreateInfo( textBuffer + bidirectionalRun.characterRun.characterIndex, - bidirectionalRun.characterRun.numberOfCharacters ); - - bidirectionalInfo.PushBack( bidirectionalRun ); - } - - // Update the character index of the next paragraph. - paragraphCharacterIndex = index + 1u; - } - ++index; - } - - // The last character is always a must-break, so there is no need to check if there is characters left. + // The Bidirectional run must have the same number of characters than the paragraph. + BidirectionalParagraphInfoRun bidirectionalRun; + bidirectionalRun.characterRun.characterIndex = paragraphCharacterIndex; + bidirectionalRun.characterRun.numberOfCharacters = ( lastScriptRunIndex - paragraphCharacterIndex ) + 1u; // The must break character is part of the paragrah. + + // Create the bidirectional info for the whole paragraph and store the index to the table with this info in the run. + bidirectionalRun.bidirectionalInfoIndex = bidirectionalSupport.CreateInfo( textBuffer + bidirectionalRun.characterRun.characterIndex, + bidirectionalRun.characterRun.numberOfCharacters ); + + bidirectionalInfo.Insert( bidirectionalInfo.Begin() + bidiInfoIndex, bidirectionalRun ); + ++bidiInfoIndex; } + + // Point to the next paragraph. + paragraphCharacterIndex = lastScriptRunIndex + 1u; + + // Reset whether there is a right to left script. + hasRightToLeftScript = false; } } + + // Update indices of the bidi runs. + for( Vector::Iterator it = bidirectionalInfo.Begin() + bidiInfoIndex, + endIt = bidirectionalInfo.End(); + it != endIt; + ++it ) + { + BidirectionalParagraphInfoRun& run = *it; + + run.characterRun.characterIndex += numberOfCharacters; + } } void ReorderLines( const Vector& bidirectionalInfo, + CharacterIndex startIndex, + Length numberOfCharacters, Vector& lineRuns, Vector& lineInfoRuns ) { + // Find where to insert the new paragraphs. + BidirectionalLineRunIndex bidiLineInfoIndex = 0u; + for( Vector::ConstIterator it = lineInfoRuns.Begin(), + endIt = lineInfoRuns.End(); + it != endIt; + ++it ) + { + const BidirectionalLineInfoRun& run = *it; + + if( startIndex < run.characterRun.characterIndex + run.characterRun.numberOfCharacters ) + { + // Found where to insert the bidi line info. + break; + } + ++bidiLineInfoIndex; + } + // Handle to the bidirectional info module in text-abstraction. TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get(); + const CharacterIndex lastCharacter = startIndex + numberOfCharacters; + // Keep an index to the first line to be checked if it's contained inside the paragraph. // Avoids check the lines from the beginning for each paragraph. unsigned int lineIndex = 0u; @@ -179,6 +229,19 @@ void ReorderLines( const Vector& bidirectionalInf ++it ) { const BidirectionalParagraphInfoRun& paragraphInfo = *it; + + if( paragraphInfo.characterRun.characterIndex < startIndex ) + { + // Do not process, the paragraph has already been processed. + continue; + } + + if( lastCharacter <= paragraphInfo.characterRun.characterIndex ) + { + // Do not process paragraphs beyond startIndex + numberOfCharacters. + break; + } + const CharacterDirection direction = bidirectionalSupport.GetParagraphDirection( paragraphInfo.bidirectionalInfoIndex ); // Get the lines for this paragraph. @@ -225,14 +288,28 @@ void ReorderLines( const Vector& bidirectionalInf } // Push the run into the vector. - lineInfoRuns.PushBack( lineInfoRun ); + lineInfoRuns.Insert( lineInfoRuns.Begin() + bidiLineInfoIndex, lineInfoRun ); + ++bidiLineInfoIndex; } } + + // Update indices of the bidi runs. + for( Vector::Iterator it = lineInfoRuns.Begin() + bidiLineInfoIndex, + endIt = lineInfoRuns.End(); + it != endIt; + ++it ) + { + BidirectionalLineInfoRun& run = *it; + + run.characterRun.characterIndex += numberOfCharacters; + } } bool GetMirroredText( const Vector& text, const Vector& directions, const Vector& bidirectionalInfo, + CharacterIndex startIndex, + Length numberOfCharacters, Vector& mirroredText ) { bool hasTextMirrored = false; @@ -245,6 +322,9 @@ bool GetMirroredText( const Vector& text, Character* mirroredTextBuffer = mirroredText.Begin(); CharacterDirection* directionsBuffer = directions.Begin(); + CharacterIndex index = startIndex; + const CharacterIndex lastCharacter = startIndex + numberOfCharacters; + // Traverse the paragraphs and mirror the right to left ones. for( Vector::ConstIterator it = bidirectionalInfo.Begin(), endIt = bidirectionalInfo.End(); @@ -253,6 +333,19 @@ bool GetMirroredText( const Vector& text, { const BidirectionalParagraphInfoRun& paragraph = *it; + if( index >= paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters ) + { + // Skip the paragraph as it has already been processed. + continue; + } + + if( lastCharacter <= paragraph.characterRun.characterIndex ) + { + // Do not get mirror characters beyond startIndex + numberOfCharacters. + break; + } + + index += paragraph.characterRun.numberOfCharacters; const bool tmpMirrored = bidirectionalSupport.GetMirroredText( mirroredTextBuffer + paragraph.characterRun.characterIndex, directionsBuffer + paragraph.characterRun.characterIndex, paragraph.characterRun.numberOfCharacters ); @@ -264,13 +357,36 @@ bool GetMirroredText( const Vector& text, } void GetCharactersDirection( const Vector& bidirectionalInfo, + Length totalNumberOfCharacters, + CharacterIndex startIndex, + Length numberOfCharacters, Vector& directions ) { // Handle to the bidirectional info module in text-abstraction. TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get(); - CharacterIndex index = 0u; - CharacterDirection* directionsBuffer = directions.Begin(); + // Resize the vector. + directions.Resize( totalNumberOfCharacters ); + + // Whether the current buffer is being updated or is set from scratch. + const bool updateCurrentBuffer = numberOfCharacters < totalNumberOfCharacters; + + CharacterDirection* directionsBuffer = NULL; + Vector newDirections; + + if( updateCurrentBuffer ) + { + newDirections.Resize( numberOfCharacters ); + directionsBuffer = newDirections.Begin(); + } + else + { + directionsBuffer = directions.Begin(); + } + + const CharacterIndex lastCharacter = startIndex + numberOfCharacters; + CharacterIndex index = startIndex; + for( Vector::ConstIterator it = bidirectionalInfo.Begin(), endIt = bidirectionalInfo.End(); it != endIt; @@ -278,17 +394,46 @@ void GetCharactersDirection( const Vector& bidire { const BidirectionalParagraphInfoRun& paragraph = *it; - // Fills with left to right those paragraphs without right to left characters. - memset( directionsBuffer + index, false, ( paragraph.characterRun.characterIndex - index ) * sizeof( bool ) ); - index += paragraph.characterRun.numberOfCharacters; + if( index >= paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters ) + { + // Skip the paragraph as it has already been processed. + continue; + } + if( lastCharacter <= paragraph.characterRun.characterIndex ) + { + // Do not get the character directions beyond startIndex + numberOfCharacters. + break; + } + + // Set the directions of any previous left to right characters. + const Length numberOfLeftToRightCharacters = paragraph.characterRun.characterIndex - index; + if( numberOfLeftToRightCharacters > 0u ) + { + memset( directionsBuffer + index - startIndex, false, numberOfLeftToRightCharacters * sizeof( bool ) ); + } + + // Set the directions of the bidirectional text. bidirectionalSupport.GetCharactersDirection( paragraph.bidirectionalInfoIndex, - directionsBuffer + paragraph.characterRun.characterIndex, + directionsBuffer + paragraph.characterRun.characterIndex - startIndex, paragraph.characterRun.numberOfCharacters ); + + // Update the index. + index += paragraph.characterRun.numberOfCharacters + numberOfLeftToRightCharacters; } // Fills with left to right those paragraphs without right to left characters. - memset( directionsBuffer + index, false, ( directions.Count() - index ) * sizeof( bool ) ); + memset( directionsBuffer + index - startIndex, false, ( lastCharacter - index ) * sizeof( bool ) ); + + // If the direction info is updated, it needs to be inserted in the model. + if( updateCurrentBuffer ) + { + // Insert the directions in the given buffer. + directions.Insert( directions.Begin() + startIndex, + newDirections.Begin(), + newDirections.End() ); + directions.Resize( totalNumberOfCharacters ); + } } } // namespace Text diff --git a/dali-toolkit/internal/text/bidirectional-support.h b/dali-toolkit/internal/text/bidirectional-support.h index e87f866..39ec9d6 100644 --- a/dali-toolkit/internal/text/bidirectional-support.h +++ b/dali-toolkit/internal/text/bidirectional-support.h @@ -42,11 +42,15 @@ namespace Text * @param[in] text Vector of UTF-32 characters. * @param[in] scripts Vector containing the script runs for the whole text. * @param[in] lineBreakInfo The line break info. + * @param[in] startIndex The character from where the bidirectional info is set. + * @param[in] numberOfCharacters The number of characters. * @param[out] bidirectionalInfo Vector with the bidirectional infor for each paragraph. */ void SetBidirectionalInfo( const Vector& text, const Vector& scripts, const Vector& lineBreakInfo, + CharacterIndex startIndex, + Length numberOfCharacters, Vector& bidirectionalInfo ); /** @@ -60,10 +64,14 @@ void SetBidirectionalInfo( const Vector& text, * @pre The @p visualModel needs to have the laid-out lines info set. * * @param[in] bidirectionalInfo Vector with the bidirectional infor for each paragraph. + * @param[in] startIndex The character from where the bidirectional info is set. + * @param[in] numberOfCharacters The number of characters. * @param[in,out] lineRuns The line runs converted to characters. * @param[out] lineInfoRuns line runs with the visual to logical conversion maps. */ void ReorderLines( const Vector& bidirectionalInfo, + CharacterIndex startIndex, + Length numberOfCharacters, Vector& lineRuns, Vector& lineInfoRuns ); @@ -73,6 +81,8 @@ void ReorderLines( const Vector& bidirectionalInf * @param[in] text The text. * @param[in] directions Vector with the direction of each paragraph. * @param[in] bidirectionalInfo Vector with the bidirectional infor for each paragraph. + * @param[in] startIndex The character from where the text is mirrored. + * @param[in] numberOfCharacters The number of characters. * @param[out] mirroredText The mirroredText. * * @return @e true if a character has been replaced. @@ -80,6 +90,8 @@ void ReorderLines( const Vector& bidirectionalInf bool GetMirroredText( const Vector& text, const Vector& directions, const Vector& bidirectionalInfo, + CharacterIndex startIndex, + Length numberOfCharacters, Vector& mirroredText ); /** @@ -89,9 +101,15 @@ bool GetMirroredText( const Vector& text, * @pre The @p logicalModel needs to have the bidirectional info indices for each paragraph set. * * @param[in] bidirectionalInfo Vector with the bidirectional infor for each paragraph. + * @param[in] totalNumberOfCharacters The number of characters of the whole text. + * @param[in] startIndex The character from where the direction info is set. + * @param[in] numberOfCharacters The number of characters. * @param[out] directions The direction, @e false is left to right and @e true is right to left, of each character of the paragraph. */ void GetCharactersDirection( const Vector& bidirectionalInfo, + Length totalNumberOfCharacters, + CharacterIndex startIndex, + Length numberOfCharacters, Vector& directions ); } // namespace Text diff --git a/dali-toolkit/internal/text/logical-model-impl.cpp b/dali-toolkit/internal/text/logical-model-impl.cpp index 822f9f8..8af3e91 100644 --- a/dali-toolkit/internal/text/logical-model-impl.cpp +++ b/dali-toolkit/internal/text/logical-model-impl.cpp @@ -79,168 +79,166 @@ CharacterDirection LogicalModel::GetCharacterDirection( CharacterIndex character } void LogicalModel::SetVisualToLogicalMap( const BidirectionalLineInfoRun* const bidirectionalInfo, - Length numberOfRuns ) + Length numberOfRuns, + CharacterIndex startIndex, + Length numberOfCharacters ) { - if( 0u == numberOfRuns ) - { - mVisualToLogicalMap.Clear(); - mLogicalToVisualMap.Clear(); - mVisualToLogicalCursorMap.Clear(); - } - else - { - const Length numberOfCharacters = mText.Count(); - mVisualToLogicalMap.Resize( numberOfCharacters ); - mLogicalToVisualMap.Resize( numberOfCharacters ); + mVisualToLogicalMap.Resize( numberOfCharacters ); + mLogicalToVisualMap.Resize( numberOfCharacters ); - const Length numberOfCharactersPlus = numberOfCharacters + 1u; - mVisualToLogicalCursorMap.Resize( numberOfCharactersPlus ); + const Length numberOfCharactersPlus = numberOfCharacters + 1u; + mVisualToLogicalCursorMap.Resize( numberOfCharactersPlus ); - CharacterIndex* modelVisualToLogicalMapBuffer = mVisualToLogicalMap.Begin(); - CharacterIndex* modelLogicalToVisualMapBuffer = mLogicalToVisualMap.Begin(); + CharacterIndex* modelVisualToLogicalMapBuffer = mVisualToLogicalMap.Begin(); + CharacterIndex* modelLogicalToVisualMapBuffer = mLogicalToVisualMap.Begin(); - CharacterIndex* modelVisualToLogicalCursorMap = mVisualToLogicalCursorMap.Begin(); + CharacterIndex* modelVisualToLogicalCursorMap = mVisualToLogicalCursorMap.Begin(); - CharacterIndex lastIndex = 0u; - for( unsigned int bidiIndex = 0u; bidiIndex < numberOfRuns; ++bidiIndex ) - { - const BidirectionalLineInfoRun& bidiLineInfo = *( bidirectionalInfo + bidiIndex ); + CharacterIndex lastIndex = startIndex; + for( unsigned int bidiIndex = 0u; bidiIndex < numberOfRuns; ++bidiIndex ) + { + const BidirectionalLineInfoRun& bidiLineInfo = *( bidirectionalInfo + bidiIndex ); - if( lastIndex < bidiLineInfo.characterRun.characterIndex ) - { - // Fill with the identity. - for( ; lastIndex < bidiLineInfo.characterRun.characterIndex; ++lastIndex ) - { - *( modelVisualToLogicalMapBuffer + lastIndex ) = lastIndex; - } - } + if( bidiLineInfo.characterRun.characterIndex + bidiLineInfo.characterRun.numberOfCharacters <= startIndex ) + { + // Skip this paragraph. It has been already processed. + continue; + } - // Fill the conversion table of the run. - for( CharacterIndex index = 0u; - index < bidiLineInfo.characterRun.numberOfCharacters; - ++index, ++lastIndex ) + if( lastIndex < bidiLineInfo.characterRun.characterIndex ) + { + // Fill with the identity. + for( ; lastIndex < bidiLineInfo.characterRun.characterIndex; ++lastIndex ) { - *( modelVisualToLogicalMapBuffer + lastIndex ) = bidiLineInfo.characterRun.characterIndex + *( bidiLineInfo.visualToLogicalMap + index ); + *( modelVisualToLogicalMapBuffer + lastIndex ) = lastIndex; } } - // Complete with the identity if there are some left to right characters after the last right to left. - for( ; lastIndex < numberOfCharacters; ++lastIndex ) + // Fill the conversion table of the run. + for( CharacterIndex index = 0u; + index < bidiLineInfo.characterRun.numberOfCharacters; + ++index, ++lastIndex ) { - *( modelVisualToLogicalMapBuffer + lastIndex ) = lastIndex; + *( modelVisualToLogicalMapBuffer + lastIndex ) = bidiLineInfo.characterRun.characterIndex + *( bidiLineInfo.visualToLogicalMap + index ); } + } - // Sets the logical to visual conversion map. - for( CharacterIndex index = 0u; index < numberOfCharacters; ++index ) - { - *( modelLogicalToVisualMapBuffer + *( modelVisualToLogicalMapBuffer + index ) ) = index; - } + // Complete with the identity if there are some left to right characters after the last right to left. + for( ; lastIndex < numberOfCharacters; ++lastIndex ) + { + *( modelVisualToLogicalMapBuffer + lastIndex ) = lastIndex; + } + + // Sets the logical to visual conversion map. + for( CharacterIndex index = startIndex; index < numberOfCharacters; ++index ) + { + *( modelLogicalToVisualMapBuffer + *( modelVisualToLogicalMapBuffer + index ) ) = index; + } - // Sets the visual to logical conversion map for cursor positions. + // Sets the visual to logical conversion map for cursor positions. - const Length numberOfBidirectionalParagraphs = mBidirectionalParagraphInfo.Count(); - BidirectionalParagraphInfoRun* bidirectionalParagraphInfoBuffer = mBidirectionalParagraphInfo.Begin(); - BidirectionalParagraphInfoRun* bidirectionalParagraph = bidirectionalParagraphInfoBuffer; + const Length numberOfBidirectionalParagraphs = mBidirectionalParagraphInfo.Count(); + BidirectionalParagraphInfoRun* bidirectionalParagraphInfoBuffer = mBidirectionalParagraphInfo.Begin(); + BidirectionalParagraphInfoRun* bidirectionalParagraph = bidirectionalParagraphInfoBuffer; - const CharacterDirection* const modelCharacterDirections = mCharacterDirections.Begin(); + const CharacterDirection* const modelCharacterDirections = mCharacterDirections.Begin(); + + Length bidirectionalParagraphIndex = 0u; + bool isRightToLeftParagraph = false; + for( CharacterIndex index = startIndex; index < numberOfCharactersPlus; ++index ) + { + if( bidirectionalParagraph && + ( bidirectionalParagraph->characterRun.characterIndex == index ) ) + { + isRightToLeftParagraph = *( modelCharacterDirections + index ); + } - Length bidirectionalParagraphIndex = 0u; - bool isRightToLeftParagraph = false; - for( CharacterIndex index = 0u; index < numberOfCharactersPlus; ++index ) + if( 0u == index ) { - if( bidirectionalParagraph && - ( bidirectionalParagraph->characterRun.characterIndex == index ) ) + if( isRightToLeftParagraph ) { - isRightToLeftParagraph = *( modelCharacterDirections + index ); + *( modelVisualToLogicalCursorMap + index ) = numberOfCharacters; } - - if( 0u == index ) + else // else logical position is zero. { - if( isRightToLeftParagraph ) - { - *( modelVisualToLogicalCursorMap + index ) = numberOfCharacters; - } - else // else logical position is zero. - { - *( modelVisualToLogicalCursorMap + index ) = 0u; - } + *( modelVisualToLogicalCursorMap + index ) = 0u; } - else if( numberOfCharacters == index ) + } + else if( numberOfCharacters == index ) + { + if( isRightToLeftParagraph ) { - if( isRightToLeftParagraph ) + *( modelVisualToLogicalCursorMap + index ) = 0u; + } + else // else logical position is the number of characters. + { + *( modelVisualToLogicalCursorMap + index ) = numberOfCharacters; + } + } + else + { + // Get the character indexed by index - 1 and index + // and calculate the logical position according the directions of + // both characters and the direction of the paragraph. + + const CharacterIndex previousIndex = index - 1u; + const CharacterIndex logicalPosition0 = *( modelVisualToLogicalMapBuffer + previousIndex ); + const CharacterIndex logicalPosition1 = *( modelVisualToLogicalMapBuffer + index ); + + const CharacterDirection direction0 = *( modelCharacterDirections + logicalPosition0 ); + const CharacterDirection direction1 = *( modelCharacterDirections + logicalPosition1 ); + + if( direction0 == direction1 ) + { + // Both glyphs have the same direction. + if( direction0 ) { - *( modelVisualToLogicalCursorMap + index ) = 0u; + *( modelVisualToLogicalCursorMap + index ) = logicalPosition0; } - else // else logical position is the number of characters. + else { - *( modelVisualToLogicalCursorMap + index ) = numberOfCharacters; + *( modelVisualToLogicalCursorMap + index ) = logicalPosition1; } } else { - // Get the character indexed by index - 1 and index - // and calculate the logical position according the directions of - // both characters and the direction of the paragraph. - - const CharacterIndex previousIndex = index - 1u; - const CharacterIndex logicalPosition0 = *( modelVisualToLogicalMapBuffer + previousIndex ); - const CharacterIndex logicalPosition1 = *( modelVisualToLogicalMapBuffer + index ); - - const CharacterDirection direction0 = *( modelCharacterDirections + logicalPosition0 ); - const CharacterDirection direction1 = *( modelCharacterDirections + logicalPosition1 ); - - if( direction0 == direction1 ) + if( isRightToLeftParagraph ) { - // Both glyphs have the same direction. - if( direction0 ) + if( direction1 ) { - *( modelVisualToLogicalCursorMap + index ) = logicalPosition0; + *( modelVisualToLogicalCursorMap + index ) = logicalPosition1 + 1u; } else { - *( modelVisualToLogicalCursorMap + index ) = logicalPosition1; + *( modelVisualToLogicalCursorMap + index ) = logicalPosition0; } } else { - if( isRightToLeftParagraph ) + if( direction0 ) { - if( direction1 ) - { - *( modelVisualToLogicalCursorMap + index ) = logicalPosition1 + 1u; - } - else - { - *( modelVisualToLogicalCursorMap + index ) = logicalPosition0; - } + *( modelVisualToLogicalCursorMap + index ) = logicalPosition1; } else { - if( direction0 ) - { - *( modelVisualToLogicalCursorMap + index ) = logicalPosition1; - } - else - { - *( modelVisualToLogicalCursorMap + index ) = logicalPosition0 + 1u; - } + *( modelVisualToLogicalCursorMap + index ) = logicalPosition0 + 1u; } } } + } - if( bidirectionalParagraph && - ( bidirectionalParagraph->characterRun.characterIndex + bidirectionalParagraph->characterRun.numberOfCharacters == index ) ) + if( bidirectionalParagraph && + ( bidirectionalParagraph->characterRun.characterIndex + bidirectionalParagraph->characterRun.numberOfCharacters == index ) ) + { + isRightToLeftParagraph = false; + ++bidirectionalParagraphIndex; + if( bidirectionalParagraphIndex < numberOfBidirectionalParagraphs ) { - isRightToLeftParagraph = false; - ++bidirectionalParagraphIndex; - if( bidirectionalParagraphIndex < numberOfBidirectionalParagraphs ) - { - bidirectionalParagraph = bidirectionalParagraphInfoBuffer + bidirectionalParagraphIndex; - } - else - { - bidirectionalParagraph = NULL; - } + bidirectionalParagraph = bidirectionalParagraphInfoBuffer + bidirectionalParagraphIndex; + } + else + { + bidirectionalParagraph = NULL; } } } diff --git a/dali-toolkit/internal/text/logical-model-impl.h b/dali-toolkit/internal/text/logical-model-impl.h index 7a28ba6..7085d79 100644 --- a/dali-toolkit/internal/text/logical-model-impl.h +++ b/dali-toolkit/internal/text/logical-model-impl.h @@ -96,9 +96,13 @@ public: * * @param[in] bidirectionalInfo Pointer to a buffer with all the bidirectional info runs. * @param[in] numberOfRuns The number of bidirectional info runs. + * @param[in] startIndex Character index from where the conversion tables are set. + * @param[in] numberOfCharacters The number of characters. */ void SetVisualToLogicalMap( const BidirectionalLineInfoRun* const bidirectionalInfo, - Length numberOfRuns ); + Length numberOfRuns, + CharacterIndex startIndex, + Length numberOfCharacters ); /** * @brief Retrieves the logical character index for the given visual character index. @@ -109,6 +113,8 @@ public: */ CharacterIndex GetLogicalCharacterIndex( CharacterIndex visualCharacterIndex ) const; + // Text style. + /** * @brief Updates the text's style runs with the added or removed text. * diff --git a/dali-toolkit/internal/text/text-controller-impl.cpp b/dali-toolkit/internal/text/text-controller-impl.cpp index 2a76cb2..9458933 100644 --- a/dali-toolkit/internal/text/text-controller-impl.cpp +++ b/dali-toolkit/internal/text/text-controller-impl.cpp @@ -416,15 +416,18 @@ void Controller::Impl::UpdateModel( OperationsMask operationsRequired ) SetBidirectionalInfo( utf32Characters, scripts, lineBreakInfo, + startIndex, + requestedNumberOfCharacters, bidirectionalInfo ); if( 0u != bidirectionalInfo.Count() ) { // Only set the character directions if there is right to left characters. Vector& directions = mLogicalModel->mCharacterDirections; - directions.Resize( numberOfCharacters ); - GetCharactersDirection( bidirectionalInfo, + numberOfCharacters, + startIndex, + requestedNumberOfCharacters, directions ); // This paragraph has right to left text. Some characters may need to be mirrored. @@ -433,6 +436,8 @@ void Controller::Impl::UpdateModel( OperationsMask operationsRequired ) textMirrored = GetMirroredText( utf32Characters, directions, bidirectionalInfo, + startIndex, + requestedNumberOfCharacters, mirroredUtf32Characters ); } else diff --git a/dali-toolkit/internal/text/text-controller.cpp b/dali-toolkit/internal/text/text-controller.cpp index 8a500f7..740e948 100644 --- a/dali-toolkit/internal/text/text-controller.cpp +++ b/dali-toolkit/internal/text/text-controller.cpp @@ -1382,32 +1382,36 @@ bool Controller::DoRelayout( const Size& size, if( REORDER & operations ) { Vector& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo; + Vector& bidirectionalLineInfo = mImpl->mLogicalModel->mBidirectionalLineInfo; // Check first if there are paragraphs with bidirectional info. if( 0u != bidirectionalInfo.Count() ) { // Get the lines const Length numberOfLines = mImpl->mVisualModel->mLines.Count(); + const CharacterIndex startIndex = 0u; + Length requestedNumberOfCharacters = mImpl->mLogicalModel->mText.Count(); // Reorder the lines. - Vector lineBidirectionalInfoRuns; - lineBidirectionalInfoRuns.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters. + bidirectionalLineInfo.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters. ReorderLines( bidirectionalInfo, + startIndex, + requestedNumberOfCharacters, lines, - lineBidirectionalInfoRuns ); - - // Set the bidirectional info into the model. - const Length numberOfBidirectionalInfoRuns = lineBidirectionalInfoRuns.Count(); - mImpl->mLogicalModel->SetVisualToLogicalMap( lineBidirectionalInfoRuns.Begin(), - numberOfBidirectionalInfoRuns ); + bidirectionalLineInfo ); // Set the bidirectional info per line into the layout parameters. - layoutParameters.lineBidirectionalInfoRunsBuffer = lineBidirectionalInfoRuns.Begin(); - layoutParameters.numberOfBidirectionalInfoRuns = numberOfBidirectionalInfoRuns; + layoutParameters.lineBidirectionalInfoRunsBuffer = bidirectionalLineInfo.Begin(); + layoutParameters.numberOfBidirectionalInfoRuns = bidirectionalLineInfo.Count(); + + // Set the bidirectional info into the model. + mImpl->mLogicalModel->SetVisualToLogicalMap( layoutParameters.lineBidirectionalInfoRunsBuffer, + layoutParameters.numberOfBidirectionalInfoRuns, + startIndex, + requestedNumberOfCharacters ); // Get the character to glyph conversion table and set into the layout. layoutParameters.charactersToGlyphsBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin(); - // Get the glyphs per character table and set into the layout. layoutParameters.glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin(); @@ -1416,15 +1420,18 @@ bool Controller::DoRelayout( const Size& size, glyphPositions ); // Free the allocated memory used to store the conversion table in the bidirectional line info run. - for( Vector::Iterator it = lineBidirectionalInfoRuns.Begin(), - endIt = lineBidirectionalInfoRuns.End(); + for( Vector::Iterator it = bidirectionalLineInfo.Begin(), + endIt = bidirectionalLineInfo.End(); it != endIt; ++it ) { BidirectionalLineInfoRun& bidiLineInfo = *it; free( bidiLineInfo.visualToLogicalMap ); + bidiLineInfo.visualToLogicalMap = NULL; } + + bidirectionalLineInfo.Clear(); } } // REORDER diff --git a/dali-toolkit/internal/text/text-definitions.h b/dali-toolkit/internal/text/text-definitions.h index dbb29cd..4879090 100644 --- a/dali-toolkit/internal/text/text-definitions.h +++ b/dali-toolkit/internal/text/text-definitions.h @@ -52,12 +52,13 @@ typedef TextAbstraction::LineBreakInfo LineBreakInfo; ///< Line break typedef TextAbstraction::WordBreakInfo WordBreakInfo; ///< Word break info (break, no break). Possible values are: @e WORD_BREAK and @e WORD_NO_BREAK (in the TextAbstraction namespace). typedef TextAbstraction::CharacterDirection CharacterDirection; ///< The character's direction: @e false is left to right, @e true is right to left. -typedef uint32_t GlyphIndex; ///< An index into an array of glyphs. -typedef uint32_t ScriptRunIndex; ///< An index into an array of script runs. -typedef uint32_t FontRunIndex; ///< An index into an array of font runs. -typedef uint32_t UnderlineRunIndex; ///< An index into an array of underline runs. -typedef uint32_t BidirectionalRunIndex; ///< An index into an array of font runs. -typedef uint32_t LineIndex; ///< An index into an array of lines. +typedef uint32_t GlyphIndex; ///< An index into an array of glyphs. +typedef uint32_t ScriptRunIndex; ///< An index into an array of script runs. +typedef uint32_t FontRunIndex; ///< An index into an array of font runs. +typedef uint32_t UnderlineRunIndex; ///< An index into an array of underline runs. +typedef uint32_t BidirectionalRunIndex; ///< An index into an array of bidirectional info. +typedef uint32_t BidirectionalLineRunIndex; ///< An index into an array of bidirectional line info. +typedef uint32_t LineIndex; ///< An index into an array of lines. } // namespace Text