2d1f5e66cd6a9a2fbe4d0cf3ff091ea62d927db2
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / multi-language-support-impl.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/multi-language-support-impl.h>
20
21 // INTERNAL INCLUDES
22 #include <dali/public-api/adaptor-framework/singleton-service.h>
23 #include <dali/public-api/text-abstraction/font-client.h>
24 #include <dali-toolkit/public-api/text/logical-model.h>
25 #include <dali-toolkit/public-api/text/font-run.h>
26 #include <dali-toolkit/public-api/text/script.h>
27 #include <dali-toolkit/public-api/text/script-run.h>
28 #include <dali/integration-api/debug.h>
29
30 // EXTERNAL INCLUDES
31 #include <memory.h>
32
33 namespace Dali
34 {
35
36 namespace Toolkit
37 {
38
39 namespace Text
40 {
41
42 namespace Internal
43 {
44
45 /**
46  * @brief Retrieves the font Id from the font run for a given character's @p index.
47  *
48  * If the character's index exceeds the current font run it increases the iterator to get the next one.
49  *
50  * @param[in] index The character's index.
51  * @param[in,out] fontRunIt Iterator to the current font run.
52  * @param[in] fontRunEndIt Iterator to one after the last font run.
53  *
54  * @return The font id.
55  */
56 FontId GetFontId( Length index,
57                   Vector<FontRun>::ConstIterator& fontRunIt,
58                   const Vector<FontRun>::ConstIterator& fontRunEndIt )
59 {
60   FontId fontId = 0u;
61
62   if( fontRunIt != fontRunEndIt )
63   {
64     const FontRun& fontRun = *fontRunIt;
65
66     if( ( index >= fontRun.characterRun.characterIndex ) &&
67         ( index < fontRun.characterRun.characterIndex + fontRun.characterRun.numberOfCharacters ) )
68     {
69       fontId = fontRun.fontId;
70     }
71
72     if( index + 1u == fontRun.characterRun.characterIndex + fontRun.characterRun.numberOfCharacters )
73     {
74       // All the characters of the current run have been traversed. Get the next one for the next iteration.
75       ++fontRunIt;
76     }
77   }
78
79   return fontId;
80 }
81
82 /**
83  * @brief Retrieves the script Id from the script run for a given character's @p index.
84  *
85  * If the character's index exceeds the current script run it increases the iterator to get the next one.
86  *
87  * @param[in] index The character's index.
88  * @param[in,out] scriptRunIt Iterator to the current font run.
89  * @param[in] scriptRunEndIt Iterator to one after the last script run.
90  *
91  * @return The script.
92  */
93 Script GetScript( Length index,
94                   Vector<ScriptRun>::ConstIterator& scriptRunIt,
95                   const Vector<ScriptRun>::ConstIterator& scriptRunEndIt )
96 {
97   Script script = TextAbstraction::UNKNOWN;
98
99   if( scriptRunIt != scriptRunEndIt )
100   {
101     const ScriptRun& scriptRun = *scriptRunIt;
102
103     if( ( index >= scriptRun.characterRun.characterIndex ) &&
104         ( index < scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters ) )
105     {
106       script = scriptRun.script;
107     }
108
109     if( index + 1u == scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters )
110     {
111       // All the characters of the current run have been traversed. Get the next one for the next iteration.
112       ++scriptRunIt;
113     }
114   }
115
116   return script;
117 }
118
119 bool ValidateFontsPerScript::FindValidFont( FontId fontId ) const
120 {
121   for( Vector<FontId>::ConstIterator it = mValidFonts.Begin(),
122          endIt = mValidFonts.End();
123        it != endIt;
124        ++it )
125   {
126     if( fontId == *it )
127     {
128       return true;
129     }
130   }
131
132   return false;
133 }
134
135 MultilanguageSupport::MultilanguageSupport()
136 : mDefaultFontPerScriptCache(),
137   mValidFontsPerScriptCache()
138 {
139   // Initializes the default font cache to zero (invalid font).
140   // Reserves space to cache the default fonts and access them with the script as an index.
141   mDefaultFontPerScriptCache.Resize( TextAbstraction::UNKNOWN, 0u );
142
143   // Initializes the valid fonts cache to NULL (no valid fonts).
144   // Reserves space to cache the valid fonts and access them with the script as an index.
145   mValidFontsPerScriptCache.Resize( TextAbstraction::UNKNOWN, NULL );
146 }
147
148 MultilanguageSupport::~MultilanguageSupport()
149 {
150   // Destroy the valid fonts per script cache.
151
152   for( Vector<ValidateFontsPerScript*>::Iterator it = mValidFontsPerScriptCache.Begin(),
153          endIt = mValidFontsPerScriptCache.End();
154        it != endIt;
155        ++it )
156   {
157     delete *it;
158   }
159 }
160
161 Text::MultilanguageSupport MultilanguageSupport::Get()
162 {
163   Text::MultilanguageSupport multilanguageSupportHandle;
164
165   SingletonService service( SingletonService::Get() );
166   if( service )
167   {
168     // Check whether the singleton is already created
169     Dali::BaseHandle handle = service.GetSingleton( typeid( Text::MultilanguageSupport ) );
170     if( handle )
171     {
172       // If so, downcast the handle
173       MultilanguageSupport* impl = dynamic_cast< Internal::MultilanguageSupport* >( handle.GetObjectPtr() );
174       multilanguageSupportHandle = Text::MultilanguageSupport( impl );
175     }
176     else // create and register the object
177     {
178       multilanguageSupportHandle = Text::MultilanguageSupport( new MultilanguageSupport );
179       service.Register( typeid( multilanguageSupportHandle ), multilanguageSupportHandle );
180     }
181   }
182
183   return multilanguageSupportHandle;
184 }
185
186 void MultilanguageSupport::SetScripts( const Vector<Character>& text,
187                                        Vector<ScriptRun>& scripts )
188 {
189   const Length numberOfCharacters = text.Count();
190
191   if( 0u == numberOfCharacters )
192   {
193     // Nothing to do if there are no characters.
194     return;
195   }
196
197   // Traverse all characters and set the scripts.
198
199   // Stores the current script run.
200   ScriptRun currentScriptRun;
201   currentScriptRun.characterRun.characterIndex = 0u;
202   currentScriptRun.characterRun.numberOfCharacters = 0u;
203   currentScriptRun.script = TextAbstraction::UNKNOWN;
204
205   // Reserve some space to reduce the number of reallocations.
206   scripts.Reserve( numberOfCharacters << 2u );
207
208   for( Length index = 0u; index < numberOfCharacters; ++index )
209   {
210     const Character character = *( text.Begin() + index );
211
212     Script script = GetCharacterScript( character );
213
214     if( TextAbstraction::UNKNOWN == script )
215     {
216       script = TextAbstraction::LATIN;
217       DALI_ASSERT_DEBUG( !"MultilanguageSupport::SetScripts. Unkown script!" );
218     }
219
220     if( script != currentScriptRun.script )
221     {
222       // Current run needs to be stored and a new one initialized.
223
224       if( 0u != currentScriptRun.characterRun.numberOfCharacters )
225       {
226         // Store the script run.
227         scripts.PushBack( currentScriptRun );
228       }
229
230       // Initialize the new one.
231       currentScriptRun.characterRun.characterIndex = currentScriptRun.characterRun.characterIndex + currentScriptRun.characterRun.numberOfCharacters;
232       currentScriptRun.characterRun.numberOfCharacters = 0u;
233       currentScriptRun.script = script;
234     }
235
236     // Add one more character to the run.
237     ++currentScriptRun.characterRun.numberOfCharacters;
238   }
239
240   if( 0u != currentScriptRun.characterRun.numberOfCharacters )
241   {
242     // Store the last run.
243     scripts.PushBack( currentScriptRun );
244   }
245 }
246
247 void MultilanguageSupport::ValidateFonts( const Vector<Character>& text,
248                                           const Vector<ScriptRun>& scripts,
249                                           Vector<FontRun>& fonts )
250 {
251   const Length numberOfCharacters = text.Count();
252
253   if( 0u == numberOfCharacters )
254   {
255     // Nothing to do if there are no characters.
256     return;
257   }
258
259   // Copy the fonts set by application developers.
260   const Length numberOfFontRuns = fonts.Count();
261   const Vector<FontRun> definedFonts = fonts;
262   fonts.Clear();
263
264   // Traverse the characters and validate/set the fonts.
265
266   // Get the caches.
267   FontId* defaultFontPerScriptCacheBuffer = mDefaultFontPerScriptCache.Begin();
268   ValidateFontsPerScript** validFontsPerScriptCacheBuffer = mValidFontsPerScriptCache.Begin();
269
270   // Stores the validated font runs.
271   fonts.Reserve( numberOfFontRuns );
272
273   // Initializes a validated font run.
274   FontRun currentFontRun;
275   currentFontRun.characterRun.characterIndex = 0u;
276   currentFontRun.characterRun.numberOfCharacters = 0u;
277   currentFontRun.fontId = 0u;
278   currentFontRun.isDefault = false;
279
280   // Get the font client.
281   TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
282
283   // Iterators of the font and script runs.
284   Vector<FontRun>::ConstIterator fontRunIt = definedFonts.Begin();
285   Vector<FontRun>::ConstIterator fontRunEndIt = definedFonts.End();
286   Vector<ScriptRun>::ConstIterator scriptRunIt = scripts.Begin();
287   Vector<ScriptRun>::ConstIterator scriptRunEndIt = scripts.End();
288
289   for( Length index = 0u; index < numberOfCharacters; ++index )
290   {
291     // Get the character.
292     const Character character = *( text.Begin() + index );
293
294     // Get the font for the character.
295     FontId fontId = GetFontId( index,
296                                fontRunIt,
297                                fontRunEndIt );
298
299     // Get the script for the character.
300     Script script = GetScript( index,
301                                scriptRunIt,
302                                scriptRunEndIt );
303
304     if( TextAbstraction::UNKNOWN == script )
305     {
306       DALI_LOG_WARNING( "MultilanguageSupport::ValidateFonts. Unknown script!" );
307       script = TextAbstraction::LATIN;
308     }
309
310     // Whether the font being validated is a default one not set by the user.
311     const bool isDefault = ( 0u == fontId );
312
313     // The default font point size.
314     PointSize26Dot6 pointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
315
316     if( !isDefault )
317     {
318       // Validate if the font set by the user supports the character.
319
320       // Check first in the caches.
321
322       // The user may have set the default font. Check it. Otherwise check in the valid fonts cache.
323       if( fontId != *( defaultFontPerScriptCacheBuffer + script ) )
324       {
325         // Check in the valid fonts cache.
326         ValidateFontsPerScript* validateFontsPerScript = *( validFontsPerScriptCacheBuffer + script );
327         if( NULL != validateFontsPerScript )
328         {
329           if( !validateFontsPerScript->FindValidFont( fontId ) )
330           {
331             // Use the font client to validate the font.
332             const GlyphIndex glyphIndex = fontClient.GetGlyphIndex( fontId, character );
333
334             if( 0u == glyphIndex )
335             {
336               // Get the point size of the current font. It will be used to get a default font id.
337               pointSize = fontClient.GetPointSize( fontId );
338
339               // The font is not valid. Set to zero and a default one will be set.
340               fontId = 0u;
341             }
342             else
343             {
344               // Add the font to the valid font cache.
345               validateFontsPerScript->mValidFonts.PushBack( fontId );
346             }
347           }
348         }
349         else
350         {
351           // Use the font client to validate the font.
352           const GlyphIndex glyphIndex = fontClient.GetGlyphIndex( fontId, character );
353
354           if( 0u == glyphIndex )
355           {
356             // Get the point size of the current font. It will be used to get a default font id.
357             pointSize = fontClient.GetPointSize( fontId );
358
359             // The font is not valid. Set to zero and a default one will be set.
360             fontId = 0u;
361           }
362           else
363           {
364             // Add the font to the valid font cache.
365             validateFontsPerScript = new ValidateFontsPerScript();
366             *( validFontsPerScriptCacheBuffer + script ) = validateFontsPerScript;
367
368             validateFontsPerScript->mValidFonts.PushBack( fontId );
369           }
370         }
371       }
372     }
373
374     // The font has not been validated. Find a default one.
375     if( 0u == fontId )
376     {
377       // The character has no font assigned. Get a default one from the cache
378       fontId = *( defaultFontPerScriptCacheBuffer + script );
379
380       // If the cache has not a default font, get one from the font client.
381       if( 0u == fontId )
382       {
383         // Find a default font.
384         fontId = fontClient.FindDefaultFont( character, pointSize );
385
386         // Cache the font.
387         *( defaultFontPerScriptCacheBuffer + script ) = fontId;
388       }
389     }
390
391     // The font is now validated.
392
393     if( ( fontId != currentFontRun.fontId ) ||
394         ( isDefault != currentFontRun.isDefault ) )
395     {
396       // Current run needs to be stored and a new one initialized.
397
398       if( 0u != currentFontRun.characterRun.numberOfCharacters )
399       {
400         // Store the font run.
401         fonts.PushBack( currentFontRun );
402       }
403
404       // Initialize the new one.
405       currentFontRun.characterRun.characterIndex = currentFontRun.characterRun.characterIndex + currentFontRun.characterRun.numberOfCharacters;
406       currentFontRun.characterRun.numberOfCharacters = 0u;
407       currentFontRun.fontId = fontId;
408       currentFontRun.isDefault = isDefault;
409     }
410
411     // Add one more character to the run.
412     ++currentFontRun.characterRun.numberOfCharacters;
413   }
414
415   if( 0u != currentFontRun.characterRun.numberOfCharacters )
416   {
417     // Store the last run.
418     fonts.PushBack( currentFontRun );
419   }
420 }
421
422 } // namespace Internal
423
424 } // namespace Text
425
426 } // namespace Toolkit
427
428 } // namespace Dali