blob: 30d92aa3d03e82a41e66847e355d552dc5d181f0 [file] [log] [blame]
// 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/fonts/AcceptLanguagesResolver.h"
#include "platform/fonts/FontGlobalContext.h"
#include "platform/text/ICUError.h"
#include "platform/text/LocaleToScriptMapping.h"
#include "platform/wtf/HashMap.h"
#include "platform/wtf/text/AtomicStringHash.h"
#include "platform/wtf/text/StringHash.h"
#include <hb.h>
#include <unicode/locid.h>
namespace blink {
static hb_language_t ToHarfbuzLanguage(const AtomicString& locale) {
CString locale_as_latin1 = locale.Latin1();
return hb_language_from_string(locale_as_latin1.data(),
locale_as_latin1.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 (string_for_sk_font_mgr_.IsNull()) {
string_for_sk_font_mgr_ = ToSkFontMgrLocale(script_);
if (string_for_sk_font_mgr_.IsNull())
string_for_sk_font_mgr_ = string_.Ascii();
}
return string_for_sk_font_mgr_.data();
}
void LayoutLocale::ComputeScriptForHan() const {
if (IsUnambiguousHanScript(script_)) {
script_for_han_ = script_;
has_script_for_han_ = true;
return;
}
script_for_han_ = ScriptCodeForHanFromSubtags(string_);
if (script_for_han_ == USCRIPT_COMMON)
script_for_han_ = USCRIPT_SIMPLIFIED_HAN;
else
has_script_for_han_ = true;
DCHECK(IsUnambiguousHanScript(script_for_han_));
}
UScriptCode LayoutLocale::GetScriptForHan() const {
if (script_for_han_ == USCRIPT_COMMON)
ComputeScriptForHan();
return script_for_han_;
}
bool LayoutLocale::HasScriptForHan() const {
if (script_for_han_ == USCRIPT_COMMON)
ComputeScriptForHan();
return has_script_for_han_;
}
const LayoutLocale* LayoutLocale::LocaleForHan(
const LayoutLocale* content_locale) {
if (content_locale && content_locale->HasScriptForHan())
return content_locale;
if (FontGlobalContext::HasDefaultLocaleForHan())
return FontGlobalContext::GetDefaultLocaleForHan();
const LayoutLocale* default_for_han;
if (const LayoutLocale* locale = AcceptLanguagesResolver::LocaleForHan())
default_for_han = locale;
else if (GetDefault().HasScriptForHan())
default_for_han = &GetDefault();
else if (GetSystem().HasScriptForHan())
default_for_han = &GetSystem();
else
default_for_han = nullptr;
FontGlobalContext::SetDefaultLocaleForHan(default_for_han);
return default_for_han;
}
const char* LayoutLocale::LocaleForHanForSkFontMgr() const {
const char* locale = ToSkFontMgrLocale(GetScriptForHan());
DCHECK(locale);
return locale;
}
LayoutLocale::LayoutLocale(const AtomicString& locale)
: string_(locale),
harfbuzz_language_(ToHarfbuzLanguage(locale)),
script_(LocaleToScriptCodeForFontSelection(locale)),
script_for_han_(USCRIPT_COMMON),
has_script_for_han_(false),
hyphenation_computed_(false) {}
const LayoutLocale* LayoutLocale::Get(const AtomicString& locale) {
if (locale.IsNull())
return nullptr;
auto result = FontGlobalContext::GetLayoutLocaleMap().insert(locale, nullptr);
if (result.is_new_entry)
result.stored_value->value = base::AdoptRef(new LayoutLocale(locale));
return result.stored_value->value.get();
}
const LayoutLocale& LayoutLocale::GetDefault() {
if (const LayoutLocale* locale = FontGlobalContext::GetDefaultLayoutLocale())
return *locale;
AtomicString language = DefaultLanguage();
const LayoutLocale* locale =
LayoutLocale::Get(!language.IsEmpty() ? language : "en");
FontGlobalContext::SetDefaultLayoutLocale(locale);
return *locale;
}
const LayoutLocale& LayoutLocale::GetSystem() {
if (const LayoutLocale* locale = FontGlobalContext::GetSystemLayoutLocale())
return *locale;
// 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();
const LayoutLocale* locale =
LayoutLocale::Get(AtomicString(name.Replace('_', '-')));
FontGlobalContext::SetSystemLayoutLocale(locale);
return *locale;
}
scoped_refptr<LayoutLocale> LayoutLocale::CreateForTesting(
const AtomicString& locale) {
return base::AdoptRef(new LayoutLocale(locale));
}
Hyphenation* LayoutLocale::GetHyphenation() const {
if (hyphenation_computed_)
return hyphenation_.get();
hyphenation_computed_ = true;
hyphenation_ = Hyphenation::PlatformGetHyphenation(LocaleString());
return hyphenation_.get();
}
void LayoutLocale::SetHyphenationForTesting(
const AtomicString& locale_string,
scoped_refptr<Hyphenation> hyphenation) {
const LayoutLocale& locale = ValueOrDefault(Get(locale_string));
locale.hyphenation_computed_ = true;
locale.hyphenation_ = std::move(hyphenation);
}
AtomicString LayoutLocale::LocaleWithBreakKeyword(
LineBreakIteratorMode mode) const {
if (string_.IsEmpty())
return string_;
// uloc_setKeywordValue_58 has a problem to handle "@" in the original
// string. crbug.com/697859
if (string_.Contains('@'))
return string_;
CString utf8_locale = string_.Utf8();
Vector<char> buffer(utf8_locale.length() + 11, 0);
memcpy(buffer.data(), utf8_locale.data(), utf8_locale.length());
const char* keyword_value = nullptr;
switch (mode) {
default:
NOTREACHED();
// Fall through.
case LineBreakIteratorMode::kDefault:
// nullptr will cause any existing values to be removed.
break;
case LineBreakIteratorMode::kNormal:
keyword_value = "normal";
break;
case LineBreakIteratorMode::kStrict:
keyword_value = "strict";
break;
case LineBreakIteratorMode::kLoose:
keyword_value = "loose";
break;
}
ICUError status;
int32_t length_needed = uloc_setKeywordValue(
"lb", keyword_value, buffer.data(), buffer.size(), &status);
if (U_SUCCESS(status))
return AtomicString::FromUTF8(buffer.data(), length_needed);
if (status == U_BUFFER_OVERFLOW_ERROR) {
buffer.Grow(length_needed + 1);
memset(buffer.data() + utf8_locale.length(), 0,
buffer.size() - utf8_locale.length());
status = U_ZERO_ERROR;
int32_t length_needed2 = uloc_setKeywordValue(
"lb", keyword_value, buffer.data(), buffer.size(), &status);
DCHECK_EQ(length_needed, length_needed2);
if (U_SUCCESS(status) && length_needed == length_needed2)
return AtomicString::FromUTF8(buffer.data(), length_needed);
}
NOTREACHED();
return string_;
}
} // namespace blink