X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=blobdiff_plain;f=dali-toolkit%2Finternal%2Ftext%2Fbidirectional-support.cpp;h=9bf27f8bc68aef52372f35343ca52e823853bd43;hp=14fb1a754544e4cba684d4499a4ecc6dc2bfb583;hb=750fadba87bb959c9af32e89e3f1bc7af6cb6dd2;hpb=fc2392c09ab526295f52c430ead7879e1e91a096 diff --git a/dali-toolkit/internal/text/bidirectional-support.cpp b/dali-toolkit/internal/text/bidirectional-support.cpp index 14fb1a7..9bf27f8 100644 --- a/dali-toolkit/internal/text/bidirectional-support.cpp +++ b/dali-toolkit/internal/text/bidirectional-support.cpp @@ -19,221 +19,324 @@ #include // EXTERNAL INCLUDES -#include +#include +#include namespace Dali { - namespace Toolkit { - namespace Text { - -namespace +void SetBidirectionalInfo(const Vector& text, + const Vector& scripts, + const Vector& lineBreakInfo, + CharacterIndex startIndex, + Length numberOfCharacters, + Vector& bidirectionalInfo, + bool matchLayoutDirection, + Dali::LayoutDirection::Type layoutDirection) { + // 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; -/** - * @brief Get the lines of a paragraph. - * - * @param[in] paragraphInfo The paragraph. - * @param[in] lines The lines. - * @param[in] lineIndex Index pointing the first line to be checked. - * @param[out] firstLine Index to the first line of the paragraph. - * @param[out] numberOfLines The number of lines. - */ -void GetLines( const BidirectionalParagraphInfoRun& paragraphInfo, - const Vector& lines, - unsigned int lineIndex, - unsigned int& firstLine, - unsigned int& numberOfLines ) -{ - firstLine = lineIndex; - numberOfLines = 0u; + 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 = startIndex; + + // Pointer to the text buffer. + const Character* textBuffer = text.Begin(); + + // Pointer to the line break info buffer. + const LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin(); + + // Handle to the bidirectional info module in text-abstraction. + TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get(); - const CharacterIndex lastCharacterIndex = paragraphInfo.characterRun.characterIndex + paragraphInfo.characterRun.numberOfCharacters; - bool firstLineFound = false; + const CharacterIndex lastCharacter = startIndex + numberOfCharacters; - for( Vector::ConstIterator it = lines.Begin() + lineIndex, - endIt = lines.End(); - it != endIt; - ++it ) + bool hasRightToLeftScript = false; + + for(Vector::ConstIterator it = scripts.Begin(), + endIt = scripts.End(); + it != endIt; + ++it) { - const LineRun& line = *it; + const ScriptRun& scriptRun = *it; + const CharacterIndex lastScriptRunIndex = scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters - 1u; - if( ( line.characterRun.characterIndex + line.characterRun.numberOfCharacters > paragraphInfo.characterRun.characterIndex ) && - ( lastCharacterIndex > line.characterRun.characterIndex ) ) + if(startIndex > lastScriptRunIndex) { - firstLineFound = true; - ++numberOfLines; + // Skip the run as it has already been processed. + continue; } - else if( lastCharacterIndex <= line.characterRun.characterIndex ) + + if(lastCharacter <= scriptRun.characterRun.characterIndex) { - // nothing else to do. + // Do not get bidirectional info beyond startIndex + numberOfCharacters. break; } - if( !firstLineFound ) + if(!hasRightToLeftScript && scriptRun.isRightToLeft) { - ++firstLine; + // The script is right to left. + hasRightToLeftScript = true; } - } -} -} // namespace + if(TextAbstraction::LINE_MUST_BREAK == *(lineBreakInfoBuffer + lastScriptRunIndex)) + { + // A new paragraph has been found. -void SetBidirectionalInfo( const Vector& text, - const Vector& scripts, - const Vector& lineBreakInfo, - Vector& bidirectionalInfo ) -{ - // 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. + if(hasRightToLeftScript) + { + // 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. - // Index pointing the first character of the current paragraph. - CharacterIndex paragraphCharacterIndex = 0u; + // 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, + matchLayoutDirection, + layoutDirection); - // Pointer to the text buffer. - const Character* textBuffer = text.Begin(); + bidirectionalRun.direction = bidirectionalSupport.GetParagraphDirection(bidirectionalRun.bidirectionalInfoIndex); - // Pointer to the line break info buffer. - const LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin(); + 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; - // The number of characters. - const Length numberOfCharacters = text.Count(); + run.characterRun.characterIndex += numberOfCharacters; + } +} +void ReorderLine(const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo, + Vector& lineInfoRuns, + BidirectionalLineRunIndex bidiLineIndex, + CharacterIndex startIndex, + Length numberOfCharacters, + CharacterIndex startIndexInSecondHalfLine, + Length numberOfCharactersInSecondHalfLine, + CharacterDirection direction) +{ // Handle to the bidirectional info module in text-abstraction. TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get(); - for( Vector::ConstIterator it = scripts.Begin(), - endIt = scripts.End(); - it != endIt; - ++it ) + // Creates a bidirectional info for the line run. + BidirectionalLineInfoRun lineInfoRun; + lineInfoRun.characterRun.characterIndex = startIndex; + lineInfoRun.characterRun.numberOfCharacters = numberOfCharacters; + lineInfoRun.direction = direction; + lineInfoRun.isIdentity = true; + + lineInfoRun.characterRunForSecondHalfLine.characterIndex = startIndexInSecondHalfLine; + lineInfoRun.characterRunForSecondHalfLine.numberOfCharacters = numberOfCharactersInSecondHalfLine; + + // Allocate space for the conversion maps. + // The memory is freed after the visual to logical to visual conversion tables are built in the logical model. + lineInfoRun.visualToLogicalMap = reinterpret_cast(malloc(numberOfCharacters * sizeof(CharacterIndex))); + + lineInfoRun.visualToLogicalMapSecondHalf = reinterpret_cast(malloc(numberOfCharactersInSecondHalfLine * sizeof(CharacterIndex))); + + if(nullptr != lineInfoRun.visualToLogicalMap && nullptr != lineInfoRun.visualToLogicalMapSecondHalf) { - const ScriptRun& scriptRun = *it; - const CharacterIndex lastScriptRunIndex = scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters; + // Reorders the line. + bidirectionalSupport.Reorder(bidirectionalParagraphInfo.bidirectionalInfoIndex, + lineInfoRun.characterRun.characterIndex - bidirectionalParagraphInfo.characterRun.characterIndex, + lineInfoRun.characterRun.numberOfCharacters, + lineInfoRun.visualToLogicalMap); + + bidirectionalSupport.Reorder(bidirectionalParagraphInfo.bidirectionalInfoIndex, + lineInfoRun.characterRunForSecondHalfLine.characterIndex - bidirectionalParagraphInfo.characterRun.characterIndex, + lineInfoRun.characterRunForSecondHalfLine.numberOfCharacters, + lineInfoRun.visualToLogicalMapSecondHalf); + + // For those LTR lines inside a bidirectional paragraph. + // It will save to relayout the line after reordering. + for(unsigned int i = 0; i < numberOfCharacters; ++i) + { + if(i != *(lineInfoRun.visualToLogicalMap + i)) + { + lineInfoRun.isIdentity = false; + break; + } + } - if( TextAbstraction::IsRightToLeftScript( scriptRun.script ) && // The script is right to left. - ( lastScriptRunIndex > paragraphCharacterIndex ) ) // It isn't part of a previous paragraph. + for(unsigned int i = 0; i < numberOfCharactersInSecondHalfLine; ++i) { - // 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 ) + if(i != *(lineInfoRun.visualToLogicalMapSecondHalf + i)) { - // 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. + lineInfoRun.isIdentity = false; + break; } } } + + // Push the run into the vector. + lineInfoRuns.Insert(lineInfoRuns.Begin() + bidiLineIndex, lineInfoRun); } -void ReplaceBidirectionalInfo( LogicalModel& model, - CharacterIndex characterIndex, - Length numberOfCharactersToRemove, - Length numberOfCharactersToInsert ) +bool GetMirroredText(const Vector& text, + const Vector& directions, + const Vector& bidirectionalInfo, + CharacterIndex startIndex, + Length numberOfCharacters, + Vector& mirroredText) { + bool hasTextMirrored = false; + + // Handle to the bidirectional info module in text-abstraction. + TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get(); + + mirroredText = 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(); + it != endIt; + ++it) + { + 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); + + hasTextMirrored = hasTextMirrored || tmpMirrored; + } + + return hasTextMirrored; } -void ReorderLines( const Vector& bidirectionalInfo, - const Vector& lineRuns, - Vector& lineInfoRuns ) +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(); - // 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; + // Resize the vector. + directions.Resize(totalNumberOfCharacters); - for( Vector::ConstIterator it = bidirectionalInfo.Begin(), - endIt = bidirectionalInfo.End(); - it != endIt; - ++it ) + // Whether the current buffer is being updated or is set from scratch. + const bool updateCurrentBuffer = numberOfCharacters < totalNumberOfCharacters; + + CharacterDirection* directionsBuffer = NULL; + Vector newDirections; + + if(updateCurrentBuffer) { - const BidirectionalParagraphInfoRun& paragraphInfo = *it; - - // Get the lines for this paragraph. - unsigned int firstLine = 0u; - unsigned int numberOfLines = 0u; - - // Get an index to the first line and the number of lines of the current paragraph. - GetLines( paragraphInfo, - lineRuns, - lineIndex, - firstLine, - numberOfLines ); - - lineIndex = firstLine + numberOfLines; - - // Traverse the lines and reorder them - for( Vector::ConstIterator lineIt = lineRuns.Begin() + firstLine, - endLineIt = lineRuns.Begin() + firstLine + numberOfLines; - lineIt != endLineIt; - ++lineIt ) - { - const LineRun& line = *lineIt; + newDirections.Resize(numberOfCharacters); + directionsBuffer = newDirections.Begin(); + } + else + { + directionsBuffer = directions.Begin(); + } - // Creates a bidirectional info for the line run. - BidirectionalLineInfoRun lineInfoRun; - lineInfoRun.characterRun.characterIndex = line.characterRun.characterIndex; - lineInfoRun.characterRun.numberOfCharacters = line.characterRun.numberOfCharacters; + const CharacterIndex lastCharacter = startIndex + numberOfCharacters; + CharacterIndex index = startIndex; - // Allocate space for the conversion maps. - // The memory is freed after the visual to logical to visual conversion tables are built in the logical model. - lineInfoRun.visualToLogicalMap = reinterpret_cast( malloc( line.characterRun.numberOfCharacters * sizeof( CharacterIndex ) ) ); + for(Vector::ConstIterator it = bidirectionalInfo.Begin(), + endIt = bidirectionalInfo.End(); + it != endIt; + ++it) + { + const BidirectionalParagraphInfoRun& paragraph = *it; + + if(index >= paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters) + { + // Skip the paragraph as it has already been processed. + continue; + } - // Reorders the line. - bidirectionalSupport.Reorder( paragraphInfo.bidirectionalInfoIndex, - line.characterRun.characterIndex, - line.characterRun.numberOfCharacters, - lineInfoRun.visualToLogicalMap ); + if(lastCharacter <= paragraph.characterRun.characterIndex) + { + // Do not get the character directions beyond startIndex + numberOfCharacters. + break; + } - // Push the run into the vector. - lineInfoRuns.PushBack( lineInfoRun ); + // 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 - startIndex, + paragraph.characterRun.numberOfCharacters); + + // Update the index. + index += paragraph.characterRun.numberOfCharacters + numberOfLeftToRightCharacters; } -} -void ReorderLines( LogicalModel& logicalModel, - const VisualModel& visualModel, - CharacterIndex characterIndex, - Length numberOfCharactersToRemove, - Length numberOfCharactersToInsert ) -{ + // Fills with left to right those paragraphs without right to left characters. + 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