2 * Copyright (c) 2019 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/shaping.h>
33 CharacterIndex min( CharacterIndex index0,
34 CharacterIndex index1 )
36 return ( index0 < index1 ) ? index0 : index1;
39 void ShapeText( const Vector<Character>& text,
40 const Vector<LineBreakInfo>& lineBreakInfo,
41 const Vector<ScriptRun>& scripts,
42 const Vector<FontRun>& fonts,
43 CharacterIndex startCharacterIndex,
44 GlyphIndex startGlyphIndex,
45 Length numberOfCharacters,
46 Vector<GlyphInfo>& glyphs,
47 Vector<CharacterIndex>& glyphToCharacterMap,
48 Vector<Length>& charactersPerGlyph,
49 Vector<GlyphIndex>& newParagraphGlyphs )
51 if( 0u == numberOfCharacters )
53 // Nothing to do if there are no characters.
58 const Length numberOfFontRuns = fonts.Count();
59 const Length numberOfScriptRuns = scripts.Count();
60 const Length totalNumberOfCharacters = text.Count();
63 DALI_ASSERT_DEBUG( ( 0u != numberOfFontRuns ) &&
64 ( totalNumberOfCharacters == fonts[numberOfFontRuns - 1u].characterRun.characterIndex + fonts[numberOfFontRuns - 1u].characterRun.numberOfCharacters ) &&
65 "Toolkit::Text::ShapeText. All characters must have a font set." );
67 DALI_ASSERT_DEBUG( ( 0u != numberOfScriptRuns ) &&
68 ( totalNumberOfCharacters == scripts[numberOfScriptRuns - 1u].characterRun.characterIndex + scripts[numberOfScriptRuns - 1u].characterRun.numberOfCharacters ) &&
69 "Toolkit::Text::ShapeText. All characters must have a script set." );
71 // The text needs to be split in chunks of consecutive characters.
72 // Each chunk must contain characters with the same font id and script set.
73 // A chunk of consecutive characters must not contain a LINE_MUST_BREAK, if there is one a new chunk has to be created.
75 TextAbstraction::Shaping shaping = TextAbstraction::Shaping::Get();
77 // To shape the text a font and an script is needed.
79 // Get the font run containing the startCharacterIndex character.
80 Vector<FontRun>::ConstIterator fontRunIt = fonts.Begin();
81 for( Vector<FontRun>::ConstIterator endIt = fonts.End(); fontRunIt < endIt; ++fontRunIt )
83 const FontRun& run = *fontRunIt;
84 if( startCharacterIndex < run.characterRun.characterIndex + run.characterRun.numberOfCharacters )
91 // Get the script run containing the startCharacterIndex character.
92 Vector<ScriptRun>::ConstIterator scriptRunIt = scripts.Begin();
93 for( Vector<ScriptRun>::ConstIterator endIt = scripts.End(); scriptRunIt < endIt; ++scriptRunIt )
95 const ScriptRun& run = *scriptRunIt;
96 if( startCharacterIndex < run.characterRun.characterIndex + run.characterRun.numberOfCharacters )
103 // Index to the the next one to be shaped. Is pointing the character after the last one it was shaped.
104 CharacterIndex previousIndex = 0u;
106 // The current font id and script used to shape the text.
107 FontId currentFontId = 0u;
108 Script currentScript = TextAbstraction::UNKNOWN;
110 // Reserve some space to allocate the glyphs and the glyph to character map.
111 // There is no way to know the number of glyphs before shaping the text.
112 // To avoid reallocations it's reserved space for a slightly biger number of glyphs than the number of characters.
115 glyphInfo.isItalicRequired = false;
116 glyphInfo.isBoldRequired = false;
118 const Length currentNumberOfGlyphs = glyphs.Count();
119 const Length numberOfGlyphsReserved = static_cast<Length>( numberOfCharacters * 1.3f );
120 glyphs.Resize( currentNumberOfGlyphs + numberOfGlyphsReserved, glyphInfo );
121 glyphToCharacterMap.Resize( currentNumberOfGlyphs + numberOfGlyphsReserved );
123 // The actual number of glyphs.
124 Length totalNumberOfGlyphs = currentNumberOfGlyphs;
125 // The number of new glyphs.
126 Length numberOfNewGlyphs = 0u;
128 const Character* const textBuffer = text.Begin();
129 const LineBreakInfo* const lineBreakInfoBuffer = lineBreakInfo.Begin();
130 CharacterIndex* glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
132 Length glyphIndex = startGlyphIndex;
134 // Traverse the characters and shape the text.
135 const CharacterIndex lastCharacter = startCharacterIndex + numberOfCharacters;
136 for( previousIndex = startCharacterIndex; previousIndex < lastCharacter; )
138 // Get the font id and the script.
139 const FontRun& fontRun = *fontRunIt;
140 const ScriptRun& scriptRun = *scriptRunIt;
142 currentFontId = fontRun.fontId;
143 currentScript = scriptRun.script;
144 const bool isItalicRequired = fontRun.isItalicRequired;
145 const bool isBoldRequired = fontRun.isBoldRequired;
147 // Get the min index to the last character of both runs.
148 CharacterIndex currentIndex = min( fontRun.characterRun.characterIndex + fontRun.characterRun.numberOfCharacters,
149 scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters );
151 // Check if there is a line must break.
152 bool mustBreak = false;
154 // Check if the current index is a new paragraph character.
155 // A new paragraph character is going to be shaped in order to not to mess the conversion tables.
156 // However, the metrics need to be changed in order to not to draw a square.
157 bool isNewParagraph = false;
159 for( CharacterIndex index = previousIndex; index < currentIndex; ++index )
161 mustBreak = TextAbstraction::LINE_MUST_BREAK == *( lineBreakInfoBuffer + index );
164 isNewParagraph = TextAbstraction::IsNewParagraph( *( textBuffer + index ) );
165 currentIndex = index + 1u;
170 // Shape the text for the current chunk.
171 const Length numberOfGlyphs = shaping.Shape( textBuffer + previousIndex,
172 ( currentIndex - previousIndex ), // The number of characters to shape.
176 // Retrieve the glyphs and the glyph to character conversion map.
177 Vector<GlyphInfo> tmpGlyphs;
178 Vector<CharacterIndex> tmpGlyphToCharacterMap;
181 glyphInfo.isItalicRequired = isItalicRequired;
182 glyphInfo.isBoldRequired = isBoldRequired;
184 tmpGlyphs.Resize( numberOfGlyphs, glyphInfo );
185 tmpGlyphToCharacterMap.Resize( numberOfGlyphs );
186 shaping.GetGlyphs( tmpGlyphs.Begin(),
187 tmpGlyphToCharacterMap.Begin() );
189 // Update the new indices of the glyph to character map.
190 if( 0u != totalNumberOfGlyphs )
192 for( Vector<CharacterIndex>::Iterator it = tmpGlyphToCharacterMap.Begin(),
193 endIt = tmpGlyphToCharacterMap.End();
197 *it += previousIndex;
201 totalNumberOfGlyphs += numberOfGlyphs;
202 numberOfNewGlyphs += numberOfGlyphs;
204 glyphs.Insert( glyphs.Begin() + glyphIndex, tmpGlyphs.Begin(), tmpGlyphs.End() );
205 glyphToCharacterMap.Insert( glyphToCharacterMap.Begin() + glyphIndex, tmpGlyphToCharacterMap.Begin(), tmpGlyphToCharacterMap.End() );
206 glyphIndex += numberOfGlyphs;
208 // Set the buffer pointers again.
209 glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
213 // Add the index of the new paragraph glyph to a vector.
214 // Their metrics will be updated in a following step.
215 newParagraphGlyphs.PushBack( glyphIndex - 1u );
218 // Update the iterators to get the next font or script run.
219 if( currentIndex == fontRun.characterRun.characterIndex + fontRun.characterRun.numberOfCharacters )
223 if( currentIndex == scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters )
228 // Update the previous index.
229 previousIndex = currentIndex;
233 for( Length index = startGlyphIndex + numberOfNewGlyphs; index < totalNumberOfGlyphs; ++index )
235 CharacterIndex& characterIndex = *( glyphToCharacterMapBuffer + index );
236 characterIndex += numberOfCharacters;
239 // Add the number of characters per glyph.
240 charactersPerGlyph.Reserve( totalNumberOfGlyphs );
241 Length* charactersPerGlyphBuffer = charactersPerGlyph.Begin();
243 const GlyphIndex lastGlyph = startGlyphIndex + numberOfNewGlyphs;
244 previousIndex = startCharacterIndex;
245 for( Length index = startGlyphIndex + 1u; index < lastGlyph; ++index )
247 const CharacterIndex characterIndex = *( glyphToCharacterMapBuffer + index );
249 charactersPerGlyph.Insert( charactersPerGlyphBuffer + index - 1u, characterIndex - previousIndex );
251 previousIndex = characterIndex;
253 charactersPerGlyph.Insert( charactersPerGlyphBuffer + lastGlyph - 1u, numberOfCharacters + startCharacterIndex - previousIndex );
255 // Resize the vectors to set the right number of items.
256 glyphs.Resize( totalNumberOfGlyphs );
257 glyphToCharacterMap.Resize( totalNumberOfGlyphs );
262 } // namespace Toolkit