blob: ae9856798d5a725d5cba105822908997b9a9b143 [file] [log] [blame]
// Copyright 2019 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "core/fxge/cfx_fontmapper.h"
#include <memory>
#include <numeric>
#include <string>
#include <utility>
#include "core/fxcrt/fx_codepage.h"
#include "core/fxge/cfx_gemodule.h"
#include "core/fxge/cfx_substfont.h"
#include "core/fxge/systemfontinfo_iface.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/utils/file_util.h"
#include "testing/utils/path_service.h"
using testing::_;
using testing::DoAll;
using testing::ElementsAre;
using testing::InSequence;
using testing::Return;
using testing::WithArg;
class MockSystemFontInfo : public SystemFontInfoIface {
public:
MockSystemFontInfo() = default;
~MockSystemFontInfo() override = default;
// SystemFontInfoIface:
MOCK_METHOD(void, EnumFontList, (CFX_FontMapper*), (override));
MOCK_METHOD(void*,
MapFont,
(int, bool, FX_Charset, int, const ByteString&),
(override));
MOCK_METHOD(void*, GetFont, (const ByteString&), (override));
MOCK_METHOD(size_t,
GetFontData,
(void*, uint32_t, pdfium::span<uint8_t>),
(override));
MOCK_METHOD(bool, GetFaceName, (void*, ByteString*), (override));
MOCK_METHOD(bool, GetFontCharset, (void*, FX_Charset*), (override));
MOCK_METHOD(void, DeleteFont, (void*), (override));
};
// Class that exposes private CFX_FontMapper methods.
class TestFontMapper : public CFX_FontMapper {
public:
TestFontMapper() : CFX_FontMapper(CFX_GEModule::Get()->GetFontMgr()) {}
using CFX_FontMapper::GetCachedFace;
using CFX_FontMapper::GetCachedTTCFace;
using CFX_FontMapper::UseExternalSubst;
};
class CFXFontMapperSystemFontInfoTest : public testing::Test {
protected:
CFXFontMapperSystemFontInfoTest() = default;
~CFXFontMapperSystemFontInfoTest() override = default;
void SetUp() override {
font_mapper_ = std::make_unique<TestFontMapper>();
auto system_font_info = std::make_unique<MockSystemFontInfo>();
system_font_info_ = system_font_info.get();
font_mapper_->SetSystemFontInfo(std::move(system_font_info));
font_mapper_->AddInstalledFont("dummy", FX_Charset::kANSI);
}
TestFontMapper& font_mapper() { return *font_mapper_; }
MockSystemFontInfo& system_font_info() { return *system_font_info_; }
private:
// Must outlive `system_font_info_`.
std::unique_ptr<TestFontMapper> font_mapper_;
UnownedPtr<MockSystemFontInfo> system_font_info_;
};
// Deliberately give this global variable external linkage.
char g_maybe_changes = '\xff';
TEST(CFXFontMapperTest, IsStandardFontName) {
EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Courier"));
EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Courier-Bold"));
EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Courier-BoldOblique"));
EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Courier-Oblique"));
EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Helvetica"));
EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Helvetica-Bold"));
EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Helvetica-BoldOblique"));
EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Helvetica-Oblique"));
EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Times-Roman"));
EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Times-Bold"));
EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Times-BoldItalic"));
EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Times-Italic"));
EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Symbol"));
EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("ZapfDingbats"));
EXPECT_FALSE(CFX_FontMapper::IsStandardFontName("Courie"));
EXPECT_FALSE(CFX_FontMapper::IsStandardFontName("Courier-"));
EXPECT_FALSE(CFX_FontMapper::IsStandardFontName("Helvetica+Bold"));
EXPECT_FALSE(CFX_FontMapper::IsStandardFontName("YapfDingbats"));
}
TEST(CFXFontMapperTest, MakeTag) {
EXPECT_EQ(0x61626364u, CFX_FontMapper::MakeTag('a', 'b', 'c', 'd'));
EXPECT_EQ(0x00000000u, CFX_FontMapper::MakeTag('\0', '\0', '\0', '\0'));
EXPECT_EQ(0xfffe0a08u, CFX_FontMapper::MakeTag('\xff', '\xfe', '\n', '\b'));
EXPECT_EQ(0xffffffffu,
CFX_FontMapper::MakeTag('\xff', '\xff', '\xff', '\xff'));
EXPECT_EQ(0xffffffffu,
CFX_FontMapper::MakeTag(g_maybe_changes, '\xff', '\xff', '\xff'));
EXPECT_EQ(0x6e616d65u, CFX_FontMapper::MakeTag('n', 'a', 'm', 'e'));
EXPECT_EQ(0x4f532f32u, CFX_FontMapper::MakeTag('O', 'S', '/', '2'));
}
TEST(CFXFontMapperTest, AddInstalledFontBasic) {
const char kFontName[] = "dummy";
CFX_FontMapper font_mapper(nullptr);
font_mapper.SetSystemFontInfo(std::make_unique<MockSystemFontInfo>());
font_mapper.AddInstalledFont(kFontName, FX_Charset::kANSI);
EXPECT_EQ(1u, font_mapper.GetFaceSize());
EXPECT_EQ(kFontName, font_mapper.GetFaceName(0));
}
#ifdef PDF_ENABLE_XFA
TEST_F(CFXFontMapperSystemFontInfoTest, RawBytesForIndex) {
{
void* const kFontHandle = reinterpret_cast<void*>(12345);
InSequence s;
EXPECT_CALL(system_font_info(), MapFont).WillOnce(Return(kFontHandle));
EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, 0, _))
.WillOnce(Return(2));
EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, 0, _))
.WillOnce(DoAll(WithArg<2>([](pdfium::span<uint8_t> buffer) {
buffer[0] = '0';
buffer[1] = '1';
}),
Return(2)));
EXPECT_CALL(system_font_info(), DeleteFont(kFontHandle));
}
FixedSizeDataVector<uint8_t> data = font_mapper().RawBytesForIndex(0);
EXPECT_THAT(data.span(), ElementsAre('0', '1'));
}
TEST_F(CFXFontMapperSystemFontInfoTest, RawBytesForIndexFailToMap) {
EXPECT_CALL(system_font_info(), MapFont).WillOnce(Return(nullptr));
FixedSizeDataVector<uint8_t> data = font_mapper().RawBytesForIndex(0);
EXPECT_TRUE(data.empty());
}
TEST_F(CFXFontMapperSystemFontInfoTest, RawBytesForIndexFailToGetDataSize) {
{
void* const kFontHandle = reinterpret_cast<void*>(12345);
InSequence s;
EXPECT_CALL(system_font_info(), MapFont).WillOnce(Return(kFontHandle));
EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, 0, _))
.WillOnce(Return(0));
EXPECT_CALL(system_font_info(), DeleteFont(kFontHandle));
}
FixedSizeDataVector<uint8_t> data = font_mapper().RawBytesForIndex(0);
EXPECT_TRUE(data.empty());
}
TEST_F(CFXFontMapperSystemFontInfoTest, RawBytesForIndexFailToGetData) {
{
void* const kFontHandle = reinterpret_cast<void*>(12345);
InSequence s;
EXPECT_CALL(system_font_info(), MapFont).WillOnce(Return(kFontHandle));
EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, 0, _))
.WillOnce(Return(2));
EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, 0, _))
.WillOnce(Return(0));
EXPECT_CALL(system_font_info(), DeleteFont(kFontHandle));
}
FixedSizeDataVector<uint8_t> data = font_mapper().RawBytesForIndex(0);
EXPECT_TRUE(data.empty());
}
#endif // PDF_ENABLE_XFA
// Regression test for crbug.com/1372234 - should not crash.
TEST_F(CFXFontMapperSystemFontInfoTest, GetCachedTTCFaceFailToGetData) {
void* const kFontHandle = reinterpret_cast<void*>(12345);
static constexpr size_t kTtcSize = 1024;
static constexpr size_t kDataSize = 2;
{
InSequence s;
EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, kTableTTCF, _))
.WillOnce(DoAll(WithArg<2>([&](pdfium::span<uint8_t> buffer) {
EXPECT_EQ(kTtcSize, buffer.size());
std::iota(buffer.begin(), buffer.end(), 0);
}),
Return(kTtcSize)));
EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, kTableTTCF, _))
.WillOnce(Return(0));
}
EXPECT_FALSE(
font_mapper().GetCachedTTCFace(kFontHandle, kTtcSize, kDataSize));
}
// Regression test for crbug.com/1372234 - should not crash.
TEST_F(CFXFontMapperSystemFontInfoTest, GetCachedFaceFailToGetData) {
void* const kFontHandle = reinterpret_cast<void*>(12345);
static constexpr char kSubstName[] = "placeholder_font";
static constexpr int kWeight = 400;
static constexpr bool kItalic = false;
static constexpr size_t kDataSize = 2;
EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, 0, _))
.WillOnce(Return(0));
EXPECT_FALSE(font_mapper().GetCachedFace(kFontHandle, kSubstName, kWeight,
kItalic, kDataSize));
}
TEST_F(CFXFontMapperSystemFontInfoTest, SetSubstFontNameWhenGetFaceNameFails) {
std::string font_path;
ASSERT_TRUE(PathService::GetThirdPartyFilePath(
"NotoSansCJK/NotoSansSC-Regular.subset.otf", &font_path));
const std::vector<uint8_t> font_data = GetFileContents(font_path.c_str());
ASSERT_FALSE(font_data.empty());
static void* const kFontHandle = reinterpret_cast<void*>(12345);
{
InSequence s;
EXPECT_CALL(system_font_info(), GetFaceName(kFontHandle, _))
.WillOnce(Return(false));
EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, kTableTTCF, _))
.WillOnce(Return(0));
EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, 0, _))
.WillOnce(DoAll(WithArg<2>([&](pdfium::span<uint8_t> buffer) {
EXPECT_EQ(0u, buffer.size());
}),
Return(font_data.size())));
EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, 0, _))
.WillOnce(DoAll(WithArg<2>([&](pdfium::span<uint8_t> buffer) {
ASSERT_EQ(font_data.size(), buffer.size());
fxcrt::spancpy(buffer, pdfium::span(font_data));
}),
Return(font_data.size())));
EXPECT_CALL(system_font_info(), DeleteFont(kFontHandle));
}
static constexpr char kSubstName[] = "placeholder_font";
static constexpr int kWeight = 400;
static constexpr bool kItalic = false;
static constexpr int kItalicAngle = 0;
static constexpr auto kCharset = FX_Charset::kANSI;
CFX_SubstFont subst_font;
EXPECT_TRUE(font_mapper().UseExternalSubst(kFontHandle, kSubstName, kWeight,
kItalic, kItalicAngle, kCharset,
&subst_font));
EXPECT_EQ("Noto Sans SC Regular", subst_font.family_);
}
TEST(CFXFontMapperTest, LoadInstalledFontsWithEnumeration) {
CFX_FontMapper font_mapper(nullptr);
auto system_font_info = std::make_unique<MockSystemFontInfo>();
auto* mock_font_info = system_font_info.get();
font_mapper.SetSystemFontInfo(std::move(system_font_info));
// Default behavior: EnumFontList should be called
EXPECT_CALL(*mock_font_info, EnumFontList(&font_mapper)).Times(1);
font_mapper.LoadInstalledFonts();
}
TEST(CFXFontMapperTest, LoadInstalledFontsSkipEnumeration) {
CFX_FontMapper font_mapper(nullptr);
auto system_font_info = std::make_unique<MockSystemFontInfo>();
auto* mock_font_info = system_font_info.get();
font_mapper.SetSystemFontInfo(std::move(system_font_info));
// Enable skip enumeration
font_mapper.SetSkipFontEnumeration(true);
// EnumFontList should NOT be called when skip_enumeration is enabled
EXPECT_CALL(*mock_font_info, EnumFontList(_)).Times(0);
font_mapper.LoadInstalledFonts();
}
TEST(CFXFontMapperTest, LoadInstalledFontsCalledOnlyOnce) {
CFX_FontMapper font_mapper(nullptr);
auto system_font_info = std::make_unique<MockSystemFontInfo>();
auto* mock_font_info = system_font_info.get();
font_mapper.SetSystemFontInfo(std::move(system_font_info));
// EnumFontList should be called only once even if LoadInstalledFonts is
// called multiple times
EXPECT_CALL(*mock_font_info, EnumFontList(&font_mapper)).Times(1);
font_mapper.LoadInstalledFonts();
font_mapper.LoadInstalledFonts();
font_mapper.LoadInstalledFonts();
}