2 * Copyright (c) 2015 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/text/shaper.h>
22 #include <dali/devel-api/text-abstraction/script.h>
23 #include <dali/devel-api/text-abstraction/shaping.h>
26 #include <dali-toolkit/internal/text/font-run.h>
27 #include <dali-toolkit/internal/text/logical-model-impl.h>
28 #include <dali-toolkit/internal/text/script-run.h>
29 #include <dali-toolkit/internal/text/visual-model-impl.h>
40 CharacterIndex min( CharacterIndex index0,
41 CharacterIndex index1 )
43 return ( index0 < index1 ) ? index0 : index1;
46 void ShapeText( const Vector<Character>& text,
47 const Vector<LineBreakInfo>& lineBreakInfo,
48 const Vector<ScriptRun>& scripts,
49 const Vector<FontRun>& fonts,
50 Vector<GlyphInfo>& glyphs,
51 Vector<CharacterIndex>& glyphToCharacterMap,
52 Vector<Length>& charactersPerGlyph )
54 const Length numberOfCharacters = text.Count();
56 if( 0u == numberOfCharacters )
58 // Nothing to do if there are no characters.
63 const Length numberOfFontRuns = fonts.Count();
64 const Length numberOfScriptRuns = scripts.Count();
67 DALI_ASSERT_DEBUG( ( 0u != numberOfFontRuns ) &&
68 ( numberOfCharacters == fonts[numberOfFontRuns - 1u].characterRun.characterIndex + fonts[numberOfFontRuns - 1u].characterRun.numberOfCharacters ) &&
69 "Toolkit::Text::ShapeText. All characters must have a font set." );
71 DALI_ASSERT_DEBUG( ( 0u != numberOfScriptRuns ) &&
72 ( numberOfCharacters == scripts[numberOfScriptRuns - 1u].characterRun.characterIndex + scripts[numberOfScriptRuns - 1u].characterRun.numberOfCharacters ) &&
73 "Toolkit::Text::ShapeText. All characters must have a script set." );
75 // The text needs to be split in chunks of consecutive characters.
76 // Each chunk must contain characters with the same font id and script set.
77 // A chunk of consecutive characters must not contain a LINE_MUST_BREAK, if there is one a new chunk have to be created.
79 TextAbstraction::Shaping shaping = TextAbstraction::Shaping::Get();
81 // To shape the text a font and an script is needed.
82 Vector<FontRun>::ConstIterator fontRunIt = fonts.Begin();
83 Vector<ScriptRun>::ConstIterator scriptRunIt = scripts.Begin();
85 // Index to the the next one to be shaped. Is pointing the character after the last one it was shaped.
86 CharacterIndex previousIndex = 0u;
88 // The current font id and script used to shape the text.
89 FontId currentFontId = 0u;
90 Script currentScript = TextAbstraction::UNKNOWN;
92 // Reserve some space to allocate the glyphs and the glyph to character map.
93 // There is no way to know the number of glyphs before shaping the text.
94 // To avoid reallocations it's reserved space for a slightly biger number of glyphs than the number of characters.
96 Length numberOfGlyphsReserved = static_cast<Length>( numberOfCharacters * 1.3f );
97 glyphs.Resize( numberOfGlyphsReserved );
98 glyphToCharacterMap.Resize( numberOfGlyphsReserved );
100 // The actual number of glyphs.
101 Length totalNumberOfGlyphs = 0u;
103 const Character* textBuffer = text.Begin();
104 const LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
105 GlyphInfo* glyphsBuffer = glyphs.Begin();
106 CharacterIndex* glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
108 // Traverse the characters and shape the text.
109 for( previousIndex = 0; previousIndex < numberOfCharacters; )
111 // Get the font id and the script.
112 const FontRun& fontRun = *fontRunIt;
113 const ScriptRun& scriptRun = *scriptRunIt;
115 currentFontId = fontRun.fontId;
116 currentScript = scriptRun.script;
118 // Get the min index to the last character of both runs.
119 CharacterIndex currentIndex = min( fontRun.characterRun.characterIndex + fontRun.characterRun.numberOfCharacters,
120 scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters );
122 // Check if there is a line must break.
123 bool mustBreak = false;
124 for( CharacterIndex index = previousIndex; index < currentIndex; ++index )
126 mustBreak = TextAbstraction::LINE_MUST_BREAK == *( lineBreakInfoBuffer + index );
129 currentIndex = index;
134 // Check if the current index is a new paragraph character.
135 // A \n is going to be shaped in order to not to mess the conversion tables.
136 // After the \n character is shaped, the glyph is going to be reset to its
137 // default in order to not to get any metric or font index for it.
138 const bool isNewParagraph = TextAbstraction::IsNewParagraph( *( textBuffer + currentIndex ) );
140 // The last character is always a must-break even if it's not a \n.
141 Length numberOfCharactersToShape = currentIndex - previousIndex;
144 // Add one more character to shape.
145 ++numberOfCharactersToShape;
148 // Shape the text for the current chunk.
149 const Length numberOfGlyphs = shaping.Shape( textBuffer + previousIndex,
150 numberOfCharactersToShape,
154 const Length glyphIndex = totalNumberOfGlyphs;
155 totalNumberOfGlyphs += numberOfGlyphs;
157 if( totalNumberOfGlyphs > numberOfGlyphsReserved )
159 // Resize the vectors to get enough space.
160 numberOfGlyphsReserved = static_cast<Length>( totalNumberOfGlyphs * 1.3f );
161 glyphs.Resize( numberOfGlyphsReserved );
162 glyphToCharacterMap.Resize( numberOfGlyphsReserved );
164 // Iterators are not valid anymore, set them again.
165 glyphsBuffer = glyphs.Begin();
166 glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
169 // Retrieve the glyphs and the glyph to character conversion map.
170 shaping.GetGlyphs( glyphsBuffer + glyphIndex,
171 glyphToCharacterMapBuffer + glyphIndex );
175 // TODO : This is a work around to avoid drawing a square in the
176 // place of a new line character.
178 // If the last character is a \n, it resets the glyph to the default
179 // to avoid getting any metric for it.
180 GlyphInfo& glyph = *( glyphsBuffer + glyphIndex + ( numberOfGlyphs - 1u ) );
186 if( 0u != glyphIndex )
188 for( Length index = glyphIndex; index < glyphIndex + numberOfGlyphs; ++index )
190 CharacterIndex& characterIndex = *( glyphToCharacterMapBuffer + index );
191 characterIndex += previousIndex;
195 // Update the iterators to get the next font or script run.
196 if( currentIndex == fontRun.characterRun.characterIndex + fontRun.characterRun.numberOfCharacters )
200 if( currentIndex == scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters )
205 // Update the previous index. Jumps the \n if needed.
206 previousIndex = mustBreak ? currentIndex + 1u : currentIndex;
209 // Add the number of characters per glyph.
210 charactersPerGlyph.Reserve( totalNumberOfGlyphs );
212 for( Length index = 1u; index < totalNumberOfGlyphs; ++index )
214 const CharacterIndex characterIndex = *( glyphToCharacterMapBuffer + index );
216 charactersPerGlyph.PushBack( characterIndex - previousIndex );
218 previousIndex = characterIndex;
221 charactersPerGlyph.PushBack( numberOfCharacters - previousIndex );
223 // Resize the vectors to set the right number of items.
224 glyphs.Resize( totalNumberOfGlyphs );
225 glyphToCharacterMap.Resize( totalNumberOfGlyphs );
230 } // namespace Toolkit