Merge branch 'devel/master' into devel/new_mesh
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / shaper.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/text/shaper.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/text-abstraction/script.h>
23 #include <dali/devel-api/text-abstraction/shaping.h>
24
25 // INTERNAL INCLUDES
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>
30
31 namespace Dali
32 {
33
34 namespace Toolkit
35 {
36
37 namespace Text
38 {
39
40 CharacterIndex min( CharacterIndex index0,
41                     CharacterIndex index1 )
42 {
43   return ( index0 < index1 ) ? index0 : index1;
44 }
45
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 )
53 {
54   const Length numberOfCharacters = text.Count();
55
56   if( 0u == numberOfCharacters )
57   {
58     // Nothing to do if there are no characters.
59     return;
60   }
61
62 #ifdef DEBUG_ENABLED
63   const Length numberOfFontRuns = fonts.Count();
64   const Length numberOfScriptRuns = scripts.Count();
65 #endif
66
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." );
70
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." );
74
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.
78
79   TextAbstraction::Shaping shaping = TextAbstraction::Shaping::Get();
80
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();
84
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;
87
88   // The current font id and script used to shape the text.
89   FontId currentFontId = 0u;
90   Script currentScript = TextAbstraction::UNKNOWN;
91
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.
95
96   Length numberOfGlyphsReserved = static_cast<Length>( numberOfCharacters * 1.3f );
97   glyphs.Resize( numberOfGlyphsReserved );
98   glyphToCharacterMap.Resize( numberOfGlyphsReserved );
99
100   // The actual number of glyphs.
101   Length totalNumberOfGlyphs = 0u;
102
103   const Character* textBuffer = text.Begin();
104   const LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
105   GlyphInfo* glyphsBuffer = glyphs.Begin();
106   CharacterIndex* glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
107
108   // Traverse the characters and shape the text.
109   for( previousIndex = 0; previousIndex < numberOfCharacters; )
110   {
111     // Get the font id and the script.
112     const FontRun& fontRun = *fontRunIt;
113     const ScriptRun& scriptRun = *scriptRunIt;
114
115     currentFontId = fontRun.fontId;
116     currentScript = scriptRun.script;
117
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 );
121
122     // Check if there is a line must break.
123     bool mustBreak = false;
124     for( CharacterIndex index = previousIndex; index < currentIndex; ++index )
125     {
126       mustBreak = TextAbstraction::LINE_MUST_BREAK == *( lineBreakInfoBuffer + index );
127       if( mustBreak )
128       {
129         currentIndex = index;
130         break;
131       }
132     }
133
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 ) );
139
140     // The last character is always a must-break even if it's not a \n.
141     Length numberOfCharactersToShape = currentIndex - previousIndex;
142     if( mustBreak )
143     {
144       // Add one more character to shape.
145       ++numberOfCharactersToShape;
146     }
147
148     // Shape the text for the current chunk.
149     const Length numberOfGlyphs = shaping.Shape( textBuffer + previousIndex,
150                                                  numberOfCharactersToShape,
151                                                  currentFontId,
152                                                  currentScript );
153
154     const Length glyphIndex = totalNumberOfGlyphs;
155     totalNumberOfGlyphs += numberOfGlyphs;
156
157     if( totalNumberOfGlyphs > numberOfGlyphsReserved )
158     {
159       // Resize the vectors to get enough space.
160       numberOfGlyphsReserved = static_cast<Length>( totalNumberOfGlyphs * 1.3f );
161       glyphs.Resize( numberOfGlyphsReserved );
162       glyphToCharacterMap.Resize( numberOfGlyphsReserved );
163
164       // Iterators are not valid anymore, set them again.
165       glyphsBuffer = glyphs.Begin();
166       glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
167     }
168
169     // Retrieve the glyphs and the glyph to character conversion map.
170     shaping.GetGlyphs( glyphsBuffer + glyphIndex,
171                        glyphToCharacterMapBuffer + glyphIndex );
172
173     if( isNewParagraph )
174     {
175       // TODO : This is a work around to avoid drawing a square in the
176       //        place of a new line character.
177
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 ) );
181
182       glyph = GlyphInfo();
183     }
184
185     // Update indices.
186     if( 0u != glyphIndex )
187     {
188       for( Length index = glyphIndex; index < glyphIndex + numberOfGlyphs; ++index )
189       {
190         CharacterIndex& characterIndex = *( glyphToCharacterMapBuffer + index );
191         characterIndex += previousIndex;
192       }
193     }
194
195     // Update the iterators to get the next font or script run.
196     if( currentIndex == fontRun.characterRun.characterIndex + fontRun.characterRun.numberOfCharacters )
197     {
198       ++fontRunIt;
199     }
200     if( currentIndex == scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters )
201     {
202       ++scriptRunIt;
203     }
204
205     // Update the previous index. Jumps the \n if needed.
206     previousIndex = mustBreak ? currentIndex + 1u : currentIndex;
207   }
208
209   // Add the number of characters per glyph.
210   charactersPerGlyph.Reserve( totalNumberOfGlyphs );
211   previousIndex = 0u;
212   for( Length index = 1u; index < totalNumberOfGlyphs; ++index )
213   {
214     const CharacterIndex characterIndex = *( glyphToCharacterMapBuffer + index );
215
216     charactersPerGlyph.PushBack( characterIndex - previousIndex );
217
218     previousIndex = characterIndex;
219   }
220
221   charactersPerGlyph.PushBack( numberOfCharacters - previousIndex );
222
223   // Resize the vectors to set the right number of items.
224   glyphs.Resize( totalNumberOfGlyphs );
225   glyphToCharacterMap.Resize( totalNumberOfGlyphs );
226 }
227
228 } // namespace Text
229
230 } // namespace Toolkit
231
232 } // namespace Dali