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-view-word-processor.h>
22 #include <dali-toolkit/internal/controls/text-view/text-view-processor-helper-functions.h>
23 #include <dali-toolkit/internal/controls/text-view/text-view-processor-dbg.h>
34 namespace TextViewProcessor
40 const std::string EMOJI_FONT_NAME( "SamsungEmoji" ); // Emoticons font family name.
48 WordLayoutInfo::WordLayoutInfo()
52 mFirstCharacter( 0u ),
53 mCharactersLayoutInfo()
57 WordLayoutInfo::~WordLayoutInfo()
61 WordLayoutInfo::WordLayoutInfo( const WordLayoutInfo& word )
62 : mSize( word.mSize ),
63 mAscender( word.mAscender ),
65 mFirstCharacter( word.mFirstCharacter ),
66 mCharactersLayoutInfo( word.mCharactersLayoutInfo )
70 WordLayoutInfo& WordLayoutInfo::operator=( const WordLayoutInfo& word )
73 mAscender = word.mAscender;
75 mFirstCharacter = word.mFirstCharacter;
76 mCharactersLayoutInfo = word.mCharactersLayoutInfo;
81 void CreateWordTextInfo( const Text& paragraph,
82 Vector<TextStyle*>& textStyles,
83 WordLayoutInfo& wordLayoutInfo )
85 DALI_LOG_INFO( gTextViewProcessorLogFilter, Debug::General, "-->TextViewProcessor::CreateWordTextInfo\n" );
86 // Split in characters.
87 std::size_t characterIndex = wordLayoutInfo.mFirstCharacter;
88 for( CharacterLayoutInfoContainer::iterator it = wordLayoutInfo.mCharactersLayoutInfo.begin(),
89 endIt = wordLayoutInfo.mCharactersLayoutInfo.end();
91 ++it, ++characterIndex )
93 // Gets a reference of the character's layout info.
94 CharacterLayoutInfo& characterLayoutInfo( *it );
96 // Gets the character and the style for that character from the paragraph.
97 Character character = paragraph[characterIndex];
98 TextStyle* textStyle = *( textStyles.Begin() + characterIndex );
100 // Checks whether the character is an emoticon.
101 characterLayoutInfo.mIsColorGlyph = GlyphImage::IsColorGlyph( character );
102 DALI_LOG_INFO( gTextViewProcessorLogFilter, Debug::General, " Is color glyph: %s\n", ( characterLayoutInfo.mIsColorGlyph ? "True" : "False" ) );
104 if( characterLayoutInfo.mIsColorGlyph )
106 // If the character is an emoticon a predefined font is set.
107 textStyle->SetFontName( EMOJI_FONT_NAME );
111 // Checks if the font family and the font style set in the text style supports the character.
112 // If not, it chooses the right font for the given character and style.
113 ChooseFontFamilyName( character, *textStyle );
116 // Checks whether the charcter is right to left.
117 const Character::CharacterDirection direction = character.GetCharacterDirection();
118 characterLayoutInfo.mIsRightToLeft = ( ( direction == Character::RightToLeft ) ||
119 ( direction == Character::RightToLeftWeak ) );
121 // Gets the metrics of the font.
122 const Font font = Font::New( FontParameters( textStyle->GetFontName(), textStyle->GetFontStyle(), textStyle->GetFontPointSize() ) );
123 const Font::Metrics metrics = font.GetMetrics( character );
124 const float ascender = font.GetAscender();
126 // Fill Natural size info for current character.
128 // The font line's height is used as character's height.
129 characterLayoutInfo.mSize.height = font.GetLineHeight();
131 // The character's advance is used as charcter's width.
132 characterLayoutInfo.mSize.width = metrics.GetAdvance();
134 // The ascender and bearing are used to position correctly glyphs of different font sizes.
135 characterLayoutInfo.mAscender = ascender;
136 characterLayoutInfo.mBearing = metrics.GetBearing();
138 if( character.IsNewLine() && !characterLayoutInfo.mIsColorGlyph )
140 // A new paragraph character '\n' doesn't have any width.
141 characterLayoutInfo.mSize.width = 0.f;
144 // Set's the underline thickness and position.
145 // Both thickness and position includes the vertical pad adjust used in effects like glow or shadow.
146 if( textStyle->IsUnderlineEnabled() )
148 characterLayoutInfo.mUnderlineThickness = font.GetUnderlineThickness();
149 characterLayoutInfo.mUnderlinePosition = font.GetUnderlinePosition();
152 // Updates the word size and ascender.
153 UpdateSize( wordLayoutInfo.mSize, characterLayoutInfo.mSize );
154 wordLayoutInfo.mAscender = std::max( wordLayoutInfo.mAscender, characterLayoutInfo.mAscender );
155 } // end of characters in the word.
156 DALI_LOG_INFO( gTextViewProcessorLogFilter, Debug::General, "<--TextViewProcessor::CreateWordTextInfo\n" );
159 void UpdateLayoutInfo( WordLayoutInfo& wordLayout )
161 // Initialize layout info for the whole word.
162 wordLayout.mSize = Size::ZERO;
163 wordLayout.mAscender = 0.f;
165 // Traverse the character layout info to update the word layout.
166 for( CharacterLayoutInfoContainer::iterator layoutIt = wordLayout.mCharactersLayoutInfo.begin(), layoutEndIt = wordLayout.mCharactersLayoutInfo.end();
167 layoutIt != layoutEndIt;
170 // Layout info for the current character.
171 CharacterLayoutInfo& layoutInfo( *layoutIt );
173 // Update layout info for the current word.
174 UpdateSize( wordLayout.mSize, layoutInfo.mSize );
175 wordLayout.mAscender = std::max( wordLayout.mAscender, layoutInfo.mAscender );
179 void RemoveCharactersFromWordInfo( TextView::RelayoutData& relayoutData,
180 const std::size_t numberOfCharacters,
182 bool& mergeParagraphs,
183 TextInfoIndices& textInfoIndicesBegin,
184 TextInfoIndices& textInfoIndicesEnd,
185 TextInfoIndices& textInfoMergeIndicesBegin,
186 TextInfoIndices& textInfoMergeIndicesEnd,
187 ParagraphLayoutInfo& paragraphLayout,
188 std::vector<TextActor>& removedTextActors )
190 const TextLayoutInfo& textLayoutInfo = relayoutData.mTextLayoutInfo;
193 WordLayoutInfo& wordLayout( *( paragraphLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex ) );
195 if( ParagraphSeparator == wordLayout.mType )
197 // If the word is a paragraph separator and there is more paragraphs, then current paragraph and the paragraph after need to be merged.
198 if( textInfoIndicesBegin.mParagraphIndex + 1u < textLayoutInfo.mParagraphsLayoutInfo.size() )
200 // current paragraph is not the last one.
202 // Update indices to merge paragraphs.
203 textInfoMergeIndicesBegin.mParagraphIndex = textInfoIndicesBegin.mParagraphIndex;
204 textInfoMergeIndicesEnd.mParagraphIndex = textInfoIndicesBegin.mParagraphIndex + 1u;
206 mergeParagraphs = true;
208 ++textInfoIndicesBegin.mParagraphIndex; // increase both indices,
209 textInfoIndicesEnd.mParagraphIndex += 2u; // will delete last paragraph.
212 ++textInfoIndicesEnd.mWordIndex; //will delete the paragraph separator;
214 else if( WordSeparator == wordLayout.mType )
216 // If the word is a word separator. Check if the word before and the word after can be merged.
218 if( ( 0u < textInfoIndicesBegin.mWordIndex ) && ( paragraphLayout.mWordsLayoutInfo.size() > textInfoIndicesBegin.mWordIndex + 1u ) )
220 const WordLayoutInfo& wordLayoutBefore( *( paragraphLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex - 1u ) );
221 const WordLayoutInfo& wordLayoutAfter( *( paragraphLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex + 1u ) );
223 if( ( NoSeparator == wordLayoutBefore.mType ) && ( NoSeparator == wordLayoutAfter.mType ) )
225 // This word is a word separator (white space) and is not the first word of the paragraph nor the last one.
228 // Set indices to merge the words.
229 textInfoMergeIndicesBegin.mWordIndex = textInfoIndicesBegin.mWordIndex - 1u; // word before word separator.
230 textInfoMergeIndicesEnd.mWordIndex = textInfoIndicesBegin.mWordIndex + 1u; // word after word separator.
232 textInfoIndicesEnd.mWordIndex += 2u; // will delete the word separator and the merged word.
236 ++textInfoIndicesEnd.mWordIndex; // will delete the word separator;
241 ++textInfoIndicesEnd.mWordIndex; // will delete the word separator;
244 else if( numberOfCharacters == wordLayout.mCharactersLayoutInfo.size() )
246 // The whole word needs to be removed.
247 ++textInfoIndicesEnd.mWordIndex; // will delete the current word.
251 // Store text-actors before removing them.
252 CollectTextActors( removedTextActors, wordLayout, textInfoIndicesBegin.mCharacterIndex, textInfoIndicesBegin.mCharacterIndex + numberOfCharacters );
254 // just remove some characters from current word.
255 RemoveCharactersFromWord( textInfoIndicesBegin.mCharacterIndex,
261 void RemoveCharactersFromWord( const std::size_t position,
262 const std::size_t numberOfCharacters,
263 WordLayoutInfo& wordLayout )
265 // Removes a given number of characters from the given word starting from the 'position' index.
268 if( 0u == numberOfCharacters )
270 // nothing to do if the number of characters is zero.
275 // Remove characters from layout and text-actor info.
276 wordLayout.mCharactersLayoutInfo.erase( wordLayout.mCharactersLayoutInfo.begin() + position, wordLayout.mCharactersLayoutInfo.begin() + position + numberOfCharacters );
278 // Some characters have been removed from the word. Update the layout info is needed.
279 UpdateLayoutInfo( wordLayout );
282 void SplitWord( const std::size_t position,
283 WordLayoutInfo& firstWordLayoutInfo,
284 WordLayoutInfo& lastWordLayoutInfo )
286 // Splits a word in two.
287 // It moves characters from the first part of the word to the last one.
292 // the whole word goes to the last part of the word.
293 lastWordLayoutInfo = firstWordLayoutInfo;
295 firstWordLayoutInfo = WordLayoutInfo();
300 if( position == firstWordLayoutInfo.mCharactersLayoutInfo.size() )
302 // the whole word goes to the first part of the word.
304 // Just delete whatever there is in the last part of the word.
305 lastWordLayoutInfo = WordLayoutInfo();
310 // Initialize output data structures.
313 lastWordLayoutInfo = WordLayoutInfo();
315 // Split layout info.
317 // Insert characters from the given index 'position' to the end.
318 lastWordLayoutInfo.mCharactersLayoutInfo.insert( lastWordLayoutInfo.mCharactersLayoutInfo.end(),
319 firstWordLayoutInfo.mCharactersLayoutInfo.begin() + position, firstWordLayoutInfo.mCharactersLayoutInfo.end() );
321 // Delete characters from the first part of the word.
322 firstWordLayoutInfo.mCharactersLayoutInfo.erase( firstWordLayoutInfo.mCharactersLayoutInfo.begin() + position, firstWordLayoutInfo.mCharactersLayoutInfo.end() );
324 // Update the layout info of both new words.
325 UpdateLayoutInfo( firstWordLayoutInfo );
326 UpdateLayoutInfo( lastWordLayoutInfo );
329 void MergeWord( WordLayoutInfo& firstWordLayoutInfo,
330 const WordLayoutInfo& lastWordLayoutInfo )
332 // Merges two given words.
335 if( lastWordLayoutInfo.mCharactersLayoutInfo.empty() )
341 if( firstWordLayoutInfo.mCharactersLayoutInfo.empty() )
343 // copy last to first
345 firstWordLayoutInfo = lastWordLayoutInfo;
350 if( ( NoSeparator != firstWordLayoutInfo.mType ) || ( NoSeparator != lastWordLayoutInfo.mType ) )
352 // Do not merge white spaces or new paragraph characters.
353 DALI_ASSERT_ALWAYS( !"TextViewProcessor::MergeWord(). ERROR: White spaces or new paragraph characters can't be merged with other words." );
357 firstWordLayoutInfo.mCharactersLayoutInfo.insert( firstWordLayoutInfo.mCharactersLayoutInfo.end(),
358 lastWordLayoutInfo.mCharactersLayoutInfo.begin(),
359 lastWordLayoutInfo.mCharactersLayoutInfo.end() );
361 // Update the word layout info.
362 UpdateSize( firstWordLayoutInfo.mSize, lastWordLayoutInfo.mSize );
363 firstWordLayoutInfo.mAscender = std::max( firstWordLayoutInfo.mAscender, lastWordLayoutInfo.mAscender );
366 CharacterLayoutInfo GetFirstCharacterLayoutInfo( const WordLayoutInfo& wordLayoutInfo )
368 CharacterLayoutInfo layoutInfo;
370 if( !wordLayoutInfo.mCharactersLayoutInfo.empty() )
372 layoutInfo = *wordLayoutInfo.mCharactersLayoutInfo.begin();
378 CharacterLayoutInfo GetLastCharacterLayoutInfo( const WordLayoutInfo& wordLayoutInfo )
380 CharacterLayoutInfo layoutInfo;
382 if( !wordLayoutInfo.mCharactersLayoutInfo.empty() )
384 layoutInfo = *( wordLayoutInfo.mCharactersLayoutInfo.end() - 1u );
390 void CollectTextActors( std::vector<TextActor>& textActors, const WordLayoutInfo& word, const std::size_t characterIndexBegin, const std::size_t characterIndexEnd )
392 for( CharacterLayoutInfoContainer::const_iterator characterIt = word.mCharactersLayoutInfo.begin() + characterIndexBegin,
393 characterEndIt = word.mCharactersLayoutInfo.begin() + characterIndexEnd;
394 characterIt != characterEndIt;
397 const CharacterLayoutInfo& characterLayout( *characterIt );
399 if( !characterLayout.mIsColorGlyph )
401 TextActor textActor = TextActor::DownCast( characterLayout.mGlyphActor );
404 textActors.push_back( textActor );
410 void CollectTextActorsFromWords( std::vector<TextActor>& textActors, const ParagraphLayoutInfo& paragraph, const std::size_t wordIndexBegin, const std::size_t wordIndexEnd )
412 for( WordLayoutInfoContainer::const_iterator wordIt = paragraph.mWordsLayoutInfo.begin() + wordIndexBegin, wordEndIt = paragraph.mWordsLayoutInfo.begin() + wordIndexEnd;
416 const WordLayoutInfo& word( *wordIt );
418 for( CharacterLayoutInfoContainer::const_iterator characterIt = word.mCharactersLayoutInfo.begin(), characterEndIt = word.mCharactersLayoutInfo.end();
419 characterIt != characterEndIt;
422 const CharacterLayoutInfo& characterLayout( *characterIt );
424 if( !characterLayout.mIsColorGlyph )
426 TextActor textActor = TextActor::DownCast( characterLayout.mGlyphActor );
429 textActors.push_back( textActor );
436 } //namespace TextViewProcessor
438 } //namespace Internal
440 } //namespace Toolkit