/*
- * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
namespace Internal
{
-bool ValidateFontsPerScript::FindValidFont( FontId fontId ) const
+bool ValidateFontsPerScript::IsValidFont( FontId fontId ) const
{
for( Vector<FontId>::ConstIterator it = mValidFonts.Begin(),
endIt = mValidFonts.End();
return false;
}
-FontId DefaultFonts::FindFont( TextAbstraction::FontClient& fontClient, PointSize26Dot6 size ) const
+FontId DefaultFonts::FindFont( TextAbstraction::FontClient& fontClient,
+ const TextAbstraction::FontDescription& description,
+ PointSize26Dot6 size ) const
{
- for( Vector<FontId>::ConstIterator it = mFonts.Begin(),
- endIt = mFonts.End();
+ for( std::vector<CacheItem>::const_iterator it = mFonts.begin(),
+ endIt = mFonts.end();
it != endIt;
++it )
{
- const FontId fontId = *it;
- if( size == fontClient.GetPointSize( fontId ) )
+ const CacheItem& item = *it;
+
+ if( ( ( TextAbstraction::FontWeight::NONE == description.weight ) || ( description.weight == item.description.weight ) ) &&
+ ( ( TextAbstraction::FontWidth::NONE == description.width ) || ( description.width == item.description.width ) ) &&
+ ( ( TextAbstraction::FontSlant::NONE == description.slant ) || ( description.slant == item.description.slant ) ) &&
+ ( size == fontClient.GetPointSize( item.fontId ) ) &&
+ ( description.family.empty() || ( description.family == item.description.family ) ) )
{
- return fontId;
+ return item.fontId;
}
}
return 0u;
}
+void DefaultFonts::Cache( const TextAbstraction::FontDescription& description, FontId fontId )
+{
+ CacheItem item;
+ item.description = description;
+ item.fontId = fontId;
+ mFonts.push_back( item );
+}
+
MultilanguageSupport::MultilanguageSupport()
: mDefaultFontPerScriptCache(),
mValidFontsPerScriptCache()
// Count the number of characters which are valid for all scripts. i.e. white spaces or '\n'.
Length numberOfAllScriptCharacters = 0u;
- // Pointers to the text and break info buffers.
+ // Pointers to the text buffer.
const Character* const textBuffer = text.Begin();
// Traverse all characters and set the scripts.
while( !endOfText &&
( TextAbstraction::COMMON == script ) )
{
+ if( TextAbstraction::EMOJI == currentScriptRun.script )
+ {
+ // Emojis doesn't mix well with characters common to all scripts. Insert the emoji run.
+ scripts.Insert( scripts.Begin() + scriptIndex, currentScriptRun );
+ ++scriptIndex;
+
+ // Initialize the new one.
+ currentScriptRun.characterRun.characterIndex = currentScriptRun.characterRun.characterIndex + currentScriptRun.characterRun.numberOfCharacters;
+ currentScriptRun.characterRun.numberOfCharacters = 0u;
+ currentScriptRun.script = TextAbstraction::UNKNOWN;
+ numberOfAllScriptCharacters = 0u;
+ }
+
// Count all these characters to be added into a script.
++numberOfAllScriptCharacters;
currentScriptRun.characterRun.numberOfCharacters = 0u;
currentScriptRun.script = TextAbstraction::UNKNOWN;
numberOfAllScriptCharacters = 0u;
- }
+ }
// Get the next character.
++index;
character = *( textBuffer + index );
script = TextAbstraction::GetCharacterScript( character );
}
- }
+ } // end while( !endOfText && ( TextAbstraction::COMMON == script ) )
if( endOfText )
{
// Check if it is the first character of a paragraph.
if( isFirstScriptToBeSet &&
( TextAbstraction::UNKNOWN != script ) &&
- ( TextAbstraction::COMMON != script ) )
+ ( TextAbstraction::COMMON != script ) &&
+ ( TextAbstraction::EMOJI != script ) )
{
// Sets the direction of the first valid script.
isParagraphRTL = TextAbstraction::IsRightToLeftScript( script );
currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters;
numberOfAllScriptCharacters = 0u;
}
+ else if( ( TextAbstraction::UNKNOWN == currentScriptRun.script ) &&
+ ( TextAbstraction::EMOJI == script ) )
+ {
+ currentScriptRun.script = TextAbstraction::LATIN;
+ currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters;
+ numberOfAllScriptCharacters = 0u;
+ }
if( 0u != currentScriptRun.characterRun.numberOfCharacters )
{
void MultilanguageSupport::ValidateFonts( const Vector<Character>& text,
const Vector<ScriptRun>& scripts,
const Vector<FontDescriptionRun>& fontDescriptions,
- FontId defaultFontId,
+ const TextAbstraction::FontDescription& defaultFontDescription,
+ TextAbstraction::PointSize26Dot6 defaultFontPointSize,
CharacterIndex startIndex,
Length numberOfCharacters,
Vector<FontRun>& fonts )
// Get the font client.
TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
- // Get the default font description and default size.
- TextAbstraction::FontDescription defaultFontDescription;
- TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
- if( defaultFontId > 0u )
- {
- fontClient.GetDescription( defaultFontId, defaultFontDescription );
- defaultPointSize = fontClient.GetPointSize( defaultFontId );
- }
-
- // Merge font descriptions
- Vector<FontId> fontIds;
- fontIds.Resize( numberOfCharacters, defaultFontId );
- MergeFontDescriptions( fontDescriptions,
- fontIds,
- defaultFontDescription,
- defaultPointSize,
- startIndex,
- numberOfCharacters );
-
const Character* const textBuffer = text.Begin();
- const FontId* const fontIdsBuffer = fontIds.Begin();
+
+ // Iterators of the script runs.
Vector<ScriptRun>::ConstIterator scriptRunIt = scripts.Begin();
Vector<ScriptRun>::ConstIterator scriptRunEndIt = scripts.End();
bool isNewParagraphCharacter = false;
- PointSize26Dot6 currentPointSize = defaultPointSize;
FontId currentFontId = 0u;
+ FontId previousFontId = 0u;
+ bool isPreviousEmojiScript = false;
CharacterIndex lastCharacter = startIndex + numberOfCharacters;
for( Length index = startIndex; index < lastCharacter; ++index )
{
- // Get the character.
+ // Get the current character.
const Character character = *( textBuffer + index );
- // Get the font for the character.
- FontId fontId = *( fontIdsBuffer + index - startIndex );
-
- // Get the script for the character.
+ TextAbstraction::FontDescription currentFontDescription;
+ TextAbstraction::PointSize26Dot6 currentFontPointSize = defaultFontPointSize;
+ bool isDefaultFont = true;
+ MergeFontDescriptions( fontDescriptions,
+ defaultFontDescription,
+ defaultFontPointSize,
+ index,
+ currentFontDescription,
+ currentFontPointSize,
+ isDefaultFont );
+
+ // Get the font for the current character.
+ FontId fontId = fontClient.GetFontId( currentFontDescription, currentFontPointSize );
+ currentFontId = fontId;
+
+ // Get the script for the current character.
Script script = GetScript( index,
scriptRunIt,
scriptRunEndIt );
- // Get the current point size.
- if( currentFontId != fontId )
- {
- currentPointSize = fontClient.GetPointSize( fontId );
- currentFontId = fontId;
- }
-
#ifdef DEBUG_ENABLED
{
Dali::TextAbstraction::FontDescription description;
description.path.c_str() );
}
#endif
+ if( script == TextAbstraction::UNKNOWN )
+ {
+ script = TextAbstraction::LATIN;
+ }
- // Whether the font being validated is a default one not set by the user.
- FontId preferredFont = fontId;
-
- // Validate if the font set by the user supports the character.
+ // Validate whether the current character is supported by the given font.
+ bool isValidFont = false;
- // Check first in the caches.
+ // Check first in the cache of default fonts per script and size.
- DefaultFonts* defaultFonts = *( defaultFontPerScriptCacheBuffer + script );
FontId cachedDefaultFontId = 0u;
+ DefaultFonts* defaultFonts = *( defaultFontPerScriptCacheBuffer + script );
if( NULL != defaultFonts )
{
- cachedDefaultFontId = defaultFonts->FindFont( fontClient, currentPointSize );
+ // This cache stores fall-back fonts.
+ cachedDefaultFontId = defaultFonts->FindFont( fontClient,
+ currentFontDescription,
+ currentFontPointSize );
}
- // The user may have set the default font. Check it. Otherwise check in the valid fonts cache.
- if( fontId != cachedDefaultFontId )
+ // Whether the cached default font is valid.
+ const bool isValidCachedDefaultFont = 0u != cachedDefaultFontId;
+
+ // The font is valid if it matches with the default one for the current script and size and it's different than zero.
+ isValidFont = isValidCachedDefaultFont && ( fontId == cachedDefaultFontId );
+
+ if( isValidFont )
{
- // Check in the valid fonts cache.
- ValidateFontsPerScript* validateFontsPerScript = *( validFontsPerScriptCacheBuffer + script );
+ // Check if the font supports the character.
+ isValidFont = fontClient.IsCharacterSupportedByFont( fontId, character );
+ }
- if( NULL == validateFontsPerScript )
- {
- validateFontsPerScript = new ValidateFontsPerScript();
+ bool isCommonScript = false;
+ bool isEmojiScript = TextAbstraction::EMOJI == script;
- *( validFontsPerScriptCacheBuffer + script ) = validateFontsPerScript;
+ if( isEmojiScript && !isPreviousEmojiScript )
+ {
+ if( 0u != currentFontRun.characterRun.numberOfCharacters )
+ {
+ // Store the font run.
+ fonts.Insert( fonts.Begin() + fontIndex, currentFontRun );
+ ++fontIndex;
}
- if( NULL != validateFontsPerScript )
+ // Initialize the new one.
+ currentFontRun.characterRun.characterIndex = currentFontRun.characterRun.characterIndex + currentFontRun.characterRun.numberOfCharacters;
+ currentFontRun.characterRun.numberOfCharacters = 0u;
+ currentFontRun.fontId = fontId;
+ }
+
+ // If the given font is not valid, it means either:
+ // - there is no cached font for the current script yet or,
+ // - the user has set a different font than the default one for the current script or,
+ // - the platform default font is different than the default font for the current script.
+
+ // Need to check if the given font supports the current character.
+ if( !isValidFont ) // (1)
+ {
+ // Whether the current character is common for all scripts (i.e. white spaces, ...)
+
+ // Is not desirable to cache fonts for the common script.
+ //
+ // i.e. Consider the text " हिंदी", the 'white space' has assigned the DEVANAGARI script.
+ // The user may have set a font or the platform's default is used.
+ //
+ // As the 'white space' is the first character, no font is cached so the font validation
+ // retrieves a glyph from the given font.
+ //
+ // Many fonts support 'white spaces' so probably the font set by the user or the platform's default
+ // supports the 'white space'. However, that font may not support the DEVANAGARI script.
+ isCommonScript = TextAbstraction::IsCommonScript( character );
+
+ if( isCommonScript )
{
- if( !validateFontsPerScript->FindValidFont( fontId ) )
+ if( isValidCachedDefaultFont &&
+ ( isDefaultFont || ( currentFontId == previousFontId ) ) &&
+ !isEmojiScript )
{
- // Use the font client to validate the font.
- GlyphIndex glyphIndex = fontClient.GetGlyphIndex( fontId, character );
+ // At this point the character common for all scripts has no font assigned.
+ // If there is a valid previously cached default font for it, use that one.
+ fontId = cachedDefaultFontId;
+ }
+ }
+ else
+ {
+ // Check in the valid fonts cache.
+ ValidateFontsPerScript* validateFontsPerScript = *( validFontsPerScriptCacheBuffer + script );
- // Emojis are present in many monochrome fonts; prefer color by default.
- if( ( TextAbstraction::EMOJI == script ) &&
- ( 0u != glyphIndex ) )
+ if( NULL != validateFontsPerScript )
+ {
+ // This cache stores valid fonts set by the user.
+ isValidFont = validateFontsPerScript->IsValidFont( fontId );
+
+ // It may happen that a validated font for a script doesn't have all the glyphs for that script.
+ // i.e a font validated for the CJK script may contain glyphs for the chinese language but not for the Japanese.
+ if( isValidFont )
{
- BufferImage bitmap = fontClient.CreateBitmap( fontId, glyphIndex );
- if( bitmap &&
- ( Pixel::BGRA8888 != bitmap.GetPixelFormat() ) )
- {
- glyphIndex = 0u;
- }
+ // Checks if the current character is supported by the font is needed.
+ isValidFont = fontClient.IsCharacterSupportedByFont( fontId, character );
}
+ }
+
+ if( !isValidFont ) // (2)
+ {
+ // The selected font is not stored in any cache.
+
+ // Checks if the current character is supported by the selected font.
+ isValidFont = fontClient.IsCharacterSupportedByFont( fontId, character );
- if( 0u == glyphIndex )
+ // Emojis are present in many monochrome fonts; prefer color by default.
+ if( isValidFont &&
+ isEmojiScript )
{
- // The font is not valid. Set to zero and a default one will be set.
- fontId = 0u;
+ const GlyphIndex glyphIndex = fontClient.GetGlyphIndex( fontId, character );
+
+ // For color emojis, the font is valid if the glyph is a color glyph (the bitmap is RGBA).
+ isValidFont = fontClient.IsColorGlyph( fontId, glyphIndex );
}
- else
+
+ // If there is a valid font, cache it.
+ if( isValidFont )
{
- // Add the font to the valid font cache.
-
- // At this point the validated font supports the given character. However, characters
- // common for all scripts, like white spaces or new paragraph characters, need to be
- // processed differently.
- //
- // i.e. A white space can have assigned a DEVANAGARI script but the font assigned may not
- // support none of the DEVANAGARI glyphs. This font can't be added to the cache as a valid
- // font for the DEVANAGARI script but the COMMON one.
- if( TextAbstraction::IsCommonScript( character ) )
+ if( NULL == validateFontsPerScript )
{
- validateFontsPerScript = *( validFontsPerScriptCacheBuffer + TextAbstraction::COMMON );
+ validateFontsPerScript = new ValidateFontsPerScript();
- if( NULL == validateFontsPerScript )
- {
- validateFontsPerScript = new ValidateFontsPerScript();
-
- *( validFontsPerScriptCacheBuffer + TextAbstraction::COMMON ) = validateFontsPerScript;
- }
+ *( validFontsPerScriptCacheBuffer + script ) = validateFontsPerScript;
}
validateFontsPerScript->mValidFonts.PushBack( fontId );
}
- }
- }
- }
- // The font has not been validated. Find a default one.
- if( 0u == fontId )
- {
- // The character has no font assigned. Get a default one from the cache
- fontId = cachedDefaultFontId;
+ if( !isValidFont && ( fontId != cachedDefaultFontId ) ) // (3)
+ {
+ // The selected font by the user or the platform's default font has failed to validate the character.
- // If the cache has not a default font, get one from the font client.
- if( 0u == fontId )
- {
- // Emojis are present in many monochrome fonts; prefer color by default.
- bool preferColor = ( TextAbstraction::EMOJI == script );
+ // Checks if the previously discarted cached default font supports the character.
+ bool isValidCachedFont = false;
+ if( isValidCachedDefaultFont )
+ {
+ isValidCachedFont = fontClient.IsCharacterSupportedByFont( cachedDefaultFontId, character );
+ }
- // Find a fallback-font.
- fontId = fontClient.FindFallbackFont( preferredFont, character, currentPointSize, preferColor );
+ if( isValidCachedFont )
+ {
+ // Use the cached default font for the script if there is one.
+ fontId = cachedDefaultFontId;
+ }
+ else
+ {
+ // There is no valid cached default font for the script.
- // If the system does not support a suitable font, fallback to Latin
- DefaultFonts* latinDefaults = NULL;
- if( 0u == fontId )
- {
- latinDefaults = *( defaultFontPerScriptCacheBuffer + TextAbstraction::LATIN );
- if( NULL != latinDefaults )
- {
- fontId = latinDefaults->FindFont( fontClient, currentPointSize );
- }
- }
+ DefaultFonts* defaultFontsPerScript = NULL;
- if( 0u == fontId )
- {
- fontId = fontClient.FindDefaultFont( UTF32_A, currentPointSize );
- }
+ // Emojis are present in many monochrome fonts; prefer color by default.
+ const bool preferColor = ( TextAbstraction::EMOJI == script );
- // Cache the font.
- if( NULL == latinDefaults )
- {
- latinDefaults = new DefaultFonts();
- *( defaultFontPerScriptCacheBuffer + script ) = latinDefaults;
- }
- latinDefaults->mFonts.PushBack( fontId );
- }
- }
+ // Find a fallback-font.
+ fontId = fontClient.FindFallbackFont( character,
+ currentFontDescription,
+ currentFontPointSize,
+ preferColor );
+
+ if( 0u == fontId )
+ {
+ // If the system does not support a suitable font, fallback to Latin
+ defaultFontsPerScript = *( defaultFontPerScriptCacheBuffer + TextAbstraction::LATIN );
+ if( NULL != defaultFontsPerScript )
+ {
+ fontId = defaultFontsPerScript->FindFont( fontClient,
+ currentFontDescription,
+ currentFontPointSize );
+ }
+ }
+
+ if( 0u == fontId )
+ {
+ fontId = fontClient.FindDefaultFont( UTF32_A, currentFontPointSize );
+ }
+
+ // Cache the font.
+ if( NULL == defaultFontsPerScript )
+ {
+ defaultFontsPerScript = *( defaultFontPerScriptCacheBuffer + script );
+
+ if( NULL == defaultFontsPerScript )
+ {
+ defaultFontsPerScript = new DefaultFonts();
+ *( defaultFontPerScriptCacheBuffer + script ) = defaultFontsPerScript;
+ }
+ }
+ defaultFontsPerScript->Cache( currentFontDescription, fontId );
+ }
+ } // !isValidFont (3)
+ } // !isValidFont (2)
+ } // !isCommonScript
+ } // !isValidFont (1)
#ifdef DEBUG_ENABLED
{
// Whether the current character is a new paragraph character.
isNewParagraphCharacter = TextAbstraction::IsNewParagraph( character );
- }
+ previousFontId = currentFontId;
+ isPreviousEmojiScript = isEmojiScript;
+ } // end traverse characters.
if( 0u != currentFontRun.characterRun.numberOfCharacters )
{