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/multi-language-support-impl.h>
23 #include <dali/integration-api/debug.h>
24 #include <dali/public-api/adaptor-framework/singleton-service.h>
25 #include <dali/public-api/text-abstraction/font-client.h>
28 #include <dali-toolkit/public-api/text/logical-model.h>
29 #include <dali-toolkit/public-api/text/font-run.h>
30 #include <dali-toolkit/public-api/text/script.h>
31 #include <dali-toolkit/public-api/text/script-run.h>
46 * @brief Retrieves the font Id from the font run for a given character's @p index.
48 * If the character's index exceeds the current font run it increases the iterator to get the next one.
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.
54 * @return The font id.
56 FontId GetFontId( Length index,
57 Vector<FontRun>::ConstIterator& fontRunIt,
58 const Vector<FontRun>::ConstIterator& fontRunEndIt )
62 if( fontRunIt != fontRunEndIt )
64 const FontRun& fontRun = *fontRunIt;
66 if( ( index >= fontRun.characterRun.characterIndex ) &&
67 ( index < fontRun.characterRun.characterIndex + fontRun.characterRun.numberOfCharacters ) )
69 fontId = fontRun.fontId;
72 if( index + 1u == fontRun.characterRun.characterIndex + fontRun.characterRun.numberOfCharacters )
74 // All the characters of the current run have been traversed. Get the next one for the next iteration.
83 * @brief Retrieves the script Id from the script run for a given character's @p index.
85 * If the character's index exceeds the current script run it increases the iterator to get the next one.
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.
93 Script GetScript( Length index,
94 Vector<ScriptRun>::ConstIterator& scriptRunIt,
95 const Vector<ScriptRun>::ConstIterator& scriptRunEndIt )
97 Script script = TextAbstraction::UNKNOWN;
99 if( scriptRunIt != scriptRunEndIt )
101 const ScriptRun& scriptRun = *scriptRunIt;
103 if( ( index >= scriptRun.characterRun.characterIndex ) &&
104 ( index < scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters ) )
106 script = scriptRun.script;
109 if( index + 1u == scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters )
111 // All the characters of the current run have been traversed. Get the next one for the next iteration.
120 * @brief Whether the character is valid for all scripts. i.e. the white space.
122 * @param[in] character The character.
124 * @return @e true if the character is valid for all scripts.
126 bool IsValidForAllScripts( Character character )
128 return ( IsWhiteSpace( character ) ||
129 IsZeroWidthNonJoiner( character ) ||
130 IsZeroWidthJoiner( character ) ||
131 IsZeroWidthSpace( character ) ||
132 IsLeftToRightMark( character ) ||
133 IsRightToLeftMark( character ) ||
134 IsThinSpace( character ) );
137 bool ValidateFontsPerScript::FindValidFont( FontId fontId ) const
139 for( Vector<FontId>::ConstIterator it = mValidFonts.Begin(),
140 endIt = mValidFonts.End();
153 MultilanguageSupport::MultilanguageSupport()
154 : mDefaultFontPerScriptCache(),
155 mValidFontsPerScriptCache()
157 // Initializes the default font cache to zero (invalid font).
158 // Reserves space to cache the default fonts and access them with the script as an index.
159 mDefaultFontPerScriptCache.Resize( TextAbstraction::UNKNOWN, 0u );
161 // Initializes the valid fonts cache to NULL (no valid fonts).
162 // Reserves space to cache the valid fonts and access them with the script as an index.
163 mValidFontsPerScriptCache.Resize( TextAbstraction::UNKNOWN, NULL );
166 MultilanguageSupport::~MultilanguageSupport()
168 // Destroy the valid fonts per script cache.
170 for( Vector<ValidateFontsPerScript*>::Iterator it = mValidFontsPerScriptCache.Begin(),
171 endIt = mValidFontsPerScriptCache.End();
179 Text::MultilanguageSupport MultilanguageSupport::Get()
181 Text::MultilanguageSupport multilanguageSupportHandle;
183 SingletonService service( SingletonService::Get() );
186 // Check whether the singleton is already created
187 Dali::BaseHandle handle = service.GetSingleton( typeid( Text::MultilanguageSupport ) );
190 // If so, downcast the handle
191 MultilanguageSupport* impl = dynamic_cast< Internal::MultilanguageSupport* >( handle.GetObjectPtr() );
192 multilanguageSupportHandle = Text::MultilanguageSupport( impl );
194 else // create and register the object
196 multilanguageSupportHandle = Text::MultilanguageSupport( new MultilanguageSupport );
197 service.Register( typeid( multilanguageSupportHandle ), multilanguageSupportHandle );
201 return multilanguageSupportHandle;
204 void MultilanguageSupport::SetScripts( const Vector<Character>& text,
205 const Vector<LineBreakInfo>& lineBreakInfo,
206 Vector<ScriptRun>& scripts )
208 const Length numberOfCharacters = text.Count();
210 if( 0u == numberOfCharacters )
212 // Nothing to do if there are no characters.
216 // Stores the current script run.
217 ScriptRun currentScriptRun;
218 currentScriptRun.characterRun.characterIndex = 0u;
219 currentScriptRun.characterRun.numberOfCharacters = 0u;
220 currentScriptRun.script = TextAbstraction::UNKNOWN;
222 // Reserve some space to reduce the number of reallocations.
223 scripts.Reserve( numberOfCharacters << 2u );
225 // Whether the first valid script need to be set.
226 bool firstValidScript = true;
228 // Whether the first valid script is a right to left script.
229 bool isParagraphRTL = false;
231 // Count the number of characters which are valid for all scripts. i.e. white spaces or '\n'.
232 Length numberOfAllScriptCharacters = 0u;
234 // Pointers to the text and break info buffers.
235 const Character* textBuffer = text.Begin();
236 const LineBreakInfo* breakInfoBuffer = lineBreakInfo.Begin();
238 // Traverse all characters and set the scripts.
239 for( Length index = 0u; index < numberOfCharacters; ++index )
241 Character character = *( textBuffer + index );
242 LineBreakInfo breakInfo = *( breakInfoBuffer + index );
244 // Some characters (like white spaces) are valid for many scripts. The rules to set a script
246 // - If they are at the begining of a paragraph they get the script of the first character with
247 // a defined script. If they are at the end, they get the script of the last one.
248 // - If they are between two scripts with the same direction, they get the script of the previous
249 // character with a defined script. If the two scripts have different directions, they get the
250 // script of the first character of the paragraph with a defined script.
252 // Skip those characters valid for many scripts like white spaces or '\n'.
253 bool endOfText = index == numberOfCharacters;
255 IsValidForAllScripts( character ) )
257 // Count all these characters to be added into a script.
258 ++numberOfAllScriptCharacters;
260 if( TextAbstraction::LINE_MUST_BREAK == breakInfo )
262 // The next character is a new paragraph.
263 // Know when there is a new paragraph is needed because if there is a white space
264 // between two scripts with different directions, it is added to the script with
265 // the same direction than the first script of the paragraph.
266 firstValidScript = true;
267 isParagraphRTL = false;
270 // Get the next character.
272 endOfText = index == numberOfCharacters;
275 character = *( textBuffer + index );
276 breakInfo = *( breakInfoBuffer + index );
282 // Last characters of the text are 'white spaces'.
283 // There is nothing else to do. Just add the remaining characters to the last script after this bucle.
287 // Get the script of the character.
288 Script script = GetCharacterScript( character );
290 // Check if it is the first character of a paragraph.
291 if( firstValidScript &&
292 ( TextAbstraction::UNKNOWN != script ) )
294 // Sets the direction of the first valid script.
295 isParagraphRTL = ( TextAbstraction::ARABIC == script );
296 firstValidScript = false;
299 if( script != currentScriptRun.script )
301 // Current run needs to be stored and a new one initialized.
303 if( isParagraphRTL != ( TextAbstraction::ARABIC == script ) )
305 // Current script has different direction than the first script of the paragraph.
306 // All the previously skipped characters need to be added to the previous script before it's stored.
307 currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters;
308 numberOfAllScriptCharacters = 0u;
311 if( 0u != currentScriptRun.characterRun.numberOfCharacters )
313 // Store the script run.
314 scripts.PushBack( currentScriptRun );
317 // Initialize the new one.
318 currentScriptRun.characterRun.characterIndex = currentScriptRun.characterRun.characterIndex + currentScriptRun.characterRun.numberOfCharacters;
319 currentScriptRun.characterRun.numberOfCharacters = numberOfAllScriptCharacters; // Adds the white spaces which are at the begining of the script.
320 currentScriptRun.script = script;
321 numberOfAllScriptCharacters = 0u;
325 // Adds white spaces between characters.
326 currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters;
327 numberOfAllScriptCharacters = 0u;
330 if( TextAbstraction::LINE_MUST_BREAK == breakInfo )
332 // The next character is a new paragraph.
333 firstValidScript = true;
334 isParagraphRTL = false;
337 // Add one more character to the run.
338 ++currentScriptRun.characterRun.numberOfCharacters;
341 // Add remaining characters into the last script.
342 currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters;
343 if( 0u != currentScriptRun.characterRun.numberOfCharacters )
345 if( TextAbstraction::UNKNOWN == currentScriptRun.script )
347 // There are only white spaces in the last script. Set the latin script.
348 currentScriptRun.script = TextAbstraction::LATIN;
351 // Store the last run.
352 scripts.PushBack( currentScriptRun );
356 void MultilanguageSupport::ReplaceScripts( LogicalModel& model,
357 CharacterIndex characterIndex,
358 Length numberOfCharactersToRemove,
359 Length numberOfCharactersToInsert )
363 void MultilanguageSupport::ValidateFonts( const Vector<Character>& text,
364 const Vector<ScriptRun>& scripts,
365 Vector<FontRun>& fonts )
367 const Length numberOfCharacters = text.Count();
369 if( 0u == numberOfCharacters )
371 // Nothing to do if there are no characters.
375 // Copy the fonts set by application developers.
376 const Length numberOfFontRuns = fonts.Count();
377 const Vector<FontRun> definedFonts = fonts;
380 // Traverse the characters and validate/set the fonts.
383 FontId* defaultFontPerScriptCacheBuffer = mDefaultFontPerScriptCache.Begin();
384 ValidateFontsPerScript** validFontsPerScriptCacheBuffer = mValidFontsPerScriptCache.Begin();
386 // Stores the validated font runs.
387 fonts.Reserve( numberOfFontRuns );
389 // Initializes a validated font run.
390 FontRun currentFontRun;
391 currentFontRun.characterRun.characterIndex = 0u;
392 currentFontRun.characterRun.numberOfCharacters = 0u;
393 currentFontRun.fontId = 0u;
394 currentFontRun.isDefault = false;
396 // Get the font client.
397 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
399 // Iterators of the font and script runs.
400 Vector<FontRun>::ConstIterator fontRunIt = definedFonts.Begin();
401 Vector<FontRun>::ConstIterator fontRunEndIt = definedFonts.End();
402 Vector<ScriptRun>::ConstIterator scriptRunIt = scripts.Begin();
403 Vector<ScriptRun>::ConstIterator scriptRunEndIt = scripts.End();
405 for( Length index = 0u; index < numberOfCharacters; ++index )
407 // Get the character.
408 const Character character = *( text.Begin() + index );
410 // Get the font for the character.
411 FontId fontId = GetFontId( index,
415 // Get the script for the character.
416 Script script = GetScript( index,
420 if( TextAbstraction::UNKNOWN == script )
422 DALI_LOG_WARNING( "MultilanguageSupport::ValidateFonts. Unknown script!" );
423 script = TextAbstraction::LATIN;
426 // Whether the font being validated is a default one not set by the user.
427 const bool isDefault = ( 0u == fontId );
429 // The default font point size.
430 PointSize26Dot6 pointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
434 // Validate if the font set by the user supports the character.
436 // Check first in the caches.
438 // The user may have set the default font. Check it. Otherwise check in the valid fonts cache.
439 if( fontId != *( defaultFontPerScriptCacheBuffer + script ) )
441 // Check in the valid fonts cache.
442 ValidateFontsPerScript* validateFontsPerScript = *( validFontsPerScriptCacheBuffer + script );
443 if( NULL != validateFontsPerScript )
445 if( !validateFontsPerScript->FindValidFont( fontId ) )
447 // Use the font client to validate the font.
448 const GlyphIndex glyphIndex = fontClient.GetGlyphIndex( fontId, character );
450 if( 0u == glyphIndex )
452 // Get the point size of the current font. It will be used to get a default font id.
453 pointSize = fontClient.GetPointSize( fontId );
455 // The font is not valid. Set to zero and a default one will be set.
460 // Add the font to the valid font cache.
461 validateFontsPerScript->mValidFonts.PushBack( fontId );
467 // Use the font client to validate the font.
468 const GlyphIndex glyphIndex = fontClient.GetGlyphIndex( fontId, character );
470 if( 0u == glyphIndex )
472 // Get the point size of the current font. It will be used to get a default font id.
473 pointSize = fontClient.GetPointSize( fontId );
475 // The font is not valid. Set to zero and a default one will be set.
480 // Add the font to the valid font cache.
481 validateFontsPerScript = new ValidateFontsPerScript();
482 *( validFontsPerScriptCacheBuffer + script ) = validateFontsPerScript;
484 validateFontsPerScript->mValidFonts.PushBack( fontId );
490 // The font has not been validated. Find a default one.
493 // The character has no font assigned. Get a default one from the cache
494 fontId = *( defaultFontPerScriptCacheBuffer + script );
496 // If the cache has not a default font, get one from the font client.
499 // Find a default font.
500 fontId = fontClient.FindDefaultFont( character, pointSize );
503 *( defaultFontPerScriptCacheBuffer + script ) = fontId;
507 // The font is now validated.
509 if( ( fontId != currentFontRun.fontId ) ||
510 ( isDefault != currentFontRun.isDefault ) )
512 // Current run needs to be stored and a new one initialized.
514 if( 0u != currentFontRun.characterRun.numberOfCharacters )
516 // Store the font run.
517 fonts.PushBack( currentFontRun );
520 // Initialize the new one.
521 currentFontRun.characterRun.characterIndex = currentFontRun.characterRun.characterIndex + currentFontRun.characterRun.numberOfCharacters;
522 currentFontRun.characterRun.numberOfCharacters = 0u;
523 currentFontRun.fontId = fontId;
524 currentFontRun.isDefault = isDefault;
527 // Add one more character to the run.
528 ++currentFontRun.characterRun.numberOfCharacters;
531 if( 0u != currentFontRun.characterRun.numberOfCharacters )
533 // Store the last run.
534 fonts.PushBack( currentFontRun );
538 void MultilanguageSupport::ValidateFonts( LogicalModel& model,
539 CharacterIndex characterIndex,
540 Length numberOfCharactersToRemove,
541 Length numberOfCharactersToInsert )
545 } // namespace Internal
549 } // namespace Toolkit