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/internal/text/logical-model.h>
29 #include <dali-toolkit/internal/text/font-run.h>
30 #include <dali-toolkit/internal/text/script.h>
31 #include <dali-toolkit/internal/text/script-run.h>
41 #if defined(DEBUG_ENABLED)
42 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_MULTI_LANGUAGE_SUPPORT");
45 const Dali::Toolkit::Text::Character UTF32_A = 0x0041;
55 * @brief Retrieves the font Id from the font run for a given character's @p index.
57 * If the character's index exceeds the current font run it increases the iterator to get the next one.
59 * @param[in] index The character's index.
60 * @param[in,out] fontRunIt Iterator to the current font run.
61 * @param[in] fontRunEndIt Iterator to one after the last font run.
63 * @return The font id.
65 FontId GetFontId( Length index,
66 Vector<FontRun>::ConstIterator& fontRunIt,
67 const Vector<FontRun>::ConstIterator& fontRunEndIt )
71 if( fontRunIt != fontRunEndIt )
73 const FontRun& fontRun = *fontRunIt;
75 if( ( index >= fontRun.characterRun.characterIndex ) &&
76 ( index < fontRun.characterRun.characterIndex + fontRun.characterRun.numberOfCharacters ) )
78 fontId = fontRun.fontId;
81 if( index + 1u == fontRun.characterRun.characterIndex + fontRun.characterRun.numberOfCharacters )
83 // All the characters of the current run have been traversed. Get the next one for the next iteration.
92 * @brief Retrieves the script Id from the script run for a given character's @p index.
94 * If the character's index exceeds the current script run it increases the iterator to get the next one.
96 * @param[in] index The character's index.
97 * @param[in,out] scriptRunIt Iterator to the current font run.
98 * @param[in] scriptRunEndIt Iterator to one after the last script run.
100 * @return The script.
102 Script GetScript( Length index,
103 Vector<ScriptRun>::ConstIterator& scriptRunIt,
104 const Vector<ScriptRun>::ConstIterator& scriptRunEndIt )
106 Script script = TextAbstraction::UNKNOWN;
108 if( scriptRunIt != scriptRunEndIt )
110 const ScriptRun& scriptRun = *scriptRunIt;
112 if( ( index >= scriptRun.characterRun.characterIndex ) &&
113 ( index < scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters ) )
115 script = scriptRun.script;
118 if( index + 1u == scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters )
120 // All the characters of the current run have been traversed. Get the next one for the next iteration.
129 * @brief Whether the character is valid for all scripts. i.e. the white space.
131 * @param[in] character The character.
133 * @return @e true if the character is valid for all scripts.
135 bool IsValidForAllScripts( Character character )
137 return ( IsWhiteSpace( character ) ||
138 IsZeroWidthNonJoiner( character ) ||
139 IsZeroWidthJoiner( character ) ||
140 IsZeroWidthSpace( character ) ||
141 IsLeftToRightMark( character ) ||
142 IsRightToLeftMark( character ) ||
143 IsThinSpace( character ) );
146 bool ValidateFontsPerScript::FindValidFont( FontId fontId ) const
148 for( Vector<FontId>::ConstIterator it = mValidFonts.Begin(),
149 endIt = mValidFonts.End();
162 MultilanguageSupport::MultilanguageSupport()
163 : mDefaultFontPerScriptCache(),
164 mValidFontsPerScriptCache()
166 // Initializes the default font cache to zero (invalid font).
167 // Reserves space to cache the default fonts and access them with the script as an index.
168 mDefaultFontPerScriptCache.Resize( TextAbstraction::UNKNOWN, 0u );
170 // Initializes the valid fonts cache to NULL (no valid fonts).
171 // Reserves space to cache the valid fonts and access them with the script as an index.
172 mValidFontsPerScriptCache.Resize( TextAbstraction::UNKNOWN, NULL );
175 MultilanguageSupport::~MultilanguageSupport()
177 // Destroy the valid fonts per script cache.
179 for( Vector<ValidateFontsPerScript*>::Iterator it = mValidFontsPerScriptCache.Begin(),
180 endIt = mValidFontsPerScriptCache.End();
188 Text::MultilanguageSupport MultilanguageSupport::Get()
190 Text::MultilanguageSupport multilanguageSupportHandle;
192 SingletonService service( SingletonService::Get() );
195 // Check whether the singleton is already created
196 Dali::BaseHandle handle = service.GetSingleton( typeid( Text::MultilanguageSupport ) );
199 // If so, downcast the handle
200 MultilanguageSupport* impl = dynamic_cast< Internal::MultilanguageSupport* >( handle.GetObjectPtr() );
201 multilanguageSupportHandle = Text::MultilanguageSupport( impl );
203 else // create and register the object
205 multilanguageSupportHandle = Text::MultilanguageSupport( new MultilanguageSupport );
206 service.Register( typeid( multilanguageSupportHandle ), multilanguageSupportHandle );
210 return multilanguageSupportHandle;
213 void MultilanguageSupport::SetScripts( const Vector<Character>& text,
214 const Vector<LineBreakInfo>& lineBreakInfo,
215 Vector<ScriptRun>& scripts )
217 const Length numberOfCharacters = text.Count();
219 if( 0u == numberOfCharacters )
221 // Nothing to do if there are no characters.
225 // Stores the current script run.
226 ScriptRun currentScriptRun;
227 currentScriptRun.characterRun.characterIndex = 0u;
228 currentScriptRun.characterRun.numberOfCharacters = 0u;
229 currentScriptRun.script = TextAbstraction::UNKNOWN;
231 // Reserve some space to reduce the number of reallocations.
232 scripts.Reserve( numberOfCharacters << 2u );
234 // Whether the first valid script need to be set.
235 bool firstValidScript = true;
237 // Whether the first valid script is a right to left script.
238 bool isParagraphRTL = false;
240 // Count the number of characters which are valid for all scripts. i.e. white spaces or '\n'.
241 Length numberOfAllScriptCharacters = 0u;
243 // Pointers to the text and break info buffers.
244 const Character* textBuffer = text.Begin();
245 const LineBreakInfo* breakInfoBuffer = lineBreakInfo.Begin();
247 // Traverse all characters and set the scripts.
248 for( Length index = 0u; index < numberOfCharacters; ++index )
250 Character character = *( textBuffer + index );
251 LineBreakInfo breakInfo = *( breakInfoBuffer + index );
253 // Some characters (like white spaces) are valid for many scripts. The rules to set a script
255 // - If they are at the begining of a paragraph they get the script of the first character with
256 // a defined script. If they are at the end, they get the script of the last one.
257 // - If they are between two scripts with the same direction, they get the script of the previous
258 // character with a defined script. If the two scripts have different directions, they get the
259 // script of the first character of the paragraph with a defined script.
261 // Skip those characters valid for many scripts like white spaces or '\n'.
262 bool endOfText = index == numberOfCharacters;
264 IsValidForAllScripts( character ) )
266 // Count all these characters to be added into a script.
267 ++numberOfAllScriptCharacters;
269 if( TextAbstraction::LINE_MUST_BREAK == breakInfo )
271 // The next character is a new paragraph.
272 // Know when there is a new paragraph is needed because if there is a white space
273 // between two scripts with different directions, it is added to the script with
274 // the same direction than the first script of the paragraph.
275 firstValidScript = true;
276 isParagraphRTL = false;
279 // Get the next character.
281 endOfText = index == numberOfCharacters;
284 character = *( textBuffer + index );
285 breakInfo = *( breakInfoBuffer + index );
291 // Last characters of the text are 'white spaces'.
292 // There is nothing else to do. Just add the remaining characters to the last script after this bucle.
296 // Get the script of the character.
297 Script script = GetCharacterScript( character );
299 // Check if it is the first character of a paragraph.
300 if( firstValidScript &&
301 ( TextAbstraction::UNKNOWN != script ) )
303 // Sets the direction of the first valid script.
304 isParagraphRTL = ( TextAbstraction::ARABIC == script );
305 firstValidScript = false;
308 if( script != currentScriptRun.script )
310 // Current run needs to be stored and a new one initialized.
312 if( isParagraphRTL != ( TextAbstraction::ARABIC == script ) )
314 // Current script has different direction than the first script of the paragraph.
315 // All the previously skipped characters need to be added to the previous script before it's stored.
316 currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters;
317 numberOfAllScriptCharacters = 0u;
320 if( 0u != currentScriptRun.characterRun.numberOfCharacters )
322 // Store the script run.
323 scripts.PushBack( currentScriptRun );
326 // Initialize the new one.
327 currentScriptRun.characterRun.characterIndex = currentScriptRun.characterRun.characterIndex + currentScriptRun.characterRun.numberOfCharacters;
328 currentScriptRun.characterRun.numberOfCharacters = numberOfAllScriptCharacters; // Adds the white spaces which are at the begining of the script.
329 currentScriptRun.script = script;
330 numberOfAllScriptCharacters = 0u;
334 // Adds white spaces between characters.
335 currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters;
336 numberOfAllScriptCharacters = 0u;
339 if( TextAbstraction::LINE_MUST_BREAK == breakInfo )
341 // The next character is a new paragraph.
342 firstValidScript = true;
343 isParagraphRTL = false;
346 // Add one more character to the run.
347 ++currentScriptRun.characterRun.numberOfCharacters;
350 // Add remaining characters into the last script.
351 currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters;
352 if( 0u != currentScriptRun.characterRun.numberOfCharacters )
354 if( TextAbstraction::UNKNOWN == currentScriptRun.script )
356 // There are only white spaces in the last script. Set the latin script.
357 currentScriptRun.script = TextAbstraction::LATIN;
360 // Store the last run.
361 scripts.PushBack( currentScriptRun );
365 void MultilanguageSupport::ReplaceScripts( LogicalModel& model,
366 CharacterIndex characterIndex,
367 Length numberOfCharactersToRemove,
368 Length numberOfCharactersToInsert )
372 void MultilanguageSupport::ValidateFonts( const Vector<Character>& text,
373 const Vector<ScriptRun>& scripts,
374 Vector<FontRun>& fonts )
376 const Length numberOfCharacters = text.Count();
378 if( 0u == numberOfCharacters )
380 // Nothing to do if there are no characters.
384 // Copy the fonts set by application developers.
385 const Length numberOfFontRuns = fonts.Count();
386 const Vector<FontRun> definedFonts = fonts;
389 // Traverse the characters and validate/set the fonts.
392 FontId* defaultFontPerScriptCacheBuffer = mDefaultFontPerScriptCache.Begin();
393 ValidateFontsPerScript** validFontsPerScriptCacheBuffer = mValidFontsPerScriptCache.Begin();
395 // Stores the validated font runs.
396 fonts.Reserve( numberOfFontRuns );
398 // Initializes a validated font run.
399 FontRun currentFontRun;
400 currentFontRun.characterRun.characterIndex = 0u;
401 currentFontRun.characterRun.numberOfCharacters = 0u;
402 currentFontRun.fontId = 0u;
403 currentFontRun.isDefault = false;
405 // Get the font client.
406 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
408 // Iterators of the font and script runs.
409 Vector<FontRun>::ConstIterator fontRunIt = definedFonts.Begin();
410 Vector<FontRun>::ConstIterator fontRunEndIt = definedFonts.End();
411 Vector<ScriptRun>::ConstIterator scriptRunIt = scripts.Begin();
412 Vector<ScriptRun>::ConstIterator scriptRunEndIt = scripts.End();
414 for( Length index = 0u; index < numberOfCharacters; ++index )
416 // Get the character.
417 const Character character = *( text.Begin() + index );
419 // Get the font for the character.
420 FontId fontId = GetFontId( index,
424 // Get the script for the character.
425 Script script = GetScript( index,
429 if( TextAbstraction::UNKNOWN == script )
431 DALI_LOG_WARNING( "MultilanguageSupport::ValidateFonts. Unknown script!" );
432 script = TextAbstraction::LATIN;
435 // Whether the font being validated is a default one not set by the user.
436 const bool isDefault = ( 0u == fontId );
438 // The default font point size.
439 PointSize26Dot6 pointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
443 // Validate if the font set by the user supports the character.
445 // Check first in the caches.
447 // The user may have set the default font. Check it. Otherwise check in the valid fonts cache.
448 if( fontId != *( defaultFontPerScriptCacheBuffer + script ) )
450 // Check in the valid fonts cache.
451 ValidateFontsPerScript* validateFontsPerScript = *( validFontsPerScriptCacheBuffer + script );
452 if( NULL != validateFontsPerScript )
454 if( !validateFontsPerScript->FindValidFont( fontId ) )
456 // Use the font client to validate the font.
457 const GlyphIndex glyphIndex = fontClient.GetGlyphIndex( fontId, character );
459 if( 0u == glyphIndex )
461 // Get the point size of the current font. It will be used to get a default font id.
462 pointSize = fontClient.GetPointSize( fontId );
464 // The font is not valid. Set to zero and a default one will be set.
469 // Add the font to the valid font cache.
470 validateFontsPerScript->mValidFonts.PushBack( fontId );
476 // Use the font client to validate the font.
477 const GlyphIndex glyphIndex = fontClient.GetGlyphIndex( fontId, character );
479 if( 0u == glyphIndex )
481 // Get the point size of the current font. It will be used to get a default font id.
482 pointSize = fontClient.GetPointSize( fontId );
484 // The font is not valid. Set to zero and a default one will be set.
489 // Add the font to the valid font cache.
490 validateFontsPerScript = new ValidateFontsPerScript();
491 *( validFontsPerScriptCacheBuffer + script ) = validateFontsPerScript;
493 validateFontsPerScript->mValidFonts.PushBack( fontId );
499 // The font has not been validated. Find a default one.
502 // The character has no font assigned. Get a default one from the cache
503 fontId = *( defaultFontPerScriptCacheBuffer + script );
505 // If the cache has not a default font, get one from the font client.
508 // Find a default font.
509 fontId = fontClient.FindDefaultFont( character, pointSize );
511 // If the system does not support a suitable font, fallback to Latin
514 fontId = *( defaultFontPerScriptCacheBuffer + TextAbstraction::LATIN );
518 fontId = fontClient.FindDefaultFont( UTF32_A, pointSize );
522 Dali::TextAbstraction::FontDescription description;
523 fontClient.GetDescription( fontId, description );
524 DALI_LOG_INFO( gLogFilter, Debug::Concise, "Script: %s; Selected font: %s\n", Dali::TextAbstraction::ScriptName[script], description.path.c_str() );
527 *( defaultFontPerScriptCacheBuffer + script ) = fontId;
531 // The font is now validated.
533 if( ( fontId != currentFontRun.fontId ) ||
534 ( isDefault != currentFontRun.isDefault ) )
536 // Current run needs to be stored and a new one initialized.
538 if( 0u != currentFontRun.characterRun.numberOfCharacters )
540 // Store the font run.
541 fonts.PushBack( currentFontRun );
544 // Initialize the new one.
545 currentFontRun.characterRun.characterIndex = currentFontRun.characterRun.characterIndex + currentFontRun.characterRun.numberOfCharacters;
546 currentFontRun.characterRun.numberOfCharacters = 0u;
547 currentFontRun.fontId = fontId;
548 currentFontRun.isDefault = isDefault;
551 // Add one more character to the run.
552 ++currentFontRun.characterRun.numberOfCharacters;
555 if( 0u != currentFontRun.characterRun.numberOfCharacters )
557 // Store the last run.
558 fonts.PushBack( currentFontRun );
562 void MultilanguageSupport::ValidateFonts( LogicalModel& model,
563 CharacterIndex characterIndex,
564 Length numberOfCharactersToRemove,
565 Length numberOfCharactersToInsert )
569 } // namespace Internal
573 } // namespace Toolkit