ecere/gfx/fontManagement: Fixed non-fixed pitch listing
[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 = fontData->fileName;
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          if(FileExists(fileName))
308          {
309             FaceInfo faceInfo
310             {
311                fileName = CopyString(fileName),
312                fakeItalic = fakeItalic,
313                fontID = fontID
314             };
315             fileNames.Add(faceInfo);
316          }
317          {
318             int c;
319             char ch;
320             char fontName[1024];
321             if(!links[linksPos]) break;
322             for(c = 0; (ch = links[linksPos + c]); c++)
323             {
324                fontName[c] = ch;
325                if(ch == ',') break;
326             }
327             fontName[c] = 0;
328             if(fontName[0] || ch == ',')
329             {
330 #if defined(__WIN32__)
331                GetWindowsDirectory(fileName, MAX_LOCATION);
332                PathCat(fileName, "fonts");
333                PathCat(fileName, fontName);
334 #elif !defined(ECERE_NOFONTCONFIG)
335                if(getenv("ECERE_FONTS"))
336                {
337                   strcpy(fileName, ecereFonts);
338                   PathCat(fileName, fontName);
339                }
340                else
341                {
342                   {
343                      char * fileName2;
344                      FcResult result = 0;
345                      FcPattern * pattern;
346                      FcPattern * matched;
347                      char * family;
348                       pattern = FcPatternBuild(null,
349                                      //FC_SOURCE, FcTypeString, "freetype",
350                                      //FC_SCALABLE, FcTypeBool, 1,
351                                      FC_FAMILY, FcTypeString, links + linksPos + c + 1,
352                                      FC_SIZE, FcTypeDouble, (double)size,
353                                      FC_WEIGHT, FcTypeInteger, flags.bold ? FC_WEIGHT_BOLD : FC_WEIGHT_MEDIUM /*FC_WEIGHT_LIGHT*/,
354                                      FC_SLANT, FcTypeInteger, flags.italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN,
355                                      null);
356                      FcDefaultSubstitute(pattern);
357                      FcConfigSubstitute(fcConfig, pattern, FcMatchPattern); //FcMatchFont);
358
359                      //printf("Locating %s\n", links + linksPos + c + 1);
360                      matched = FcFontMatch (0, pattern, &result);
361                      if(matched)
362                      {
363                         FcPatternGetString(matched, FC_FAMILY, 0, (FcChar8 **)&family);
364                         // printf("Fontconfig returned %s\n", family);
365                      }
366                      if(matched && (result == FcResultMatch /*|| result == FcResultNoId*/) &&
367                         FcPatternGetString(matched, FC_FAMILY, 0, (FcChar8 **)&family) == FcResultMatch /*&& !strcmpi(family, links + linksPos + c + 1)*/)
368                      {
369                         double fontSize;
370                         FcPatternGetString (matched, FC_FILE, 0, (FcChar8 **)&fileName2);
371                         FcPatternGetInteger(matched, FC_INDEX, 0, &fontID);
372                         FcPatternGetDouble(matched, FC_SIZE, 0, &fontSize);
373                         strcpy(fileName, fileName2);
374                         //size = (float)fontSize;
375                         // printf("Matched to %s, %f\n", fileName, size);
376                      }
377                      else
378                      {
379                         // 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);
380                      }
381                      if(pattern) FcPatternDestroy(pattern);
382                      if(matched) FcPatternDestroy(matched);
383                   }
384                }
385 #endif
386
387             }
388             linksPos += c;
389             while(links[linksPos] && links[linksPos] != ',') linksPos++;
390             linksPos++;
391          }
392       }
393    }
394
395 #if !defined(__WIN32__)
396    delete linkCfg;
397 #endif
398
399 #endif
400    if(!fileNames.count)
401       delete fileNames;
402    return fileNames;
403 }
404
405 import "AVLTree"
406
407 public struct FontInfo
408 {
409    bool fixedPitch;
410    bool defaultOrAnsiCharSet;
411 };
412
413 #if defined(__WIN32__)
414 static int CALLBACK fontLister(ENUMLOGFONTEXW * font, NEWTEXTMETRICEX *lpntme, int fontType, LPARAM lParam)
415 {
416    // const String faceName = font->elfLogFont.lfFaceName;
417    uint16 * faceName = font->elfLogFont.lfFaceName;
418    String s = UTF16toUTF8(faceName);
419    if(s[0] != '@')
420    {
421       Map<String, FontInfo> fonts = (Map<String, FontInfo>)lParam;
422       MapIterator<String, FontInfo> it { map = fonts };
423       if(!it.Index(s, true))
424          it.data = { (font->elfLogFont.lfPitchAndFamily & 3) == FIXED_PITCH, defaultOrAnsiCharSet = (font->elfLogFont.lfCharSet == ANSI_CHARSET || font->elfLogFont.lfCharSet == DEFAULT_CHARSET) };
425    }
426    delete s;
427    return 1;
428 }
429 #endif
430
431 public Map<String, FontInfo> ListAvailableFonts()
432 {
433    Map<String, FontInfo> fonts { };
434
435 #if defined(__WIN32__)
436    LOGFONTW logFont = { 0 };
437    HDC hdc = GetDC(0);
438    logFont.lfCharSet = DEFAULT_CHARSET;
439
440    EnumFontFamiliesExW(hdc, &logFont, (void *)fontLister, (LPARAM)fonts, 0);
441
442    ReleaseDC(0, hdc);
443 #elif !defined(ECERE_NOFONTCONFIG)
444    int i;
445    FcPattern * pattern;
446    FcObjectSet * objectSet;
447    FcFontSet * fontSet;
448
449    if(!fcConfig)
450       fcConfig = FcInitLoadConfigAndFonts();
451
452    pattern = FcPatternCreate();
453    objectSet = FcObjectSetBuild(FC_FAMILY, FC_SPACING, FC_CHARSET, null);
454    fontSet = FcFontList(fcConfig, pattern, objectSet);
455    if(fontSet)
456    {
457       MapIterator<String, FontInfo> it { map = fonts };
458       for(i = 0; i < fontSet->nfont; i++)
459       {
460          FcPattern * font = fontSet->fonts[i];
461          String family = null;
462          int spacing = 0;
463          FcCharSet * charSet = null;
464
465          FcPatternGetString(font, FC_FAMILY, 0, (byte **)&family);
466          FcPatternGetInteger(font, FC_SPACING, 0, &spacing);
467          FcPatternGetCharSet(font, FC_CHARSET, 0, &charSet);
468
469          if(family && !it.Index(family, true))
470          {
471             it.data =
472             {
473                fixedPitch = spacing == FC_MONO,
474                defaultOrAnsiCharSet =
475                   FcCharSetHasChar(charSet, '[') && FcCharSetHasChar(charSet, '{') &&
476                   FcCharSetHasChar(charSet, 'a') && FcCharSetHasChar(charSet, 'Z');
477             };
478          }
479       }
480    }
481    if(pattern) FcPatternDestroy(pattern);
482    if(fontSet) FcFontSetDestroy(fontSet);
483 #endif
484    return fonts;
485 }
486
487 public FaceInfo ResolveCharFont(const String faceName, float size, FontFlags flags, const String lang, unichar testChar)
488 {
489    FaceInfo info = null;
490 #if !defined(__WIN32__) && !defined(ECERE_NOFONTCONFIG)
491    int fontID = 0;
492    double fontSize = size;
493    FcResult result = 0;
494    FcPattern * pattern;
495    FcPattern * matched;
496    FcCharSet * charSet = FcCharSetCreate();
497    String family;
498    String fileName = null;
499
500    FcCharSetAddChar(charSet, testChar);
501    //printf("Loading with char %x\n", testChar);
502
503    pattern = FcPatternBuild(null,
504                    //FC_SOURCE, FcTypeString, "freetype",
505                    //FC_SCALABLE, FcTypeBool, 1,
506                    FC_FAMILY, FcTypeString, faceName,
507                    FC_SIZE, FcTypeDouble, (double)size,
508                    FC_WEIGHT, FcTypeInteger, flags.bold ? FC_WEIGHT_BOLD : FC_WEIGHT_MEDIUM,
509                    FC_SLANT, FcTypeInteger, flags.italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN,
510                    FC_CHARSET,FcTypeCharSet, charSet,
511                    lang ? FC_LANG : 0, FcTypeString, lang,
512                    null);
513    FcDefaultSubstitute(pattern);
514    FcConfigSubstitute(fcConfig, pattern, FcMatchPattern); //FcMatchFont);
515
516    //printf("Locating %s for script %d\n", faceName, curScript);
517    matched = FcFontMatch (0, pattern, &result);
518    if(matched)
519    {
520       FcPatternGetString(matched, FC_FAMILY, 0, (FcChar8 **)&family);
521       //printf("Fontconfig returned %s\n", family);
522    }
523    if(matched && (result == FcResultMatch) && FcPatternGetString(matched, FC_FAMILY, 0, (FcChar8 **)&family) == FcResultMatch)
524    {
525       FcPatternGetString (matched, FC_FILE, 0, (FcChar8 **)&fileName);
526       FcPatternGetInteger(matched, FC_INDEX, 0, &fontID);
527       FcPatternGetDouble(matched, FC_SIZE, 0, &fontSize);
528       // printf("\nMatched to %s, %f\n", fileName, fontSize);
529
530       info =
531       {
532          fileName = CopyString(fileName);
533          fontID = fontID
534       };
535    }
536    else
537    {
538       //printf("Could not find a match for %s, %f, %s %s (%d)\n", faceName, size, flags.bold ? "bold" : "", flags.italic ? "italic" : "", (int)result);
539    }
540    if(pattern) FcPatternDestroy(pattern);
541    if(matched) FcPatternDestroy(matched);
542    if(charSet) FcCharSetDestroy(charSet);
543 #elif defined(__WIN32__) && !defined(ECERE_NOFONTCONFIG)
544    {
545       // Fall back to Arial Unicode MS
546       char fileName[MAX_LOCATION];
547       GetWindowsDirectory(fileName, MAX_LOCATION);
548       PathCat(fileName, "fonts/arialuni.ttf");
549       info = { fileName = CopyString(fileName) };
550    }
551 #endif
552    return info;
553 }