Multiple text background color support for left-to-right text only in TextField
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / shaper.cpp
1 /*
2  * Copyright (c) 2019 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/shaping.h>
23
24 namespace Dali
25 {
26
27 namespace Toolkit
28 {
29
30 namespace Text
31 {
32
33 CharacterIndex min( CharacterIndex index0,
34                     CharacterIndex index1 )
35 {
36   return ( index0 < index1 ) ? index0 : index1;
37 }
38
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 )
50 {
51   if( 0u == numberOfCharacters )
52   {
53     // Nothing to do if there are no characters.
54     return;
55   }
56
57 #ifdef DEBUG_ENABLED
58   const Length numberOfFontRuns = fonts.Count();
59   const Length numberOfScriptRuns = scripts.Count();
60   const Length totalNumberOfCharacters = text.Count();
61 #endif
62
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." );
66
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." );
70
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.
74
75   TextAbstraction::Shaping shaping = TextAbstraction::Shaping::Get();
76
77   // To shape the text a font and an script is needed.
78
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 )
82   {
83     const FontRun& run = *fontRunIt;
84     if( startCharacterIndex < run.characterRun.characterIndex + run.characterRun.numberOfCharacters )
85     {
86       // Found.
87       break;
88     }
89   }
90
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 )
94   {
95     const ScriptRun& run = *scriptRunIt;
96     if( startCharacterIndex < run.characterRun.characterIndex + run.characterRun.numberOfCharacters )
97     {
98       // Found.
99       break;
100     }
101   }
102
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;
105
106   // The current font id and script used to shape the text.
107   FontId currentFontId = 0u;
108   Script currentScript = TextAbstraction::UNKNOWN;
109
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.
113
114   GlyphInfo glyphInfo;
115   glyphInfo.isItalicRequired = false;
116   glyphInfo.isBoldRequired = false;
117
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 );
122
123   // The actual number of glyphs.
124   Length totalNumberOfGlyphs = currentNumberOfGlyphs;
125   // The number of new glyphs.
126   Length numberOfNewGlyphs = 0u;
127
128   const Character* const textBuffer = text.Begin();
129   const LineBreakInfo* const lineBreakInfoBuffer = lineBreakInfo.Begin();
130   CharacterIndex* glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
131
132   Length glyphIndex = startGlyphIndex;
133
134   // Traverse the characters and shape the text.
135   const CharacterIndex lastCharacter = startCharacterIndex + numberOfCharacters;
136   for( previousIndex = startCharacterIndex; previousIndex < lastCharacter; )
137   {
138     // Get the font id and the script.
139     const FontRun& fontRun = *fontRunIt;
140     const ScriptRun& scriptRun = *scriptRunIt;
141
142     currentFontId = fontRun.fontId;
143     currentScript = scriptRun.script;
144     const bool isItalicRequired = fontRun.isItalicRequired;
145     const bool isBoldRequired = fontRun.isBoldRequired;
146
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 );
150
151     // Check if there is a line must break.
152     bool mustBreak = false;
153
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;
158
159     for( CharacterIndex index = previousIndex; index < currentIndex; ++index )
160     {
161       mustBreak = TextAbstraction::LINE_MUST_BREAK == *( lineBreakInfoBuffer + index );
162       if( mustBreak )
163       {
164         isNewParagraph = TextAbstraction::IsNewParagraph( *( textBuffer + index ) );
165         currentIndex = index + 1u;
166         break;
167       }
168     }
169
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.
173                                                  currentFontId,
174                                                  currentScript );
175
176     // Retrieve the glyphs and the glyph to character conversion map.
177     Vector<GlyphInfo> tmpGlyphs;
178     Vector<CharacterIndex> tmpGlyphToCharacterMap;
179
180     GlyphInfo glyphInfo;
181     glyphInfo.isItalicRequired = isItalicRequired;
182     glyphInfo.isBoldRequired = isBoldRequired;
183
184     tmpGlyphs.Resize( numberOfGlyphs, glyphInfo );
185     tmpGlyphToCharacterMap.Resize( numberOfGlyphs );
186     shaping.GetGlyphs( tmpGlyphs.Begin(),
187                        tmpGlyphToCharacterMap.Begin() );
188
189     // Update the new indices of the glyph to character map.
190     if( 0u != totalNumberOfGlyphs )
191     {
192       for( Vector<CharacterIndex>::Iterator it = tmpGlyphToCharacterMap.Begin(),
193              endIt = tmpGlyphToCharacterMap.End();
194            it != endIt;
195            ++it )
196       {
197         *it += previousIndex;
198       }
199     }
200
201     totalNumberOfGlyphs += numberOfGlyphs;
202     numberOfNewGlyphs += numberOfGlyphs;
203
204     glyphs.Insert( glyphs.Begin() + glyphIndex, tmpGlyphs.Begin(), tmpGlyphs.End() );
205     glyphToCharacterMap.Insert( glyphToCharacterMap.Begin() + glyphIndex, tmpGlyphToCharacterMap.Begin(), tmpGlyphToCharacterMap.End() );
206     glyphIndex += numberOfGlyphs;
207
208     // Set the buffer pointers again.
209     glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
210
211     if( isNewParagraph )
212     {
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 );
216     }
217
218     // Update the iterators to get the next font or script run.
219     if( currentIndex == fontRun.characterRun.characterIndex + fontRun.characterRun.numberOfCharacters )
220     {
221       ++fontRunIt;
222     }
223     if( currentIndex == scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters )
224     {
225       ++scriptRunIt;
226     }
227
228     // Update the previous index.
229     previousIndex = currentIndex;
230   }
231
232   // Update indices.
233   for( Length index = startGlyphIndex + numberOfNewGlyphs; index < totalNumberOfGlyphs; ++index )
234   {
235     CharacterIndex& characterIndex = *( glyphToCharacterMapBuffer + index );
236     characterIndex += numberOfCharacters;
237   }
238
239   // Add the number of characters per glyph.
240   charactersPerGlyph.Reserve( totalNumberOfGlyphs );
241   Length* charactersPerGlyphBuffer = charactersPerGlyph.Begin();
242
243   const GlyphIndex lastGlyph = startGlyphIndex + numberOfNewGlyphs;
244   previousIndex = startCharacterIndex;
245   for( Length index = startGlyphIndex + 1u; index < lastGlyph; ++index )
246   {
247     const CharacterIndex characterIndex = *( glyphToCharacterMapBuffer + index );
248
249     charactersPerGlyph.Insert( charactersPerGlyphBuffer + index - 1u, characterIndex - previousIndex );
250
251     previousIndex = characterIndex;
252   }
253   charactersPerGlyph.Insert( charactersPerGlyphBuffer + lastGlyph - 1u, numberOfCharacters + startCharacterIndex - previousIndex );
254
255   // Resize the vectors to set the right number of items.
256   glyphs.Resize( totalNumberOfGlyphs );
257   glyphToCharacterMap.Resize( totalNumberOfGlyphs );
258 }
259
260 } // namespace Text
261
262 } // namespace Toolkit
263
264 } // namespace Dali