xref: /aosp_15_r20/external/pdfium/core/fxge/cfx_folderfontinfo.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
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