ecere/gfx/fontManagement: Falling back to Arial Unicode MS on Windows
[sdk] / ecere / src / gfx / fontManagement.ec
1 import "File"
2
3 namespace gfx;
4
5 #if defined(__WIN32__)
6    #define WIN32_LEAN_AND_MEAN
7    #define String _String
8    #include <windows.h>
9    #undef String
10 #elif !defined(ECERE_NOTRUETYPE) && !defined(ECERE_NOFONTCONFIG)
11    #define set _set
12    #include <fontconfig/fontconfig.h>
13    static FcConfig * fcConfig;
14    #undef set
15 #endif
16
17 #if defined(__WIN32__) && !defined(ECERE_NOTRUETYPE)
18 struct FontData
19 {
20    char fileName[MAX_FILENAME];
21    FontFlags flags;
22    bool forgive;
23 };
24
25 static int CALLBACK MyFontProc(ENUMLOGFONTEX * font, NEWTEXTMETRICEX *lpntme, int fontType, LPARAM lParam)
26 {
27    //if(fontType == TRUETYPE_FONTTYPE)
28    {
29       FontData * fontData = (FontData *) lParam;
30       char * fileName = (char *)lParam;
31       HKEY key;
32       int weight = (fontData->flags.bold) ? FW_BOLD : FW_NORMAL;
33       int italic = (fontData->flags.italic) ? 1 : 0;
34       if((fontData->forgive || weight == font->elfLogFont.lfWeight) && italic == (font->elfLogFont.lfItalic != 0))
35       {
36          if(!RegOpenKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts",0,KEY_READ,&key) ||
37             !RegOpenKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts",0,KEY_READ,&key))
38          {
39             int value = 0;
40             while(true)
41             {
42                char entryName[1024];
43                char fontFileName[1024];
44                DWORD type;
45                DWORD size = 1024;
46                DWORD sizeFileName = 1024;
47                char * occurrence;
48                if(RegEnumValue(key, value++, entryName, &size, null, (PDWORD)&type, (LPBYTE)fontFileName, &sizeFileName) != ERROR_SUCCESS)
49                   break;
50                if((occurrence = SearchString(entryName, 0, (const char *)font->elfFullName, false, false)))
51                {
52                   int c;
53                   for(c = (int)(occurrence - entryName) - 1; c >= 0; c--)
54                   {
55                      char ch = entryName[c];
56                      if(ch == '&') { c = -1; break; }
57                      else if(ch != ' ') break;
58                   }
59                   if(c >= 0) continue;
60                   for(c = (int)(occurrence - entryName) + strlen((char *)font->elfFullName); ; c++)
61                   {
62                      char ch = entryName[c];
63                      if(ch == 0 || ch == '&' || ch == '(') { c = -1; break; }
64                      else if(ch != ' ') break;
65                   }
66
67                   if(atoi(entryName + c))
68                      c = -1;
69                   if(c >= 0) continue;
70
71                   strcpy(fileName, fontFileName);
72                   RegCloseKey(key);
73                   return 0;
74                }
75             }
76             RegCloseKey(key);
77             return 1;
78          }
79       }
80    }
81    return 1;
82 }
83 #endif
84
85 public class FaceInfo : struct
86 {
87    String fileName;
88    bool fakeItalic;
89    int fontID;
90
91    ~FaceInfo() { delete fileName; }
92 }
93
94 public Array<FaceInfo> ResolveFont(const String faceName, float size, FontFlags flags)
95 {
96    Array<FaceInfo> fileNames { };
97 #if !defined(ECERE_NOTRUETYPE)
98    char fileName[MAX_LOCATION];
99    bool fakeItalic = flags.italic;
100    int fontID = 0;
101 #if !defined(__WIN32__)
102    File linkCfg;
103 #endif
104    const char * ecereFonts = getenv("ECERE_FONTS");
105    if(!ecereFonts) ecereFonts = "<:ecere>";
106 #if !defined(__WIN32__)
107    {
108       char linkCfgPath[MAX_LOCATION];
109
110       strcpy(linkCfgPath, ecereFonts);
111       PathCat(linkCfgPath, "linking.cfg");
112       linkCfg = FileOpen(linkCfgPath, read);
113    }
114 #endif
115    strcpy(fileName, faceName);
116
117    if(!FileExists(fileName))
118    {
119       strcpy(fileName, ecereFonts);
120       PathCat(fileName, faceName);
121       if(flags.bold && flags.italic) strcat(fileName, "bi");
122       else if(flags.bold) strcat(fileName, "bd");
123       else if(flags.italic) strcat(fileName, "i");
124       strcat(fileName, ".ttf");
125       strlwr(fileName);
126       fakeItalic = false;
127
128       if(flags.italic && !FileExists(fileName))
129       {
130          strcpy(fileName, ecereFonts);
131          PathCat(fileName, faceName);
132          if(flags.bold) strcat(fileName, "bd");
133          strcat(fileName, ".ttf");
134          strlwr(fileName);
135          fakeItalic = true;
136       }
137
138       // Search in current working directory
139       if(!FileExists(fileName))
140       {
141          strcpy(fileName, faceName);
142          if(flags.bold && flags.italic) strcat(fileName, "bi");
143          else if(flags.bold) strcat(fileName, "bd");
144          else if(flags.italic) strcat(fileName, "i");
145          strcat(fileName, ".ttf");
146          strlwr(fileName);
147          fakeItalic = false;
148
149          if(flags.italic && !FileExists(fileName))
150          {
151             strcpy(fileName, faceName);
152             if(flags.bold) strcat(fileName, "bd");
153             strcat(fileName, ".ttf");
154             strlwr(fileName);
155             fakeItalic = true;
156          }
157       }
158
159 #if defined(__WIN32__)
160       if(!FileExists(fileName))
161       {
162          FontData fontData = { { 0 } };
163          LOGFONT logFont = { 0 };
164          HDC hdc = GetDC(0);
165
166          fakeItalic = false;
167
168          logFont.lfCharSet = DEFAULT_CHARSET;
169          strcpy(logFont.lfFaceName, faceName);
170          fontData.flags = flags;
171
172          EnumFontFamiliesEx(hdc, &logFont, (void *)MyFontProc, (LPARAM)&fontData, 0);
173          if(!fontData.fileName[0] && flags.bold)
174          {
175             fontData.forgive = true;
176             EnumFontFamiliesEx(hdc, &logFont, (void *)MyFontProc, (LPARAM)&fontData, 0);
177          }
178          if(!fontData.fileName[0])
179          {
180             // Fake italic
181             fontData.flags.italic = false;
182             EnumFontFamiliesEx(hdc, &logFont, (void *)MyFontProc, (LPARAM)&fontData, 0);
183             fakeItalic = true;
184          }
185
186          if(fontData.fileName[0])
187          {
188             GetWindowsDirectory(fileName, MAX_LOCATION);
189             PathCat(fileName, "fonts");
190             PathCat(fileName, fontData.fileName);
191          }
192          ReleaseDC(0, hdc);
193       }
194 #elif !defined(ECERE_NOFONTCONFIG)
195       {
196          char * fileName2;
197          FcResult result = 0;
198          FcPattern * pattern;
199          FcPattern * matched;
200          char * family;
201          unichar testChar = 0;
202          FcCharSet * charSet;
203          if(!fcConfig)
204             fcConfig = FcInitLoadConfigAndFonts();
205
206          charSet = FcCharSetCreate();
207
208          if(!strcmpi(faceName, "Mangal"))
209          {
210             testChar = 0x905;
211          }
212
213          if(testChar)
214             FcCharSetAddChar(charSet, testChar);
215
216          pattern = FcPatternBuild(null,
217                          //FC_SOURCE, FcTypeString, "freetype",
218                          FC_FAMILY, FcTypeString, faceName,
219                          //FC_SCALABLE, FcTypeBool, 1,
220                          FC_SIZE, FcTypeDouble, (double)size,
221                          FC_WEIGHT, FcTypeInteger, flags.bold ? FC_WEIGHT_BOLD : FC_WEIGHT_MEDIUM /*FC_WEIGHT_LIGHT*/,
222                          FC_SLANT, FcTypeInteger, flags.italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN,
223                          testChar ? FC_CHARSET : 0,FcTypeCharSet, charSet,
224                          null);
225          FcDefaultSubstitute(pattern);
226          FcConfigSubstitute(fcConfig, pattern, FcMatchPattern); //FcMatchFont);
227
228          matched = FcFontMatch (0, pattern, &result);
229          // printf("Locating %s\n", faceName);
230          if(matched)
231          {
232             FcPatternGetString(matched, FC_FAMILY, 0, (FcChar8 **)&family);
233             //printf("Fontconfig returned %s\n", family);
234          }
235          if(matched && (result == FcResultMatch /*|| result == FcResultNoId*/) /*&& !strcmpi(family, faceName)*/)
236          {
237             double fontSize;
238             FcPatternGetString (matched, FC_FILE, 0, (FcChar8 **)&fileName2);
239             FcPatternGetInteger(matched, FC_INDEX, 0, &fontID);
240             FcPatternGetDouble(matched, FC_SIZE, 0, &fontSize);
241             strcpy(fileName, fileName2);
242             // size = (float)fontSize;
243
244             //printf("Matched to %s, %f\n", fileName, size);
245          }
246          else
247          {
248             //printf("Could not find a match for %s, %f, %s %s (%d)\n", faceName, size, flags.bold ? "bold" : "", flags.italic ? "italic" : "", (int)result);
249          }
250          if(pattern) FcPatternDestroy(pattern);
251          if(matched) FcPatternDestroy(matched);
252          if(charSet) FcCharSetDestroy(charSet);
253       }
254 #endif
255    }
256
257    if(!FileExists(fileName))
258       ChangeExtension(fileName, "otf", fileName);
259    if(!FileExists(fileName))
260       ChangeExtension(fileName, "ttc", fileName);
261
262    //if(FileExists(fileName))
263    {
264       char links[1024] = "";
265       int linksPos = 0;
266 #if defined(__WIN32__)
267       HKEY key;
268       links[0] = 0;
269       if(!RegOpenKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontLink\\SystemLink",0,KEY_READ,&key) ||
270          !RegOpenKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\FontLink\\SystemLink",0,KEY_READ,&key))
271       {
272          // int value = 0;
273          DWORD type;
274          DWORD size = 1024;
275          RegQueryValueEx(key, faceName, null, &type, (LPBYTE)links, &size);
276          memset(links + size, 0, 1024 - size);
277          RegCloseKey(key);
278       }
279 #else
280       links[0] = 0;
281       if(linkCfg)
282       {
283          char line[512];
284          while(linkCfg.GetLine(line, sizeof(line)))
285          {
286             int len = strlen(faceName);
287             if(line[0] == '[' && !strncasecmp(line + 1, faceName, len) && line[len + 1] == ']')
288             {
289                while(linkCfg.GetLine(line, sizeof(line)))
290                {
291                   TrimLSpaces(line, line);
292                   if(!line[0] || line[0] == '[')
293                      break;
294                   len = strlen(line);
295                   memcpy(links + linksPos, line, len);
296                   linksPos += len;
297                   links[linksPos] = 0;
298                   linksPos++;
299                }
300             }
301          }
302          linksPos = 0;
303       }
304 #endif
305       while(true)
306       {
307          FaceInfo faceInfo
308          {
309             fileName = CopyString(fileName),
310             fakeItalic = fakeItalic,
311             fontID = fontID
312          };
313          fileNames.Add(faceInfo);
314          {
315             int c;
316             char ch;
317             char fontName[1024];
318             if(!links[linksPos]) break;
319             for(c = 0; (ch = links[linksPos + c]); c++)
320             {
321                fontName[c] = ch;
322                if(ch == ',') break;
323             }
324             fontName[c] = 0;
325             if(fontName[0] || ch == ',')
326             {
327 #if defined(__WIN32__)
328                GetWindowsDirectory(fileName, MAX_LOCATION);
329                PathCat(fileName, "fonts");
330                PathCat(fileName, fontName);
331 #elif !defined(ECERE_NOFONTCONFIG)
332                if(getenv("ECERE_FONTS"))
333                {
334                   strcpy(fileName, ecereFonts);
335                   PathCat(fileName, fontName);
336                }
337                else
338                {
339                   {
340                      char * fileName2;
341                      FcResult result = 0;
342                      FcPattern * pattern;
343                      FcPattern * matched;
344                      char * family;
345                       pattern = FcPatternBuild(null,
346                                      //FC_SOURCE, FcTypeString, "freetype",
347                                      //FC_SCALABLE, FcTypeBool, 1,
348                                      FC_FAMILY, FcTypeString, links + linksPos + c + 1,
349                                      FC_SIZE, FcTypeDouble, (double)size,
350                                      FC_WEIGHT, FcTypeInteger, flags.bold ? FC_WEIGHT_BOLD : FC_WEIGHT_MEDIUM /*FC_WEIGHT_LIGHT*/,
351                                      FC_SLANT, FcTypeInteger, flags.italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN,
352                                      null);
353                      FcDefaultSubstitute(pattern);
354                      FcConfigSubstitute(fcConfig, pattern, FcMatchPattern); //FcMatchFont);
355
356                      //printf("Locating %s\n", links + linksPos + c + 1);
357                      matched = FcFontMatch (0, pattern, &result);
358                      if(matched)
359                      {
360                         FcPatternGetString(matched, FC_FAMILY, 0, (FcChar8 **)&family);
361                         // printf("Fontconfig returned %s\n", family);
362                      }
363                      if(matched && (result == FcResultMatch /*|| result == FcResultNoId*/) &&
364                         FcPatternGetString(matched, FC_FAMILY, 0, (FcChar8 **)&family) == FcResultMatch /*&& !strcmpi(family, links + linksPos + c + 1)*/)
365                      {
366                         double fontSize;
367                         FcPatternGetString (matched, FC_FILE, 0, (FcChar8 **)&fileName2);
368                         FcPatternGetInteger(matched, FC_INDEX, 0, &fontID);
369                         FcPatternGetDouble(matched, FC_SIZE, 0, &fontSize);
370                         strcpy(fileName, fileName2);
371                         //size = (float)fontSize;
372                         // printf("Matched to %s, %f\n", fileName, size);
373                      }
374                      else
375                      {
376                         // printf("Could not find a match for %s, %f, %s %s (%d)\n", links + linksPos + c + 1, size, flags.bold ? "bold" : "", flags.italic ? "italic" : "", (int)result);
377                      }
378                      if(pattern) FcPatternDestroy(pattern);
379                      if(matched) FcPatternDestroy(matched);
380                   }
381                }
382 #endif
383
384             }
385             linksPos += c;
386             while(links[linksPos] && links[linksPos] != ',') linksPos++;
387             linksPos++;
388          }
389       }
390    }
391
392 #if !defined(__WIN32__)
393    delete linkCfg;
394 #endif
395
396 #endif
397    return fileNames;
398 }
399
400 public FaceInfo ResolveCharFont(const String faceName, float size, FontFlags flags, const String lang, unichar testChar)
401 {
402    FaceInfo info = null;
403 #if !defined(__WIN32__) && !defined(ECERE_NOFONTCONFIG)
404    int fontID = 0;
405    double fontSize = size;
406    FcResult result = 0;
407    FcPattern * pattern;
408    FcPattern * matched;
409    FcCharSet * charSet = FcCharSetCreate();
410    String family;
411    String fileName = null;
412
413    FcCharSetAddChar(charSet, testChar);
414    //printf("Loading with char %x\n", testChar);
415
416    pattern = FcPatternBuild(null,
417                    //FC_SOURCE, FcTypeString, "freetype",
418                    //FC_SCALABLE, FcTypeBool, 1,
419                    FC_FAMILY, FcTypeString, faceName,
420                    FC_SIZE, FcTypeDouble, (double)size,
421                    FC_WEIGHT, FcTypeInteger, flags.bold ? FC_WEIGHT_BOLD : FC_WEIGHT_MEDIUM,
422                    FC_SLANT, FcTypeInteger, flags.italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN,
423                    FC_CHARSET,FcTypeCharSet, charSet,
424                    lang ? FC_LANG : 0, FcTypeString, lang,
425                    null);
426    FcDefaultSubstitute(pattern);
427    FcConfigSubstitute(fcConfig, pattern, FcMatchPattern); //FcMatchFont);
428
429    //printf("Locating %s for script %d\n", faceName, curScript);
430    matched = FcFontMatch (0, pattern, &result);
431    if(matched)
432    {
433       FcPatternGetString(matched, FC_FAMILY, 0, (FcChar8 **)&family);
434       //printf("Fontconfig returned %s\n", family);
435    }
436    if(matched && (result == FcResultMatch) && FcPatternGetString(matched, FC_FAMILY, 0, (FcChar8 **)&family) == FcResultMatch)
437    {
438       FcPatternGetString (matched, FC_FILE, 0, (FcChar8 **)&fileName);
439       FcPatternGetInteger(matched, FC_INDEX, 0, &fontID);
440       FcPatternGetDouble(matched, FC_SIZE, 0, &fontSize);
441       // printf("\nMatched to %s, %f\n", fileName, fontSize);
442
443       info =
444       {
445          fileName = CopyString(fileName);
446          fontID = fontID
447       };
448    }
449    else
450    {
451       //printf("Could not find a match for %s, %f, %s %s (%d)\n", faceName, size, flags.bold ? "bold" : "", flags.italic ? "italic" : "", (int)result);
452    }
453    if(pattern) FcPatternDestroy(pattern);
454    if(matched) FcPatternDestroy(matched);
455    if(charSet) FcCharSetDestroy(charSet);
456 #elif defined(__WIN32__) && !defined(ECERE_NOFONTCONFIG)
457    {
458       // Fall back to Arial Unicode MS
459       char fileName[MAX_LOCATION];
460       GetWindowsDirectory(fileName, MAX_LOCATION);
461       PathCat(fileName, "fonts/arialuni.ttf");
462       info = { fileName = CopyString(fileName) };
463    }
464 #endif
465    return info;
466 }