1 // Copyright 2016 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 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7 #include "core/fxge/cfx_folderfontinfo.h"
8
9 #include <iterator>
10 #include <limits>
11 #include <utility>
12
13 #include "build/build_config.h"
14 #include "core/fxcrt/fx_codepage.h"
15 #include "core/fxcrt/fx_extension.h"
16 #include "core/fxcrt/fx_folder.h"
17 #include "core/fxcrt/fx_memory_wrappers.h"
18 #include "core/fxcrt/fx_safe_types.h"
19 #include "core/fxcrt/fx_system.h"
20 #include "core/fxge/cfx_fontmapper.h"
21 #include "core/fxge/fx_font.h"
22 #include "third_party/base/containers/contains.h"
23
24 namespace {
25
26 const struct {
27 const char* m_pName;
28 const char* m_pSubstName;
29 } Base14Substs[] = {
30 {"Courier", "Courier New"},
31 {"Courier-Bold", "Courier New Bold"},
32 {"Courier-BoldOblique", "Courier New Bold Italic"},
33 {"Courier-Oblique", "Courier New Italic"},
34 {"Helvetica", "Arial"},
35 {"Helvetica-Bold", "Arial Bold"},
36 {"Helvetica-BoldOblique", "Arial Bold Italic"},
37 {"Helvetica-Oblique", "Arial Italic"},
38 {"Times-Roman", "Times New Roman"},
39 {"Times-Bold", "Times New Roman Bold"},
40 {"Times-BoldItalic", "Times New Roman Bold Italic"},
41 {"Times-Italic", "Times New Roman Italic"},
42 };
43
44 // Used with std::unique_ptr to automatically call fclose().
45 struct FxFileCloser {
operator ()__anon28a183bc0111::FxFileCloser46 inline void operator()(FILE* h) const {
47 if (h)
48 fclose(h);
49 }
50 };
51
FindFamilyNameMatch(ByteStringView family_name,const ByteString & installed_font_name)52 bool FindFamilyNameMatch(ByteStringView family_name,
53 const ByteString& installed_font_name) {
54 absl::optional<size_t> result = installed_font_name.Find(family_name, 0);
55 if (!result.has_value())
56 return false;
57
58 size_t next_index = result.value() + family_name.GetLength();
59 // Rule out the case that |family_name| is a substring of
60 // |installed_font_name| but their family names are actually different words.
61 // For example: "Univers" and "Universal" are not a match because they have
62 // different family names, but "Univers" and "Univers Bold" are a match.
63 if (installed_font_name.IsValidIndex(next_index) &&
64 FXSYS_IsLowerASCII(installed_font_name[next_index])) {
65 return false;
66 }
67
68 return true;
69 }
70
ReadStringFromFile(FILE * pFile,uint32_t size)71 ByteString ReadStringFromFile(FILE* pFile, uint32_t size) {
72 ByteString result;
73 {
74 // Span's lifetime must end before ReleaseBuffer() below.
75 pdfium::span<char> buffer = result.GetBuffer(size);
76 if (!fread(buffer.data(), size, 1, pFile))
77 return ByteString();
78 }
79 result.ReleaseBuffer(size);
80 return result;
81 }
82
LoadTableFromTT(FILE * pFile,const uint8_t * pTables,uint32_t nTables,uint32_t tag,FX_FILESIZE fileSize)83 ByteString LoadTableFromTT(FILE* pFile,
84 const uint8_t* pTables,
85 uint32_t nTables,
86 uint32_t tag,
87 FX_FILESIZE fileSize) {
88 for (uint32_t i = 0; i < nTables; i++) {
89 const uint8_t* p = pTables + i * 16;
90 if (FXSYS_UINT32_GET_MSBFIRST(p) == tag) {
91 uint32_t offset = FXSYS_UINT32_GET_MSBFIRST(p + 8);
92 uint32_t size = FXSYS_UINT32_GET_MSBFIRST(p + 12);
93 if (offset > std::numeric_limits<uint32_t>::max() - size ||
94 static_cast<FX_FILESIZE>(offset + size) > fileSize ||
95 fseek(pFile, offset, SEEK_SET) < 0) {
96 return ByteString();
97 }
98 return ReadStringFromFile(pFile, size);
99 }
100 }
101 return ByteString();
102 }
103
GetCharset(FX_Charset charset)104 uint32_t GetCharset(FX_Charset charset) {
105 switch (charset) {
106 case FX_Charset::kShiftJIS:
107 return CHARSET_FLAG_SHIFTJIS;
108 case FX_Charset::kChineseSimplified:
109 return CHARSET_FLAG_GB;
110 case FX_Charset::kChineseTraditional:
111 return CHARSET_FLAG_BIG5;
112 case FX_Charset::kHangul:
113 return CHARSET_FLAG_KOREAN;
114 case FX_Charset::kSymbol:
115 return CHARSET_FLAG_SYMBOL;
116 case FX_Charset::kANSI:
117 return CHARSET_FLAG_ANSI;
118 default:
119 break;
120 }
121 return 0;
122 }
123
GetSimilarValue(int weight,bool bItalic,int pitch_family,uint32_t style,bool bMatchName,size_t familyNameLength,size_t bsNameLength)124 int32_t GetSimilarValue(int weight,
125 bool bItalic,
126 int pitch_family,
127 uint32_t style,
128 bool bMatchName,
129 size_t familyNameLength,
130 size_t bsNameLength) {
131 int32_t iSimilarValue = 0;
132 if (bMatchName && (familyNameLength == bsNameLength))
133 iSimilarValue += 4;
134 if (FontStyleIsForceBold(style) == (weight > 400))
135 iSimilarValue += 16;
136 if (FontStyleIsItalic(style) == bItalic)
137 iSimilarValue += 16;
138 if (FontStyleIsSerif(style) == FontFamilyIsRoman(pitch_family))
139 iSimilarValue += 16;
140 if (FontStyleIsScript(style) == FontFamilyIsScript(pitch_family))
141 iSimilarValue += 8;
142 if (FontStyleIsFixedPitch(style) == FontFamilyIsFixedPitch(pitch_family))
143 iSimilarValue += 8;
144 return iSimilarValue;
145 }
146
147 } // namespace
148
149 CFX_FolderFontInfo::CFX_FolderFontInfo() = default;
150
151 CFX_FolderFontInfo::~CFX_FolderFontInfo() = default;
152
AddPath(const ByteString & path)153 void CFX_FolderFontInfo::AddPath(const ByteString& path) {
154 m_PathList.push_back(path);
155 }
156
EnumFontList(CFX_FontMapper * pMapper)157 bool CFX_FolderFontInfo::EnumFontList(CFX_FontMapper* pMapper) {
158 m_pMapper = pMapper;
159 for (const auto& path : m_PathList)
160 ScanPath(path);
161 return true;
162 }
163
ScanPath(const ByteString & path)164 void CFX_FolderFontInfo::ScanPath(const ByteString& path) {
165 std::unique_ptr<FX_Folder> handle = FX_Folder::OpenFolder(path);
166 if (!handle)
167 return;
168
169 ByteString filename;
170 bool bFolder;
171 while (handle->GetNextFile(&filename, &bFolder)) {
172 if (bFolder) {
173 if (filename == "." || filename == "..")
174 continue;
175 } else {
176 ByteString ext = filename.Last(4);
177 ext.MakeLower();
178 if (ext != ".ttf" && ext != ".ttc" && ext != ".otf")
179 continue;
180 }
181
182 ByteString fullpath = path;
183 #if BUILDFLAG(IS_WIN)
184 fullpath += "\\";
185 #else
186 fullpath += "/";
187 #endif
188
189 fullpath += filename;
190 bFolder ? ScanPath(fullpath) : ScanFile(fullpath);
191 }
192 }
193
ScanFile(const ByteString & path)194 void CFX_FolderFontInfo::ScanFile(const ByteString& path) {
195 std::unique_ptr<FILE, FxFileCloser> pFile(fopen(path.c_str(), "rb"));
196 if (!pFile)
197 return;
198
199 fseek(pFile.get(), 0, SEEK_END);
200
201 FX_FILESIZE filesize = ftell(pFile.get());
202 uint8_t buffer[16];
203 fseek(pFile.get(), 0, SEEK_SET);
204
205 size_t readCnt = fread(buffer, 12, 1, pFile.get());
206 if (readCnt != 1)
207 return;
208
209 if (FXSYS_UINT32_GET_MSBFIRST(buffer) != kTableTTCF) {
210 ReportFace(path, pFile.get(), filesize, 0);
211 return;
212 }
213
214 uint32_t nFaces = FXSYS_UINT32_GET_MSBFIRST(buffer + 8);
215 FX_SAFE_SIZE_T safe_face_bytes = nFaces;
216 safe_face_bytes *= 4;
217 if (!safe_face_bytes.IsValid())
218 return;
219
220 const size_t face_bytes = safe_face_bytes.ValueOrDie();
221 std::unique_ptr<uint8_t, FxFreeDeleter> offsets(
222 FX_Alloc(uint8_t, face_bytes));
223 readCnt = fread(offsets.get(), 1, face_bytes, pFile.get());
224 if (readCnt != face_bytes)
225 return;
226
227 auto offsets_span = pdfium::make_span(offsets.get(), face_bytes);
228 for (uint32_t i = 0; i < nFaces; i++) {
229 ReportFace(path, pFile.get(), filesize,
230 FXSYS_UINT32_GET_MSBFIRST(&offsets_span[i * 4]));
231 }
232 }
233
ReportFace(const ByteString & path,FILE * pFile,FX_FILESIZE filesize,uint32_t offset)234 void CFX_FolderFontInfo::ReportFace(const ByteString& path,
235 FILE* pFile,
236 FX_FILESIZE filesize,
237 uint32_t offset) {
238 char buffer[16];
239 if (fseek(pFile, offset, SEEK_SET) < 0 || !fread(buffer, 12, 1, pFile))
240 return;
241
242 uint32_t nTables = FXSYS_UINT16_GET_MSBFIRST(buffer + 4);
243 ByteString tables = ReadStringFromFile(pFile, nTables * 16);
244 if (tables.IsEmpty())
245 return;
246
247 static constexpr uint32_t kNameTag =
248 CFX_FontMapper::MakeTag('n', 'a', 'm', 'e');
249 ByteString names =
250 LoadTableFromTT(pFile, tables.raw_str(), nTables, kNameTag, filesize);
251 if (names.IsEmpty())
252 return;
253
254 ByteString facename = GetNameFromTT(names.raw_span(), 1);
255 if (facename.IsEmpty())
256 return;
257
258 ByteString style = GetNameFromTT(names.raw_span(), 2);
259 if (style != "Regular")
260 facename += " " + style;
261
262 if (pdfium::Contains(m_FontList, facename))
263 return;
264
265 auto pInfo =
266 std::make_unique<FontFaceInfo>(path, facename, tables, offset, filesize);
267 static constexpr uint32_t kOs2Tag =
268 CFX_FontMapper::MakeTag('O', 'S', '/', '2');
269 ByteString os2 =
270 LoadTableFromTT(pFile, tables.raw_str(), nTables, kOs2Tag, filesize);
271 if (os2.GetLength() >= 86) {
272 const uint8_t* p = os2.raw_str() + 78;
273 uint32_t codepages = FXSYS_UINT32_GET_MSBFIRST(p);
274 if (codepages & (1U << 17)) {
275 m_pMapper->AddInstalledFont(facename, FX_Charset::kShiftJIS);
276 pInfo->m_Charsets |= CHARSET_FLAG_SHIFTJIS;
277 }
278 if (codepages & (1U << 18)) {
279 m_pMapper->AddInstalledFont(facename, FX_Charset::kChineseSimplified);
280 pInfo->m_Charsets |= CHARSET_FLAG_GB;
281 }
282 if (codepages & (1U << 20)) {
283 m_pMapper->AddInstalledFont(facename, FX_Charset::kChineseTraditional);
284 pInfo->m_Charsets |= CHARSET_FLAG_BIG5;
285 }
286 if ((codepages & (1U << 19)) || (codepages & (1U << 21))) {
287 m_pMapper->AddInstalledFont(facename, FX_Charset::kHangul);
288 pInfo->m_Charsets |= CHARSET_FLAG_KOREAN;
289 }
290 if (codepages & (1U << 31)) {
291 m_pMapper->AddInstalledFont(facename, FX_Charset::kSymbol);
292 pInfo->m_Charsets |= CHARSET_FLAG_SYMBOL;
293 }
294 }
295 m_pMapper->AddInstalledFont(facename, FX_Charset::kANSI);
296 pInfo->m_Charsets |= CHARSET_FLAG_ANSI;
297 pInfo->m_Styles = 0;
298 if (style.Contains("Bold"))
299 pInfo->m_Styles |= FXFONT_FORCE_BOLD;
300 if (style.Contains("Italic") || style.Contains("Oblique"))
301 pInfo->m_Styles |= FXFONT_ITALIC;
302 if (facename.Contains("Serif"))
303 pInfo->m_Styles |= FXFONT_SERIF;
304
305 m_FontList[facename] = std::move(pInfo);
306 }
307
GetSubstFont(const ByteString & face)308 void* CFX_FolderFontInfo::GetSubstFont(const ByteString& face) {
309 for (size_t iBaseFont = 0; iBaseFont < std::size(Base14Substs); iBaseFont++) {
310 if (face == Base14Substs[iBaseFont].m_pName)
311 return GetFont(Base14Substs[iBaseFont].m_pSubstName);
312 }
313 return nullptr;
314 }
315
FindFont(int weight,bool bItalic,FX_Charset charset,int pitch_family,const ByteString & family,bool bMatchName)316 void* CFX_FolderFontInfo::FindFont(int weight,
317 bool bItalic,
318 FX_Charset charset,
319 int pitch_family,
320 const ByteString& family,
321 bool bMatchName) {
322 FontFaceInfo* pFind = nullptr;
323
324 ByteStringView bsFamily = family.AsStringView();
325 uint32_t charset_flag = GetCharset(charset);
326 int32_t iBestSimilar = 0;
327 for (const auto& it : m_FontList) {
328 const ByteString& bsName = it.first;
329 FontFaceInfo* pFont = it.second.get();
330 if (!(pFont->m_Charsets & charset_flag) && charset != FX_Charset::kDefault)
331 continue;
332
333 if (bMatchName && !FindFamilyNameMatch(bsFamily, bsName))
334 continue;
335
336 int32_t iSimilarValue =
337 GetSimilarValue(weight, bItalic, pitch_family, pFont->m_Styles,
338 bMatchName, bsFamily.GetLength(), bsName.GetLength());
339 if (iSimilarValue > iBestSimilar) {
340 iBestSimilar = iSimilarValue;
341 pFind = pFont;
342 }
343 }
344
345 if (pFind) {
346 return pFind;
347 }
348
349 if (charset == FX_Charset::kANSI && FontFamilyIsFixedPitch(pitch_family)) {
350 auto* courier_new = GetFont("Courier New");
351 if (courier_new)
352 return courier_new;
353 }
354
355 return nullptr;
356 }
357
MapFont(int weight,bool bItalic,FX_Charset charset,int pitch_family,const ByteString & face)358 void* CFX_FolderFontInfo::MapFont(int weight,
359 bool bItalic,
360 FX_Charset charset,
361 int pitch_family,
362 const ByteString& face) {
363 return nullptr;
364 }
365
GetFont(const ByteString & face)366 void* CFX_FolderFontInfo::GetFont(const ByteString& face) {
367 auto it = m_FontList.find(face);
368 return it != m_FontList.end() ? it->second.get() : nullptr;
369 }
370
GetFontData(void * hFont,uint32_t table,pdfium::span<uint8_t> buffer)371 size_t CFX_FolderFontInfo::GetFontData(void* hFont,
372 uint32_t table,
373 pdfium::span<uint8_t> buffer) {
374 if (!hFont)
375 return 0;
376
377 const FontFaceInfo* pFont = static_cast<FontFaceInfo*>(hFont);
378 uint32_t datasize = 0;
379 uint32_t offset = 0;
380 if (table == 0) {
381 datasize = pFont->m_FontOffset ? 0 : pFont->m_FileSize;
382 } else if (table == kTableTTCF) {
383 datasize = pFont->m_FontOffset ? pFont->m_FileSize : 0;
384 } else {
385 size_t nTables = pFont->m_FontTables.GetLength() / 16;
386 for (size_t i = 0; i < nTables; i++) {
387 const uint8_t* p = pFont->m_FontTables.raw_str() + i * 16;
388 if (FXSYS_UINT32_GET_MSBFIRST(p) == table) {
389 offset = FXSYS_UINT32_GET_MSBFIRST(p + 8);
390 datasize = FXSYS_UINT32_GET_MSBFIRST(p + 12);
391 }
392 }
393 }
394
395 if (!datasize || buffer.size() < datasize)
396 return datasize;
397
398 std::unique_ptr<FILE, FxFileCloser> pFile(
399 fopen(pFont->m_FilePath.c_str(), "rb"));
400 if (!pFile)
401 return 0;
402
403 if (fseek(pFile.get(), offset, SEEK_SET) < 0 ||
404 fread(buffer.data(), datasize, 1, pFile.get()) != 1) {
405 return 0;
406 }
407 return datasize;
408 }
409
DeleteFont(void * hFont)410 void CFX_FolderFontInfo::DeleteFont(void* hFont) {}
411
GetFaceName(void * hFont,ByteString * name)412 bool CFX_FolderFontInfo::GetFaceName(void* hFont, ByteString* name) {
413 if (!hFont)
414 return false;
415 *name = static_cast<FontFaceInfo*>(hFont)->m_FaceName;
416 return true;
417 }
418
GetFontCharset(void * hFont,FX_Charset * charset)419 bool CFX_FolderFontInfo::GetFontCharset(void* hFont, FX_Charset* charset) {
420 return false;
421 }
422
FontFaceInfo(ByteString filePath,ByteString faceName,ByteString fontTables,uint32_t fontOffset,uint32_t fileSize)423 CFX_FolderFontInfo::FontFaceInfo::FontFaceInfo(ByteString filePath,
424 ByteString faceName,
425 ByteString fontTables,
426 uint32_t fontOffset,
427 uint32_t fileSize)
428 : m_FilePath(filePath),
429 m_FaceName(faceName),
430 m_FontTables(fontTables),
431 m_FontOffset(fontOffset),
432 m_FileSize(fileSize) {}
433