2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/controls/text-view/text-processor.h>
22 #include <fribidi/fribidi.h>
33 namespace TextProcessor
36 void SplitInParagraphs( const MarkupProcessor::StyledTextArray& text,
37 std::vector<MarkupProcessor::StyledTextArray>& paragraphs )
39 MarkupProcessor::StyledTextArray paragraph;
40 for( MarkupProcessor::StyledTextArray::const_iterator it = text.begin(), endIt = text.end(); it != endIt; ++it )
42 const MarkupProcessor::StyledText& styledText( *it );
44 for( size_t i = 0u, length = styledText.mText.GetLength(); i < length; ++i )
46 const Dali::Character character = styledText.mText[i];
48 if( character.IsNewLine() ) // LF
50 Text newText( character );
51 MarkupProcessor::StyledText newStyledText( newText, styledText.mStyle );
52 paragraph.push_back( newStyledText );
54 paragraphs.push_back( paragraph );
59 Text newText( character );
60 MarkupProcessor::StyledText newStyledText( newText, styledText.mStyle );
61 paragraph.push_back( newStyledText );
66 // This paragraph could be empty if the last character of the previous paragraph is a 'new paragraph' character
67 // and is the last of the text.
68 paragraphs.push_back( paragraph );
71 void SplitInWords( const MarkupProcessor::StyledTextArray& paragraph,
72 std::vector<MarkupProcessor::StyledTextArray>& words )
74 MarkupProcessor::StyledTextArray word;
75 for( MarkupProcessor::StyledTextArray::const_iterator it = paragraph.begin(), endIt = paragraph.end(); it != endIt; ++it )
77 const MarkupProcessor::StyledText& styledText( *it );
78 const Dali::Character character = styledText.mText[0u];
80 if( character.IsWhiteSpace() )
82 // When a separator is found, the previous word is added to the list,
83 // then a new word is initialized and the separator is also added as a word.
86 words.push_back( word );
87 word.clear(); // initializes a new word.
90 // Separator added as a word.
91 MarkupProcessor::StyledText separatorChar;
92 separatorChar.mText.Append( character );
93 separatorChar.mStyle = styledText.mStyle;
95 MarkupProcessor::StyledTextArray separatorWord;
96 separatorWord.push_back( separatorChar );
98 words.push_back( separatorWord );
102 MarkupProcessor::StyledText styledChar;
103 styledChar.mStyle = styledText.mStyle;
104 styledChar.mText.Append( character );
106 // Add the character to the current word.
107 word.push_back( styledChar );
111 //Finally the last word need to be added.
114 words.push_back( word );
118 bool BeginsRightToLeftCharacter( const MarkupProcessor::StyledTextArray& styledText )
120 for( MarkupProcessor::StyledTextArray::const_iterator it = styledText.begin(), endIt = styledText.end(); it != endIt; ++it )
122 const Text& text( (*it).mText );
124 for( size_t i = 0u, length = text.GetLength(); i < length; ++i )
126 Character::CharacterDirection direction = text[i].GetCharacterDirection();
127 if( direction != Character::Neutral )
129 return ( direction == Character::RightToLeft || direction == Character::RightToLeftWeak );
137 bool BeginsRightToLeftCharacter( const Text& text )
139 for( size_t i = 0u, length = text.GetLength(); i < length; ++i )
141 Character::CharacterDirection direction = text[i].GetCharacterDirection();
142 if( direction != Character::Neutral )
144 return ( direction == Character::RightToLeft || direction == Character::RightToLeftWeak );
151 bool ContainsRightToLeftCharacter( const MarkupProcessor::StyledTextArray& styledText )
153 for( MarkupProcessor::StyledTextArray::const_iterator it = styledText.begin(), endIt = styledText.end(); it != endIt; ++it )
155 const Text& text( (*it).mText );
157 for( size_t i = 0u, length = text.GetLength(); i < length; ++i )
159 Character::CharacterDirection direction = text[i].GetCharacterDirection();
160 if( ( Character::RightToLeft == direction ) || ( Character::RightToLeftWeak == direction ) )
170 bool ContainsRightToLeftCharacter( const Dali::Text& text )
172 for( size_t i = 0u, length = text.GetLength(); i < length; ++i )
174 Character::CharacterDirection direction = ( text[i] ).GetCharacterDirection();
175 if( ( Character::RightToLeft == direction ) || ( Character::RightToLeftWeak == direction ) )
184 void ConvertBidirectionalText( const MarkupProcessor::StyledTextArray& line,
185 MarkupProcessor::StyledTextArray& convertedText,
186 std::vector<int>& logicalToVisualMap,
187 std::vector<int>& visualToLogicalMap )
189 // Clean vectors first. This function doesn't use any previous value.
190 logicalToVisualMap.clear();
191 visualToLogicalMap.clear();
192 convertedText.clear();
196 // nothing to do if the line is empty.
200 // Get the plain text from the line to be reordered by the BiDirectional algorithm.
201 std::string textToBeConverted;
202 GetPlainString( line, textToBeConverted );
204 const std::size_t stringSize = textToBeConverted.size();
206 std::vector<FriBidiChar> logicalStrBuffer;
207 std::vector<FriBidiChar> visualStrBuffer;
208 // unicode length <= UTF-8 length in bytes (reserve one extra for terminator)
209 // pad these buffers with 0's, as it's unclear what fribidi_log2vis does w.r.t.
210 // the length of it's output content (appears the same as input content, and does
211 // not seem to generate bidi marks i.e. FRIBIDI_CHAR_LRM/FRIBIDI_CHAR_RLM)
212 logicalStrBuffer.resize( stringSize+1u, 0u );
213 visualStrBuffer.resize( stringSize+1u, 0u );
214 FriBidiChar *logicalStr( &logicalStrBuffer[0u] );
215 FriBidiChar *visualStr( &visualStrBuffer[0u] );
217 // Convert UTF-8 string to unicode string
218 const std::size_t length = fribidi_charset_to_unicode( FRIBIDI_CHAR_SET_UTF8, textToBeConverted.c_str(), stringSize, logicalStr );
222 DALI_ASSERT_DEBUG( !"TextProcessor::ConvertBidirectionalText. Error when calling at fribidi_charset_to_unicode" );
227 logicalToVisualMap.resize( length );
228 visualToLogicalMap.resize( length );
230 // Convert and reorder the string as specified by the Unicode Bidirectional Algorithm
231 FriBidiCharType baseDirection = FRIBIDI_TYPE_ON;
232 fribidi_boolean log2vis = fribidi_log2vis( logicalStr, length, &baseDirection, visualStr, &logicalToVisualMap[0u], &visualToLogicalMap[0u], NULL );
236 // Convert the unicode string back to the UTF-8 string
237 std::vector<char> bidiTextConverted;
239 bidiTextConverted.resize( length * 4u + 1u ); // Maximum bytes to represent one UTF-8 character is 6.
240 // Currently Dali doesn't support this UTF-8 extension. Dali only supports 'regular' UTF-8 which has a maximum of 4 bytes per character.
242 fribidi_unicode_to_charset( FRIBIDI_CHAR_SET_UTF8, visualStr, length, &bidiTextConverted[0u] );
244 textToBeConverted = &bidiTextConverted[0u];
246 // After reorder the text, rebuild the text with the original styles is needed.
247 // To assign the original style is needed to use the characterLogicalToVisualMap table.
248 Text text( &bidiTextConverted[0u] );
250 // Split the line in words.
251 // Add the correct styles for the characters after they are reordered.
253 for( size_t i = 0u; i < length; ++i )
255 const Character character( text[i] );
257 MarkupProcessor::StyledText styledText;
258 styledText.mText.Append( character );
259 styledText.mStyle = line[visualToLogicalMap[i]].mStyle;
261 convertedText.push_back( styledText );
267 * Wheather the character of the text pointed by the given offset is a white space.
269 * @param[in] text The text.
270 * @param[in] offset Offset pointing the character.
272 * @return \e true if the character pointed by the offset is a white space.
274 bool IsWhiteSpace( const MarkupProcessor::StyledTextArray& text, size_t offset )
276 DALI_ASSERT_DEBUG( offset < text.size() );
278 // assume 1 Character per StyledText
279 return text[offset].mText[0u].IsWhiteSpace();
282 void FindNearestWord( const MarkupProcessor::StyledTextArray& text, size_t offset, size_t& start, size_t& end)
284 const size_t size(text.size());
285 offset = std::min(offset, size-1u);
290 // if currently looking at whitespace, then search left and right for non-whitespace.
291 if(IsWhiteSpace(text, offset))
293 // scan left until non-white space / beginning of string.
294 while(i > 0u && IsWhiteSpace(text, i))
299 // scan right until non-white space / end of string.
300 while(j < size && IsWhiteSpace(text, j))
306 // check if r.h.s. word is closer than l.h.s. word
307 if( (j - offset) < // distance to closest right word <
308 (offset - i) ) // distance to closest left word
310 // point left and right markers on start of right word
315 // point left and right markers on end of left word
319 // expand left and right markers to encompase entire word
320 while(i > 0u && !IsWhiteSpace(text, i-1u))
325 while(j < size && !IsWhiteSpace(text, j))
330 // If both markers at same position and is whitespace then word is a whitespace word
333 while(j < size && IsWhiteSpace(text, j))
343 } // namespace TextProcessor
345 } // namespace Internal
347 } // namespace DaliToolkit