0031501: Foundation Classes, Message_Printer - remove theToPutEndl argument -- use...
[occt.git] / src / Font / Font_FontMgr.cxx
1 // Created on: 2008-01-20
2 // Created by: Alexander A. BORODIN
3 // Copyright (c) 2008-2014 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 #include <Font_FontMgr.hxx>
17
18 #include <Font_NameOfFont.hxx>
19 #include <Font_FTLibrary.hxx>
20 #include <Font_SystemFont.hxx>
21 #include <Message.hxx>
22 #include <Message_Messenger.hxx>
23 #include <NCollection_Buffer.hxx>
24 #include <NCollection_List.hxx>
25 #include <NCollection_Map.hxx>
26 #include <OSD_Environment.hxx>
27 #include <Standard_Stream.hxx>
28 #include <Standard_Type.hxx>
29 #include <TCollection_HAsciiString.hxx>
30
31 #include "Font_DejavuSans_Latin_woff.pxx"
32
33 #include <ft2build.h>
34 #include FT_FREETYPE_H
35 IMPLEMENT_STANDARD_RTTIEXT(Font_FontMgr,Standard_Transient)
36
37 #if defined(_WIN32)
38
39   #include <windows.h>
40   #include <stdlib.h>
41
42   #ifdef _MSC_VER
43     #pragma comment (lib, "freetype.lib")
44   #endif
45
46   namespace
47   {
48     // list of supported extensions
49     static Standard_CString Font_FontMgr_Extensions[] =
50     {
51       "ttf",
52       "otf",
53       "ttc",
54       NULL
55     };
56   }
57
58 #else
59
60   #include <OSD_DirectoryIterator.hxx>
61   #include <OSD_FileIterator.hxx>
62   #include <OSD_Path.hxx>
63   #include <OSD_File.hxx>
64   #include <OSD_OpenMode.hxx>
65   #include <OSD_Protection.hxx>
66
67   namespace
68   {
69
70     // list of supported extensions
71     static Standard_CString Font_FontMgr_Extensions[] =
72     {
73       "ttf",
74       "otf",
75       "ttc",
76       "pfa",
77       "pfb",
78     #ifdef __APPLE__
79       // Datafork TrueType (OS X), obsolete
80       //"dfont",
81     #endif
82       NULL
83     };
84
85   #if !defined(__ANDROID__) && !defined(__APPLE__) && !defined(__EMSCRIPTEN__)
86     // X11 configuration file in plain text format (obsolete - doesn't exists in modern distributives)
87     static Standard_CString myFontServiceConf[] = {"/etc/X11/fs/config",
88                                                    "/usr/X11R6/lib/X11/fs/config",
89                                                    "/usr/X11/lib/X11/fs/config",
90                                                    NULL
91                                                   };
92
93     // Although fontconfig library can be built for various platforms,
94     // practically it is useful only on desktop Linux distributions, where it is always packaged.
95     #include <fontconfig/fontconfig.h>
96   #endif
97
98   #ifdef __APPLE__
99     // default fonts paths in Mac OS X
100     static Standard_CString myDefaultFontsDirs[] = {"/System/Library/Fonts",
101                                                     "/Library/Fonts",
102                                                     NULL
103                                                    };
104   #else
105     // default fonts paths in most Unix systems (Linux and others)
106     static Standard_CString myDefaultFontsDirs[] = {"/system/fonts",         // Android
107                                                     "/usr/share/fonts",
108                                                     "/usr/local/share/fonts",
109                                                     NULL
110                                                    };
111   #endif
112
113     static void addDirsRecursively (const OSD_Path&                           thePath,
114                                     NCollection_Map<TCollection_AsciiString>& theDirsMap)
115     {
116       TCollection_AsciiString aDirName;
117       thePath.SystemName (aDirName);
118       if (!theDirsMap.Add (aDirName))
119       {
120         return;
121       }
122
123       for (OSD_DirectoryIterator aDirIterator (thePath, "*"); aDirIterator.More(); aDirIterator.Next())
124       {
125         OSD_Path aChildDirPath;
126         aDirIterator.Values().Path (aChildDirPath);
127
128         TCollection_AsciiString aChildDirName;
129         aChildDirPath.SystemName (aChildDirName);
130         if (!aChildDirName.IsEqual (".") && !aChildDirName.IsEqual (".."))
131         {
132           aChildDirName = aDirName + "/" + aChildDirName;
133           OSD_Path aPath (aChildDirName);
134           addDirsRecursively (aPath, theDirsMap);
135         }
136       }
137     }
138
139   } // anonymous namespace
140
141 #endif
142
143 //! Retrieve font information.
144 //! @param theFonts   [out] list of validated fonts
145 //! @param theFTLib    [in] font library
146 //! @param theFontPath [in] path to the file
147 //! @param theFaceId   [in] face id, or -1 to load all faces within the file
148 //! @return TRUE if at least one font face has been detected
149 static bool checkFont (NCollection_Sequence<Handle(Font_SystemFont)>& theFonts,
150                        const Handle(Font_FTLibrary)& theFTLib,
151                        const TCollection_AsciiString& theFontPath,
152                        FT_Long theFaceId = -1)
153 {
154   const FT_Long aFaceId = theFaceId != -1 ? theFaceId : 0;
155   FT_Face aFontFace;
156   FT_Error aFaceError = FT_New_Face (theFTLib->Instance(), theFontPath.ToCString(), aFaceId, &aFontFace);
157   if (aFaceError != FT_Err_Ok)
158   {
159     return false;
160   }
161   if (aFontFace->family_name == NULL // skip broken fonts (error in FreeType?)
162    || FT_Select_Charmap (aFontFace, ft_encoding_unicode) != 0) // Font_FTFont supports only UNICODE fonts
163   {
164     FT_Done_Face (aFontFace);
165     return false;
166   }
167
168   // FreeType decomposes font definition into Family Name and Style Name,
169   // so that fonts within the same Family and different Styles can be identified.
170   // OCCT Font Manager natively handles 4 basic styles: Regular, Bold, Italic and Bold+Italic.
171   // To include other non-standard Styles, their names can be appended to Family Name; for this, names of normal Styles should be removed.
172   TCollection_AsciiString aFamily (aFontFace->family_name);
173   TCollection_AsciiString aStyle (aFontFace->style_name != NULL ? aFontFace->style_name : "");
174   Font_FontAspect anAspect = Font_FA_Regular;
175   if (aFontFace->style_flags == (FT_STYLE_FLAG_ITALIC | FT_STYLE_FLAG_BOLD))
176   {
177     anAspect = Font_FA_BoldItalic;
178     const Standard_Integer aRemoveItalic = aStyle.Search ("Italic");
179     if (aRemoveItalic != -1)
180     {
181       aStyle.Remove (aRemoveItalic, 6);
182     }
183     else
184     {
185       // synonym
186       const Standard_Integer aRemoveOblique = aStyle.Search ("Oblique");
187       if (aRemoveOblique != -1)
188       {
189         aStyle.Remove (aRemoveOblique, 7);
190       }
191     }
192
193     const Standard_Integer aRemoveBold = aStyle.Search ("Bold");
194     if (aRemoveBold != -1)
195     {
196       aStyle.Remove (aRemoveBold, 4);
197     }
198   }
199   else if (aFontFace->style_flags == FT_STYLE_FLAG_ITALIC)
200   {
201     anAspect = Font_FA_Italic;
202     const Standard_Integer aRemoveItalic = aStyle.Search ("Italic");
203     if (aRemoveItalic != -1)
204     {
205       aStyle.Remove (aRemoveItalic, 6);
206     }
207     else
208     {
209       // synonym
210       const Standard_Integer aRemoveOblique = aStyle.Search ("Oblique");
211       if (aRemoveOblique != -1)
212       {
213         aStyle.Remove (aRemoveOblique, 7);
214       }
215     }
216   }
217   else if (aFontFace->style_flags == FT_STYLE_FLAG_BOLD)
218   {
219     anAspect = Font_FA_Bold;
220     const Standard_Integer aRemoveBold = aStyle.Search ("Bold");
221     if (aRemoveBold != -1)
222     {
223       aStyle.Remove (aRemoveBold, 4);
224     }
225   }
226
227   const Standard_Integer aRemoveReg = aStyle.Search ("Regular");
228   if (aRemoveReg != -1)
229   {
230     aStyle.Remove (aRemoveReg, 7);
231   }
232   else
233   {
234     // synonym
235     const Standard_Integer aRemoveBook = aStyle.Search ("Book");
236     if (aRemoveBook != -1)
237     {
238       aStyle.Remove (aRemoveBook, 4);
239     }
240   }
241
242   aStyle.LeftAdjust();
243   aStyle.RightAdjust();
244   for (;;)
245   {
246     // remove double spaces after removal of several keywords in-between, like "Condensed Bold Italic Oblique"
247     const Standard_Integer aRemoveSpace = aStyle.Search ("  ");
248     if (aRemoveSpace == -1)
249     {
250       break;
251     }
252
253     aStyle.Remove (aRemoveSpace, 1);
254   }
255
256   if (!aStyle.IsEmpty())
257   {
258     aFamily = aFamily + " " + aStyle;
259   }
260
261   Handle(Font_SystemFont) aResult = new Font_SystemFont (aFamily);
262   aResult->SetFontPath (anAspect, theFontPath, (Standard_Integer )aFaceId);
263   // automatically identify some known single-line fonts
264   aResult->SetSingleStrokeFont (aResult->FontKey().StartsWith ("olf "));
265   theFonts.Append (aResult);
266
267   if (theFaceId < aFontFace->num_faces)
268   {
269     const FT_Long aNbInstances = aFontFace->style_flags >> 16;
270     for (FT_Long anInstIter = 1; anInstIter < aNbInstances; ++anInstIter)
271     {
272       const FT_Long aSubFaceId = aFaceId + (anInstIter << 16);
273       checkFont (theFonts, theFTLib, theFontPath, aSubFaceId);
274     }
275   }
276   if (theFaceId == -1)
277   {
278     for (FT_Long aFaceIter = 1; aFaceIter < aFontFace->num_faces; ++aFaceIter)
279     {
280       checkFont (theFonts, theFTLib, theFontPath, aFaceIter);
281     }
282   }
283
284   FT_Done_Face (aFontFace);
285   return true;
286 }
287
288 // =======================================================================
289 // function : GetInstance
290 // purpose  :
291 // =======================================================================
292 Handle(Font_FontMgr) Font_FontMgr::GetInstance()
293 {
294   static Handle(Font_FontMgr) _mgr;
295   if (_mgr.IsNull())
296   {
297     _mgr = new Font_FontMgr();
298   }
299
300   return _mgr;
301 }
302
303 // =======================================================================
304 // function : ToUseUnicodeSubsetFallback
305 // purpose  :
306 // =======================================================================
307 Standard_Boolean& Font_FontMgr::ToUseUnicodeSubsetFallback()
308 {
309   static Standard_Boolean TheToUseUnicodeSubsetFallback = true;
310   return TheToUseUnicodeSubsetFallback;
311 }
312
313 // =======================================================================
314 // function : addFontAlias
315 // purpose  :
316 // =======================================================================
317 void Font_FontMgr::addFontAlias (const TCollection_AsciiString& theAliasName,
318                                  const Handle(Font_FontAliasSequence)& theAliases,
319                                  Font_FontAspect theAspect)
320 {
321   if (theAliases.IsNull()
322    || theAliases->IsEmpty())
323   {
324     return;
325   }
326
327   Handle(Font_FontAliasSequence) anAliases = theAliases;
328   if (theAspect != Font_FA_Undefined)
329   {
330     anAliases = new Font_FontAliasSequence();
331     for (Font_FontAliasSequence::Iterator anAliasIter (*theAliases); anAliasIter.More(); anAliasIter.Next())
332     {
333       const TCollection_AsciiString& aName = anAliasIter.Value().FontName;
334       anAliases->Append (Font_FontAlias (aName, theAspect));
335     }
336   }
337
338   TCollection_AsciiString anAliasName (theAliasName);
339   anAliasName.LowerCase();
340   myFontAliases.Bind (anAliasName, anAliases);
341 }
342
343 // =======================================================================
344 // function : Font_FontMgr
345 // purpose  :
346 // =======================================================================
347 Font_FontMgr::Font_FontMgr()
348 : myToTraceAliases (Standard_False)
349 {
350   Handle(Font_FontAliasSequence) aMono   = new Font_FontAliasSequence();
351   Handle(Font_FontAliasSequence) aSerif  = new Font_FontAliasSequence();
352   Handle(Font_FontAliasSequence) aSans   = new Font_FontAliasSequence();
353   Handle(Font_FontAliasSequence) aSymbol = new Font_FontAliasSequence();
354   Handle(Font_FontAliasSequence) aScript = new Font_FontAliasSequence();
355   Handle(Font_FontAliasSequence) aWinDin = new Font_FontAliasSequence();
356   Handle(Font_FontAliasSequence) anIris  = new Font_FontAliasSequence();
357   Handle(Font_FontAliasSequence) aCJK    = new Font_FontAliasSequence();
358   Handle(Font_FontAliasSequence) aKorean = new Font_FontAliasSequence();
359   Handle(Font_FontAliasSequence) anArab  = new Font_FontAliasSequence();
360
361   // best matches - pre-installed on Windows, some of them are pre-installed on macOS,
362   // and sometimes them can be found installed on other systems (by user)
363   aMono  ->Append (Font_FontAlias ("courier new"));
364   aSerif ->Append (Font_FontAlias ("times new roman"));
365   aSans  ->Append (Font_FontAlias ("arial"));
366   aSymbol->Append (Font_FontAlias ("symbol"));
367   aScript->Append (Font_FontAlias ("script"));
368   aWinDin->Append (Font_FontAlias ("wingdings"));
369   anIris ->Append (Font_FontAlias ("lucida console"));
370
371 #if defined(__ANDROID__)
372   // Noto font family is usually installed on Android 6+ devices
373   aMono  ->Append (Font_FontAlias ("noto mono"));
374   aSerif ->Append (Font_FontAlias ("noto serif"));
375   // Droid font family is usually installed on Android 4+ devices
376   aMono  ->Append (Font_FontAlias ("droid sans mono"));
377   aSerif ->Append (Font_FontAlias ("droid serif"));
378   aSans  ->Append (Font_FontAlias ("roboto")); // actually DroidSans.ttf
379 #elif !defined(_WIN32) && !defined(__APPLE__) //X11
380   aSerif ->Append (Font_FontAlias ("times"));
381   aSans  ->Append (Font_FontAlias ("helvetica"));
382   // GNU FreeFonts family is usually installed on Linux
383   aMono  ->Append (Font_FontAlias ("freemono"));
384   aSerif ->Append (Font_FontAlias ("freeserif"));
385   aSans  ->Append (Font_FontAlias ("freesans"));
386   // DejaVu font family is usually installed on Linux
387   aMono  ->Append (Font_FontAlias ("dejavu sans mono"));
388   aSerif ->Append (Font_FontAlias ("dejavu serif"));
389   aSans  ->Append (Font_FontAlias ("dejavu sans"));
390 #endif
391
392   // default CJK (Chinese/Japanese/Korean) fonts
393   aCJK   ->Append (Font_FontAlias ("simsun"));        // Windows
394   aCJK   ->Append (Font_FontAlias ("droid sans fallback")); // Android, Linux
395   aCJK   ->Append (Font_FontAlias ("noto sans sc"));  // Android
396
397 #if defined(_WIN32)
398   aKorean->Append (Font_FontAlias ("malgun gothic")); // introduced since Vista
399   aKorean->Append (Font_FontAlias ("gulim"));         // used on older systems (Windows XP)
400 #elif defined(__APPLE__)
401   aKorean->Append (Font_FontAlias ("applegothic"));
402   aKorean->Append (Font_FontAlias ("stfangsong"));
403 #endif
404   aKorean->Append (Font_FontAlias ("nanumgothic"));   // Android, Linux
405   aKorean->Append (Font_FontAlias ("noto sans kr"));  // Android
406   aKorean->Append (Font_FontAlias ("nanummyeongjo")); // Linux
407   aKorean->Append (Font_FontAlias ("noto serif cjk jp")); // Linux
408   aKorean->Append (Font_FontAlias ("noto sans cjk jp")); // Linux
409   aKorean->Append (Font_FontAlias ("droid sans fallback")); // Linux
410
411 #if defined(_WIN32)
412   anArab->Append (Font_FontAlias ("times new roman"));
413 #elif defined(__APPLE__)
414   anArab->Append (Font_FontAlias ("decotype naskh"));
415 #elif defined(__ANDROID__)
416   anArab->Append (Font_FontAlias ("droid arabic naskh"));
417   anArab->Append (Font_FontAlias ("noto naskh arabic"));
418 #endif
419
420   addFontAlias ("mono",              aMono);
421   addFontAlias ("courier",           aMono);                      // Font_NOF_ASCII_MONO
422   addFontAlias ("monospace",         aMono);                      // Font_NOF_MONOSPACE
423   addFontAlias ("rock",              aSans);                      // Font_NOF_CARTOGRAPHIC_SIMPLEX
424   addFontAlias ("sansserif",         aSans);                      // Font_NOF_SANS_SERIF
425   addFontAlias ("sans-serif",        aSans);
426   addFontAlias ("sans",              aSans);
427   addFontAlias ("arial",             aSans);
428   addFontAlias ("times",             aSerif);
429   addFontAlias ("serif",             aSerif);                     // Font_NOF_SERIF
430   addFontAlias ("times-roman",       aSerif);                     // Font_NOF_ASCII_SIMPLEX
431   addFontAlias ("times-bold",        aSerif, Font_FA_Bold);       // Font_NOF_ASCII_DUPLEX
432   addFontAlias ("times-italic",      aSerif, Font_FA_Italic);     // Font_NOF_ASCII_ITALIC_COMPLEX
433   addFontAlias ("times-bolditalic",  aSerif, Font_FA_BoldItalic); // Font_NOF_ASCII_ITALIC_TRIPLEX
434   addFontAlias ("symbol",            aSymbol);                    // Font_NOF_GREEK_MONO
435   addFontAlias ("iris",              anIris);                     // Font_NOF_KANJI_MONO
436   addFontAlias ("korean",            aKorean);                    // Font_NOF_KOREAN
437   addFontAlias ("cjk",               aCJK);                       // Font_NOF_CJK
438   addFontAlias ("nsimsun",           aCJK);
439   addFontAlias ("arabic",            anArab);                     // Font_NOF_ARABIC
440   addFontAlias (Font_NOF_SYMBOL_MONO,          aWinDin);
441   addFontAlias (Font_NOF_ASCII_SCRIPT_SIMPLEX, aScript);
442
443   myFallbackAlias = aSans;
444
445   InitFontDataBase();
446 }
447
448 // =======================================================================
449 // function : CheckFont
450 // purpose  :
451 // =======================================================================
452 Standard_Boolean Font_FontMgr::CheckFont (NCollection_Sequence<Handle(Font_SystemFont)>& theFonts,
453                                           const TCollection_AsciiString& theFontPath) const
454 {
455   Handle(Font_FTLibrary) aFtLibrary = new Font_FTLibrary();
456   return checkFont (theFonts, aFtLibrary, theFontPath, 0);
457 }
458
459 // =======================================================================
460 // function : CheckFont
461 // purpose  :
462 // =======================================================================
463 Handle(Font_SystemFont) Font_FontMgr::CheckFont (Standard_CString theFontPath) const
464 {
465   Handle(Font_FTLibrary) aFtLibrary = new Font_FTLibrary();
466   NCollection_Sequence<Handle(Font_SystemFont)> aFonts;
467   return checkFont (aFonts, aFtLibrary, theFontPath, 0)
468        ? aFonts.First()
469        : Handle(Font_SystemFont)();
470 }
471
472 // =======================================================================
473 // function : RegisterFont
474 // purpose  :
475 // =======================================================================
476 Standard_Boolean Font_FontMgr::RegisterFont (const Handle(Font_SystemFont)& theFont,
477                                              const Standard_Boolean         theToOverride)
478 {
479   if (theFont.IsNull())
480   {
481     return Standard_False;
482   }
483
484   const Standard_Integer anOldIndex = myFontMap.FindIndex (theFont);
485   if (anOldIndex == 0)
486   {
487     myFontMap.Add (theFont);
488     return Standard_True;
489   }
490
491   Handle(Font_SystemFont) anOldFont = myFontMap.FindKey (anOldIndex);
492   for (int anAspectIter = 0; anAspectIter < Font_FontAspect_NB; ++anAspectIter)
493   {
494     if (anOldFont->FontPath ((Font_FontAspect )anAspectIter).IsEqual (theFont->FontPath ((Font_FontAspect )anAspectIter))
495      && anOldFont->FontFaceId ((Font_FontAspect )anAspectIter) == theFont->FontFaceId ((Font_FontAspect )anAspectIter))
496     {
497       continue;
498     }
499     else if (theToOverride
500          || !anOldFont->HasFontAspect ((Font_FontAspect )anAspectIter))
501     {
502       anOldFont->SetFontPath ((Font_FontAspect )anAspectIter,
503                               theFont->FontPath ((Font_FontAspect )anAspectIter),
504                               theFont->FontFaceId ((Font_FontAspect )anAspectIter));
505     }
506     else if (theFont->HasFontAspect ((Font_FontAspect )anAspectIter))
507     {
508       return Standard_False;
509     }
510   }
511   return Standard_True;
512 }
513
514 // =======================================================================
515 // function : ClearFontDataBase()
516 // purpose  :
517 // =======================================================================
518 void Font_FontMgr::ClearFontDataBase()
519 {
520   myFontMap.Clear();
521 }
522
523 // =======================================================================
524 // function : InitFontDataBase
525 // purpose  :
526 // =======================================================================
527 void Font_FontMgr::InitFontDataBase()
528 {
529   myFontMap.Clear();
530   Handle(Font_FTLibrary) aFtLibrary = new Font_FTLibrary();
531   NCollection_Sequence<Handle(Font_SystemFont)> aFonts;
532
533 #if defined(OCCT_UWP)
534   // system font files are not accessible
535   (void )aFtLibrary;
536 #elif defined(_WIN32)
537
538   // font directory is placed in "C:\Windows\Fonts\"
539   UINT aStrLength = GetSystemWindowsDirectoryA (NULL, 0);
540   if (aStrLength == 0)
541   {
542     return;
543   }
544
545   char* aWinDir = new char[aStrLength];
546   GetSystemWindowsDirectoryA (aWinDir, aStrLength);
547   TCollection_AsciiString aFontsDir (aWinDir);
548   aFontsDir.AssignCat ("\\Fonts\\");
549   delete[] aWinDir;
550
551   // read fonts list from registry
552   HKEY aFontsKey;
553   if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts",
554                      0, KEY_READ, &aFontsKey) != ERROR_SUCCESS)
555   {
556     return;
557   }
558
559   NCollection_Map<TCollection_AsciiString> aSupportedExtensions;
560   for (Standard_Integer anIter = 0; Font_FontMgr_Extensions[anIter] != NULL; ++anIter)
561   {
562     Standard_CString anExt = Font_FontMgr_Extensions[anIter];
563     aSupportedExtensions.Add (TCollection_AsciiString (anExt));
564   }
565
566   static const DWORD aBufferSize = 256;
567   char aNameBuff[aBufferSize];
568   char aPathBuff[aBufferSize];
569   DWORD aNameSize = aBufferSize;
570   DWORD aPathSize = aBufferSize;
571   for (DWORD anIter = 0;
572        RegEnumValueA (aFontsKey, anIter,
573                       aNameBuff, &aNameSize, NULL, NULL,
574                       (LPBYTE )aPathBuff, &aPathSize) != ERROR_NO_MORE_ITEMS;
575       ++anIter, aNameSize = aBufferSize, aPathSize = aBufferSize)
576   {
577     aPathBuff[(aPathSize < aBufferSize) ? aPathSize : (aBufferSize - 1)] = '\0'; // ensure string is NULL-terminated
578
579     TCollection_AsciiString aFontName (aNameBuff), aFontPath (aPathBuff);
580     if (aFontPath.Search ("\\") == -1)
581     {
582       aFontPath.Insert (1, aFontsDir); // make absolute path
583     }
584
585     // check file extension is in list of supported
586     const Standard_Integer anExtensionPosition = aFontPath.SearchFromEnd (".") + 1;
587     if (anExtensionPosition > 0 && anExtensionPosition < aFontPath.Length())
588     {
589       TCollection_AsciiString aFontExtension = aFontPath.SubString (anExtensionPosition, aFontPath.Length());
590       aFontExtension.LowerCase();
591       if (aSupportedExtensions.Contains (aFontExtension))
592       {
593         aFonts.Clear();
594         checkFont (aFonts, aFtLibrary, aFontPath.ToCString());
595         RegisterFonts (aFonts, false);
596       }
597     }
598   }
599
600   // close registry key
601   RegCloseKey (aFontsKey);
602
603 #else
604
605   NCollection_Map<TCollection_AsciiString> aMapOfFontsDirs;
606 #if !defined(__ANDROID__) && !defined(__APPLE__) && !defined(__EMSCRIPTEN__)
607   if (FcConfig* aFcCfg = FcInitLoadConfig())
608   {
609     if (FcStrList* aFcFontDir = FcConfigGetFontDirs (aFcCfg))
610     {
611       for (;;)
612       {
613         FcChar8* aFcFolder = FcStrListNext (aFcFontDir);
614         if (aFcFolder == NULL)
615         {
616           break;
617         }
618
619         TCollection_AsciiString aPathStr ((const char* )aFcFolder);
620         OSD_Path aPath (aPathStr);
621         addDirsRecursively (aPath, aMapOfFontsDirs);
622       }
623       FcStrListDone (aFcFontDir);
624     }
625     FcConfigDestroy (aFcCfg);
626   }
627
628   const OSD_Protection aProtectRead (OSD_R, OSD_R, OSD_R, OSD_R);
629   if (aMapOfFontsDirs.IsEmpty())
630   {
631     Message::SendAlarm ("Font_FontMgr, fontconfig library returns an empty folder list");
632
633     // read fonts directories from font service config file (obsolete)
634     for (Standard_Integer anIter = 0; myFontServiceConf[anIter] != NULL; ++anIter)
635     {
636       const TCollection_AsciiString aFileOfFontsPath (myFontServiceConf[anIter]);
637       OSD_File aFile (aFileOfFontsPath);
638       if (!aFile.Exists())
639       {
640         continue;
641       }
642
643       aFile.Open (OSD_ReadOnly, aProtectRead);
644       if (!aFile.IsOpen())
645       {
646         continue;
647       }
648
649       Standard_Integer aNByte = 256;
650       Standard_Integer aNbyteRead;
651       TCollection_AsciiString aStr; // read string with information
652       while (!aFile.IsAtEnd())
653       {
654         Standard_Integer aLocation = -1;
655         Standard_Integer aPathLocation = -1;
656
657         aFile.ReadLine (aStr, aNByte, aNbyteRead); // reading 1 line (256 bytes)
658         aLocation = aStr.Search ("catalogue=");
659         if (aLocation < 0)
660         {
661           aLocation = aStr.Search ("catalogue =");
662         }
663
664         aPathLocation = aStr.Search ("/");
665         if (aLocation > 0 && aPathLocation > 0)
666         {
667           aStr = aStr.Split (aPathLocation - 1);
668           TCollection_AsciiString aFontPath;
669           Standard_Integer aPathNumber = 1;
670           do
671           {
672             // Getting directory paths, which can be splitted by "," or ":"
673             aFontPath = aStr.Token (":,", aPathNumber);
674             aFontPath.RightAdjust();
675             if (!aFontPath.IsEmpty())
676             {
677               OSD_Path aPath(aFontPath);
678               addDirsRecursively (aPath, aMapOfFontsDirs);
679             }
680             aPathNumber++;
681           }
682           while (!aFontPath.IsEmpty());
683         }
684       }
685       aFile.Close();
686     }
687   }
688 #endif
689
690   // append default directories
691   for (Standard_Integer anIter = 0; myDefaultFontsDirs[anIter] != NULL; ++anIter)
692   {
693     Standard_CString anItem = myDefaultFontsDirs[anIter];
694     TCollection_AsciiString aPathStr (anItem);
695     OSD_Path aPath (aPathStr);
696     addDirsRecursively (aPath, aMapOfFontsDirs);
697   }
698
699   NCollection_Map<TCollection_AsciiString> aSupportedExtensions;
700   for (Standard_Integer anIter = 0; Font_FontMgr_Extensions[anIter] != NULL; ++anIter)
701   {
702     Standard_CString anExt = Font_FontMgr_Extensions[anIter];
703     aSupportedExtensions.Add (TCollection_AsciiString (anExt));
704   }
705
706   for (NCollection_Map<TCollection_AsciiString>::Iterator anIter (aMapOfFontsDirs);
707        anIter.More(); anIter.Next())
708   {
709   #if !defined(__ANDROID__) && !defined(__APPLE__) && !defined(__EMSCRIPTEN__)
710     OSD_File aReadFile (anIter.Value() + "/fonts.dir");
711     if (!aReadFile.Exists())
712     {
713   #endif
714       OSD_Path aFolderPath (anIter.Value());
715       for (OSD_FileIterator aFileIter (aFolderPath, "*"); aFileIter.More(); aFileIter.Next())
716       {
717         OSD_Path aFontFilePath;
718         aFileIter.Values().Path (aFontFilePath);
719
720         TCollection_AsciiString aFontFileName;
721         aFontFilePath.SystemName (aFontFileName);
722         aFontFileName = anIter.Value() + "/" + aFontFileName;
723
724         aFonts.Clear();
725         checkFont (aFonts, aFtLibrary, aFontFileName);
726         RegisterFonts (aFonts, false);
727       }
728
729   #if !defined(__ANDROID__) && !defined(__APPLE__) && !defined(__EMSCRIPTEN__)
730       continue;
731     }
732
733     aReadFile.Open (OSD_ReadOnly, aProtectRead);
734     if (!aReadFile.IsOpen())
735     {
736       continue; // invalid fonts directory
737     }
738
739     Standard_Integer aNbyteRead, aNByte = 256;
740     TCollection_AsciiString aLine (aNByte);
741     Standard_Boolean isFirstLine = Standard_True;
742     const TCollection_AsciiString anEncoding ("iso8859-1\n");
743     while (!aReadFile.IsAtEnd())
744     {
745       aReadFile.ReadLine (aLine, aNByte, aNbyteRead);
746       if (isFirstLine)
747       {
748         // first line contains the number of fonts in this file
749         // just ignoring it...
750         isFirstLine = Standard_False;
751         continue;
752       }
753
754       Standard_Integer anExtensionPosition = aLine.Search (".") + 1;
755       if (anExtensionPosition == 0)
756       {
757         continue; // can't find extension position in the font description
758       }
759
760       Standard_Integer anEndOfFileName = aLine.Location (" ", anExtensionPosition, aLine.Length()) - 1;
761       if (anEndOfFileName < 0 || anEndOfFileName < anExtensionPosition)
762       {
763         continue; // font description have empty extension
764       }
765
766       TCollection_AsciiString aFontExtension = aLine.SubString (anExtensionPosition, anEndOfFileName);
767       aFontExtension.LowerCase();
768       if (aSupportedExtensions.Contains (aFontExtension) && (aLine.Search (anEncoding) > 0))
769       {
770         // In current implementation use fonts with ISO-8859-1 coding page.
771         // OCCT not give to manage coding page by means of programm interface.
772         // TODO: make high level interface for choosing necessary coding page.
773         TCollection_AsciiString aXLFD (aLine.SubString (anEndOfFileName + 2, aLine.Length()));
774         TCollection_AsciiString aFontPath (anIter.Value().ToCString());
775         if (aFontPath.SearchFromEnd ("/") != aFontPath.Length())
776         {
777           aFontPath.AssignCat ("/");
778         }
779         TCollection_AsciiString aFontFileName (aLine.SubString (1, anEndOfFileName));
780         aFontPath.AssignCat (aFontFileName);
781
782         aFonts.Clear();
783         if (checkFont (aFonts, aFtLibrary, aFontPath))
784         {
785           RegisterFonts (aFonts, false);
786           const Handle(Font_SystemFont)& aNewFont = aFonts.First();
787           if (!aXLFD.IsEmpty()
788             && aXLFD.Search ("-0-0-0-0-") != -1) // ignore non-resizable fonts
789           {
790             const TCollection_AsciiString anXName = aXLFD.Token ("-", 2);
791             Font_FontAspect anXAspect = Font_FA_Regular;
792             if (aXLFD.Token ("-", 3).IsEqual ("bold")
793              && (aXLFD.Token ("-", 4).IsEqual ("i")
794               || aXLFD.Token ("-", 4).IsEqual ("o")))
795             {
796               anXAspect = Font_FA_BoldItalic;
797             }
798             else if (aXLFD.Token ("-", 3).IsEqual ("bold"))
799             {
800               anXAspect = Font_FA_Bold;
801             }
802             else if (aXLFD.Token ("-", 4).IsEqual ("i")
803                   || aXLFD.Token ("-", 4).IsEqual ("o"))
804             {
805               anXAspect = Font_FA_Italic;
806             }
807
808             Handle(Font_SystemFont) aNewFontFromXLFD = new Font_SystemFont (anXName);
809             aNewFontFromXLFD->SetFontPath (anXAspect, aFontPath, 0);
810             if (!aNewFont->IsEqual (aNewFontFromXLFD))
811             {
812               RegisterFont (aNewFontFromXLFD, false);
813             }
814           }
815         }
816       }
817     }
818     aReadFile.Close();
819   #endif
820   }
821 #endif
822 }
823
824 // =======================================================================
825 // function : GetAvailableFontsNames
826 // purpose  :
827 // =======================================================================
828 void Font_FontMgr::GetAvailableFontsNames (TColStd_SequenceOfHAsciiString& theFontsNames) const
829 {
830   theFontsNames.Clear();
831   for (NCollection_IndexedMap<Handle(Font_SystemFont), Font_SystemFont>::Iterator aFontIter (myFontMap);
832        aFontIter.More(); aFontIter.Next())
833   {
834     const Handle(Font_SystemFont)& aFont = aFontIter.Value();
835     theFontsNames.Append (new TCollection_HAsciiString(aFont->FontName()));
836   }
837 }
838
839 // =======================================================================
840 // function : GetFont
841 // purpose  :
842 // =======================================================================
843 Handle(Font_SystemFont) Font_FontMgr::GetFont (const Handle(TCollection_HAsciiString)& theFontName,
844                                                const Font_FontAspect  theFontAspect,
845                                                const Standard_Integer theFontSize) const
846 {
847   if ((theFontSize < 2 && theFontSize != -1) || theFontName.IsNull())
848   {
849     return Handle(Font_SystemFont)();
850   }
851
852   Handle(Font_SystemFont) aFont = myFontMap.Find (theFontName->String());
853   return (aFont.IsNull()
854        || theFontAspect == Font_FontAspect_UNDEFINED
855        || aFont->HasFontAspect (theFontAspect))
856        ? aFont
857        : Handle(Font_SystemFont)();
858 }
859
860 // =======================================================================
861 // function : GetFont
862 // purpose  :
863 // =======================================================================
864 Handle(Font_SystemFont) Font_FontMgr::GetFont (const TCollection_AsciiString& theFontName) const
865 {
866   return myFontMap.Find (theFontName);
867 }
868
869 // =======================================================================
870 // function : FindFallbackFont
871 // purpose  :
872 // =======================================================================
873 Handle(Font_SystemFont) Font_FontMgr::FindFallbackFont (Font_UnicodeSubset theSubset,
874                                                         Font_FontAspect theFontAspect) const
875 {
876   Font_FontAspect aFontAspect = theFontAspect;
877   Handle(Font_SystemFont) aFont;
878   switch (theSubset)
879   {
880     case Font_UnicodeSubset_Western: aFont = FindFont (Font_NOF_SANS_SERIF, Font_StrictLevel_Aliases, aFontAspect, false); break;
881     case Font_UnicodeSubset_Korean:  aFont = FindFont (Font_NOF_KOREAN,     Font_StrictLevel_Aliases, aFontAspect, false); break;
882     case Font_UnicodeSubset_CJK:     aFont = FindFont (Font_NOF_CJK,        Font_StrictLevel_Aliases, aFontAspect, false); break;
883     case Font_UnicodeSubset_Arabic:  aFont = FindFont (Font_NOF_ARABIC,     Font_StrictLevel_Aliases, aFontAspect, false); break;
884   }
885   if (aFont.IsNull())
886   {
887     const char* aRange = "";
888     switch (theSubset)
889     {
890       case Font_UnicodeSubset_Western: aRange = "Western"; break;
891       case Font_UnicodeSubset_Korean:  aRange = "Korean";  break;
892       case Font_UnicodeSubset_CJK:     aRange = "CJK";     break;
893       case Font_UnicodeSubset_Arabic:  aRange = "Arabic";  break;
894     }
895     Message::SendFail (TCollection_AsciiString("Font_FontMgr, error: unable to find ") + aRange + " fallback font!");
896   }
897   return aFont;
898 }
899
900 // =======================================================================
901 // function : FindFont
902 // purpose  :
903 // =======================================================================
904 Handle(Font_SystemFont) Font_FontMgr::FindFont (const TCollection_AsciiString& theFontName,
905                                                 Font_StrictLevel theStrictLevel,
906                                                 Font_FontAspect& theFontAspect,
907                                                 Standard_Boolean theDoFailMsg) const
908 {
909   TCollection_AsciiString aFontName (theFontName);
910   aFontName.LowerCase();
911   Handle(Font_SystemFont) aFont = myFontMap.Find (aFontName);
912   if (!aFont.IsNull()
913     || theStrictLevel == Font_StrictLevel_Strict)
914   {
915     return aFont;
916   }
917
918   // Trying to use font names mapping
919   for (int aPass = 0; aPass < 2; ++aPass)
920   {
921     Handle(Font_FontAliasSequence) anAliases;
922     if (aPass == 0)
923     {
924       myFontAliases.Find (aFontName, anAliases);
925     }
926     else if (theStrictLevel == Font_StrictLevel_Any)
927     {
928       anAliases = myFallbackAlias;
929     }
930
931     if (anAliases.IsNull()
932      || anAliases->IsEmpty())
933     {
934       continue;
935     }
936
937     bool isAliasUsed = false, isBestAlias = false;
938     for (Font_FontAliasSequence::Iterator anAliasIter (*anAliases); anAliasIter.More(); anAliasIter.Next())
939     {
940       const Font_FontAlias& anAlias = anAliasIter.Value();
941       if (Handle(Font_SystemFont) aFont2 = myFontMap.Find (anAlias.FontName))
942       {
943         if (aFont.IsNull())
944         {
945           aFont = aFont2;
946           isAliasUsed = true;
947         }
948
949         if ((anAlias.FontAspect != Font_FontAspect_UNDEFINED
950           && aFont2->HasFontAspect (anAlias.FontAspect)))
951         {
952           // special case - alias refers to styled font (e.g. "times-bold")
953           isBestAlias = true;
954           theFontAspect = anAlias.FontAspect;
955           break;
956         }
957         else if (anAlias.FontAspect == Font_FontAspect_UNDEFINED
958               && (theFontAspect == Font_FontAspect_UNDEFINED
959                 || aFont2->HasFontAspect (theFontAspect)))
960         {
961           isBestAlias = true;
962           break;
963         }
964       }
965     }
966
967     if (aPass == 0)
968     {
969       if (isAliasUsed && myToTraceAliases)
970       {
971         Message::SendTrace (TCollection_AsciiString("Font_FontMgr, using font alias '") + aFont->FontName() + "'"
972                           " instead of requested '" + theFontName + "'");
973       }
974       if (isBestAlias)
975       {
976         return aFont;
977       }
978       else if (!aFont.IsNull())
979       {
980         break;
981       }
982     }
983   }
984
985   if (aFont.IsNull()
986    && theStrictLevel == Font_StrictLevel_Any)
987   {
988     // try finding ANY font in case if even default fallback alias myFallbackAlias cannot be found
989     aFont = myFontMap.Find (TCollection_AsciiString());
990   }
991   if (aFont.IsNull())
992   {
993     if (theDoFailMsg)
994     {
995       Message::SendFail ("Font_FontMgr, error: unable to find any font!");
996     }
997     return Handle(Font_SystemFont)();
998   }
999
1000   if ((theFontAspect != Font_FA_Undefined
1001     && !aFont->HasFontAspect (theFontAspect))
1002    || (!aFontName.IsEmpty()
1003     && !aFontName.IsEqual (aFont->FontKey())))
1004   {
1005     TCollection_AsciiString aDesc = TCollection_AsciiString() + "'" + theFontName + "'"
1006                                   + TCollection_AsciiString() + " [" + Font_FontMgr::FontAspectToString (theFontAspect) + "]";
1007     Message::SendWarning (TCollection_AsciiString("Font_FontMgr, warning: unable to find font ")
1008                         + aDesc + "; " + aFont->ToString() + " is used instead");
1009   }
1010   return aFont;
1011 }
1012
1013 // =======================================================================
1014 // function : Font_FontMap::Find
1015 // purpose  :
1016 // =======================================================================
1017 Handle(Font_SystemFont) Font_FontMgr::Font_FontMap::Find (const TCollection_AsciiString& theFontName) const
1018 {
1019   if (IsEmpty())
1020   {
1021     return Handle(Font_SystemFont)();
1022   }
1023   else if (theFontName.IsEmpty())
1024   {
1025     return FindKey (1); // return any font
1026   }
1027
1028   TCollection_AsciiString aFontName (theFontName);
1029   aFontName.LowerCase();
1030   for (IndexedMapNode* aNodeIter = (IndexedMapNode* )myData1[::HashCode (aFontName, NbBuckets())];
1031        aNodeIter != NULL; aNodeIter = (IndexedMapNode* )aNodeIter->Next())
1032   {
1033     const Handle(Font_SystemFont)& aKey = aNodeIter->Key1();
1034     if (aKey->FontKey().IsEqual (aFontName))
1035     {
1036       return aKey;
1037     }
1038   }
1039   return Handle(Font_SystemFont)();
1040 }
1041
1042 // =======================================================================
1043 // function : EmbedFallbackFont
1044 // purpose  :
1045 // =======================================================================
1046 Handle(NCollection_Buffer) Font_FontMgr::EmbedFallbackFont()
1047 {
1048   return new NCollection_Buffer (Handle(NCollection_BaseAllocator)(),
1049                                  Font_DejavuSans_Latin_woff_size,
1050                                  const_cast<Standard_Byte*>(Font_DejavuSans_Latin_woff));
1051 }