X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=blobdiff_plain;f=dali-toolkit%2Finternal%2Ftext%2Fmulti-language-support-impl.cpp;h=a8834b993e2ec95cef2c5044e9ac4997239c8b0e;hp=ec9167bc379c979217044363f8eefb72ce6e474f;hb=fd31db0942835fccbf5d2ae21a764e73150e6870;hpb=ad48a2df15896480b1b6aaf1c744e5463f56ee06 diff --git a/dali-toolkit/internal/text/multi-language-support-impl.cpp b/dali-toolkit/internal/text/multi-language-support-impl.cpp old mode 100644 new mode 100755 index ec9167b..a8834b9 --- a/dali-toolkit/internal/text/multi-language-support-impl.cpp +++ b/dali-toolkit/internal/text/multi-language-support-impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * Copyright (c) 2019 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. @@ -47,7 +47,7 @@ namespace Text namespace Internal { -bool ValidateFontsPerScript::FindValidFont( FontId fontId ) const +bool ValidateFontsPerScript::IsValidFont( FontId fontId ) const { for( Vector::ConstIterator it = mValidFonts.Begin(), endIt = mValidFonts.End(); @@ -63,34 +63,49 @@ bool ValidateFontsPerScript::FindValidFont( FontId fontId ) const 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::ConstIterator it = mFonts.Begin(), - endIt = mFonts.End(); + for( std::vector::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() { // Initializes the default font cache to zero (invalid font). // Reserves space to cache the default fonts and access them with the script as an index. - mDefaultFontPerScriptCache.Resize( TextAbstraction::UNKNOWN, NULL ); + mDefaultFontPerScriptCache.Resize( TextAbstraction::UNKNOWN + 1, NULL ); // Initializes the valid fonts cache to NULL (no valid fonts). // Reserves space to cache the valid fonts and access them with the script as an index. - mValidFontsPerScriptCache.Resize( TextAbstraction::UNKNOWN, NULL ); + mValidFontsPerScriptCache.Resize( TextAbstraction::UNKNOWN + 1, NULL ); } MultilanguageSupport::~MultilanguageSupport() @@ -186,9 +201,12 @@ void MultilanguageSupport::SetScripts( const Vector& text, // 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(); + // Initialize whether is right to left direction + currentScriptRun.isRightToLeft = false; + // Traverse all characters and set the scripts. const Length lastCharacter = startIndex + numberOfCharacters; for( Length index = startIndex; index < lastCharacter; ++index ) @@ -211,6 +229,22 @@ void MultilanguageSupport::SetScripts( const Vector& text, while( !endOfText && ( TextAbstraction::COMMON == script ) ) { + // Check if whether is right to left markup and Keeps true if the previous value was true. + currentScriptRun.isRightToLeft = currentScriptRun.isRightToLeft || TextAbstraction::IsRightToLeftMark( character ); + + 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; @@ -226,10 +260,6 @@ void MultilanguageSupport::SetScripts( const Vector& text, currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters; // Store the script run. - if( TextAbstraction::UNKNOWN == currentScriptRun.script ) - { - currentScriptRun.script = TextAbstraction::LATIN; - } scripts.Insert( scripts.Begin() + scriptIndex, currentScriptRun ); ++scriptIndex; @@ -238,7 +268,9 @@ void MultilanguageSupport::SetScripts( const Vector& text, currentScriptRun.characterRun.numberOfCharacters = 0u; currentScriptRun.script = TextAbstraction::UNKNOWN; numberOfAllScriptCharacters = 0u; - } + // Initialize whether is right to left direction + currentScriptRun.isRightToLeft = false; + } // Get the next character. ++index; @@ -248,7 +280,7 @@ void MultilanguageSupport::SetScripts( const Vector& text, character = *( textBuffer + index ); script = TextAbstraction::GetCharacterScript( character ); } - } + } // end while( !endOfText && ( TextAbstraction::COMMON == script ) ) if( endOfText ) { @@ -260,10 +292,11 @@ void MultilanguageSupport::SetScripts( const Vector& text, // 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 ); + isParagraphRTL = currentScriptRun.isRightToLeft || TextAbstraction::IsRightToLeftScript( script ); isFirstScriptToBeSet = false; } @@ -288,6 +321,12 @@ void MultilanguageSupport::SetScripts( const Vector& text, currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters; numberOfAllScriptCharacters = 0u; } + else if( ( TextAbstraction::UNKNOWN == currentScriptRun.script ) && + ( TextAbstraction::EMOJI == script ) ) + { + currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters; + numberOfAllScriptCharacters = 0u; + } if( 0u != currentScriptRun.characterRun.numberOfCharacters ) { @@ -301,6 +340,8 @@ void MultilanguageSupport::SetScripts( const Vector& text, currentScriptRun.characterRun.numberOfCharacters = numberOfAllScriptCharacters + 1u; // Adds the white spaces which are at the begining of the script. currentScriptRun.script = script; numberOfAllScriptCharacters = 0u; + // Check if whether is right to left script. + currentScriptRun.isRightToLeft = TextAbstraction::IsRightToLeftScript( currentScriptRun.script ); } else { @@ -321,12 +362,6 @@ void MultilanguageSupport::SetScripts( const Vector& text, if( 0u != currentScriptRun.characterRun.numberOfCharacters ) { - if( TextAbstraction::UNKNOWN == currentScriptRun.script ) - { - // There are only white spaces in the last script. Set the latin script. - currentScriptRun.script = TextAbstraction::LATIN; - } - // Store the last run. scripts.Insert( scripts.Begin() + scriptIndex, currentScriptRun ); ++scriptIndex; @@ -353,7 +388,8 @@ void MultilanguageSupport::SetScripts( const Vector& text, void MultilanguageSupport::ValidateFonts( const Vector& text, const Vector& scripts, const Vector& fontDescriptions, - FontId defaultFontId, + const TextAbstraction::FontDescription& defaultFontDescription, + TextAbstraction::PointSize26Dot6 defaultFontPointSize, CharacterIndex startIndex, Length numberOfCharacters, Vector& fonts ) @@ -399,59 +435,49 @@ void MultilanguageSupport::ValidateFonts( const Vector& text, currentFontRun.characterRun.characterIndex = startIndex; currentFontRun.characterRun.numberOfCharacters = 0u; currentFontRun.fontId = 0u; + currentFontRun.isBoldRequired = false; + currentFontRun.isItalicRequired = false; // 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 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::ConstIterator scriptRunIt = scripts.Begin(); Vector::ConstIterator scriptRunEndIt = scripts.End(); bool isNewParagraphCharacter = false; - PointSize26Dot6 currentPointSize = defaultPointSize; - FontId currentFontId = 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. + bool isItalicRequired = false; + bool isBoldRequired = false; + + // new description for current 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 ); + + // 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; @@ -466,126 +492,164 @@ void MultilanguageSupport::ValidateFonts( const Vector& text, } #endif - // Whether the font being validated is a default one not set by the user. - FontId preferredFont = fontId; + // Validate whether the current character is supported by the given font. + bool isValidFont = false; - // Validate if the font set by the user supports the character. + // Check first in the cache of default fonts per script and size. - // Check first in the caches. - - 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 if the font supports the character. + isValidFont = fontClient.IsCharacterSupportedByFont( fontId, character ); + } + + bool isCommonScript = false; + bool isEmojiScript = TextAbstraction::EMOJI == script; + + if( isEmojiScript && !isPreviousEmojiScript ) + { + if( 0u != currentFontRun.characterRun.numberOfCharacters ) + { + // Store the font run. + fonts.Insert( fonts.Begin() + fontIndex, currentFontRun ); + ++fontIndex; + } + + // Initialize the new one. + currentFontRun.characterRun.characterIndex = currentFontRun.characterRun.characterIndex + currentFontRun.characterRun.numberOfCharacters; + currentFontRun.characterRun.numberOfCharacters = 0u; + currentFontRun.fontId = fontId; + currentFontRun.isItalicRequired = false; + currentFontRun.isBoldRequired = false; + } + + // 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 ); + // Check in the valid fonts cache. ValidateFontsPerScript* validateFontsPerScript = *( validFontsPerScriptCacheBuffer + script ); - if( NULL == validateFontsPerScript ) + if( NULL != validateFontsPerScript ) { - validateFontsPerScript = new ValidateFontsPerScript(); + // This cache stores valid fonts set by the user. + isValidFont = validateFontsPerScript->IsValidFont( fontId ); - *( validFontsPerScriptCacheBuffer + script ) = validateFontsPerScript; + // 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 ) + { + // Checks if the current character is supported by the font is needed. + isValidFont = fontClient.IsCharacterSupportedByFont( fontId, character ); + } } - if( NULL != validateFontsPerScript ) + if( !isValidFont ) // (2) { - if( !validateFontsPerScript->FindValidFont( fontId ) ) + // 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 there is a valid font, cache it. + if( isValidFont && !isCommonScript ) { - // Use the font client to validate the font. - GlyphIndex glyphIndex = fontClient.GetGlyphIndex( fontId, character ); + if( NULL == validateFontsPerScript ) + { + validateFontsPerScript = new ValidateFontsPerScript(); + + *( validFontsPerScriptCacheBuffer + script ) = validateFontsPerScript; + } - // Emojis are present in many monochrome fonts; prefer color by default. - if( ( TextAbstraction::EMOJI == script ) && - ( 0u != glyphIndex ) ) + validateFontsPerScript->mValidFonts.PushBack( fontId ); + } + + if( !isValidFont && ( fontId != cachedDefaultFontId ) && ( !TextAbstraction::IsNewParagraph( character ) )) // (3) + { + // The selected font by the user or the platform's default font has failed to validate the character. + + // Checks if the previously discarted cached default font supports the character. + bool isValidCachedFont = false; + if( isValidCachedDefaultFont ) { - BufferImage bitmap = fontClient.CreateBitmap( fontId, glyphIndex ); - if( bitmap && - ( Pixel::BGRA8888 != bitmap.GetPixelFormat() ) ) - { - glyphIndex = 0u; - } + isValidCachedFont = fontClient.IsCharacterSupportedByFont( cachedDefaultFontId, character ); } - if( 0u == glyphIndex ) + if( isValidCachedFont ) { - // The font is not valid. Set to zero and a default one will be set. - fontId = 0u; + // Use the cached default font for the script if there is one. + fontId = cachedDefaultFontId; } else { - // 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 ) ) + // There is no valid cached default font for the script. + + DefaultFonts* defaultFontsPerScript = NULL; + + // Find a fallback-font. + fontId = fontClient.FindFallbackFont( character, + currentFontDescription, + currentFontPointSize, + false ); + + if( 0u == fontId ) { - validateFontsPerScript = *( validFontsPerScriptCacheBuffer + TextAbstraction::COMMON ); + fontId = fontClient.FindDefaultFont( UTF32_A, currentFontPointSize ); + } - if( NULL == validateFontsPerScript ) + if ( !isCommonScript && (script != TextAbstraction::UNKNOWN) ) + { + // Cache the font if it is not an unknown script + if( NULL == defaultFontsPerScript ) { - validateFontsPerScript = new ValidateFontsPerScript(); + defaultFontsPerScript = *( defaultFontPerScriptCacheBuffer + script ); - *( validFontsPerScriptCacheBuffer + TextAbstraction::COMMON ) = validateFontsPerScript; + if( NULL == defaultFontsPerScript ) + { + defaultFontsPerScript = new DefaultFonts(); + *( defaultFontPerScriptCacheBuffer + script ) = defaultFontsPerScript; + } } + defaultFontsPerScript->Cache( currentFontDescription, fontId ); } - - 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 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 ); - - // Find a fallback-font. - fontId = fontClient.FindFallbackFont( preferredFont, character, currentPointSize, preferColor ); - - // 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 ); - } - } - - if( 0u == fontId ) - { - fontId = fontClient.FindDefaultFont( UTF32_A, currentPointSize ); - } - - // Cache the font. - if( NULL == latinDefaults ) - { - latinDefaults = new DefaultFonts(); - *( defaultFontPerScriptCacheBuffer + script ) = latinDefaults; - } - latinDefaults->mFonts.PushBack( fontId ); - } - } + } // !isValidFont (3) + } // !isValidFont (2) + } // !isValidFont (1) #ifdef DEBUG_ENABLED { @@ -600,9 +664,17 @@ void MultilanguageSupport::ValidateFonts( const Vector& text, } #endif + // Whether bols style is required. + isBoldRequired = ( currentFontDescription.weight >= TextAbstraction::FontWeight::BOLD ); + + // Whether italic style is required. + isItalicRequired = ( currentFontDescription.slant >= TextAbstraction::FontSlant::ITALIC ); + // The font is now validated. if( ( fontId != currentFontRun.fontId ) || - isNewParagraphCharacter ) + isNewParagraphCharacter || + // If font id is same as previous but style is diffrent, initialize new one + ( ( fontId == currentFontRun.fontId ) && ( ( isBoldRequired != currentFontRun.isBoldRequired ) || ( isItalicRequired != currentFontRun.isItalicRequired ) ) ) ) { // Current run needs to be stored and a new one initialized. @@ -617,6 +689,8 @@ void MultilanguageSupport::ValidateFonts( const Vector& text, currentFontRun.characterRun.characterIndex = currentFontRun.characterRun.characterIndex + currentFontRun.characterRun.numberOfCharacters; currentFontRun.characterRun.numberOfCharacters = 0u; currentFontRun.fontId = fontId; + currentFontRun.isBoldRequired = isBoldRequired; + currentFontRun.isItalicRequired = isItalicRequired; } // Add one more character to the run. @@ -624,7 +698,8 @@ void MultilanguageSupport::ValidateFonts( const Vector& text, // Whether the current character is a new paragraph character. isNewParagraphCharacter = TextAbstraction::IsNewParagraph( character ); - } + isPreviousEmojiScript = isEmojiScript; + } // end traverse characters. if( 0u != currentFontRun.characterRun.numberOfCharacters ) {