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/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 Vector<GlyphInfo>& glyphs,
44 Vector<CharacterIndex>& glyphToCharacterMap,
45 Vector<Length>& charactersPerGlyph,
46 Vector<GlyphIndex>& newParagraphGlyphs )
48 const Length numberOfCharacters = text.Count();
50 if( 0u == numberOfCharacters )
52 // Nothing to do if there are no characters.
57 const Length numberOfFontRuns = fonts.Count();
58 const Length numberOfScriptRuns = scripts.Count();
61 DALI_ASSERT_DEBUG( ( 0u != numberOfFontRuns ) &&
62 ( numberOfCharacters == fonts[numberOfFontRuns - 1u].characterRun.characterIndex + fonts[numberOfFontRuns - 1u].characterRun.numberOfCharacters ) &&
63 "Toolkit::Text::ShapeText. All characters must have a font set." );
65 DALI_ASSERT_DEBUG( ( 0u != numberOfScriptRuns ) &&
66 ( numberOfCharacters == scripts[numberOfScriptRuns - 1u].characterRun.characterIndex + scripts[numberOfScriptRuns - 1u].characterRun.numberOfCharacters ) &&
67 "Toolkit::Text::ShapeText. All characters must have a script set." );
69 // The text needs to be split in chunks of consecutive characters.
70 // Each chunk must contain characters with the same font id and script set.
71 // A chunk of consecutive characters must not contain a LINE_MUST_BREAK, if there is one a new chunk has to be created.
73 TextAbstraction::Shaping shaping = TextAbstraction::Shaping::Get();
75 // To shape the text a font and an script is needed.
76 Vector<FontRun>::ConstIterator fontRunIt = fonts.Begin();
77 Vector<ScriptRun>::ConstIterator scriptRunIt = scripts.Begin();
79 // Index to the the next one to be shaped. Is pointing the character after the last one it was shaped.
80 CharacterIndex previousIndex = 0u;
82 // The current font id and script used to shape the text.
83 FontId currentFontId = 0u;
84 Script currentScript = TextAbstraction::UNKNOWN;
86 // Reserve some space to allocate the glyphs and the glyph to character map.
87 // There is no way to know the number of glyphs before shaping the text.
88 // To avoid reallocations it's reserved space for a slightly biger number of glyphs than the number of characters.
90 Length numberOfGlyphsReserved = static_cast<Length>( numberOfCharacters * 1.3f );
91 glyphs.Resize( numberOfGlyphsReserved );
92 glyphToCharacterMap.Resize( numberOfGlyphsReserved );
94 // The actual number of glyphs.
95 Length totalNumberOfGlyphs = 0u;
97 const Character* const textBuffer = text.Begin();
98 const LineBreakInfo* const lineBreakInfoBuffer = lineBreakInfo.Begin();
99 GlyphInfo* glyphsBuffer = glyphs.Begin();
100 CharacterIndex* glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
102 // Traverse the characters and shape the text.
103 for( previousIndex = 0; previousIndex < numberOfCharacters; )
105 // Get the font id and the script.
106 const FontRun& fontRun = *fontRunIt;
107 const ScriptRun& scriptRun = *scriptRunIt;
109 currentFontId = fontRun.fontId;
110 currentScript = scriptRun.script;
112 // Get the min index to the last character of both runs.
113 CharacterIndex currentIndex = min( fontRun.characterRun.characterIndex + fontRun.characterRun.numberOfCharacters,
114 scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters );
116 // Check if there is a line must break.
117 bool mustBreak = false;
119 // Check if the current index is a new paragraph character.
120 // A new paragraph character is going to be shaped in order to not to mess the conversion tables.
121 // However, the metrics need to be changed in order to not to draw a square.
122 bool isNewParagraph = false;
124 for( CharacterIndex index = previousIndex; index < currentIndex; ++index )
126 mustBreak = TextAbstraction::LINE_MUST_BREAK == *( lineBreakInfoBuffer + index );
129 isNewParagraph = TextAbstraction::IsNewParagraph( *( textBuffer + index ) );
130 currentIndex = index + 1u;
135 // Shape the text for the current chunk.
136 const Length numberOfGlyphs = shaping.Shape( textBuffer + previousIndex,
137 ( currentIndex - previousIndex ), // The number of characters to shape.
141 const Length glyphIndex = totalNumberOfGlyphs;
142 totalNumberOfGlyphs += numberOfGlyphs;
144 if( totalNumberOfGlyphs > numberOfGlyphsReserved )
146 // Resize the vectors to get enough space.
147 numberOfGlyphsReserved = static_cast<Length>( totalNumberOfGlyphs * 1.3f );
148 glyphs.Resize( numberOfGlyphsReserved );
149 glyphToCharacterMap.Resize( numberOfGlyphsReserved );
151 // Iterators are not valid anymore, set them again.
152 glyphsBuffer = glyphs.Begin();
153 glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
156 // Retrieve the glyphs and the glyph to character conversion map.
157 shaping.GetGlyphs( glyphsBuffer + glyphIndex,
158 glyphToCharacterMapBuffer + glyphIndex );
162 // Add the index of the new paragraph glyph to a vector.
163 // Their metrics will be updated in a following step.
164 newParagraphGlyphs.PushBack( totalNumberOfGlyphs - 1u );
168 if( 0u != glyphIndex )
170 for( Length index = glyphIndex; index < glyphIndex + numberOfGlyphs; ++index )
172 CharacterIndex& characterIndex = *( glyphToCharacterMapBuffer + index );
173 characterIndex += previousIndex;
177 // Update the iterators to get the next font or script run.
178 if( currentIndex == fontRun.characterRun.characterIndex + fontRun.characterRun.numberOfCharacters )
182 if( currentIndex == scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters )
187 // Update the previous index.
188 previousIndex = currentIndex;
191 // Add the number of characters per glyph.
192 charactersPerGlyph.Reserve( totalNumberOfGlyphs );
194 for( Length index = 1u; index < totalNumberOfGlyphs; ++index )
196 const CharacterIndex characterIndex = *( glyphToCharacterMapBuffer + index );
198 charactersPerGlyph.PushBack( characterIndex - previousIndex );
200 previousIndex = characterIndex;
203 charactersPerGlyph.PushBack( numberOfCharacters - previousIndex );
205 // Resize the vectors to set the right number of items.
206 glyphs.Resize( totalNumberOfGlyphs );
207 glyphToCharacterMap.Resize( totalNumberOfGlyphs );
212 } // namespace Toolkit