0023415: OSD_FontMgr can't idenify aspect for fonts with names dependant on system...
[occt.git] / src / Font / Font_FontMgr.cxx
1 // Created on: 2008-01-20
2 // Created by: Alexander A. BORODIN
3 // Copyright (c) 2008-2012 OPEN CASCADE SAS
4 //
5 // The content of this file is subject to the Open CASCADE Technology Public
6 // License Version 6.5 (the "License"). You may not use the content of this file
7 // except in compliance with the License. Please obtain a copy of the License
8 // at http://www.opencascade.org and read it completely before using this file.
9 //
10 // The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
11 // main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
12 //
13 // The Original Code and all software distributed under the License is
14 // distributed on an "AS IS" basis, without warranty of any kind, and the
15 // Initial Developer hereby disclaims all such warranties, including without
16 // limitation, any warranties of merchantability, fitness for a particular
17 // purpose or non-infringement. Please see the License for the specific terms
18 // and conditions governing the rights and limitations under the License.
19
20 #include <Font_FontMgr.ixx>
21
22 #include <OSD_Environment.hxx>
23 #include <NCollection_List.hxx>
24 #include <NCollection_Map.hxx>
25 #include <Standard_Stream.hxx>
26 #include <TCollection_HAsciiString.hxx>
27
28 #include <ft2build.h>
29 #include FT_FREETYPE_H
30
31 struct Font_FontMgr_FontAliasMapNode
32 {
33   const char *    EnumName;
34   const char *    FontName;
35   Font_FontAspect FontAspect;
36 };
37
38 static const Font_FontMgr_FontAliasMapNode Font_FontMgr_MapOfFontsAliases[] =
39 {
40
41 #ifdef WNT
42
43   { "Courier"                  , "Courier New"    , Font_FA_Regular },
44   { "Times-Roman"              , "Times New Roman", Font_FA_Regular  },
45   { "Times-Bold"               , "Times New Roman", Font_FA_Bold },
46   { "Times-Italic"             , "Times New Roman", Font_FA_Italic  },
47   { "Times-BoldItalic"         , "Times New Roman", Font_FA_BoldItalic  },
48   { "ZapfChancery-MediumItalic", "Script"         , Font_FA_Regular  },
49   { "Symbol"                   , "Symbol"         , Font_FA_Regular  },
50   { "ZapfDingbats"             , "WingDings"      , Font_FA_Regular  },
51   { "Rock"                     , "Arial"          , Font_FA_Regular  },
52   { "Iris"                     , "Lucida Console" , Font_FA_Regular  }
53
54 #else   //X11
55
56   { "Courier"                  , "Courier"      , Font_FA_Regular },
57   { "Times-Roman"              , "Times"        , Font_FA_Regular  },
58   { "Times-Bold"               , "Times"        , Font_FA_Bold },
59   { "Times-Italic"             , "Times"        , Font_FA_Italic  },
60   { "Times-BoldItalic"         , "Times"        , Font_FA_BoldItalic  },
61   { "Arial"                    , "Helvetica"    , Font_FA_Regular  }, 
62   { "ZapfChancery-MediumItalic", "-adobe-itc zapf chancery-medium-i-normal--*-*-*-*-*-*-iso8859-1"              , Font_FA_Regular  },
63   { "Symbol"                   , "-adobe-symbol-medium-r-normal--*-*-*-*-*-*-adobe-fontspecific"                , Font_FA_Regular  },
64   { "ZapfDingbats"             , "-adobe-itc zapf dingbats-medium-r-normal--*-*-*-*-*-*-adobe-fontspecific"     , Font_FA_Regular  },
65   { "Rock"                     , "-sgi-rock-medium-r-normal--*-*-*-*-p-*-iso8859-1"                             , Font_FA_Regular  },
66   { "Iris"                     , "--iris-medium-r-normal--*-*-*-*-m-*-iso8859-1"                                , Font_FA_Regular  }
67 #endif
68
69 };
70
71 #define NUM_FONT_ENTRIES (sizeof(Font_FontMgr_MapOfFontsAliases)/sizeof(Font_FontMgr_FontAliasMapNode))
72
73 #if (defined(_WIN32) || defined(__WIN32__))
74
75   #include <windows.h>
76   #include <stdlib.h>
77
78   #ifdef _MSC_VER
79     #pragma comment (lib, "freetype.lib")
80   #endif
81
82   namespace
83   {
84
85     // list of supported extensions
86     static Standard_CString Font_FontMgr_Extensions[] =
87     {
88       "ttf",
89       "otf",
90       "ttc",
91       NULL
92     };
93
94   };
95
96 #else
97
98   #include <OSD_DirectoryIterator.hxx>
99   #include <OSD_Path.hxx>
100   #include <OSD_File.hxx>
101   #include <OSD_OpenMode.hxx>
102   #include <OSD_Protection.hxx>
103
104   namespace
105   {
106
107     // list of supported extensions
108     static Standard_CString Font_FontMgr_Extensions[] =
109     {
110       "ttf",
111       "otf",
112       "ttc",
113       "pfa",
114       "pfb",
115       NULL
116     };
117
118     // X11 configuration file in plain text format (obsolete - doesn't exists in modern distributives)
119     static Standard_CString myFontServiceConf[] = {"/etc/X11/fs/config",
120                                                    "/usr/X11R6/lib/X11/fs/config",
121                                                    "/usr/X11/lib/X11/fs/config",
122                                                    NULL
123                                                   };
124
125   #ifdef __APPLE__
126     // default fonts paths in Mac OS X
127     static Standard_CString myDefaultFontsDirs[] = {"/System/Library/Fonts",
128                                                     "/Library/Fonts",
129                                                     NULL
130                                                    };
131   #else
132     // default fonts paths in most Unix systems (Linux and others)
133     static Standard_CString myDefaultFontsDirs[] = {"/usr/share/fonts",
134                                                     "/usr/local/share/fonts",
135                                                     NULL
136                                                    };
137   #endif
138
139     static void addDirsRecursively (const OSD_Path&                           thePath,
140                                     NCollection_Map<TCollection_AsciiString>& theDirsMap)
141     {
142       TCollection_AsciiString aDirName;
143       thePath.SystemName (aDirName);
144       if (!theDirsMap.Add (aDirName))
145       {
146         return;
147       }
148
149       for (OSD_DirectoryIterator aDirIterator (thePath, "*"); aDirIterator.More(); aDirIterator.Next())
150       {
151         OSD_Path aChildDirPath;
152         aDirIterator.Values().Path (aChildDirPath);
153
154         TCollection_AsciiString aChildDirName;
155         aChildDirPath.SystemName (aChildDirName);
156         if (!aChildDirName.IsEqual (".") && !aChildDirName.IsEqual (".."))
157         {
158           aChildDirName = aDirName + "/" + aChildDirName;
159           OSD_Path aPath (aChildDirName);
160           addDirsRecursively (aPath, theDirsMap);
161         }
162       }
163     }
164
165   };
166
167 #endif
168
169 // =======================================================================
170 // function : checkFont
171 // purpose  :
172 // =======================================================================
173 static Handle(Font_SystemFont) checkFont (FT_Library             theFTLib,
174                                           const Standard_CString theFontPath)
175 {
176   FT_Face aFontFace;
177   FT_Error aFaceError = FT_New_Face (theFTLib, theFontPath, 0, &aFontFace);
178   if (aFaceError != FT_Err_Ok)
179   {
180     return NULL;
181   }
182
183   Font_FontAspect anAspect = Font_FA_Regular;
184   if (aFontFace->style_flags == (FT_STYLE_FLAG_ITALIC | FT_STYLE_FLAG_BOLD))
185   {
186     anAspect = Font_FA_BoldItalic;
187   }
188   else if (aFontFace->style_flags == FT_STYLE_FLAG_ITALIC)
189   {
190     anAspect = Font_FA_Italic;
191   }
192   else if (aFontFace->style_flags == FT_STYLE_FLAG_BOLD)
193   {
194     anAspect = Font_FA_Bold;
195   }
196
197   Handle(TCollection_HAsciiString) aFontName = new TCollection_HAsciiString (aFontFace->family_name);
198   Handle(TCollection_HAsciiString) aFontPath = new TCollection_HAsciiString (theFontPath);
199   Handle(Font_SystemFont) aResult = new Font_SystemFont (aFontName, anAspect, aFontPath);
200
201   FT_Done_Face (aFontFace);
202
203   return aResult;
204 }
205
206 // =======================================================================
207 // function : GetInstance
208 // purpose  :
209 // =======================================================================
210 Handle(Font_FontMgr) Font_FontMgr::GetInstance()
211 {
212   static Handle(Font_FontMgr) _mgr;
213   if (_mgr.IsNull())
214   {
215     _mgr = new Font_FontMgr();
216   }
217
218   return _mgr;
219 }
220
221 // =======================================================================
222 // function : Font_FontMgr
223 // purpose  :
224 // =======================================================================
225 Font_FontMgr::Font_FontMgr()
226 {
227   InitFontDataBase();
228 }
229
230 // =======================================================================
231 // function : InitFontDataBase
232 // purpose  :
233 // =======================================================================
234 void Font_FontMgr::InitFontDataBase()
235 {
236   myListOfFonts.Clear();
237   FT_Library aFtLibrary = NULL;
238
239 #if (defined(_WIN32) || defined(__WIN32__))
240
241   // font directory is placed in "C:\Windows\Fonts\"
242   UINT aStrLength = GetSystemWindowsDirectoryA (NULL, 0);
243   if (aStrLength == 0)
244   {
245     return;
246   }
247
248   char* aWinDir = new char[aStrLength];
249   GetSystemWindowsDirectoryA (aWinDir, aStrLength);
250   Handle(TCollection_HAsciiString) aFontsDir = new TCollection_HAsciiString (aWinDir);
251   aFontsDir->AssignCat ("\\Fonts\\");
252   delete[] aWinDir;
253
254   // read fonts list from registry
255   HKEY aFontsKey;
256   if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts",
257                      0, KEY_READ, &aFontsKey) != ERROR_SUCCESS)
258   {
259     return;
260   }
261
262   NCollection_Map<TCollection_AsciiString> aSupportedExtensions;
263   for (Standard_Integer anIter = 0; Font_FontMgr_Extensions[anIter] != NULL; ++anIter)
264   {
265     Standard_CString anExt = Font_FontMgr_Extensions[anIter];
266     aSupportedExtensions.Add (TCollection_AsciiString (anExt));
267   }
268
269   FT_Init_FreeType (&aFtLibrary);
270   static const DWORD aBufferSize = 256;
271   char aNameBuff[aBufferSize];
272   char aPathBuff[aBufferSize];
273   DWORD aNameSize = aBufferSize;
274   DWORD aPathSize = aBufferSize;
275   for (DWORD anIter = 0;
276        RegEnumValueA (aFontsKey, anIter,
277                       aNameBuff, &aNameSize, NULL, NULL,
278                       (LPBYTE )aPathBuff, &aPathSize) != ERROR_NO_MORE_ITEMS;
279       ++anIter, aNameSize = aBufferSize, aPathSize = aBufferSize)
280   {
281     aPathBuff[(aPathSize < aBufferSize) ? aPathSize : (aBufferSize - 1)] = '\0'; // ensure string is NULL-terminated
282
283     Handle(TCollection_HAsciiString) aFontName = new TCollection_HAsciiString (aNameBuff);
284     Handle(TCollection_HAsciiString) aFontPath = new TCollection_HAsciiString (aPathBuff);
285     if (aFontPath->Search ("\\") == -1)
286     {
287       aFontPath->Insert (1, aFontsDir); // make absolute path
288     }
289
290     // check file extension is in list of supported
291     const Standard_Integer anExtensionPosition = aFontPath->SearchFromEnd (".") + 1;
292     if (anExtensionPosition > 0 && anExtensionPosition < aFontPath->Length())
293     {
294       Handle(TCollection_HAsciiString) aFontExtension = aFontPath->SubString (anExtensionPosition, aFontPath->Length());
295       aFontExtension->LowerCase();
296       if (aSupportedExtensions.Contains (aFontExtension->String()))
297       {
298         Handle(Font_SystemFont) aNewFont = checkFont (aFtLibrary, aFontPath->ToCString());
299         if (!aNewFont.IsNull())
300         {
301           myListOfFonts.Append (aNewFont);
302         }
303       }
304     }
305   }
306
307   // close registry key
308   RegCloseKey (aFontsKey);
309
310 #else
311
312   NCollection_Map<TCollection_AsciiString> aMapOfFontsDirs;
313   const OSD_Protection aProtectRead (OSD_R, OSD_R, OSD_R, OSD_R);
314
315   // read fonts directories from font service config file (obsolete)
316   for (Standard_Integer anIter = 0; myFontServiceConf[anIter] != NULL; ++anIter)
317   {
318     const TCollection_AsciiString aFileOfFontsPath (myFontServiceConf[anIter]);
319     OSD_File aFile (aFileOfFontsPath);
320     if (!aFile.Exists())
321     {
322       continue;
323     }
324
325     aFile.Open (OSD_ReadOnly, aProtectRead);
326     if (!aFile.IsOpen())
327     {
328       continue;
329     }
330
331     Standard_Integer aNByte = 256;
332     Standard_Integer aNbyteRead;
333     TCollection_AsciiString aStr; // read string with information
334     while (!aFile.IsAtEnd())
335     {
336       Standard_Integer aLocation = -1;
337       Standard_Integer aPathLocation = -1;
338
339       aFile.ReadLine (aStr, aNByte, aNbyteRead); // reading 1 line (256 bytes)
340       aLocation = aStr.Search ("catalogue=");
341       if (aLocation < 0)
342       {
343         aLocation = aStr.Search ("catalogue =");
344       }
345
346       aPathLocation = aStr.Search ("/");
347       if (aLocation > 0 && aPathLocation > 0)
348       {
349         aStr = aStr.Split (aPathLocation - 1);
350         TCollection_AsciiString aFontPath;
351         Standard_Integer aPathNumber = 1;
352         do
353         {
354           // Getting directory paths, which can be splitted by "," or ":"
355           aFontPath = aStr.Token (":,", aPathNumber);
356           aFontPath.RightAdjust();
357           if (!aFontPath.IsEmpty())
358           {
359             OSD_Path aPath(aFontPath);
360             addDirsRecursively (aPath, aMapOfFontsDirs);
361           }
362           aPathNumber++;
363         }
364         while (!aFontPath.IsEmpty());
365       }
366     }
367     aFile.Close();
368   }
369
370   // append default directories
371   for (Standard_Integer anIter = 0; myDefaultFontsDirs[anIter] != NULL; ++anIter)
372   {
373     Standard_CString anItem = myDefaultFontsDirs[anIter];
374     TCollection_AsciiString aPathStr (anItem);
375     OSD_Path aPath (aPathStr);
376     addDirsRecursively (aPath, aMapOfFontsDirs);
377   }
378
379   NCollection_Map<TCollection_AsciiString> aSupportedExtensions;
380   for (Standard_Integer anIter = 0; Font_FontMgr_Extensions[anIter] != NULL; ++anIter)
381   {
382     Standard_CString anExt = Font_FontMgr_Extensions[anIter];
383     aSupportedExtensions.Add (TCollection_AsciiString (anExt));
384   }
385
386   FT_Init_FreeType (&aFtLibrary);
387   for (NCollection_Map<TCollection_AsciiString>::Iterator anIter (aMapOfFontsDirs);
388        anIter.More(); anIter.Next())
389   {
390     OSD_File aReadFile (anIter.Value() + "/fonts.dir");
391     if (!aReadFile.Exists())
392     {
393       continue; // invalid fonts directory
394     }
395
396     aReadFile.Open (OSD_ReadOnly, aProtectRead);
397     if (!aReadFile.IsOpen())
398     {
399       continue; // invalid fonts directory
400     }
401
402     Standard_Integer aNbyteRead, aNByte = 256;
403     TCollection_AsciiString aLine (aNByte);
404     Standard_Boolean isFirstLine = Standard_True;
405     const TCollection_AsciiString anEncoding ("iso8859-1\n");
406     while (!aReadFile.IsAtEnd())
407     {
408       aReadFile.ReadLine (aLine, aNByte, aNbyteRead);
409       if (isFirstLine)
410       {
411         // first line contains the number of fonts in this file
412         // just ignoring it...
413         isFirstLine = Standard_False;
414         continue;
415       }
416
417       Standard_Integer anExtensionPosition = aLine.Search (".") + 1;
418       if (anExtensionPosition == 0)
419       {
420         continue; // can't find extension position in the font description
421       }
422
423       Standard_Integer anEndOfFileName = aLine.Location (" ", anExtensionPosition, aLine.Length()) - 1;
424       if (anEndOfFileName < 0 || anEndOfFileName < anExtensionPosition)
425       {
426         continue; // font description have empty extension
427       }
428
429       TCollection_AsciiString aFontExtension = aLine.SubString (anExtensionPosition, anEndOfFileName);
430       aFontExtension.LowerCase();
431       if (aSupportedExtensions.Contains (aFontExtension) && (aLine.Search (anEncoding) > 0))
432       {
433         // In current implementation use fonts with ISO-8859-1 coding page.
434         // OCCT not give to manage coding page by means of programm interface.
435         // TODO: make high level interface for choosing necessary coding page.
436         Handle(TCollection_HAsciiString) aXLFD =
437           new TCollection_HAsciiString (aLine.SubString (anEndOfFileName + 2, aLine.Length()));
438         Handle(TCollection_HAsciiString) aFontPath =
439           new TCollection_HAsciiString (anIter.Value().ToCString());
440         if (aFontPath->SearchFromEnd ("/") != aFontPath->Length())
441         {
442           aFontPath->AssignCat ("/");
443         }
444         Handle(TCollection_HAsciiString) aFontFileName =
445         new TCollection_HAsciiString (aLine.SubString (1, anEndOfFileName));
446         aFontPath->AssignCat (aFontFileName);
447
448         Handle(Font_SystemFont) aNewFontFromXLFD = new Font_SystemFont (aXLFD, aFontPath);
449         Handle(Font_SystemFont) aNewFont = checkFont (aFtLibrary, aFontPath->ToCString());
450
451         if (aNewFontFromXLFD->IsValid() && !aNewFont.IsNull() &&
452            !aNewFont->IsEqual (aNewFontFromXLFD))
453         {
454           myListOfFonts.Append (aNewFont);
455           myListOfFonts.Append (aNewFontFromXLFD);
456         }
457         else if (!aNewFont.IsNull())
458         {
459           myListOfFonts.Append (aNewFont);
460         }
461         else if (aNewFontFromXLFD->IsValid())
462         {
463           myListOfFonts.Append (aNewFontFromXLFD);
464         }
465
466       }
467     }
468     aReadFile.Close();
469   }
470 #endif
471   FT_Done_FreeType (aFtLibrary);
472 }
473
474 // =======================================================================
475 // function : GetAvailableFonts
476 // purpose  :
477 // =======================================================================
478 const Font_NListOfSystemFont& Font_FontMgr::GetAvailableFonts() const
479 {
480   return myListOfFonts;
481 }
482
483 void Font_FontMgr::GetAvailableFontsNames (TColStd_SequenceOfHAsciiString& theFontsNames) const
484 {
485   theFontsNames.Clear();
486   for (Font_NListOfSystemFont::Iterator anIter(myListOfFonts); anIter.More(); anIter.Next())
487   {
488     theFontsNames.Append (anIter.Value()->FontName());
489   }
490 }
491
492 Handle(Font_SystemFont) Font_FontMgr::GetFont (const Handle(TCollection_HAsciiString)& theFontName,
493                                                const Font_FontAspect theFontAspect,
494                                                const Standard_Integer theFontSize) const
495 {
496   if ( (theFontSize < 2 && theFontSize != -1) || theFontName.IsNull())
497   {
498     return NULL; 
499   }
500
501   Font_NListOfSystemFont::Iterator aFontsIterator (myListOfFonts);
502
503   for (; aFontsIterator.More(); aFontsIterator.Next())
504   {
505     if (!theFontName->IsEmpty() && !aFontsIterator.Value()->FontName()->IsSameString (theFontName, Standard_False))
506     {
507       continue;
508     }
509
510     if (theFontAspect != Font_FA_Undefined && aFontsIterator.Value()->FontAspect() != theFontAspect)
511     {
512       continue;
513     }
514
515     if (theFontSize == -1 || aFontsIterator.Value()->FontHeight() == -1 ||
516         theFontSize == aFontsIterator.Value()->FontHeight())
517     {
518       return aFontsIterator.Value();
519     }
520   }
521
522   return NULL;
523 }
524
525 Handle(Font_SystemFont) Font_FontMgr::FindFont (const Handle(TCollection_HAsciiString)& theFontName,
526                                                 const Font_FontAspect theFontAspect,
527                                                 const Standard_Integer theFontSize) const
528 {
529   Handle(TCollection_HAsciiString) aFontName   = theFontName;
530   Font_FontAspect                  aFontAspect = theFontAspect;
531   Standard_Integer                 aFontSize = theFontSize;
532
533   Handle(Font_SystemFont) aFont = GetFont (aFontName, aFontAspect, aFontSize);
534
535   if (!aFont.IsNull())
536   {
537     return aFont;
538   }
539
540   // Trying to use font names mapping
541   for (Standard_Integer anIter = 0; anIter < NUM_FONT_ENTRIES; ++anIter)
542   {
543     Handle(TCollection_HAsciiString) aFontAlias =
544       new TCollection_HAsciiString (Font_FontMgr_MapOfFontsAliases[anIter].EnumName);
545
546     if (aFontAlias->IsSameString (aFontName, Standard_False))
547     {
548       aFontName = new TCollection_HAsciiString (Font_FontMgr_MapOfFontsAliases[anIter].FontName);
549       aFontAspect = Font_FontMgr_MapOfFontsAliases[anIter].FontAspect;
550       break;
551     }
552   }
553
554   aFont = GetFont (aFontName, aFontAspect, aFontSize);
555
556   if (!aFont.IsNull())
557   {
558     return aFont;
559   }
560
561   // Requested family name not found -> search for any font family with given aspect and height
562   aFontName = new TCollection_HAsciiString ("");
563   aFont = GetFont (aFontName, aFontAspect, aFontSize);
564
565   if (!aFont.IsNull())
566   {
567     return aFont;
568   }
569
570   // The last resort: trying to use ANY font available in the system
571   aFontAspect = Font_FA_Undefined;
572   aFontSize = -1;
573   aFont = GetFont (aFontName, aFontAspect, aFontSize);
574   
575   if (!aFont.IsNull())
576   {
577     return aFont;
578   }
579
580   return NULL; // Fonts are not found in the system.
581 }