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>
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>
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.
119 bool ValidateFontsPerScript::FindValidFont( FontId fontId ) const
121 for( Vector<FontId>::ConstIterator it = mValidFonts.Begin(),
122 endIt = mValidFonts.End();
135 MultilanguageSupport::MultilanguageSupport()
136 : mDefaultFontPerScriptCache(),
137 mValidFontsPerScriptCache()
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 );
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 );
148 MultilanguageSupport::~MultilanguageSupport()
150 // Destroy the valid fonts per script cache.
152 for( Vector<ValidateFontsPerScript*>::Iterator it = mValidFontsPerScriptCache.Begin(),
153 endIt = mValidFontsPerScriptCache.End();
161 Text::MultilanguageSupport MultilanguageSupport::Get()
163 Text::MultilanguageSupport multilanguageSupportHandle;
165 SingletonService service( SingletonService::Get() );
168 // Check whether the singleton is already created
169 Dali::BaseHandle handle = service.GetSingleton( typeid( Text::MultilanguageSupport ) );
172 // If so, downcast the handle
173 MultilanguageSupport* impl = dynamic_cast< Internal::MultilanguageSupport* >( handle.GetObjectPtr() );
174 multilanguageSupportHandle = Text::MultilanguageSupport( impl );
176 else // create and register the object
178 multilanguageSupportHandle = Text::MultilanguageSupport( new MultilanguageSupport );
179 service.Register( typeid( multilanguageSupportHandle ), multilanguageSupportHandle );
183 return multilanguageSupportHandle;
186 void MultilanguageSupport::SetScripts( const Vector<Character>& text,
187 Vector<ScriptRun>& scripts )
189 const Length numberOfCharacters = text.Count();
191 if( 0u == numberOfCharacters )
193 // Nothing to do if there are no characters.
197 // Traverse all characters and set the scripts.
199 // Stores the current script run.
200 ScriptRun currentScriptRun;
201 currentScriptRun.characterRun.characterIndex = 0u;
202 currentScriptRun.characterRun.numberOfCharacters = 0u;
203 currentScriptRun.script = TextAbstraction::UNKNOWN;
205 // Reserve some space to reduce the number of reallocations.
206 scripts.Reserve( numberOfCharacters << 2u );
208 for( Length index = 0u; index < numberOfCharacters; ++index )
210 const Character character = *( text.Begin() + index );
212 Script script = GetCharacterScript( character );
214 if( TextAbstraction::UNKNOWN == script )
216 if( IsZeroWidthNonJoiner( character ) ||
217 IsZeroWidthJoiner( character ) ||
218 IsZeroWidthSpace( character ) ||
219 IsLeftToRightMark( character ) ||
220 IsRightToLeftMark( character ) ||
221 IsThinSpace( character ) )
223 // Keep previous script if the character is a zero width joiner or a zero width non joiner.
224 script = currentScriptRun.script;
228 script = TextAbstraction::LATIN;
229 DALI_ASSERT_DEBUG( !"MultilanguageSupport::SetScripts. Unkown script!" );
233 if( script != currentScriptRun.script )
235 // Current run needs to be stored and a new one initialized.
237 if( 0u != currentScriptRun.characterRun.numberOfCharacters )
239 // Store the script run.
240 scripts.PushBack( currentScriptRun );
243 // Initialize the new one.
244 currentScriptRun.characterRun.characterIndex = currentScriptRun.characterRun.characterIndex + currentScriptRun.characterRun.numberOfCharacters;
245 currentScriptRun.characterRun.numberOfCharacters = 0u;
246 currentScriptRun.script = script;
249 // Add one more character to the run.
250 ++currentScriptRun.characterRun.numberOfCharacters;
253 if( 0u != currentScriptRun.characterRun.numberOfCharacters )
255 // Store the last run.
256 scripts.PushBack( currentScriptRun );
260 void MultilanguageSupport::ValidateFonts( const Vector<Character>& text,
261 const Vector<ScriptRun>& scripts,
262 Vector<FontRun>& fonts )
264 const Length numberOfCharacters = text.Count();
266 if( 0u == numberOfCharacters )
268 // Nothing to do if there are no characters.
272 // Copy the fonts set by application developers.
273 const Length numberOfFontRuns = fonts.Count();
274 const Vector<FontRun> definedFonts = fonts;
277 // Traverse the characters and validate/set the fonts.
280 FontId* defaultFontPerScriptCacheBuffer = mDefaultFontPerScriptCache.Begin();
281 ValidateFontsPerScript** validFontsPerScriptCacheBuffer = mValidFontsPerScriptCache.Begin();
283 // Stores the validated font runs.
284 fonts.Reserve( numberOfFontRuns );
286 // Initializes a validated font run.
287 FontRun currentFontRun;
288 currentFontRun.characterRun.characterIndex = 0u;
289 currentFontRun.characterRun.numberOfCharacters = 0u;
290 currentFontRun.fontId = 0u;
291 currentFontRun.isDefault = false;
293 // Get the font client.
294 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
296 // Iterators of the font and script runs.
297 Vector<FontRun>::ConstIterator fontRunIt = definedFonts.Begin();
298 Vector<FontRun>::ConstIterator fontRunEndIt = definedFonts.End();
299 Vector<ScriptRun>::ConstIterator scriptRunIt = scripts.Begin();
300 Vector<ScriptRun>::ConstIterator scriptRunEndIt = scripts.End();
302 for( Length index = 0u; index < numberOfCharacters; ++index )
304 // Get the character.
305 const Character character = *( text.Begin() + index );
307 // Get the font for the character.
308 FontId fontId = GetFontId( index,
312 // Get the script for the character.
313 Script script = GetScript( index,
317 if( TextAbstraction::UNKNOWN == script )
319 DALI_LOG_WARNING( "MultilanguageSupport::ValidateFonts. Unknown script!" );
320 script = TextAbstraction::LATIN;
323 // Whether the font being validated is a default one not set by the user.
324 const bool isDefault = ( 0u == fontId );
326 // The default font point size.
327 PointSize26Dot6 pointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
331 // Validate if the font set by the user supports the character.
333 // Check first in the caches.
335 // The user may have set the default font. Check it. Otherwise check in the valid fonts cache.
336 if( fontId != *( defaultFontPerScriptCacheBuffer + script ) )
338 // Check in the valid fonts cache.
339 ValidateFontsPerScript* validateFontsPerScript = *( validFontsPerScriptCacheBuffer + script );
340 if( NULL != validateFontsPerScript )
342 if( !validateFontsPerScript->FindValidFont( fontId ) )
344 // Use the font client to validate the font.
345 const GlyphIndex glyphIndex = fontClient.GetGlyphIndex( fontId, character );
347 if( 0u == glyphIndex )
349 // Get the point size of the current font. It will be used to get a default font id.
350 pointSize = fontClient.GetPointSize( fontId );
352 // The font is not valid. Set to zero and a default one will be set.
357 // Add the font to the valid font cache.
358 validateFontsPerScript->mValidFonts.PushBack( fontId );
364 // Use the font client to validate the font.
365 const GlyphIndex glyphIndex = fontClient.GetGlyphIndex( fontId, character );
367 if( 0u == glyphIndex )
369 // Get the point size of the current font. It will be used to get a default font id.
370 pointSize = fontClient.GetPointSize( fontId );
372 // The font is not valid. Set to zero and a default one will be set.
377 // Add the font to the valid font cache.
378 validateFontsPerScript = new ValidateFontsPerScript();
379 *( validFontsPerScriptCacheBuffer + script ) = validateFontsPerScript;
381 validateFontsPerScript->mValidFonts.PushBack( fontId );
387 // The font has not been validated. Find a default one.
390 // The character has no font assigned. Get a default one from the cache
391 fontId = *( defaultFontPerScriptCacheBuffer + script );
393 // If the cache has not a default font, get one from the font client.
396 // Find a default font.
397 fontId = fontClient.FindDefaultFont( character, pointSize );
400 *( defaultFontPerScriptCacheBuffer + script ) = fontId;
404 // The font is now validated.
406 if( ( fontId != currentFontRun.fontId ) ||
407 ( isDefault != currentFontRun.isDefault ) )
409 // Current run needs to be stored and a new one initialized.
411 if( 0u != currentFontRun.characterRun.numberOfCharacters )
413 // Store the font run.
414 fonts.PushBack( currentFontRun );
417 // Initialize the new one.
418 currentFontRun.characterRun.characterIndex = currentFontRun.characterRun.characterIndex + currentFontRun.characterRun.numberOfCharacters;
419 currentFontRun.characterRun.numberOfCharacters = 0u;
420 currentFontRun.fontId = fontId;
421 currentFontRun.isDefault = isDefault;
424 // Add one more character to the run.
425 ++currentFontRun.characterRun.numberOfCharacters;
428 if( 0u != currentFontRun.characterRun.numberOfCharacters )
430 // Store the last run.
431 fonts.PushBack( currentFontRun );
435 } // namespace Internal
439 } // namespace Toolkit