blob: 5cf2364151e337b38d6ddfb34081e10674725867 [file]
// Copyright 2026 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file tests the cross-platform font mapping and selection logic of
// CFX_AndroidFontInfo. It is compiled and run on Linux host tests as the
// logic does not depend on Android-specific system APIs.
#include "core/fxge/android/cfx_androidfontinfo.h"
#include <memory>
#include <utility>
#include "core/fxcrt/fx_codepage.h"
#include "core/fxge/fx_font.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
constexpr char kArial[] = "Arial";
constexpr char kDroidSansFallback[] = "Droid Sans Fallback";
constexpr char kRoboto[] = "Roboto";
constexpr char kSimSun[] = "SimSun";
} // namespace
class CFXAndroidFontInfoTest : public ::testing::Test {
public:
CFXAndroidFontInfoTest() = default;
void* MapFont(int weight,
bool bItalic,
FX_Charset charset,
int pitch_family,
const char* family) {
return font_info_.MapFont(weight, bItalic, charset, pitch_family, family);
}
ByteString GetFaceName(void* font) {
return static_cast<CFX_AndroidFontInfo::FontFaceInfo*>(font)->face_name_;
}
void AddDummyFont(const char* font_name,
FX_CharsetFlag charset,
uint32_t glyph_count = 0,
uint32_t styles = 0) {
auto info = std::make_unique<CFX_AndroidFontInfo::FontFaceInfo>(
/*filePath=*/"", font_name, /*fontTables=*/"",
/*fontOffset=*/0, /*fileSize=*/0);
info->charsets_ = charset;
info->glyph_count_ = glyph_count;
info->styles_ = styles;
font_info_.font_list_[font_name] = std::move(info);
}
private:
CFX_AndroidFontInfo font_info_;
};
TEST_F(CFXAndroidFontInfoTest, MapFontPreferOriginalNonCJK) {
// Install both original (Arial) and substitution (Roboto).
AddDummyFont(kArial, FX_CharsetFlag::kANSI);
AddDummyFont(kRoboto, FX_CharsetFlag::kANSI);
// Map "Arial" -> should prefer "Arial" over "Roboto" (subst).
void* font = MapFont(/*weight=*/400, /*bItalic=*/false, FX_Charset::kANSI,
pdfium::kFontPitchFamilyRoman, kArial);
ASSERT_TRUE(font);
EXPECT_EQ(GetFaceName(font), kArial);
}
TEST_F(CFXAndroidFontInfoTest, MapFontFallbackToSubstNonCJK) {
// Install only substitution (Roboto).
AddDummyFont(kRoboto, FX_CharsetFlag::kANSI);
// Map "Arial" -> should fallback to "Roboto".
void* font = MapFont(/*weight=*/400, /*bItalic=*/false, FX_Charset::kANSI,
pdfium::kFontPitchFamilyRoman, kArial);
ASSERT_TRUE(font);
EXPECT_EQ(GetFaceName(font), kRoboto);
}
TEST_F(CFXAndroidFontInfoTest, MapFontPreferOriginalCJK) {
// Install both original (SimSun) and substitution (Droid Sans Fallback).
// Droid Sans Fallback might have more glyphs, but we should prefer original.
AddDummyFont(kSimSun, FX_CharsetFlag::kChineseSimplified,
/*glyph_count=*/1000);
AddDummyFont(kDroidSansFallback, FX_CharsetFlag::kChineseSimplified,
/*glyph_count=*/2000);
// Map "SimSun" -> should prefer "SimSun" (original) over "Droid Sans
// Fallback" (subst).
void* font =
MapFont(/*weight=*/400, /*bItalic=*/false, FX_Charset::kChineseSimplified,
pdfium::kFontPitchFamilyRoman, kSimSun);
ASSERT_TRUE(font);
EXPECT_EQ(GetFaceName(font), kSimSun);
}
TEST_F(CFXAndroidFontInfoTest, MapFontFallbackToSubstCJK) {
// Install only substitution (Droid Sans Fallback).
AddDummyFont(kDroidSansFallback, FX_CharsetFlag::kChineseSimplified,
/*glyph_count=*/2000);
// Map "SimSun" -> should fallback to "Droid Sans Fallback".
void* font =
MapFont(/*weight=*/400, /*bItalic=*/false, FX_Charset::kChineseSimplified,
pdfium::kFontPitchFamilyRoman, kSimSun);
ASSERT_TRUE(font);
EXPECT_EQ(GetFaceName(font), kDroidSansFallback);
}
TEST_F(CFXAndroidFontInfoTest, MapFontCJKFallbackNoMatch) {
// Install some other CJK font (e.g. Droid Sans Fallback).
AddDummyFont(kDroidSansFallback, FX_CharsetFlag::kChineseSimplified,
/*glyph_count=*/2000);
// Map some random CJK name that has no subst rule (e.g. "MyCJKFont").
// Since must_match_name will eventually be false, it should find Droid Sans
// Fallback.
void* font =
MapFont(/*weight=*/400, /*bItalic=*/false, FX_Charset::kChineseSimplified,
pdfium::kFontPitchFamilyRoman, "MyCJKFont");
ASSERT_TRUE(font);
EXPECT_EQ(GetFaceName(font), kDroidSansFallback);
}
TEST_F(CFXAndroidFontInfoTest, MapFontCJKDifferentWeights) {
// Install "SimSun" (Regular) and "SimSun Bold".
AddDummyFont("SimSun", FX_CharsetFlag::kChineseSimplified, 1000, 0);
AddDummyFont("SimSun Bold", FX_CharsetFlag::kChineseSimplified, 1000,
pdfium::kFontStyleForceBold);
// Map "SimSun" with bold weight (700) -> should prefer "SimSun Bold".
void* font =
MapFont(/*weight=*/700, /*bItalic=*/false, FX_Charset::kChineseSimplified,
pdfium::kFontPitchFamilyRoman, "SimSun");
ASSERT_TRUE(font);
EXPECT_EQ(GetFaceName(font), "SimSun Bold");
}
TEST_F(CFXAndroidFontInfoTest, MapFontCJKDifferentGlyphCountsNoNameMatch) {
// Install two non-matching CJK fonts with different glyph counts.
AddDummyFont("CJK Font 1", FX_CharsetFlag::kChineseSimplified, 1000);
AddDummyFont("CJK Font 2", FX_CharsetFlag::kChineseSimplified, 2000);
// Map "MyCJKFont" (no match) -> should prefer CJK Font 2 (more glyphs).
void* font =
MapFont(/*weight=*/400, /*bItalic=*/false, FX_Charset::kChineseSimplified,
pdfium::kFontPitchFamilyRoman, "MyCJKFont");
ASSERT_TRUE(font);
EXPECT_EQ(GetFaceName(font), "CJK Font 2");
}