| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "platform/LayoutLocale.h" |
| |
| #include "platform/Language.h" |
| #include "platform/text/LocaleToScriptMapping.h" |
| #include "wtf/HashMap.h" |
| #include "wtf/text/AtomicStringHash.h" |
| #include "wtf/text/StringHash.h" |
| |
| #include <hb.h> |
| #include <unicode/locid.h> |
| |
| namespace blink { |
| |
| const LayoutLocale* LayoutLocale::s_default = nullptr; |
| const LayoutLocale* LayoutLocale::s_system = nullptr; |
| const LayoutLocale* LayoutLocale::s_defaultForHan = nullptr; |
| bool LayoutLocale::s_defaultForHanComputed = false; |
| |
| static hb_language_t toHarfbuzLanguage(const AtomicString& locale) |
| { |
| CString localeAsLatin1 = locale.latin1(); |
| return hb_language_from_string(localeAsLatin1.data(), localeAsLatin1.length()); |
| } |
| |
| // SkFontMgr requires script-based locale names, like "zh-Hant" and "zh-Hans", |
| // instead of "zh-CN" and "zh-TW". |
| static const char* toSkFontMgrLocale(UScriptCode script) |
| { |
| switch (script) { |
| case USCRIPT_KATAKANA_OR_HIRAGANA: |
| return "ja-JP"; |
| case USCRIPT_HANGUL: |
| return "ko-KR"; |
| case USCRIPT_SIMPLIFIED_HAN: |
| return "zh-Hans"; |
| case USCRIPT_TRADITIONAL_HAN: |
| return "zh-Hant"; |
| default: |
| return nullptr; |
| } |
| } |
| |
| const char* LayoutLocale::localeForSkFontMgr() const |
| { |
| if (m_stringForSkFontMgr.isNull()) { |
| m_stringForSkFontMgr = toSkFontMgrLocale(m_script); |
| if (m_stringForSkFontMgr.isNull()) |
| m_stringForSkFontMgr = m_string.ascii(); |
| } |
| return m_stringForSkFontMgr.data(); |
| } |
| |
| static bool isUnambiguousHanScript(UScriptCode script) |
| { |
| // localeToScriptCodeForFontSelection() does not return these values. |
| DCHECK(script != USCRIPT_HIRAGANA && script != USCRIPT_KATAKANA); |
| return script == USCRIPT_KATAKANA_OR_HIRAGANA |
| || script == USCRIPT_SIMPLIFIED_HAN |
| || script == USCRIPT_TRADITIONAL_HAN |
| || script == USCRIPT_HANGUL; |
| } |
| |
| void LayoutLocale::computeScriptForHan() const |
| { |
| if (isUnambiguousHanScript(m_script)) { |
| m_scriptForHan = m_script; |
| m_hasScriptForHan = true; |
| return; |
| } |
| |
| m_scriptForHan = scriptCodeForHanFromSubtags(m_string); |
| if (m_scriptForHan == USCRIPT_COMMON) |
| m_scriptForHan = USCRIPT_SIMPLIFIED_HAN; |
| else |
| m_hasScriptForHan = true; |
| DCHECK(isUnambiguousHanScript(m_scriptForHan)); |
| } |
| |
| UScriptCode LayoutLocale::scriptForHan() const |
| { |
| if (m_scriptForHan == USCRIPT_COMMON) |
| computeScriptForHan(); |
| return m_scriptForHan; |
| } |
| |
| bool LayoutLocale::hasScriptForHan() const |
| { |
| if (m_scriptForHan == USCRIPT_COMMON) |
| computeScriptForHan(); |
| return m_hasScriptForHan; |
| } |
| |
| const LayoutLocale* LayoutLocale::localeForHan(const LayoutLocale* contentLocale) |
| { |
| if (contentLocale && contentLocale->hasScriptForHan()) |
| return contentLocale; |
| if (!s_defaultForHanComputed) |
| setLocaleForHan(nullptr); |
| return s_defaultForHan; |
| } |
| |
| void LayoutLocale::setLocaleForHan(const LayoutLocale* locale) |
| { |
| if (locale) |
| s_defaultForHan = locale; |
| else if (getDefault().hasScriptForHan()) |
| s_defaultForHan = &getDefault(); |
| else if (getSystem().hasScriptForHan()) |
| s_defaultForHan = &getSystem(); |
| else |
| s_defaultForHan = nullptr; |
| s_defaultForHanComputed = true; |
| } |
| |
| const char* LayoutLocale::localeForHanForSkFontMgr() const |
| { |
| const char* locale = toSkFontMgrLocale(scriptForHan()); |
| DCHECK(locale); |
| return locale; |
| } |
| |
| LayoutLocale::LayoutLocale(const AtomicString& locale) |
| : m_string(locale) |
| , m_harfbuzzLanguage(toHarfbuzLanguage(locale)) |
| , m_script(localeToScriptCodeForFontSelection(locale)) |
| , m_scriptForHan(USCRIPT_COMMON) |
| , m_hasScriptForHan(false) |
| , m_hyphenationComputed(false) |
| { |
| } |
| |
| using LayoutLocaleMap = HashMap<AtomicString, RefPtr<LayoutLocale>, CaseFoldingHash>; |
| |
| static LayoutLocaleMap& getLocaleMap() |
| { |
| DEFINE_STATIC_LOCAL(LayoutLocaleMap, localeMap, ()); |
| return localeMap; |
| } |
| |
| const LayoutLocale* LayoutLocale::get(const AtomicString& locale) |
| { |
| if (locale.isNull()) |
| return nullptr; |
| |
| auto result = getLocaleMap().add(locale, nullptr); |
| if (result.isNewEntry) |
| result.storedValue->value = adoptRef(new LayoutLocale(locale)); |
| return result.storedValue->value.get(); |
| } |
| |
| const LayoutLocale& LayoutLocale::getDefault() |
| { |
| if (s_default) |
| return *s_default; |
| |
| AtomicString locale = defaultLanguage(); |
| s_default = get(!locale.isEmpty() ? locale : "en"); |
| return *s_default; |
| } |
| |
| const LayoutLocale& LayoutLocale::getSystem() |
| { |
| if (s_system) |
| return *s_system; |
| |
| // Platforms such as Windows can give more information than the default |
| // locale, such as "en-JP" for English speakers in Japan. |
| String name = icu::Locale::getDefault().getName(); |
| s_system = get(AtomicString(name.replace('_', '-'))); |
| return *s_system; |
| } |
| |
| PassRefPtr<LayoutLocale> LayoutLocale::createForTesting(const AtomicString& locale) |
| { |
| return adoptRef(new LayoutLocale(locale)); |
| } |
| |
| void LayoutLocale::clearForTesting() |
| { |
| s_default = nullptr; |
| s_system = nullptr; |
| s_defaultForHan = nullptr; |
| s_defaultForHanComputed = false; |
| getLocaleMap().clear(); |
| } |
| |
| Hyphenation* LayoutLocale::getHyphenation() const |
| { |
| if (m_hyphenationComputed) |
| return m_hyphenation.get(); |
| |
| m_hyphenationComputed = true; |
| m_hyphenation = Hyphenation::platformGetHyphenation(localeString()); |
| return m_hyphenation.get(); |
| } |
| |
| void LayoutLocale::setHyphenationForTesting(const AtomicString& localeString, PassRefPtr<Hyphenation> hyphenation) |
| { |
| const LayoutLocale& locale = valueOrDefault(get(localeString)); |
| locale.m_hyphenationComputed = true; |
| locale.m_hyphenation = hyphenation; |
| } |
| |
| } // namespace blink |