1 // Copyright 2019 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "core/fxge/cfx_fontmapper.h"
6
7 #include <memory>
8 #include <numeric>
9 #include <utility>
10
11 #include "core/fxcrt/fx_codepage.h"
12 #include "core/fxge/cfx_gemodule.h"
13 #include "core/fxge/systemfontinfo_iface.h"
14 #include "testing/gmock/include/gmock/gmock.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16
17 using testing::_;
18 using testing::DoAll;
19 using testing::ElementsAre;
20 using testing::InSequence;
21 using testing::Invoke;
22 using testing::Return;
23 using testing::WithArg;
24
25 class MockSystemFontInfo : public SystemFontInfoIface {
26 public:
27 MockSystemFontInfo() = default;
28 ~MockSystemFontInfo() override = default;
29
30 // SystemFontInfoIface:
31 MOCK_METHOD(bool, EnumFontList, (CFX_FontMapper*), (override));
32 MOCK_METHOD(void*,
33 MapFont,
34 (int, bool, FX_Charset, int, const ByteString&),
35 (override));
36 MOCK_METHOD(void*, GetFont, (const ByteString&), (override));
37 MOCK_METHOD(size_t,
38 GetFontData,
39 (void*, uint32_t, pdfium::span<uint8_t>),
40 (override));
41 MOCK_METHOD(bool, GetFaceName, (void*, ByteString*), (override));
42 MOCK_METHOD(bool, GetFontCharset, (void*, FX_Charset*), (override));
43 MOCK_METHOD(void, DeleteFont, (void*), (override));
44 };
45
46 // Class that exposes private CFX_FontMapper methods.
47 class TestFontMapper : public CFX_FontMapper {
48 public:
TestFontMapper()49 TestFontMapper() : CFX_FontMapper(CFX_GEModule::Get()->GetFontMgr()) {}
50
GetCachedTTCFace(void * font_handle,size_t ttc_size,size_t data_size)51 RetainPtr<CFX_Face> GetCachedTTCFace(void* font_handle,
52 size_t ttc_size,
53 size_t data_size) {
54 return CFX_FontMapper::GetCachedTTCFace(font_handle, ttc_size, data_size);
55 }
56
GetCachedFace(void * font_handle,ByteString subst_name,int weight,bool is_italic,size_t data_size)57 RetainPtr<CFX_Face> GetCachedFace(void* font_handle,
58 ByteString subst_name,
59 int weight,
60 bool is_italic,
61 size_t data_size) {
62 return CFX_FontMapper::GetCachedFace(font_handle, subst_name, weight,
63 is_italic, data_size);
64 }
65 };
66
67 class CFXFontMapperSystemFontInfoTest : public testing::Test {
68 protected:
69 CFXFontMapperSystemFontInfoTest() = default;
70 ~CFXFontMapperSystemFontInfoTest() override = default;
71
SetUp()72 void SetUp() override {
73 font_mapper_ = std::make_unique<TestFontMapper>();
74 auto system_font_info = std::make_unique<MockSystemFontInfo>();
75 system_font_info_ = system_font_info.get();
76 font_mapper_->SetSystemFontInfo(std::move(system_font_info));
77 font_mapper_->AddInstalledFont("dummy", FX_Charset::kANSI);
78 }
79
font_mapper()80 TestFontMapper& font_mapper() { return *font_mapper_; }
system_font_info()81 MockSystemFontInfo& system_font_info() { return *system_font_info_; }
82
83 private:
84 // Must outlive `system_font_info_`.
85 std::unique_ptr<TestFontMapper> font_mapper_;
86 UnownedPtr<MockSystemFontInfo> system_font_info_;
87 };
88
89 // Deliberately give this global variable external linkage.
90 char g_maybe_changes = '\xff';
91
TEST(CFXFontMapperTest,IsStandardFontName)92 TEST(CFXFontMapperTest, IsStandardFontName) {
93 EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Courier"));
94 EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Courier-Bold"));
95 EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Courier-BoldOblique"));
96 EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Courier-Oblique"));
97 EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Helvetica"));
98 EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Helvetica-Bold"));
99 EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Helvetica-BoldOblique"));
100 EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Helvetica-Oblique"));
101 EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Times-Roman"));
102 EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Times-Bold"));
103 EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Times-BoldItalic"));
104 EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Times-Italic"));
105 EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Symbol"));
106 EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("ZapfDingbats"));
107
108 EXPECT_FALSE(CFX_FontMapper::IsStandardFontName("Courie"));
109 EXPECT_FALSE(CFX_FontMapper::IsStandardFontName("Courier-"));
110 EXPECT_FALSE(CFX_FontMapper::IsStandardFontName("Helvetica+Bold"));
111 EXPECT_FALSE(CFX_FontMapper::IsStandardFontName("YapfDingbats"));
112 }
113
TEST(CFXFontMapperTest,MakeTag)114 TEST(CFXFontMapperTest, MakeTag) {
115 EXPECT_EQ(0x61626364u, CFX_FontMapper::MakeTag('a', 'b', 'c', 'd'));
116 EXPECT_EQ(0x00000000u, CFX_FontMapper::MakeTag('\0', '\0', '\0', '\0'));
117 EXPECT_EQ(0xfffe0a08u, CFX_FontMapper::MakeTag('\xff', '\xfe', '\n', '\b'));
118 EXPECT_EQ(0xffffffffu,
119 CFX_FontMapper::MakeTag('\xff', '\xff', '\xff', '\xff'));
120 EXPECT_EQ(0xffffffffu,
121 CFX_FontMapper::MakeTag(g_maybe_changes, '\xff', '\xff', '\xff'));
122 EXPECT_EQ(0x6e616d65u, CFX_FontMapper::MakeTag('n', 'a', 'm', 'e'));
123 EXPECT_EQ(0x4f532f32u, CFX_FontMapper::MakeTag('O', 'S', '/', '2'));
124 EXPECT_EQ(FT_MAKE_TAG('G', 'S', 'U', 'B'),
125 CFX_FontMapper::MakeTag('G', 'S', 'U', 'B'));
126 }
127
TEST(CFXFontMapperTest,AddInstalledFontBasic)128 TEST(CFXFontMapperTest, AddInstalledFontBasic) {
129 const char kFontName[] = "dummy";
130 CFX_FontMapper font_mapper(nullptr);
131 font_mapper.SetSystemFontInfo(std::make_unique<MockSystemFontInfo>());
132
133 font_mapper.AddInstalledFont(kFontName, FX_Charset::kANSI);
134 EXPECT_EQ(1u, font_mapper.GetFaceSize());
135 EXPECT_EQ(kFontName, font_mapper.GetFaceName(0));
136 }
137
138 #ifdef PDF_ENABLE_XFA
TEST_F(CFXFontMapperSystemFontInfoTest,RawBytesForIndex)139 TEST_F(CFXFontMapperSystemFontInfoTest, RawBytesForIndex) {
140 {
141 void* const kFontHandle = reinterpret_cast<void*>(12345);
142
143 InSequence s;
144 EXPECT_CALL(system_font_info(), MapFont).WillOnce(Return(kFontHandle));
145 EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, 0, _))
146 .WillOnce(Return(2));
147 EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, 0, _))
148 .WillOnce(DoAll(WithArg<2>(Invoke([](pdfium::span<uint8_t> buffer) {
149 buffer[0] = '0';
150 buffer[1] = '1';
151 })),
152 Return(2)));
153 EXPECT_CALL(system_font_info(), DeleteFont(kFontHandle));
154 }
155
156 FixedSizeDataVector<uint8_t> data = font_mapper().RawBytesForIndex(0);
157 EXPECT_THAT(data.span(), ElementsAre('0', '1'));
158 }
159
TEST_F(CFXFontMapperSystemFontInfoTest,RawBytesForIndexFailToMap)160 TEST_F(CFXFontMapperSystemFontInfoTest, RawBytesForIndexFailToMap) {
161 EXPECT_CALL(system_font_info(), MapFont).WillOnce(Return(nullptr));
162
163 FixedSizeDataVector<uint8_t> data = font_mapper().RawBytesForIndex(0);
164 EXPECT_TRUE(data.empty());
165 }
166
TEST_F(CFXFontMapperSystemFontInfoTest,RawBytesForIndexFailToGetDataSize)167 TEST_F(CFXFontMapperSystemFontInfoTest, RawBytesForIndexFailToGetDataSize) {
168 {
169 void* const kFontHandle = reinterpret_cast<void*>(12345);
170
171 InSequence s;
172 EXPECT_CALL(system_font_info(), MapFont).WillOnce(Return(kFontHandle));
173 EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, 0, _))
174 .WillOnce(Return(0));
175 EXPECT_CALL(system_font_info(), DeleteFont(kFontHandle));
176 }
177
178 FixedSizeDataVector<uint8_t> data = font_mapper().RawBytesForIndex(0);
179 EXPECT_TRUE(data.empty());
180 }
181
TEST_F(CFXFontMapperSystemFontInfoTest,RawBytesForIndexFailToGetData)182 TEST_F(CFXFontMapperSystemFontInfoTest, RawBytesForIndexFailToGetData) {
183 {
184 void* const kFontHandle = reinterpret_cast<void*>(12345);
185
186 InSequence s;
187 EXPECT_CALL(system_font_info(), MapFont).WillOnce(Return(kFontHandle));
188 EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, 0, _))
189 .WillOnce(Return(2));
190 EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, 0, _))
191 .WillOnce(Return(0));
192 EXPECT_CALL(system_font_info(), DeleteFont(kFontHandle));
193 }
194
195 FixedSizeDataVector<uint8_t> data = font_mapper().RawBytesForIndex(0);
196 EXPECT_TRUE(data.empty());
197 }
198 #endif // PDF_ENABLE_XFA
199
200 // Regression test for crbug.com/1372234 - should not crash.
TEST_F(CFXFontMapperSystemFontInfoTest,GetCachedTTCFaceFailToGetData)201 TEST_F(CFXFontMapperSystemFontInfoTest, GetCachedTTCFaceFailToGetData) {
202 void* const kFontHandle = reinterpret_cast<void*>(12345);
203 constexpr size_t kTtcSize = 1024;
204 constexpr size_t kDataSize = 2;
205
206 {
207 InSequence s;
208 EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, kTableTTCF, _))
209 .WillOnce(DoAll(WithArg<2>(Invoke([&](pdfium::span<uint8_t> buffer) {
210 EXPECT_EQ(kTtcSize, buffer.size());
211 std::iota(buffer.begin(), buffer.end(), 0);
212 })),
213 Return(kTtcSize)));
214 EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, kTableTTCF, _))
215 .WillOnce(Return(0));
216 }
217
218 EXPECT_FALSE(
219 font_mapper().GetCachedTTCFace(kFontHandle, kTtcSize, kDataSize));
220 }
221
222 // Regression test for crbug.com/1372234 - should not crash.
TEST_F(CFXFontMapperSystemFontInfoTest,GetCachedFaceFailToGetData)223 TEST_F(CFXFontMapperSystemFontInfoTest, GetCachedFaceFailToGetData) {
224 void* const kFontHandle = reinterpret_cast<void*>(12345);
225 constexpr char kSubstName[] = "dummy_font";
226 constexpr int kWeight = 400;
227 constexpr bool kItalic = false;
228 constexpr size_t kDataSize = 2;
229
230 EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, 0, _))
231 .WillOnce(Return(0));
232
233 EXPECT_FALSE(font_mapper().GetCachedFace(kFontHandle, kSubstName, kWeight,
234 kItalic, kDataSize));
235 }
236