| // 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)); |
| } |