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 "text-view-line-processor.h"
20 #include "text-view-word-group-processor.h"
21 #include "text-view-word-processor.h"
22 #include "text-view-processor-helper-functions.h"
23 #include "text-processor.h"
34 namespace TextViewProcessor
41 LineLayoutInfo::LineLayoutInfo()
44 mLineHeightOffset( 0.f ),
45 mWordGroupsLayoutInfo(),
46 mNumberOfCharacters( 0 )
50 LineLayoutInfo::LineLayoutInfo( const LineLayoutInfo& line )
51 : mSize( line.mSize ),
52 mAscender( line.mAscender ),
53 mLineHeightOffset( line.mLineHeightOffset ),
54 mWordGroupsLayoutInfo( line.mWordGroupsLayoutInfo ),
55 mNumberOfCharacters( line.mNumberOfCharacters )
59 LineLayoutInfo& LineLayoutInfo::operator=( const LineLayoutInfo& line )
62 mAscender = line.mAscender;
63 mLineHeightOffset = line.mLineHeightOffset;
64 mWordGroupsLayoutInfo = line.mWordGroupsLayoutInfo;
65 mNumberOfCharacters = line.mNumberOfCharacters;
70 void UpdateLineLayoutInfo( TextViewProcessor::LineLayoutInfo& lineLayoutInfo, const float lineHeightOffset )
72 lineLayoutInfo.mSize = Size();
74 for( WordGroupLayoutInfoContainer::iterator it = lineLayoutInfo.mWordGroupsLayoutInfo.begin(), endIt = lineLayoutInfo.mWordGroupsLayoutInfo.end();
78 WordGroupLayoutInfo& layoutInfo( *it );
80 UpdateSize( lineLayoutInfo.mSize, layoutInfo.mSize );
82 lineLayoutInfo.mSize.height += lineHeightOffset;
85 void CreateLineInfo( const MarkupProcessor::StyledTextArray& line,
86 TextView::RelayoutData& relayoutData,
87 TextViewProcessor::LineLayoutInfo& lineLayoutInfo )
89 // 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.
90 // TODO: set the wordgroup direction (LTR or RTL)
91 std::vector<MarkupProcessor::StyledTextArray> wordGroups;
92 if( TextProcessor::ContainsRightToLeftCharacter( line ) )
94 // If the text is bidirectional, the characters will be converted and reordered
95 // as specified by the Unicode Bidirectional Algorithm.
97 // Reorders the line and converts arabic glyphs (if any).
98 // It also split words in different groups if there are a mix of left to right
99 // and right to left text.
100 // If the whole line is left to right or right to left all words are grouped in the same group.
101 TextProcessor::ConvertBidirectionalText( line,
103 relayoutData.mCharacterLogicalToVisualMap,
104 relayoutData.mCharacterVisualToLogicalMap);
108 // No bidirectional text to process.
112 // Add all words in a group.
113 wordGroups.push_back( line );
115 // Create trivial bidirectional map tables.
116 std::size_t index = 0;
117 for( MarkupProcessor::StyledTextArray::const_iterator it = line.begin(), endIt = line.end(); it != endIt; ++it )
119 const MarkupProcessor::StyledText& styledText( *it );
121 for( std::size_t i = 0, length = styledText.mText.GetLength(); i < length; ++i )
123 relayoutData.mCharacterLogicalToVisualMap.push_back( relayoutData.mTextLayoutInfo.mNumberOfCharacters + index );
124 relayoutData.mCharacterVisualToLogicalMap.push_back( relayoutData.mTextLayoutInfo.mNumberOfCharacters + index );
131 // Traverse all group of words.
132 for( std::vector<MarkupProcessor::StyledTextArray>::const_iterator groupIt = wordGroups.begin(), groupEndIt = wordGroups.end(); groupIt != groupEndIt; ++groupIt )
134 const MarkupProcessor::StyledTextArray& wordGroup( *groupIt );
136 // Data structures for the new group of words.
137 WordGroupLayoutInfo wordGroupLayoutInfo;
139 CreateWordGroupInfo( wordGroup,
140 relayoutData.mTextLayoutInfo,
141 wordGroupLayoutInfo );
143 // Update layout info for the current line.
144 lineLayoutInfo.mAscender = std::max( lineLayoutInfo.mAscender, wordGroupLayoutInfo.mAscender );
145 lineLayoutInfo.mNumberOfCharacters += wordGroupLayoutInfo.mNumberOfCharacters;
146 UpdateSize( lineLayoutInfo.mSize, wordGroupLayoutInfo.mSize );
148 // Add the group of words to the current line.
149 lineLayoutInfo.mWordGroupsLayoutInfo.push_back( wordGroupLayoutInfo );
150 } // end of group of words
153 void RemoveWordGroupsFromLine( const std::size_t groupIndex,
154 const std::size_t numberOfGroups,
155 const PointSize& lineHeightOffset,
156 LineLayoutInfo& lineLayout )
158 // Removes groups of words from a line.
160 // * 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.
162 // * Note: Currently it's only used to remove a number of groups of words from the beginning, or
163 // from groupIndex index to the end. This function doesn't merge groups of words (if a whole group is removed)
164 // TODO: merge groups of words if required.
166 const std::size_t groupEndIndex = groupIndex + numberOfGroups;
168 // Remove word groups from layout info.
169 lineLayout.mWordGroupsLayoutInfo.erase( lineLayout.mWordGroupsLayoutInfo.begin() + groupIndex,
170 lineLayout.mWordGroupsLayoutInfo.begin() + groupEndIndex );
172 // Update layout info.
173 lineLayout.mSize = Size();
174 lineLayout.mAscender = 0.f;
175 lineLayout.mNumberOfCharacters = 0;
176 for( WordGroupLayoutInfoContainer::const_iterator it = lineLayout.mWordGroupsLayoutInfo.begin(), endIt = lineLayout.mWordGroupsLayoutInfo.end();
180 const WordGroupLayoutInfo& group( *it );
182 UpdateSize( lineLayout.mSize, group.mSize );
183 lineLayout.mAscender = std::max( lineLayout.mAscender, group.mAscender );
184 lineLayout.mNumberOfCharacters += group.mNumberOfCharacters;
186 lineLayout.mSize.height += lineHeightOffset;
187 lineLayout.mLineHeightOffset = lineHeightOffset;
190 void SplitLine( const TextInfoIndices& indices,
191 const PointSize& lineHeightOffset,
192 LineLayoutInfo& firstLineLayoutInfo,
193 LineLayoutInfo& lastLineLayoutInfo )
195 // Splits a line in two.
196 // A group of words and a word may be split in two as well.
198 // * Split the group of words within the line.
199 // * Add last part of the group of words to the new line.
200 // * Add groups of words from groupPosition + 1 to the end.
201 // * Update layout info of the last line.
202 // * Remove groups of words added to the last part of the line from the first line.
205 if( ( 0 == indices.mGroupIndex ) && ( 0 == indices.mWordIndex ) && ( 0 == indices.mCharacterIndex ) )
207 // the whole line goes to the last part.
208 lastLineLayoutInfo = firstLineLayoutInfo;
210 firstLineLayoutInfo = LineLayoutInfo();
215 if( !firstLineLayoutInfo.mWordGroupsLayoutInfo.empty() )
217 const std::size_t numberOfGroups = firstLineLayoutInfo.mWordGroupsLayoutInfo.size();
218 if( indices.mGroupIndex == numberOfGroups - 1 )
220 const WordGroupLayoutInfo& group( *( firstLineLayoutInfo.mWordGroupsLayoutInfo.end() - 1 ) );
222 const std::size_t numberOfWords = group.mWordsLayoutInfo.size();
223 if( indices.mWordIndex == numberOfWords - 1 )
225 const WordLayoutInfo& word( *( group.mWordsLayoutInfo.end() - 1 ) );
226 if( indices.mCharacterIndex == word.mCharactersLayoutInfo.size() )
228 // the whole line goes to the first part.
230 // Just delete whatever there is in the last part of the line.
231 lastLineLayoutInfo = LineLayoutInfo();
239 lastLineLayoutInfo = LineLayoutInfo();
241 // 1) Split the group of words whitin the line.
242 WordGroupLayoutInfo& firstWordGroupLayoutInfo( *( firstLineLayoutInfo.mWordGroupsLayoutInfo.begin() + indices.mGroupIndex ) );
243 WordGroupLayoutInfo lastWordGroupLayoutInfo;
245 SplitWordGroup( indices,
246 firstWordGroupLayoutInfo,
247 lastWordGroupLayoutInfo );
249 // 2) Add last part of the group of words to the new line.
250 if( !lastWordGroupLayoutInfo.mWordsLayoutInfo.empty() )
252 lastLineLayoutInfo.mWordGroupsLayoutInfo.push_back( lastWordGroupLayoutInfo );
255 // 3) Add groups from group-position + 1 to the end.
256 lastLineLayoutInfo.mWordGroupsLayoutInfo.insert( lastLineLayoutInfo.mWordGroupsLayoutInfo.end(),
257 firstLineLayoutInfo.mWordGroupsLayoutInfo.begin() + indices.mGroupIndex + 1, firstLineLayoutInfo.mWordGroupsLayoutInfo.end() );
259 // 4) update layout info of the last line.
260 for( WordGroupLayoutInfoContainer::iterator it = lastLineLayoutInfo.mWordGroupsLayoutInfo.begin(), endIt = lastLineLayoutInfo.mWordGroupsLayoutInfo.end();
264 WordGroupLayoutInfo& layoutInfo( *it );
266 lastLineLayoutInfo.mNumberOfCharacters += layoutInfo.mNumberOfCharacters;
267 UpdateSize( lastLineLayoutInfo.mSize, layoutInfo.mSize );
268 lastLineLayoutInfo.mAscender = std::max( lastLineLayoutInfo.mAscender, layoutInfo.mAscender );
270 lastLineLayoutInfo.mSize.height += lineHeightOffset;
271 lastLineLayoutInfo.mLineHeightOffset = lineHeightOffset;
273 // 5) Remove groups of words added to the last part of the line from the first line.
275 // if the number of characters of the last group of words of the first line is zero, it should be removed.
276 const std::size_t index = ( 0 == firstWordGroupLayoutInfo.mNumberOfCharacters ? indices.mGroupIndex : indices.mGroupIndex + 1 );
278 firstLineLayoutInfo.mWordGroupsLayoutInfo.erase( firstLineLayoutInfo.mWordGroupsLayoutInfo.begin() + index, firstLineLayoutInfo.mWordGroupsLayoutInfo.end() );
280 // 6) update layout info of the first line.
281 firstLineLayoutInfo.mNumberOfCharacters = 0;
282 firstLineLayoutInfo.mSize = Size();
283 firstLineLayoutInfo.mAscender = 0.f;
284 for( WordGroupLayoutInfoContainer::iterator it = firstLineLayoutInfo.mWordGroupsLayoutInfo.begin(), endIt = firstLineLayoutInfo.mWordGroupsLayoutInfo.end();
288 WordGroupLayoutInfo& layoutInfo( *it );
290 firstLineLayoutInfo.mNumberOfCharacters += layoutInfo.mNumberOfCharacters;
291 UpdateSize( firstLineLayoutInfo.mSize, layoutInfo.mSize );
292 firstLineLayoutInfo.mAscender = std::max( firstLineLayoutInfo.mAscender, layoutInfo.mAscender );
294 firstLineLayoutInfo.mSize.height += lineHeightOffset;
295 firstLineLayoutInfo.mLineHeightOffset = lineHeightOffset;
298 void MergeLine( LineLayoutInfo& firstLineLineLayoutInfo,
299 const LineLayoutInfo& lastLineLayoutInfo )
301 // Merges two given lines.
303 // Can't merge two lines if the last word of the first one is a line separator (new line character)
307 if( lastLineLayoutInfo.mWordGroupsLayoutInfo.empty() )
309 // Nothing to merge if last line is empty.
313 if( firstLineLineLayoutInfo.mWordGroupsLayoutInfo.empty() )
315 // If first line is empty, just copy the last line to the first one.
316 firstLineLineLayoutInfo = lastLineLayoutInfo;
321 if( 1 == firstLineLineLayoutInfo.mWordGroupsLayoutInfo.size() )
323 WordGroupLayoutInfo& wordGroupLayout( *firstLineLineLayoutInfo.mWordGroupsLayoutInfo.begin() );
325 if( wordGroupLayout.mWordsLayoutInfo.empty() )
327 // If first line is empty, just copy the last line to the first one.
328 firstLineLineLayoutInfo = lastLineLayoutInfo;
334 // Check the last word of the last group of the first line doesn't finish with a new line character.
335 WordGroupLayoutInfo& lastWordGroupLayout( *( firstLineLineLayoutInfo.mWordGroupsLayoutInfo.end() - 1 ) );
336 WordLayoutInfo& lastWordLayout( *( lastWordGroupLayout.mWordsLayoutInfo.end() - 1 ) );
337 if( LineSeparator == lastWordLayout.mType )
339 DALI_ASSERT_ALWAYS( !"TextViewProcessor::MergeLine(). ERROR: A line can't be merged to another line which finishes with a new line character." );
342 // 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.
343 // Otherwise, both groups have to be merged first.
344 const WordGroupLayoutInfo& firstWordGroupLayout( *lastLineLayoutInfo.mWordGroupsLayoutInfo.begin() );
346 std::size_t index = 0;
347 if( lastWordGroupLayout.mDirection == firstWordGroupLayout.mDirection )
349 // Both groups of words have the same direction. They need to be merged.
350 MergeWordGroup( lastWordGroupLayout,
351 firstWordGroupLayout );
353 // After merging two groups of words, the rest of groups need to be added.
354 ++index; // By increasing this index the group of words already merged won't be added again.
358 firstLineLineLayoutInfo.mWordGroupsLayoutInfo.insert( firstLineLineLayoutInfo.mWordGroupsLayoutInfo.end(),
359 lastLineLayoutInfo.mWordGroupsLayoutInfo.begin() + index, lastLineLayoutInfo.mWordGroupsLayoutInfo.end() );
360 UpdateSize( firstLineLineLayoutInfo.mSize, lastLineLayoutInfo.mSize );
361 firstLineLineLayoutInfo.mAscender = std::max( firstLineLineLayoutInfo.mAscender, lastLineLayoutInfo.mAscender );
362 firstLineLineLayoutInfo.mLineHeightOffset = std::max( firstLineLineLayoutInfo.mLineHeightOffset, lastLineLayoutInfo.mLineHeightOffset );
363 firstLineLineLayoutInfo.mNumberOfCharacters += lastLineLayoutInfo.mNumberOfCharacters;
367 WordLayoutInfo GetLastWordLayoutInfo( const LineLayoutInfo& lineLayoutInfo )
369 WordLayoutInfo layoutInfo;
371 if( !lineLayoutInfo.mWordGroupsLayoutInfo.empty() )
373 const WordGroupLayoutInfo& groupInfo( *( lineLayoutInfo.mWordGroupsLayoutInfo.end() - 1 ) );
375 if( !groupInfo.mWordsLayoutInfo.empty() )
377 layoutInfo = *( groupInfo.mWordsLayoutInfo.end() - 1 );
384 CharacterLayoutInfo GetFirstCharacterLayoutInfo( const LineLayoutInfo& lineLayoutInfo )
386 CharacterLayoutInfo layoutInfo;
388 if( !lineLayoutInfo.mWordGroupsLayoutInfo.empty() )
390 const WordGroupLayoutInfo& groupInfo( *lineLayoutInfo.mWordGroupsLayoutInfo.begin() );
392 if( !groupInfo.mWordsLayoutInfo.empty() )
394 const WordLayoutInfo& wordInfo( *groupInfo.mWordsLayoutInfo.begin() );
396 layoutInfo = GetFirstCharacterLayoutInfo( wordInfo );
403 CharacterLayoutInfo GetLastCharacterLayoutInfo( const LineLayoutInfo& lineLayoutInfo )
405 const WordLayoutInfo wordInfo = GetLastWordLayoutInfo( lineLayoutInfo );
407 return GetLastCharacterLayoutInfo( wordInfo );
410 void CollectTextActorsFromLines( std::vector<TextActor>& textActors, const TextLayoutInfo& textLayoutInfo, const std::size_t lineIndexBegin, const std::size_t lineIndexEnd )
412 for( LineLayoutInfoContainer::const_iterator lineIt = textLayoutInfo.mLinesLayoutInfo.begin() + lineIndexBegin, lineEndIt = textLayoutInfo.mLinesLayoutInfo.begin() + lineIndexEnd;
416 const LineLayoutInfo& line( *lineIt );
418 CollectTextActorsFromGroups( textActors, line, 0, line.mWordGroupsLayoutInfo.size() );
422 } //namespace TextViewProcessor
424 } //namespace Internal
426 } //namespace Toolkit