#include <dali-toolkit/internal/text/bidirectional-support.h>
// EXTERNAL INCLUDES
-#include <memory.h>
#include <dali/devel-api/text-abstraction/bidirectional-support.h>
+#include <memory.h>
namespace Dali
{
-
namespace Toolkit
{
-
namespace Text
{
-
-namespace
-{
-
-/**
- * @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<LineRun>& lines,
- unsigned int lineIndex,
- unsigned int& firstLine,
- unsigned int& numberOfLines )
+void SetBidirectionalInfo(const Vector<Character>& text,
+ const Vector<ScriptRun>& scripts,
+ const Vector<LineBreakInfo>& lineBreakInfo,
+ CharacterIndex startIndex,
+ Length numberOfCharacters,
+ Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
+ bool matchLayoutDirection,
+ Dali::LayoutDirection::Type layoutDirection)
{
- firstLine = lineIndex;
- numberOfLines = 0u;
-
- const CharacterIndex lastCharacterIndex = paragraphInfo.characterRun.characterIndex + paragraphInfo.characterRun.numberOfCharacters;
- bool firstLineFound = false;
-
- for( Vector<LineRun>::ConstIterator it = lines.Begin() + lineIndex,
- endIt = lines.End();
- it != endIt;
- ++it )
+ // Find where to insert the new paragraphs.
+ BidirectionalRunIndex bidiInfoIndex = 0u;
+ for(Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
+ endIt = bidirectionalInfo.End();
+ it != endIt;
+ ++it)
{
- const LineRun& line = *it;
+ const BidirectionalParagraphInfoRun& run = *it;
- if( ( line.characterRun.characterIndex + line.characterRun.numberOfCharacters > paragraphInfo.characterRun.characterIndex ) &&
- ( lastCharacterIndex > line.characterRun.characterIndex ) )
- {
- firstLineFound = true;
- ++numberOfLines;
- }
- else if( lastCharacterIndex <= line.characterRun.characterIndex )
+ if(startIndex < run.characterRun.characterIndex + run.characterRun.numberOfCharacters)
{
- // nothing else to do.
+ // Found where to insert the bidi info.
break;
}
-
- if( !firstLineFound )
- {
- ++firstLine;
- }
+ ++bidiInfoIndex;
}
-}
-
-} // namespace
-void SetBidirectionalInfo( const Vector<Character>& text,
- const Vector<ScriptRun>& scripts,
- const Vector<LineBreakInfo>& lineBreakInfo,
- Vector<BidirectionalParagraphInfoRun>& 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.
// 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();
// 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();
- for( Vector<ScriptRun>::ConstIterator it = scripts.Begin(),
- endIt = scripts.End();
- it != endIt;
- ++it )
+ const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
+
+ bool hasRightToLeftScript = false;
+
+ for(Vector<ScriptRun>::ConstIterator it = scripts.Begin(),
+ endIt = scripts.End();
+ it != endIt;
+ ++it)
{
- const ScriptRun& scriptRun = *it;
- const CharacterIndex lastScriptRunIndex = scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters;
+ const ScriptRun& scriptRun = *it;
+ const CharacterIndex lastScriptRunIndex = scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters - 1u;
- if( TextAbstraction::IsRightToLeftScript( scriptRun.script ) && // The script is right to left.
- ( lastScriptRunIndex > paragraphCharacterIndex ) ) // It isn't part of a previous paragraph.
+ if(startIndex > 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 )
+ // 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 && scriptRun.isRightToLeft)
+ {
+ // The script is right to left.
+ hasRightToLeftScript = true;
+ }
+
+ if(TextAbstraction::LINE_MUST_BREAK == *(lineBreakInfoBuffer + lastScriptRunIndex))
+ {
+ // 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,
+ matchLayoutDirection,
+ layoutDirection);
+
+ bidirectionalRun.direction = bidirectionalSupport.GetParagraphDirection(bidirectionalRun.bidirectionalInfoIndex);
+
+ 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<BidirectionalParagraphInfoRun>::Iterator it = bidirectionalInfo.Begin() + bidiInfoIndex,
+ endIt = bidirectionalInfo.End();
+ it != endIt;
+ ++it)
+ {
+ BidirectionalParagraphInfoRun& run = *it;
+
+ run.characterRun.characterIndex += numberOfCharacters;
+ }
}
-void ReorderLines( const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
- Vector<LineRun>& lineRuns,
- Vector<BidirectionalLineInfoRun>& lineInfoRuns )
+void ReorderLine(const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo,
+ Vector<BidirectionalLineInfoRun>& 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();
- // 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;
+ // Creates a bidirectional info for the line run.
+ BidirectionalLineInfoRun lineInfoRun;
+ lineInfoRun.characterRun.characterIndex = startIndex;
+ lineInfoRun.characterRun.numberOfCharacters = numberOfCharacters;
+ lineInfoRun.direction = direction;
+ lineInfoRun.isIdentity = true;
- for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
- endIt = bidirectionalInfo.End();
- it != endIt;
- ++it )
- {
- const BidirectionalParagraphInfoRun& paragraphInfo = *it;
- const CharacterDirection direction = bidirectionalSupport.GetParagraphDirection( paragraphInfo.bidirectionalInfoIndex );
-
- // 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<LineRun>::Iterator lineIt = lineRuns.Begin() + firstLine,
- endLineIt = lineRuns.Begin() + firstLine + numberOfLines;
- lineIt != endLineIt;
- ++lineIt )
- {
- LineRun& line = *lineIt;
-
- // Sets the paragraph's direction.
- line.direction = direction;
+ lineInfoRun.characterRunForSecondHalfLine.characterIndex = startIndexInSecondHalfLine;
+ lineInfoRun.characterRunForSecondHalfLine.numberOfCharacters = numberOfCharactersInSecondHalfLine;
- // Creates a bidirectional info for the line run.
- BidirectionalLineInfoRun lineInfoRun;
- lineInfoRun.characterRun.characterIndex = line.characterRun.characterIndex;
- lineInfoRun.characterRun.numberOfCharacters = line.characterRun.numberOfCharacters;
- lineInfoRun.direction = direction;
+ // 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<CharacterIndex*>(malloc(numberOfCharacters * sizeof(CharacterIndex)));
- // 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<CharacterIndex*>( malloc( line.characterRun.numberOfCharacters * sizeof( CharacterIndex ) ) );
+ lineInfoRun.visualToLogicalMapSecondHalf = reinterpret_cast<CharacterIndex*>(malloc(numberOfCharactersInSecondHalfLine * sizeof(CharacterIndex)));
- // Reorders the line.
- bidirectionalSupport.Reorder( paragraphInfo.bidirectionalInfoIndex,
- line.characterRun.characterIndex - paragraphInfo.characterRun.characterIndex,
- line.characterRun.numberOfCharacters,
- lineInfoRun.visualToLogicalMap );
+ if(nullptr != lineInfoRun.visualToLogicalMap && nullptr != lineInfoRun.visualToLogicalMapSecondHalf)
+ {
+ // 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;
+ }
+ }
- // Push the run into the vector.
- lineInfoRuns.PushBack( lineInfoRun );
+ for(unsigned int i = 0; i < numberOfCharactersInSecondHalfLine; ++i)
+ {
+ if(i != *(lineInfoRun.visualToLogicalMapSecondHalf + i))
+ {
+ lineInfoRun.isIdentity = false;
+ break;
+ }
}
}
+
+ // Push the run into the vector.
+ lineInfoRuns.Insert(lineInfoRuns.Begin() + bidiLineIndex, lineInfoRun);
}
-bool GetMirroredText( const Vector<Character>& text,
- Vector<Character>& mirroredText )
+bool GetMirroredText(const Vector<Character>& text,
+ const Vector<CharacterDirection>& directions,
+ const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
+ CharacterIndex startIndex,
+ Length numberOfCharacters,
+ Vector<Character>& mirroredText)
{
+ bool hasTextMirrored = false;
+
// Handle to the bidirectional info module in text-abstraction.
TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
mirroredText = text;
- return bidirectionalSupport.GetMirroredText( mirroredText.Begin(),
- mirroredText.Count() );
+ 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<BidirectionalParagraphInfoRun>::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 GetCharactersDirection( const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
- Vector<CharacterDirection>& directions )
+void GetCharactersDirection(const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
+ Length totalNumberOfCharacters,
+ CharacterIndex startIndex,
+ Length numberOfCharacters,
+ Vector<CharacterDirection>& directions)
{
// Handle to the bidirectional info module in text-abstraction.
TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
- CharacterIndex index = 0u;
- CharacterDirection* directionsBuffer = directions.Begin();
- for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
- endIt = bidirectionalInfo.End();
- it != endIt;
- ++it )
+ // 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<CharacterDirection> newDirections;
+
+ if(updateCurrentBuffer)
+ {
+ newDirections.Resize(numberOfCharacters);
+ directionsBuffer = newDirections.Begin();
+ }
+ else
+ {
+ directionsBuffer = directions.Begin();
+ }
+
+ const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
+ CharacterIndex index = startIndex;
+
+ for(Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
+ endIt = bidirectionalInfo.End();
+ it != endIt;
+ ++it)
{
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;
+ }
- bidirectionalSupport.GetCharactersDirection( paragraph.bidirectionalInfoIndex,
- directionsBuffer + paragraph.characterRun.characterIndex,
- paragraph.characterRun.numberOfCharacters );
+ 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 - 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