• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/android/cfpf_skiafontmgr.h"
8 
9 #define FPF_SKIAMATCHWEIGHT_NAME1 62
10 #define FPF_SKIAMATCHWEIGHT_NAME2 60
11 #define FPF_SKIAMATCHWEIGHT_1 16
12 #define FPF_SKIAMATCHWEIGHT_2 8
13 
14 #include <algorithm>
15 
16 #include "core/fxcrt/fx_ext.h"
17 #include "core/fxcrt/fx_memory.h"
18 #include "core/fxcrt/fx_system.h"
19 #include "core/fxge/android/cfpf_skiafont.h"
20 #include "core/fxge/android/cfpf_skiafontdescriptor.h"
21 #include "core/fxge/android/cfpf_skiapathfont.h"
22 #include "core/fxge/fx_freetype.h"
23 
24 #ifdef __cplusplus
25 extern "C" {
26 #endif
FPF_SkiaStream_Read(FXFT_Stream stream,unsigned long offset,unsigned char * buffer,unsigned long count)27 static unsigned long FPF_SkiaStream_Read(FXFT_Stream stream,
28                                          unsigned long offset,
29                                          unsigned char* buffer,
30                                          unsigned long count) {
31   if (count == 0)
32     return 0;
33 
34   IFX_SeekableReadStream* pFileRead =
35       static_cast<IFX_SeekableReadStream*>(stream->descriptor.pointer);
36   if (!pFileRead)
37     return 0;
38 
39   if (!pFileRead->ReadBlock(buffer, (FX_FILESIZE)offset, (size_t)count))
40     return 0;
41 
42   return count;
43 }
44 
FPF_SkiaStream_Close(FXFT_Stream stream)45 static void FPF_SkiaStream_Close(FXFT_Stream stream) {}
46 #ifdef __cplusplus
47 };
48 #endif
49 
50 namespace {
51 
52 struct FPF_SKIAFONTMAP {
53   uint32_t dwFamily;
54   uint32_t dwSubSt;
55 };
56 
57 const FPF_SKIAFONTMAP g_SkiaFontmap[] = {
58     {0x58c5083, 0xc8d2e345},  {0x5dfade2, 0xe1633081},
59     {0x684317d, 0xe1633081},  {0x14ee2d13, 0xc8d2e345},
60     {0x3918fe2d, 0xbbeeec72}, {0x3b98b31c, 0xe1633081},
61     {0x3d49f40e, 0xe1633081}, {0x432c41c5, 0xe1633081},
62     {0x491b6ad0, 0xe1633081}, {0x5612cab1, 0x59b9f8f1},
63     {0x779ce19d, 0xc8d2e345}, {0x7cc9510b, 0x59b9f8f1},
64     {0x83746053, 0xbbeeec72}, {0xaaa60c03, 0xbbeeec72},
65     {0xbf85ff26, 0xe1633081}, {0xc04fe601, 0xbbeeec72},
66     {0xca3812d5, 0x59b9f8f1}, {0xca383e15, 0x59b9f8f1},
67     {0xcad5eaf6, 0x59b9f8f1}, {0xcb7a04c8, 0xc8d2e345},
68     {0xfb4ce0de, 0xe1633081},
69 };
70 
71 const FPF_SKIAFONTMAP g_SkiaSansFontMap[] = {
72     {0x58c5083, 0xd5b8d10f},  {0x14ee2d13, 0xd5b8d10f},
73     {0x779ce19d, 0xd5b8d10f}, {0xcb7a04c8, 0xd5b8d10f},
74     {0xfb4ce0de, 0xd5b8d10f},
75 };
76 
FPF_SkiaGetSubstFont(uint32_t dwHash,const FPF_SKIAFONTMAP * skFontMap,size_t length)77 uint32_t FPF_SkiaGetSubstFont(uint32_t dwHash,
78                               const FPF_SKIAFONTMAP* skFontMap,
79                               size_t length) {
80   const FPF_SKIAFONTMAP* pEnd = skFontMap + length;
81   const FPF_SKIAFONTMAP* pFontMap = std::lower_bound(
82       skFontMap, pEnd, dwHash, [](const FPF_SKIAFONTMAP& item, uint32_t hash) {
83         return item.dwFamily < hash;
84       });
85   if (pFontMap < pEnd && pFontMap->dwFamily == dwHash)
86     return pFontMap->dwSubSt;
87   return 0;
88 }
89 
FPF_GetHashCode_StringA(const FX_CHAR * pStr,int32_t iLength)90 uint32_t FPF_GetHashCode_StringA(const FX_CHAR* pStr, int32_t iLength) {
91   if (!pStr)
92     return 0;
93   if (iLength < 0)
94     iLength = FXSYS_strlen(pStr);
95   const FX_CHAR* pStrEnd = pStr + iLength;
96   uint32_t uHashCode = 0;
97   while (pStr < pStrEnd)
98     uHashCode = 31 * uHashCode + FXSYS_tolower(*pStr++);
99   return uHashCode;
100 }
101 
102 enum FPF_SKIACHARSET {
103   FPF_SKIACHARSET_Ansi = 1 << 0,
104   FPF_SKIACHARSET_Default = 1 << 1,
105   FPF_SKIACHARSET_Symbol = 1 << 2,
106   FPF_SKIACHARSET_ShiftJIS = 1 << 3,
107   FPF_SKIACHARSET_Korean = 1 << 4,
108   FPF_SKIACHARSET_Johab = 1 << 5,
109   FPF_SKIACHARSET_GB2312 = 1 << 6,
110   FPF_SKIACHARSET_BIG5 = 1 << 7,
111   FPF_SKIACHARSET_Greek = 1 << 8,
112   FPF_SKIACHARSET_Turkish = 1 << 9,
113   FPF_SKIACHARSET_Vietnamese = 1 << 10,
114   FPF_SKIACHARSET_Hebrew = 1 << 11,
115   FPF_SKIACHARSET_Arabic = 1 << 12,
116   FPF_SKIACHARSET_Baltic = 1 << 13,
117   FPF_SKIACHARSET_Cyrillic = 1 << 14,
118   FPF_SKIACHARSET_Thai = 1 << 15,
119   FPF_SKIACHARSET_EeasternEuropean = 1 << 16,
120   FPF_SKIACHARSET_PC = 1 << 17,
121   FPF_SKIACHARSET_OEM = 1 << 18,
122 };
123 
FPF_SkiaGetCharset(uint8_t uCharset)124 uint32_t FPF_SkiaGetCharset(uint8_t uCharset) {
125   switch (uCharset) {
126     case FXFONT_ANSI_CHARSET:
127       return FPF_SKIACHARSET_Ansi;
128     case FXFONT_DEFAULT_CHARSET:
129       return FPF_SKIACHARSET_Default;
130     case FXFONT_SYMBOL_CHARSET:
131       return FPF_SKIACHARSET_Symbol;
132     case FXFONT_SHIFTJIS_CHARSET:
133       return FPF_SKIACHARSET_ShiftJIS;
134     case FXFONT_HANGUL_CHARSET:
135       return FPF_SKIACHARSET_Korean;
136     case FXFONT_GB2312_CHARSET:
137       return FPF_SKIACHARSET_GB2312;
138     case FXFONT_CHINESEBIG5_CHARSET:
139       return FPF_SKIACHARSET_BIG5;
140     case FXFONT_GREEK_CHARSET:
141       return FPF_SKIACHARSET_Greek;
142     case FXFONT_TURKISH_CHARSET:
143       return FPF_SKIACHARSET_Turkish;
144     case FXFONT_HEBREW_CHARSET:
145       return FPF_SKIACHARSET_Hebrew;
146     case FXFONT_ARABIC_CHARSET:
147       return FPF_SKIACHARSET_Arabic;
148     case FXFONT_BALTIC_CHARSET:
149       return FPF_SKIACHARSET_Baltic;
150     case FXFONT_RUSSIAN_CHARSET:
151       return FPF_SKIACHARSET_Cyrillic;
152     case FXFONT_THAI_CHARSET:
153       return FPF_SKIACHARSET_Thai;
154     case FXFONT_EASTEUROPE_CHARSET:
155       return FPF_SKIACHARSET_EeasternEuropean;
156   }
157   return FPF_SKIACHARSET_Default;
158 }
159 
FPF_SKIANormalizeFontName(const CFX_ByteStringC & bsfamily)160 uint32_t FPF_SKIANormalizeFontName(const CFX_ByteStringC& bsfamily) {
161   uint32_t dwHash = 0;
162   int32_t iLength = bsfamily.GetLength();
163   const FX_CHAR* pBuffer = bsfamily.c_str();
164   for (int32_t i = 0; i < iLength; i++) {
165     FX_CHAR ch = pBuffer[i];
166     if (ch == ' ' || ch == '-' || ch == ',')
167       continue;
168     dwHash = 31 * dwHash + FXSYS_tolower(ch);
169   }
170   return dwHash;
171 }
172 
FPF_SKIAGetFamilyHash(const CFX_ByteStringC & bsFamily,uint32_t dwStyle,uint8_t uCharset)173 uint32_t FPF_SKIAGetFamilyHash(const CFX_ByteStringC& bsFamily,
174                                uint32_t dwStyle,
175                                uint8_t uCharset) {
176   CFX_ByteString bsFont(bsFamily);
177   if (dwStyle & FXFONT_BOLD)
178     bsFont += "Bold";
179   if (dwStyle & FXFONT_ITALIC)
180     bsFont += "Italic";
181   if (dwStyle & FXFONT_SERIF)
182     bsFont += "Serif";
183   bsFont += uCharset;
184   return FPF_GetHashCode_StringA(bsFont.c_str(), bsFont.GetLength());
185 }
186 
FPF_SkiaIsCJK(uint8_t uCharset)187 bool FPF_SkiaIsCJK(uint8_t uCharset) {
188   return (uCharset == FXFONT_GB2312_CHARSET) ||
189          (uCharset == FXFONT_CHINESEBIG5_CHARSET) ||
190          (uCharset == FXFONT_HANGUL_CHARSET) ||
191          (uCharset == FXFONT_SHIFTJIS_CHARSET);
192 }
193 
FPF_SkiaMaybeSymbol(const CFX_ByteStringC & bsFacename)194 bool FPF_SkiaMaybeSymbol(const CFX_ByteStringC& bsFacename) {
195   CFX_ByteString bsName(bsFacename);
196   bsName.MakeLower();
197   return bsName.Find("symbol") > -1;
198 }
199 
FPF_SkiaMaybeArabic(const CFX_ByteStringC & bsFacename)200 bool FPF_SkiaMaybeArabic(const CFX_ByteStringC& bsFacename) {
201   CFX_ByteString bsName(bsFacename);
202   bsName.MakeLower();
203   return bsName.Find("arabic") > -1;
204 }
205 
206 const uint32_t g_FPFSkiaFontCharsets[] = {
207     FPF_SKIACHARSET_Ansi,
208     FPF_SKIACHARSET_EeasternEuropean,
209     FPF_SKIACHARSET_Cyrillic,
210     FPF_SKIACHARSET_Greek,
211     FPF_SKIACHARSET_Turkish,
212     FPF_SKIACHARSET_Hebrew,
213     FPF_SKIACHARSET_Arabic,
214     FPF_SKIACHARSET_Baltic,
215     0,
216     0,
217     0,
218     0,
219     0,
220     0,
221     0,
222     0,
223     FPF_SKIACHARSET_Thai,
224     FPF_SKIACHARSET_ShiftJIS,
225     FPF_SKIACHARSET_GB2312,
226     FPF_SKIACHARSET_Korean,
227     FPF_SKIACHARSET_BIG5,
228     FPF_SKIACHARSET_Johab,
229     0,
230     0,
231     0,
232     0,
233     0,
234     0,
235     0,
236     0,
237     FPF_SKIACHARSET_OEM,
238     FPF_SKIACHARSET_Symbol,
239 };
240 
FPF_SkiaGetFaceCharset(TT_OS2 * pOS2)241 uint32_t FPF_SkiaGetFaceCharset(TT_OS2* pOS2) {
242   uint32_t dwCharset = 0;
243   if (pOS2) {
244     for (int32_t i = 0; i < 32; i++) {
245       if (pOS2->ulCodePageRange1 & (1 << i))
246         dwCharset |= g_FPFSkiaFontCharsets[i];
247     }
248   }
249   dwCharset |= FPF_SKIACHARSET_Default;
250   return dwCharset;
251 }
252 
253 }  // namespace
254 
CFPF_SkiaFontMgr()255 CFPF_SkiaFontMgr::CFPF_SkiaFontMgr() : m_bLoaded(false), m_FTLibrary(nullptr) {}
256 
~CFPF_SkiaFontMgr()257 CFPF_SkiaFontMgr::~CFPF_SkiaFontMgr() {
258   for (const auto& pair : m_FamilyFonts) {
259     if (pair.second)
260       pair.second->Release();
261   }
262   m_FamilyFonts.clear();
263   for (auto it = m_FontFaces.begin(); it != m_FontFaces.end(); ++it)
264     delete *it;
265   m_FontFaces.clear();
266   if (m_FTLibrary)
267     FXFT_Done_FreeType(m_FTLibrary);
268 }
269 
InitFTLibrary()270 bool CFPF_SkiaFontMgr::InitFTLibrary() {
271   if (!m_FTLibrary)
272     FXFT_Init_FreeType(&m_FTLibrary);
273   return !!m_FTLibrary;
274 }
275 
LoadSystemFonts()276 void CFPF_SkiaFontMgr::LoadSystemFonts() {
277   if (m_bLoaded)
278     return;
279   ScanPath("/system/fonts");
280   m_bLoaded = true;
281 }
282 
CreateFont(const CFX_ByteStringC & bsFamilyname,uint8_t uCharset,uint32_t dwStyle,uint32_t dwMatch)283 CFPF_SkiaFont* CFPF_SkiaFontMgr::CreateFont(const CFX_ByteStringC& bsFamilyname,
284                                             uint8_t uCharset,
285                                             uint32_t dwStyle,
286                                             uint32_t dwMatch) {
287   uint32_t dwHash = FPF_SKIAGetFamilyHash(bsFamilyname, dwStyle, uCharset);
288   auto it = m_FamilyFonts.find(dwHash);
289   if (it != m_FamilyFonts.end() && it->second)
290     return it->second->Retain();
291 
292   uint32_t dwFaceName = FPF_SKIANormalizeFontName(bsFamilyname);
293   uint32_t dwSubst = FPF_SkiaGetSubstFont(dwFaceName, g_SkiaFontmap,
294                                           FX_ArraySize(g_SkiaFontmap));
295   uint32_t dwSubstSans = FPF_SkiaGetSubstFont(dwFaceName, g_SkiaSansFontMap,
296                                               FX_ArraySize(g_SkiaSansFontMap));
297   bool bMaybeSymbol = FPF_SkiaMaybeSymbol(bsFamilyname);
298   if (uCharset != FXFONT_ARABIC_CHARSET && FPF_SkiaMaybeArabic(bsFamilyname)) {
299     uCharset = FXFONT_ARABIC_CHARSET;
300   } else if (uCharset == FXFONT_ANSI_CHARSET &&
301              (dwMatch & FPF_MATCHFONT_REPLACEANSI)) {
302     uCharset = FXFONT_DEFAULT_CHARSET;
303   }
304   int32_t nExpectVal = FPF_SKIAMATCHWEIGHT_NAME1 + FPF_SKIAMATCHWEIGHT_1 * 3 +
305                        FPF_SKIAMATCHWEIGHT_2 * 2;
306   CFPF_SkiaFontDescriptor* pBestFontDes = nullptr;
307   int32_t nMax = -1;
308   int32_t nGlyphNum = 0;
309   for (auto it = m_FontFaces.rbegin(); it != m_FontFaces.rend(); ++it) {
310     CFPF_SkiaPathFont* pFontDes = static_cast<CFPF_SkiaPathFont*>(*it);
311     if (!(pFontDes->m_dwCharsets & FPF_SkiaGetCharset(uCharset)))
312       continue;
313     int32_t nFind = 0;
314     uint32_t dwSysFontName = FPF_SKIANormalizeFontName(pFontDes->m_pFamily);
315     if (dwFaceName == dwSysFontName)
316       nFind += FPF_SKIAMATCHWEIGHT_NAME1;
317     bool bMatchedName = (nFind == FPF_SKIAMATCHWEIGHT_NAME1);
318     if ((dwStyle & FXFONT_BOLD) == (pFontDes->m_dwStyle & FXFONT_BOLD))
319       nFind += FPF_SKIAMATCHWEIGHT_1;
320     if ((dwStyle & FXFONT_ITALIC) == (pFontDes->m_dwStyle & FXFONT_ITALIC))
321       nFind += FPF_SKIAMATCHWEIGHT_1;
322     if ((dwStyle & FXFONT_FIXED_PITCH) ==
323         (pFontDes->m_dwStyle & FXFONT_FIXED_PITCH)) {
324       nFind += FPF_SKIAMATCHWEIGHT_2;
325     }
326     if ((dwStyle & FXFONT_SERIF) == (pFontDes->m_dwStyle & FXFONT_SERIF))
327       nFind += FPF_SKIAMATCHWEIGHT_1;
328     if ((dwStyle & FXFONT_SCRIPT) == (pFontDes->m_dwStyle & FXFONT_SCRIPT))
329       nFind += FPF_SKIAMATCHWEIGHT_2;
330     if (dwSubst == dwSysFontName || dwSubstSans == dwSysFontName) {
331       nFind += FPF_SKIAMATCHWEIGHT_NAME2;
332       bMatchedName = true;
333     }
334     if (uCharset == FXFONT_DEFAULT_CHARSET || bMaybeSymbol) {
335       if (nFind > nMax && bMatchedName) {
336         nMax = nFind;
337         pBestFontDes = *it;
338       }
339     } else if (FPF_SkiaIsCJK(uCharset)) {
340       if (bMatchedName || pFontDes->m_iGlyphNum > nGlyphNum) {
341         pBestFontDes = *it;
342         nGlyphNum = pFontDes->m_iGlyphNum;
343       }
344     } else if (nFind > nMax) {
345       nMax = nFind;
346       pBestFontDes = *it;
347     }
348     if (nExpectVal <= nFind) {
349       pBestFontDes = *it;
350       break;
351     }
352   }
353   if (pBestFontDes) {
354     CFPF_SkiaFont* pFont = new CFPF_SkiaFont;
355     if (pFont->InitFont(this, pBestFontDes, bsFamilyname, dwStyle, uCharset)) {
356       m_FamilyFonts[dwHash] = pFont;
357       return pFont->Retain();
358     }
359     pFont->Release();
360   }
361   return nullptr;
362 }
363 
GetFontFace(const CFX_RetainPtr<IFX_SeekableReadStream> & pFileRead,int32_t iFaceIndex)364 FXFT_Face CFPF_SkiaFontMgr::GetFontFace(
365     const CFX_RetainPtr<IFX_SeekableReadStream>& pFileRead,
366     int32_t iFaceIndex) {
367   if (!pFileRead)
368     return nullptr;
369   if (pFileRead->GetSize() == 0)
370     return nullptr;
371   if (iFaceIndex < 0)
372     return nullptr;
373   FXFT_StreamRec streamRec;
374   FXSYS_memset(&streamRec, 0, sizeof(FXFT_StreamRec));
375   streamRec.size = pFileRead->GetSize();
376   streamRec.descriptor.pointer = static_cast<void*>(pFileRead.Get());
377   streamRec.read = FPF_SkiaStream_Read;
378   streamRec.close = FPF_SkiaStream_Close;
379   FXFT_Open_Args args;
380   args.flags = FT_OPEN_STREAM;
381   args.stream = &streamRec;
382   FXFT_Face face;
383   if (FXFT_Open_Face(m_FTLibrary, &args, iFaceIndex, &face))
384     return nullptr;
385   FXFT_Set_Pixel_Sizes(face, 0, 64);
386   return face;
387 }
388 
GetFontFace(const CFX_ByteStringC & bsFile,int32_t iFaceIndex)389 FXFT_Face CFPF_SkiaFontMgr::GetFontFace(const CFX_ByteStringC& bsFile,
390                                         int32_t iFaceIndex) {
391   if (bsFile.IsEmpty())
392     return nullptr;
393   if (iFaceIndex < 0)
394     return nullptr;
395   FXFT_Open_Args args;
396   args.flags = FT_OPEN_PATHNAME;
397   args.pathname = const_cast<FT_String*>(bsFile.c_str());
398   FXFT_Face face;
399   if (FXFT_Open_Face(m_FTLibrary, &args, iFaceIndex, &face))
400     return nullptr;
401   FXFT_Set_Pixel_Sizes(face, 0, 64);
402   return face;
403 }
404 
GetFontFace(const uint8_t * pBuffer,size_t szBuffer,int32_t iFaceIndex)405 FXFT_Face CFPF_SkiaFontMgr::GetFontFace(const uint8_t* pBuffer,
406                                         size_t szBuffer,
407                                         int32_t iFaceIndex) {
408   if (!pBuffer || szBuffer < 1)
409     return nullptr;
410   if (iFaceIndex < 0)
411     return nullptr;
412   FXFT_Open_Args args;
413   args.flags = FT_OPEN_MEMORY;
414   args.memory_base = pBuffer;
415   args.memory_size = szBuffer;
416   FXFT_Face face;
417   if (FXFT_Open_Face(m_FTLibrary, &args, iFaceIndex, &face))
418     return nullptr;
419   FXFT_Set_Pixel_Sizes(face, 0, 64);
420   return face;
421 }
422 
ScanPath(const CFX_ByteString & path)423 void CFPF_SkiaFontMgr::ScanPath(const CFX_ByteString& path) {
424   DIR* handle = FX_OpenFolder(path.c_str());
425   if (!handle)
426     return;
427   CFX_ByteString filename;
428   bool bFolder = false;
429   while (FX_GetNextFile(handle, &filename, &bFolder)) {
430     if (bFolder) {
431       if (filename == "." || filename == "..")
432         continue;
433     } else {
434       CFX_ByteString ext = filename.Right(4);
435       ext.MakeLower();
436       if (ext != ".ttf" && ext != ".ttc" && ext != ".otf")
437         continue;
438     }
439     CFX_ByteString fullpath(path);
440     fullpath += "/";
441     fullpath += filename;
442     if (bFolder)
443       ScanPath(fullpath);
444     else
445       ScanFile(fullpath);
446   }
447   FX_CloseFolder(handle);
448 }
449 
ScanFile(const CFX_ByteString & file)450 void CFPF_SkiaFontMgr::ScanFile(const CFX_ByteString& file) {
451   FXFT_Face face = GetFontFace(file.AsStringC());
452   if (!face)
453     return;
454   CFPF_SkiaPathFont* pFontDesc = new CFPF_SkiaPathFont;
455   pFontDesc->SetPath(file.c_str());
456   ReportFace(face, pFontDesc);
457   m_FontFaces.push_back(pFontDesc);
458   FXFT_Done_Face(face);
459 }
460 
ReportFace(FXFT_Face face,CFPF_SkiaFontDescriptor * pFontDesc)461 void CFPF_SkiaFontMgr::ReportFace(FXFT_Face face,
462                                   CFPF_SkiaFontDescriptor* pFontDesc) {
463   if (!face || !pFontDesc)
464     return;
465   pFontDesc->SetFamily(FXFT_Get_Face_Family_Name(face));
466   if (FXFT_Is_Face_Bold(face))
467     pFontDesc->m_dwStyle |= FXFONT_BOLD;
468   if (FXFT_Is_Face_Italic(face))
469     pFontDesc->m_dwStyle |= FXFONT_ITALIC;
470   if (FT_IS_FIXED_WIDTH(face))
471     pFontDesc->m_dwStyle |= FXFONT_FIXED_PITCH;
472   TT_OS2* pOS2 = (TT_OS2*)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
473   if (pOS2) {
474     if (pOS2->ulCodePageRange1 & (1 << 31))
475       pFontDesc->m_dwStyle |= FXFONT_SYMBOLIC;
476     if (pOS2->panose[0] == 2) {
477       uint8_t uSerif = pOS2->panose[1];
478       if ((uSerif > 1 && uSerif < 10) || uSerif > 13)
479         pFontDesc->m_dwStyle |= FXFONT_SERIF;
480     }
481   }
482   if (pOS2 && (pOS2->ulCodePageRange1 & (1 << 31)))
483     pFontDesc->m_dwStyle |= FXFONT_SYMBOLIC;
484   pFontDesc->m_dwCharsets = FPF_SkiaGetFaceCharset(pOS2);
485   pFontDesc->m_iFaceIndex = face->face_index;
486   pFontDesc->m_iGlyphNum = face->num_glyphs;
487 }
488