2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 // Licensed under the Flora License, Version 1.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://floralicense.org/license/
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.
18 #include "text-view-line-processor.h"
19 #include "text-view-word-group-processor.h"
20 #include "text-view-word-processor.h"
21 #include "text-view-processor-helper-functions.h"
22 #include "text-processor.h"
33 namespace TextViewProcessor
40 LineLayoutInfo::LineLayoutInfo()
43 mLineHeightOffset( 0.f ),
44 mWordGroupsLayoutInfo(),
45 mNumberOfCharacters( 0 )
49 LineLayoutInfo::LineLayoutInfo( const LineLayoutInfo& line )
50 : mSize( line.mSize ),
51 mAscender( line.mAscender ),
52 mLineHeightOffset( line.mLineHeightOffset ),
53 mWordGroupsLayoutInfo( line.mWordGroupsLayoutInfo ),
54 mNumberOfCharacters( line.mNumberOfCharacters )
58 LineLayoutInfo& LineLayoutInfo::operator=( const LineLayoutInfo& line )
61 mAscender = line.mAscender;
62 mLineHeightOffset = line.mLineHeightOffset;
63 mWordGroupsLayoutInfo = line.mWordGroupsLayoutInfo;
64 mNumberOfCharacters = line.mNumberOfCharacters;
69 void UpdateLineLayoutInfo( TextViewProcessor::LineLayoutInfo& lineLayoutInfo, const float lineHeightOffset )
71 lineLayoutInfo.mSize = Size();
73 for( WordGroupLayoutInfoContainer::iterator it = lineLayoutInfo.mWordGroupsLayoutInfo.begin(), endIt = lineLayoutInfo.mWordGroupsLayoutInfo.end();
77 WordGroupLayoutInfo& layoutInfo( *it );
79 UpdateSize( lineLayoutInfo.mSize, layoutInfo.mSize );
81 lineLayoutInfo.mSize.height += lineHeightOffset;
84 void CreateLineInfo( const MarkupProcessor::StyledTextArray& line,
85 TextView::RelayoutData& relayoutData,
86 TextViewProcessor::LineLayoutInfo& lineLayoutInfo )
88 // TODO: Split the line in group of words. Each group of words has only left to right characters or right to left characters but not a mix of both.
89 // TODO: set the wordgroup direction (LTR or RTL)
90 std::vector<MarkupProcessor::StyledTextArray> wordGroups;
91 if( TextProcessor::ContainsRightToLeftCharacter( line ) )
93 // If the text is bidirectional, the characters will be converted and reordered
94 // as specified by the Unicode Bidirectional Algorithm.
96 // Reorders the line and converts arabic glyphs (if any).
97 // It also split words in different groups if there are a mix of left to right
98 // and right to left text.
99 // If the whole line is left to right or right to left all words are grouped in the same group.
100 TextProcessor::ConvertBidirectionalText( line,
102 relayoutData.mCharacterLogicalToVisualMap,
103 relayoutData.mCharacterVisualToLogicalMap);
107 // No bidirectional text to process.
111 // Add all words in a group.
112 wordGroups.push_back( line );
114 // Create trivial bidirectional map tables.
115 std::size_t index = 0;
116 for( MarkupProcessor::StyledTextArray::const_iterator it = line.begin(), endIt = line.end(); it != endIt; ++it )
118 const MarkupProcessor::StyledText& styledText( *it );
120 for( std::size_t i = 0, length = styledText.mText.GetLength(); i < length; ++i )
122 relayoutData.mCharacterLogicalToVisualMap.push_back( relayoutData.mTextLayoutInfo.mNumberOfCharacters + index );
123 relayoutData.mCharacterVisualToLogicalMap.push_back( relayoutData.mTextLayoutInfo.mNumberOfCharacters + index );
130 // Traverse all group of words.
131 for( std::vector<MarkupProcessor::StyledTextArray>::const_iterator groupIt = wordGroups.begin(), groupEndIt = wordGroups.end(); groupIt != groupEndIt; ++groupIt )
133 const MarkupProcessor::StyledTextArray& wordGroup( *groupIt );
135 // Data structures for the new group of words.
136 WordGroupLayoutInfo wordGroupLayoutInfo;
138 CreateWordGroupInfo( wordGroup,
139 relayoutData.mTextLayoutInfo,
140 wordGroupLayoutInfo );
142 // Update layout info for the current line.
143 lineLayoutInfo.mAscender = std::max( lineLayoutInfo.mAscender, wordGroupLayoutInfo.mAscender );
144 lineLayoutInfo.mNumberOfCharacters += wordGroupLayoutInfo.mNumberOfCharacters;
145 UpdateSize( lineLayoutInfo.mSize, wordGroupLayoutInfo.mSize );
147 // Add the group of words to the current line.
148 lineLayoutInfo.mWordGroupsLayoutInfo.push_back( wordGroupLayoutInfo );
149 } // end of group of words
152 void RemoveWordGroupsFromLine( const std::size_t groupIndex,
153 const std::size_t numberOfGroups,
154 const PointSize& lineHeightOffset,
155 LineLayoutInfo& lineLayout )
157 // Removes groups of words from a line.
159 // * Check if words or lines can be merged after removing a group of words or a line separator have to be done outside this method.
161 // * Note: Currently it's only used to remove a number of groups of words from the beginning, or
162 // from groupIndex index to the end. This function doesn't merge groups of words (if a whole group is removed)
163 // TODO: merge groups of words if required.
165 const std::size_t groupEndIndex = groupIndex + numberOfGroups;
167 // Remove word groups from layout info.
168 lineLayout.mWordGroupsLayoutInfo.erase( lineLayout.mWordGroupsLayoutInfo.begin() + groupIndex,
169 lineLayout.mWordGroupsLayoutInfo.begin() + groupEndIndex );
171 // Update layout info.
172 lineLayout.mSize = Size();
173 lineLayout.mAscender = 0.f;
174 lineLayout.mNumberOfCharacters = 0;
175 for( WordGroupLayoutInfoContainer::const_iterator it = lineLayout.mWordGroupsLayoutInfo.begin(), endIt = lineLayout.mWordGroupsLayoutInfo.end();
179 const WordGroupLayoutInfo& group( *it );
181 UpdateSize( lineLayout.mSize, group.mSize );
182 lineLayout.mAscender = std::max( lineLayout.mAscender, group.mAscender );
183 lineLayout.mNumberOfCharacters += group.mNumberOfCharacters;
185 lineLayout.mSize.height += lineHeightOffset;
186 lineLayout.mLineHeightOffset = lineHeightOffset;
189 void SplitLine( const TextInfoIndices& indices,
190 const PointSize& lineHeightOffset,
191 LineLayoutInfo& firstLineLayoutInfo,
192 LineLayoutInfo& lastLineLayoutInfo )
194 // Splits a line in two.
195 // A group of words and a word may be split in two as well.
197 // * Split the group of words within the line.
198 // * Add last part of the group of words to the new line.
199 // * Add groups of words from groupPosition + 1 to the end.
200 // * Update layout info of the last line.
201 // * Remove groups of words added to the last part of the line from the first line.
204 if( ( 0 == indices.mGroupIndex ) && ( 0 == indices.mWordIndex ) && ( 0 == indices.mCharacterIndex ) )
206 // the whole line goes to the last part.
207 lastLineLayoutInfo = firstLineLayoutInfo;
209 firstLineLayoutInfo = LineLayoutInfo();
214 if( !firstLineLayoutInfo.mWordGroupsLayoutInfo.empty() )
216 const std::size_t numberOfGroups = firstLineLayoutInfo.mWordGroupsLayoutInfo.size();
217 if( indices.mGroupIndex == numberOfGroups - 1 )
219 const WordGroupLayoutInfo& group( *( firstLineLayoutInfo.mWordGroupsLayoutInfo.end() - 1 ) );
221 const std::size_t numberOfWords = group.mWordsLayoutInfo.size();
222 if( indices.mWordIndex == numberOfWords - 1 )
224 const WordLayoutInfo& word( *( group.mWordsLayoutInfo.end() - 1 ) );
225 if( indices.mCharacterIndex == word.mCharactersLayoutInfo.size() )
227 // the whole line goes to the first part.
229 // Just delete whatever there is in the last part of the line.
230 lastLineLayoutInfo = LineLayoutInfo();
238 lastLineLayoutInfo = LineLayoutInfo();
240 // 1) Split the group of words whitin the line.
241 WordGroupLayoutInfo& firstWordGroupLayoutInfo( *( firstLineLayoutInfo.mWordGroupsLayoutInfo.begin() + indices.mGroupIndex ) );
242 WordGroupLayoutInfo lastWordGroupLayoutInfo;
244 SplitWordGroup( indices,
245 firstWordGroupLayoutInfo,
246 lastWordGroupLayoutInfo );
248 // 2) Add last part of the group of words to the new line.
249 if( !lastWordGroupLayoutInfo.mWordsLayoutInfo.empty() )
251 lastLineLayoutInfo.mWordGroupsLayoutInfo.push_back( lastWordGroupLayoutInfo );
254 // 3) Add groups from group-position + 1 to the end.
255 lastLineLayoutInfo.mWordGroupsLayoutInfo.insert( lastLineLayoutInfo.mWordGroupsLayoutInfo.end(),
256 firstLineLayoutInfo.mWordGroupsLayoutInfo.begin() + indices.mGroupIndex + 1, firstLineLayoutInfo.mWordGroupsLayoutInfo.end() );
258 // 4) update layout info of the last line.
259 for( WordGroupLayoutInfoContainer::iterator it = lastLineLayoutInfo.mWordGroupsLayoutInfo.begin(), endIt = lastLineLayoutInfo.mWordGroupsLayoutInfo.end();
263 WordGroupLayoutInfo& layoutInfo( *it );
265 lastLineLayoutInfo.mNumberOfCharacters += layoutInfo.mNumberOfCharacters;
266 UpdateSize( lastLineLayoutInfo.mSize, layoutInfo.mSize );
267 lastLineLayoutInfo.mAscender = std::max( lastLineLayoutInfo.mAscender, layoutInfo.mAscender );
269 lastLineLayoutInfo.mSize.height += lineHeightOffset;
270 lastLineLayoutInfo.mLineHeightOffset = lineHeightOffset;
272 // 5) Remove groups of words added to the last part of the line from the first line.
274 // if the number of characters of the last group of words of the first line is zero, it should be removed.
275 const std::size_t index = ( 0 == firstWordGroupLayoutInfo.mNumberOfCharacters ? indices.mGroupIndex : indices.mGroupIndex + 1 );
277 firstLineLayoutInfo.mWordGroupsLayoutInfo.erase( firstLineLayoutInfo.mWordGroupsLayoutInfo.begin() + index, firstLineLayoutInfo.mWordGroupsLayoutInfo.end() );
279 // 6) update layout info of the first line.
280 firstLineLayoutInfo.mNumberOfCharacters = 0;
281 firstLineLayoutInfo.mSize = Size();
282 firstLineLayoutInfo.mAscender = 0.f;
283 for( WordGroupLayoutInfoContainer::iterator it = firstLineLayoutInfo.mWordGroupsLayoutInfo.begin(), endIt = firstLineLayoutInfo.mWordGroupsLayoutInfo.end();
287 WordGroupLayoutInfo& layoutInfo( *it );
289 firstLineLayoutInfo.mNumberOfCharacters += layoutInfo.mNumberOfCharacters;
290 UpdateSize( firstLineLayoutInfo.mSize, layoutInfo.mSize );
291 firstLineLayoutInfo.mAscender = std::max( firstLineLayoutInfo.mAscender, layoutInfo.mAscender );
293 firstLineLayoutInfo.mSize.height += lineHeightOffset;
294 firstLineLayoutInfo.mLineHeightOffset = lineHeightOffset;
297 void MergeLine( LineLayoutInfo& firstLineLineLayoutInfo,
298 const LineLayoutInfo& lastLineLayoutInfo )
300 // Merges two given lines.
302 // Can't merge two lines if the last word of the first one is a line separator (new line character)
306 if( lastLineLayoutInfo.mWordGroupsLayoutInfo.empty() )
308 // Nothing to merge if last line is empty.
312 if( firstLineLineLayoutInfo.mWordGroupsLayoutInfo.empty() )
314 // If first line is empty, just copy the last line to the first one.
315 firstLineLineLayoutInfo = lastLineLayoutInfo;
320 if( 1 == firstLineLineLayoutInfo.mWordGroupsLayoutInfo.size() )
322 WordGroupLayoutInfo& wordGroupLayout( *firstLineLineLayoutInfo.mWordGroupsLayoutInfo.begin() );
324 if( wordGroupLayout.mWordsLayoutInfo.empty() )
326 // If first line is empty, just copy the last line to the first one.
327 firstLineLineLayoutInfo = lastLineLayoutInfo;
333 // Check the last word of the last group of the first line doesn't finish with a new line character.
334 WordGroupLayoutInfo& lastWordGroupLayout( *( firstLineLineLayoutInfo.mWordGroupsLayoutInfo.end() - 1 ) );
335 WordLayoutInfo& lastWordLayout( *( lastWordGroupLayout.mWordsLayoutInfo.end() - 1 ) );
336 if( LineSeparator == lastWordLayout.mType )
338 DALI_ASSERT_ALWAYS( !"TextViewProcessor::MergeLine(). ERROR: A line can't be merged to another line which finishes with a new line character." );
341 // If the last group of the first line has the same direction than the first group of the last line, both lines can be concatenated.
342 // Otherwise, both groups have to be merged first.
343 const WordGroupLayoutInfo& firstWordGroupLayout( *lastLineLayoutInfo.mWordGroupsLayoutInfo.begin() );
345 std::size_t index = 0;
346 if( lastWordGroupLayout.mDirection == firstWordGroupLayout.mDirection )
348 // Both groups of words have the same direction. They need to be merged.
349 MergeWordGroup( lastWordGroupLayout,
350 firstWordGroupLayout );
352 // After merging two groups of words, the rest of groups need to be added.
353 ++index; // By increasing this index the group of words already merged won't be added again.
357 firstLineLineLayoutInfo.mWordGroupsLayoutInfo.insert( firstLineLineLayoutInfo.mWordGroupsLayoutInfo.end(),
358 lastLineLayoutInfo.mWordGroupsLayoutInfo.begin() + index, lastLineLayoutInfo.mWordGroupsLayoutInfo.end() );
359 UpdateSize( firstLineLineLayoutInfo.mSize, lastLineLayoutInfo.mSize );
360 firstLineLineLayoutInfo.mAscender = std::max( firstLineLineLayoutInfo.mAscender, lastLineLayoutInfo.mAscender );
361 firstLineLineLayoutInfo.mLineHeightOffset = std::max( firstLineLineLayoutInfo.mLineHeightOffset, lastLineLayoutInfo.mLineHeightOffset );
362 firstLineLineLayoutInfo.mNumberOfCharacters += lastLineLayoutInfo.mNumberOfCharacters;
366 WordLayoutInfo GetLastWordLayoutInfo( const LineLayoutInfo& lineLayoutInfo )
368 WordLayoutInfo layoutInfo;
370 if( !lineLayoutInfo.mWordGroupsLayoutInfo.empty() )
372 const WordGroupLayoutInfo& groupInfo( *( lineLayoutInfo.mWordGroupsLayoutInfo.end() - 1 ) );
374 if( !groupInfo.mWordsLayoutInfo.empty() )
376 layoutInfo = *( groupInfo.mWordsLayoutInfo.end() - 1 );
383 CharacterLayoutInfo GetFirstCharacterLayoutInfo( const LineLayoutInfo& lineLayoutInfo )
385 CharacterLayoutInfo layoutInfo;
387 if( !lineLayoutInfo.mWordGroupsLayoutInfo.empty() )
389 const WordGroupLayoutInfo& groupInfo( *lineLayoutInfo.mWordGroupsLayoutInfo.begin() );
391 if( !groupInfo.mWordsLayoutInfo.empty() )
393 const WordLayoutInfo& wordInfo( *groupInfo.mWordsLayoutInfo.begin() );
395 layoutInfo = GetFirstCharacterLayoutInfo( wordInfo );
402 CharacterLayoutInfo GetLastCharacterLayoutInfo( const LineLayoutInfo& lineLayoutInfo )
404 const WordLayoutInfo wordInfo = GetLastWordLayoutInfo( lineLayoutInfo );
406 return GetLastCharacterLayoutInfo( wordInfo );
409 void CollectTextActorsFromLines( std::vector<TextActor>& textActors, const TextLayoutInfo& textLayoutInfo, const std::size_t lineIndexBegin, const std::size_t lineIndexEnd )
411 for( LineLayoutInfoContainer::const_iterator lineIt = textLayoutInfo.mLinesLayoutInfo.begin() + lineIndexBegin, lineEndIt = textLayoutInfo.mLinesLayoutInfo.begin() + lineIndexEnd;
415 const LineLayoutInfo& line( *lineIt );
417 CollectTextActorsFromGroups( textActors, line, 0, line.mWordGroupsLayoutInfo.size() );
421 } //namespace TextViewProcessor
423 } //namespace Internal
425 } //namespace Toolkit