blob: 2695814240545857ca752bbbe2605ce6ca6be479 [file] [edit]
// Copyright 2016 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
#include "core/fxge/android/cfx_androidfontinfo.h"
#include <algorithm>
#include <functional>
#include <utility>
#include "core/fxcrt/compiler_specific.h"
#include "core/fxcrt/fx_codepage.h"
#include "core/fxcrt/fx_extension.h"
#include "core/fxcrt/span.h"
namespace {
constexpr char kRoboto[] = "Roboto";
constexpr char kDroidSans[] = "Droid Sans";
constexpr char kDroidSerif[] = "Droid Serif";
constexpr char kDroidSansMono[] = "Droid Sans Mono";
constexpr char kDroidSansFallback[] = "Droid Sans Fallback";
// Repeat the function from bytestring.cpp here, so it can be used in
// a constexpr context. Then make the compiler prove the mappings are
// correct.
constexpr uint32_t HashNormalizeFontName(const char* family) {
uint32_t hash_code = 0;
UNSAFE_BUFFERS({
for (size_t i = 0; family[i] != '\0'; ++i) {
char ch = family[i];
if (ch == ' ' || ch == '-' || ch == ',') {
continue;
}
hash_code = 31 * hash_code + FXSYS_ToLowerASCII(ch);
}
});
return hash_code;
}
static_assert(HashNormalizeFontName("Arial") == 0x058c5083);
static_assert(HashNormalizeFontName("Baskerville") == 0x3d49f40e);
static_assert(HashNormalizeFontName("Courier New") == 0x83746053);
static_assert(HashNormalizeFontName("Courier") == 0x3918fe2d);
static_assert(HashNormalizeFontName("Cursive") == 0x432c41c5);
static_assert(HashNormalizeFontName("Fantasy") == 0xbf85ff26);
static_assert(HashNormalizeFontName("Georgia") == 0xfb4ce0de);
static_assert(HashNormalizeFontName("Goudy") == 0x05dfade2);
static_assert(HashNormalizeFontName("Monaco") == 0xc04fe601);
static_assert(HashNormalizeFontName("Monospace") == 0xaaa60c03);
static_assert(HashNormalizeFontName("Palatino") == 0x3b98b31c);
static_assert(HashNormalizeFontName("STSong") == 0xcad5eaf6);
static_assert(HashNormalizeFontName("Sanserif") == 0x779ce19d);
static_assert(HashNormalizeFontName("Serif") == 0x0684317d);
static_assert(HashNormalizeFontName("SimHei") == 0xca3812d5);
static_assert(HashNormalizeFontName("SimSun") == 0xca383e15);
static_assert(HashNormalizeFontName("Tahoma") == 0xcb7a04c8);
static_assert(HashNormalizeFontName("Verdana") == 0x14ee2d13);
struct AndroidFontMap {
uint32_t family;
const char* subst;
};
const AndroidFontMap kAndroidFontmap[] = {
{0x058c5083, kRoboto}, // Arial
{0x05dfade2, kDroidSerif}, // Goudy
{0x0684317d, kDroidSerif}, // Serif
{0x14ee2d13, kRoboto}, // Verdana
{0x3918fe2d, kDroidSansMono}, // Courier
{0x3b98b31c, kDroidSerif}, // Palatino
{0x3d49f40e, kDroidSerif}, // Baskerville
{0x432c41c5, kDroidSerif}, // Cursive
{0x491b6ad0, kDroidSerif}, // Unknown
{0x5612cab1, kDroidSansFallback}, // Unknown
{0x779ce19d, kRoboto}, // Sanserif
{0x7cc9510b, kDroidSansFallback}, // Unknown
{0x83746053, kDroidSansMono}, // Courier New
{0xaaa60c03, kDroidSansMono}, // Monospace
{0xbf85ff26, kDroidSerif}, // Fantasy
{0xc04fe601, kDroidSansMono}, // Monaco
{0xca3812d5, kDroidSansFallback}, // SimHei
{0xca383e15, kDroidSansFallback}, // SimSun
{0xcad5eaf6, kDroidSansFallback}, // STSong
{0xcb7a04c8, kRoboto}, // Tahoma
{0xfb4ce0de, kDroidSerif}, // Georgia
};
const AndroidFontMap kAndroidSansFontMap[] = {
{0x058c5083, kDroidSans}, // Arial
{0x14ee2d13, kDroidSans}, // Verdana
{0x779ce19d, kDroidSans}, // Sanserif
{0xcb7a04c8, kDroidSans}, // Tahoma
{0xfb4ce0de, kDroidSans}, // Georgia
};
uint32_t AndroidNormalizeFontName(ByteStringView family) {
uint32_t hash_code = 0;
for (char ch : family) {
if (ch == ' ' || ch == '-' || ch == ',') {
continue;
}
hash_code = 31 * hash_code + FXSYS_ToLowerASCII(ch);
}
return hash_code;
}
const char* AndroidGetSubstFont(uint32_t hash,
pdfium::span<const AndroidFontMap> font_map) {
auto it = std::ranges::lower_bound(font_map, hash, std::less<>{},
&AndroidFontMap::family);
if (it != font_map.end() && it->family == hash) {
return it->subst;
}
return nullptr;
}
bool AndroidMaybeArabic(ByteStringView facename) {
ByteString name(facename);
name.MakeLower();
return name.Contains("arabic");
}
bool AndroidMaybeSymbol(ByteStringView facename) {
ByteString name(facename);
name.MakeLower();
return name.Contains("symbol");
}
} // namespace
CFX_AndroidFontInfo::CFX_AndroidFontInfo() = default;
CFX_AndroidFontInfo::~CFX_AndroidFontInfo() = default;
void CFX_AndroidFontInfo::Init(
std::optional<pdfium::span<const char* const>> user_paths) {
AddPath("/system/fonts");
if (user_paths.has_value()) {
for (const char* path : user_paths.value()) {
AddPath(path);
}
}
}
void* CFX_AndroidFontInfo::MapFont(CFX_FontMapper* mapper,
int weight,
bool bItalic,
FX_Charset charset,
int pitch_family,
const ByteString& face) {
bool is_cjk = FX_CharSetIsCJK(charset);
if (charset != FX_Charset::kMSWin_Arabic &&
AndroidMaybeArabic(face.AsStringView())) {
charset = FX_Charset::kMSWin_Arabic;
} else if (charset == FX_Charset::kANSI ||
AndroidMaybeSymbol(face.AsStringView())) {
charset = FX_Charset::kDefault;
}
// First try to match the actual face name.
void* font = FindFont(weight, bItalic, charset, pitch_family, face,
/*must_match_name=*/true);
if (font) {
return font;
}
const uint32_t face_name_hash = AndroidNormalizeFontName(face.AsStringView());
const char* subst_name = AndroidGetSubstFont(face_name_hash, kAndroidFontmap);
if (subst_name) {
font = FindFont(weight, bItalic, charset, pitch_family, subst_name,
/*must_match_name=*/true);
if (font) {
return font;
}
}
const char* subst_sans_name =
AndroidGetSubstFont(face_name_hash, kAndroidSansFontMap);
if (subst_sans_name) {
font = FindFont(weight, bItalic, charset, pitch_family, subst_sans_name,
/*must_match_name=*/true);
if (font) {
return font;
}
}
font = GetSubstFont(face);
if (font) {
return font;
}
if (is_cjk) {
return FindFont(weight, bItalic, charset, pitch_family, face,
/*must_match_name=*/false);
}
return nullptr;
}
bool CFX_AndroidFontInfo::IsBetterMatch(const FontFaceInfo* candidate,
int32_t candidate_score,
const FontFaceInfo* current_best,
int32_t current_best_score,
FX_Charset charset,
const ByteString& family,
bool must_match_name) const {
if (!FX_CharSetIsCJK(charset)) {
return CFX_FolderFontInfo::IsBetterMatch(candidate, candidate_score,
current_best, current_best_score,
charset, family, must_match_name);
}
if (must_match_name) {
// When a name match is required, prefer score over glyph count.
if (current_best && candidate_score < current_best_score) {
return false;
}
ByteStringView family_view = family.AsStringView();
if (candidate->face_name_ != family &&
!FindFamilyNameMatch(family_view, candidate->face_name_)) {
return false;
}
if (current_best) {
if (candidate_score != current_best_score) {
return candidate_score > current_best_score;
}
return candidate->glyph_count_ > current_best->glyph_count_;
}
return true;
}
// When a name match is not required, prefer glyph count over score to
// avoid empty boxes.
uint32_t current_best_glyphs = current_best ? current_best->glyph_count_ : 0;
if (candidate->glyph_count_ != current_best_glyphs) {
return candidate->glyph_count_ > current_best_glyphs;
}
return candidate_score > current_best_score;
}