blob: ab0c8586524f3fe476f5d6d294b19af67de3919a [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 <utility>
#include "core/fxcrt/fx_codepage.h"
#include "core/fxge/cfx_gemodule.h"
#include "core/fxge/systemfontinfo_iface.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
using testing::DoAll;
using testing::ElementsAre;
using testing::InSequence;
using testing::Invoke;
using testing::Return;
using testing::WithArg;
class MockSystemFontInfo : public SystemFontInfoIface {
public:
MockSystemFontInfo() = default;
~MockSystemFontInfo() override = default;
// SystemFontInfoIface:
MOCK_METHOD(bool, 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()) {}
RetainPtr<CFX_Face> GetCachedTTCFace(void* font_handle,
size_t ttc_size,
size_t data_size) {
return CFX_FontMapper::GetCachedTTCFace(font_handle, ttc_size, data_size);
}
RetainPtr<CFX_Face> GetCachedFace(void* font_handle,
ByteString subst_name,
int weight,
bool is_italic,
size_t data_size) {
return CFX_FontMapper::GetCachedFace(font_handle, subst_name, weight,
is_italic, data_size);
}
};
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(CFX_FontMapper, 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(CFX_FontMapper, 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'));
EXPECT_EQ(FT_MAKE_TAG('G', 'S', 'U', 'B'),
CFX_FontMapper::MakeTag('G', 'S', 'U', 'B'));
}
TEST(CFX_FontMapper, 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>(Invoke([](pdfium::span<uint8_t> buffer) {
buffer[0] = '0';
buffer[1] = '1';
})),
Return(2)));
EXPECT_CALL(system_font_info(), DeleteFont(kFontHandle));
}
FixedUninitDataVector<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));
FixedUninitDataVector<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));
}
FixedUninitDataVector<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));
}
FixedUninitDataVector<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);
constexpr size_t kTtcSize = 1024;
constexpr size_t kDataSize = 2;
{
InSequence s;
EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, kTableTTCF, _))
.WillOnce(DoAll(WithArg<2>(Invoke([&](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);
constexpr char kSubstName[] = "dummy_font";
constexpr int kWeight = 400;
constexpr bool kItalic = false;
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));
}