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