1 // Written in the D programming language.
2 
3 /**
4 
5 This module contains implementation of Win32 fonts support
6 
7 Part of Win32 platform support.
8 
9 Usually you don't need to use this module directly.
10 
11 
12 Synopsis:
13 
14 ----
15 import dlangui.platforms.windows.win32fonts;
16 ----
17 
18 Copyright: Vadim Lopatin, 2014
19 License:   Boost License 1.0
20 Authors:   Vadim Lopatin, coolreader.org@gmail.com
21 */
22 module dlangui.platforms.windows.win32fonts;
23 
24 version(Windows):
25 public import dlangui.core.config;
26 static if (BACKEND_GUI):
27 
28 import core.sys.windows.windows;
29 import dlangui.graphics.fonts;
30 import dlangui.platforms.windows.win32drawbuf;
31 import std.string;
32 import std.utf;
33 
34 /// define debug=FontResources for logging of font file resources creation/freeing
35 //debug = FontResources;
36 
37 //auto toUTF16z(S)(S s)
38 //{
39     //return toUTFz!(const(wchar)*)(s);
40 //}
41 
42 private struct FontDef {
43     immutable FontFamily _family;
44     immutable string _face;
45     immutable ubyte _pitchAndFamily;
46     @property FontFamily family() { return _family; }
47     @property string face() { return _face; }
48     @property ubyte pitchAndFamily() { return _pitchAndFamily; }
49     this(FontFamily family, string face, ubyte putchAndFamily) {
50         _family = family;
51         _face = face;
52         _pitchAndFamily = pitchAndFamily;
53     }
54 }
55 
56 // support of subpixel rendering
57 // from AntigrainGeometry https://rsdn.ru/forum/src/830679.1
58 import std.math;
59 // Sub-pixel energy distribution lookup table.
60 // See description by Steve Gibson: http://grc.com/cttech.htm
61 // The class automatically normalizes the coefficients
62 // in such a way that primary + 2*secondary + 3*tertiary = 1.0
63 // Also, the input values are in range of 0...NumLevels, output ones
64 // are 0...255
65 //---------------------------------
66 struct lcd_distribution_lut(int maxv = 65)
67 {
68     this(double prim, double second, double tert)
69     {
70         double norm = (255.0 / (maxv - 1)) / (prim + second*2 + tert*2);
71         prim   *= norm;
72         second *= norm;
73         tert   *= norm;
74         for(int i = 0; i < maxv; i++)
75         {
76             m_primary[i]   = cast(ubyte)floor(prim   * i);
77             m_secondary[i] = cast(ubyte)floor(second * i);
78             m_tertiary[i]  = cast(ubyte)floor(tert   * i);
79         }
80     }
81 
82     uint primary(int v)   const {
83         if (v >= maxv) {
84             Log.e("pixel value returned from font engine > 64: ", v);
85             v = maxv - 1;
86         }
87         return m_primary[v];   
88     }
89     uint secondary(int v) const { 
90         if (v >= maxv) {
91             Log.e("pixel value returned from font engine > 64: ", v);
92             v = maxv - 1;
93         }
94         return m_secondary[v]; 
95     }
96     uint tertiary(int v)  const { 
97         if (v >= maxv) {
98             Log.e("pixel value returned from font engine > 64: ", v);
99             v = maxv - 1;
100         }
101         return m_tertiary[v];  
102     }
103 
104 private:
105     ubyte[maxv] m_primary;
106     ubyte[maxv] m_secondary;
107     ubyte[maxv] m_tertiary;
108 };
109 
110 private __gshared lcd_distribution_lut!65 lut;
111 void initWin32FontsTables() {
112     lut = lcd_distribution_lut!65(0.5, 0.25, 0.125);
113 }
114 
115 
116 
117 private int myabs(int n) {
118     return n < 0 ? -n : n;
119 }
120 private int colorStat(ubyte * p) {
121     int avg = (cast(int)p[0] + cast(int)p[1] + cast(int)p[2]) / 3;
122     return myabs(avg - cast(int)p[0]) + myabs(avg - cast(int)p[1]) + myabs(avg - cast(int)p[2]);
123 }
124 
125 private int minIndex(int n0, int n1, int n2) {
126     if (n0 <= n1 && n0 <= n2)
127         return 0;
128     if (n1 <= n0 && n1 <= n2)
129         return 1;
130     return n2;
131 }
132 
133 // This function prepares the alpha-channel information 
134 // for the glyph averaging the values in accordance with 
135 // the method suggested by Steve Gibson. The function
136 // extends the width by 4 extra pixels, 2 at the beginning 
137 // and 2 at the end. Also, it doesn't align the new width 
138 // to 4 bytes, that is, the output gm.gmBlackBoxX is the 
139 // actual width of the array.
140 // returns dst glyph width
141 //---------------------------------
142 ushort prepare_lcd_glyph(ubyte * gbuf1, 
143                        ref GLYPHMETRICS gm, 
144                        ref ubyte[] gbuf2,
145                        ref int shiftedBy)
146 {
147     shiftedBy = 0;
148     uint src_stride = (gm.gmBlackBoxX + 3) / 4 * 4;
149     uint dst_width  = src_stride + 4;
150     gbuf2 = new ubyte[dst_width * gm.gmBlackBoxY];
151 
152     for(uint y = 0; y < gm.gmBlackBoxY; ++y)
153     {
154         ubyte * src_ptr = gbuf1 + src_stride * y;
155         ubyte * dst_ptr = gbuf2.ptr + dst_width * y;
156         for(uint x = 0; x < gm.gmBlackBoxX; ++x)
157         {
158             uint v = *src_ptr++;
159             dst_ptr[0] += lut.tertiary(v);
160             dst_ptr[1] += lut.secondary(v);
161             dst_ptr[2] += lut.primary(v);
162             dst_ptr[3] += lut.secondary(v);
163             dst_ptr[4] += lut.tertiary(v);
164             ++dst_ptr;
165         }
166     }
167     /*
168     int dx = (dst_width - 2) / 3;
169     int stats[3] = [0, 0, 0];
170     for (uint y = 0; y < gm.gmBlackBoxY; y++) {
171         for(uint x = 0; x < dx; ++x)
172         {
173             for (uint x0 = 0; x0 < 3; x0++) {
174                 stats[x0] += colorStat(gbuf2.ptr + dst_width * y + x0);
175             }
176         }
177     }
178     shiftedBy = 0; //minIndex(stats[0], stats[1], stats[2]);
179     if (shiftedBy) {
180         for (uint y = 0; y < gm.gmBlackBoxY; y++) {
181             ubyte * dst_ptr = gbuf2.ptr + dst_width * y;
182             for(uint x = 0; x < gm.gmBlackBoxX; ++x)
183             {
184                 if (x + shiftedBy < gm.gmBlackBoxX)
185                     dst_ptr[x] = dst_ptr[x + shiftedBy];
186                 else
187                     dst_ptr[x] = 0;
188             }
189         }
190     }
191     */
192     return cast(ushort) dst_width;
193 }
194 
195 /**
196 * Font implementation based on Win32 API system fonts.
197 */
198 class Win32Font : Font {
199     protected HFONT _hfont;
200     protected int _dpi;
201     protected int _size;
202     protected int _height;
203     protected int _weight;
204     protected int _baseline;
205     protected bool _italic;
206     protected string _face;
207     protected FontFamily _family;
208     protected LOGFONTA _logfont;
209     protected Win32ColorDrawBuf _drawbuf;
210     protected GlyphCache _glyphCache;
211 
212     /// need to call create() after construction to initialize font
213     this() {
214     }
215 
216     /// do cleanup
217     ~this() {
218         clear();
219     }
220 
221     /// cleanup resources
222     override void clear() {
223         if (_hfont !is null)
224         {
225             DeleteObject(_hfont);
226             _hfont = NULL;
227             _height = 0;
228             _baseline = 0;
229             _size = 0;
230         }
231         if (_drawbuf !is null) {
232             destroy(_drawbuf);
233             _drawbuf = null;
234         }
235     }
236 
237     uint getGlyphIndex(dchar code)
238     {
239         if (_drawbuf is null)
240             return 0;
241         wchar[2] s;
242         wchar[2] g;
243         s[0] = cast(wchar)code;
244         s[1] = 0;
245         g[0] = 0;
246         GCP_RESULTSW gcp;
247         gcp.lStructSize = GCP_RESULTSW.sizeof;
248         gcp.lpOutString = null;
249         gcp.lpOrder = null;
250         gcp.lpDx = null;
251         gcp.lpCaretPos = null;
252         gcp.lpClass = null;
253         gcp.lpGlyphs = g.ptr;
254         gcp.nGlyphs = 2;
255         gcp.nMaxFit = 2;
256 
257         DWORD res = GetCharacterPlacementW(
258                                            _drawbuf.dc, s.ptr, 1,
259                                            1000,
260                                            &gcp,
261                                            0
262                                            );
263         if (!res)
264             return 0;
265         return g[0];
266     }
267 
268     override Glyph * getCharGlyph(dchar ch, bool withImage = true) {
269         Glyph * found = _glyphCache.find(ch);
270         if (found !is null)
271             return found;
272         uint glyphIndex = getGlyphIndex(ch);
273         if (!glyphIndex)
274             return null;
275         if (glyphIndex >= 0xFFFF)
276             return null;
277         GLYPHMETRICS metrics;
278 
279         bool needSubpixelRendering = FontManager.subpixelRenderingMode && antialiased;
280         MAT2 scaleMatrix = { {0,1}, {0,0}, {0,0}, {0,1} };
281 
282         uint res;
283         res = GetGlyphOutlineW( _drawbuf.dc, cast(wchar)ch,
284                                 GGO_METRICS,
285                                &metrics,
286                                0,
287                                null,
288                                &scaleMatrix );
289         if (res == GDI_ERROR)
290             return null;
291 
292         Glyph * g = new Glyph;
293         static if (ENABLE_OPENGL) {
294             g.id = nextGlyphId();
295         }
296         //g.blackBoxX = cast(ushort)metrics.gmBlackBoxX;
297         //g.blackBoxY = cast(ubyte)metrics.gmBlackBoxY;
298         //g.originX = cast(byte)metrics.gmptGlyphOrigin.x;
299         //g.originY = cast(byte)metrics.gmptGlyphOrigin.y;
300         //g.width = cast(ubyte)metrics.gmCellIncX;
301         g.subpixelMode = SubpixelRenderingMode.None;
302 
303         if (needSubpixelRendering) {
304             scaleMatrix.eM11.value = 3; // request glyph 3 times wider for subpixel antialiasing
305         }
306 
307         int gs = 0;
308         // calculate bitmap size
309         if (antialiased) {
310             gs = GetGlyphOutlineW( _drawbuf.dc, cast(wchar)ch,
311                                        GGO_GRAY8_BITMAP,
312                                       &metrics,
313                                       0,
314                                       NULL,
315                                       &scaleMatrix );
316         } else {
317             gs = GetGlyphOutlineW( _drawbuf.dc, cast(wchar)ch,
318                                    GGO_BITMAP,
319                                   &metrics,
320                                   0,
321                                   NULL,
322                                   &scaleMatrix );
323         }
324 
325         if (gs >= 0x10000 || gs < 0)
326             return null;
327 
328         if (needSubpixelRendering) {
329             //Log.d("ch=", ch);
330             //Log.d("NORMAL:  blackBoxX=", g.blackBoxX, " \tblackBoxY=", g.blackBoxY, " \torigin.x=", g.originX, " \torigin.y=", g.originY, "\tgmCellIncX=", g.width);
331             g.blackBoxX = cast(ushort)metrics.gmBlackBoxX;
332             g.blackBoxY = cast(ubyte)metrics.gmBlackBoxY;
333             g.originX = cast(byte)((metrics.gmptGlyphOrigin.x + 0) / 3);
334             g.originY = cast(byte)metrics.gmptGlyphOrigin.y;
335             g.width = cast(ubyte)((metrics.gmCellIncX  + 2) / 3);
336             g.subpixelMode = FontManager.subpixelRenderingMode;
337             //Log.d(" *3   :  blackBoxX=", metrics.gmBlackBoxX, " \tblackBoxY=", metrics.gmBlackBoxY, " \torigin.x=", metrics.gmptGlyphOrigin.x, " \torigin.y=", metrics.gmptGlyphOrigin.y, " \tgmCellIncX=", metrics.gmCellIncX);
338             //Log.d("  /3  :  blackBoxX=", g.blackBoxX, " \tblackBoxY=", g.blackBoxY, " \torigin.x=", g.originX, " \torigin.y=", g.originY, "\tgmCellIncX=", g.width);
339         } else {
340             g.blackBoxX = cast(ushort)metrics.gmBlackBoxX;
341             g.blackBoxY = cast(ubyte)metrics.gmBlackBoxY;
342             g.originX = cast(byte)metrics.gmptGlyphOrigin.x;
343             g.originY = cast(byte)metrics.gmptGlyphOrigin.y;
344             g.width = cast(ubyte)metrics.gmCellIncX;
345         }
346 
347         if (g.blackBoxX > 0 && g.blackBoxY > 0)    {
348             g.glyph = new ubyte[g.blackBoxX * g.blackBoxY];
349             if (gs>0)
350             {
351                 if (antialiased) {
352                     // antialiased glyph
353                     ubyte[] glyph = new ubyte[gs];
354                     res = GetGlyphOutlineW( _drawbuf.dc, cast(wchar)ch,
355                                             GGO_GRAY8_BITMAP, //GGO_METRICS
356                                            &metrics,
357                                            gs,
358                                            glyph.ptr,
359                                            &scaleMatrix);
360                     if (res==GDI_ERROR)
361                     {
362                         return null;
363                     }
364                     if (needSubpixelRendering) {
365                         ubyte[] newglyph;
366                         int shiftedBy = 0;
367                         g.blackBoxX = prepare_lcd_glyph(glyph.ptr,
368                                                  metrics, 
369                                                  newglyph,
370                                                  shiftedBy);
371                         g.glyph = newglyph;
372                         //g.originX = cast(ubyte)((metrics.gmptGlyphOrigin.x + 2 - shiftedBy) / 3);
373                         //g.width = cast(ubyte)((metrics.gmCellIncX  + 2 - shiftedBy) / 3);
374                     } else {
375                         int glyph_row_size = (g.blackBoxX + 3) / 4 * 4;
376                         ubyte * src = glyph.ptr;
377                         ubyte * dst = g.glyph.ptr;
378                         for (int y = 0; y < g.blackBoxY; y++)
379                         {
380                             for (int x = 0; x < g.blackBoxX; x++)
381                             {
382                                 dst[x] = _gamma65.correct(src[x]);
383                             }
384                             src += glyph_row_size;
385                             dst += g.blackBoxX;
386                         }
387                     }
388                 } else {
389                     // bitmap glyph
390                     ubyte[] glyph = new ubyte[gs];
391                     res = GetGlyphOutlineW( _drawbuf.dc, cast(wchar)ch,
392                                             GGO_BITMAP, //GGO_METRICS
393                                            &metrics,
394                                            gs,
395                                            glyph.ptr,
396                                            &scaleMatrix );
397                     if (res==GDI_ERROR)
398                     {
399                         return null;
400                     }
401                     int glyph_row_bytes = ((g.blackBoxX + 7) / 8);
402                     int glyph_row_size = (glyph_row_bytes + 3) / 4 * 4;
403                     ubyte * src = glyph.ptr;
404                     ubyte * dst = g.glyph.ptr;
405                     for (int y = 0; y < g.blackBoxY; y++)
406                     {
407                         for (int x = 0; x < g.blackBoxX; x++)
408                         {
409                             int offset = x >> 3;
410                             int shift = 7 - (x & 7);
411                             ubyte b = ((src[offset] >> shift) & 1) ? 255 : 0;
412                             dst[x] = b;
413                         }
414                         src += glyph_row_size;
415                         dst += g.blackBoxX;
416                     }
417                 }
418             }
419             else
420             {
421                 // empty glyph
422                 for (int i = g.blackBoxX * g.blackBoxY - 1; i >= 0; i--)
423                     g.glyph[i] = 0;
424             }
425         }
426         // found!
427         return _glyphCache.put(ch, g);
428     }
429 
430     /// init from font definition
431     bool create(FontDef * def, int size, int weight, bool italic) {
432         if (!isNull())
433             clear();
434         LOGFONTA lf;
435         lf.lfCharSet = ANSI_CHARSET; //DEFAULT_CHARSET;
436         lf.lfFaceName[0..def.face.length] = def.face;
437         lf.lfFaceName[def.face.length] = 0;
438         lf.lfHeight = -size; //pixelsToPoints(size);
439         lf.lfItalic = italic;
440         lf.lfWeight = weight;
441         lf.lfOutPrecision = OUT_TT_ONLY_PRECIS; //OUT_OUTLINE_PRECIS; //OUT_TT_ONLY_PRECIS;
442         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
443         //lf.lfQuality = NONANTIALIASED_QUALITY; //ANTIALIASED_QUALITY;
444         //lf.lfQuality = PROOF_QUALITY; //ANTIALIASED_QUALITY;
445         lf.lfQuality = antialiased ? NONANTIALIASED_QUALITY : ANTIALIASED_QUALITY; //PROOF_QUALITY; //ANTIALIASED_QUALITY; //size < 18 ? NONANTIALIASED_QUALITY : PROOF_QUALITY; //ANTIALIASED_QUALITY;
446         lf.lfPitchAndFamily = def.pitchAndFamily;
447         _hfont = CreateFontIndirectA(&lf);
448         _drawbuf = new Win32ColorDrawBuf(1, 1);
449         SelectObject(_drawbuf.dc, _hfont);
450 
451         TEXTMETRICW tm;
452         GetTextMetricsW(_drawbuf.dc, &tm);
453 
454         _size = size;
455         _height = tm.tmHeight;
456         debug(FontResources) Log.d("Win32Font.create: height=", _height, " for size=", _size, "  points=", lf.lfHeight, " dpi=", _dpi);
457         _baseline = _height - tm.tmDescent;
458         _weight = weight;
459         _italic = italic;
460         _face = def.face;
461         _family = def.family;
462         debug(FontResources) Log.d("Created font ", _face, " ", _size);
463         return true;
464     }
465 
466     /// clear usage flags for all entries
467     override void checkpoint() {
468         _glyphCache.checkpoint();
469     }
470 
471     /// removes entries not used after last call of checkpoint() or cleanup()
472     override void cleanup() {
473         _glyphCache.cleanup();
474     }
475 
476     /// clears glyph cache
477     override void clearGlyphCache() {
478         _glyphCache.clear();
479     }
480 
481     @property override int size() { return _size; }
482     @property override int height() { return _height; }
483     @property override int weight() { return _weight; }
484     @property override int baseline() { return _baseline; }
485     @property override bool italic() { return _italic; }
486     @property override string face() { return _face; }
487     @property override FontFamily family() { return _family; }
488     @property override bool isNull() { return _hfont is null; }
489 }
490 
491 
492 /**
493 * Font manager implementation based on Win32 API system fonts.
494 */
495 class Win32FontManager : FontManager {
496     private FontList _activeFonts;
497     private FontDef[] _fontFaces;
498     private FontDef*[string] _faceByName;
499 
500     /// override to return list of font faces available
501     override FontFaceProps[] getFaces() {
502         FontFaceProps[] res;
503         for (int i = 0; i < _fontFaces.length; i++) {
504             FontFaceProps item = FontFaceProps(_fontFaces[i].face, _fontFaces[i].family);
505             bool found = false;
506             for (int j = 0; j < res.length; j++) {
507                 if (res[j].face == item.face) {
508                     found = true;
509                     break;
510                 }
511             }
512             if (!found)
513                 res ~= item;
514         }
515         return res;
516     }
517 
518 
519     /// initialize in constructor
520     this() {
521         debug Log.i("Creating Win32FontManager");
522         //instance = this;
523         initialize();
524     }
525     ~this() {
526         debug Log.i("Destroying Win32FontManager");
527     }
528 
529     /// initialize font manager by enumerating of system fonts
530     bool initialize() {
531         debug Log.i("Win32FontManager.initialize()");
532         Win32ColorDrawBuf drawbuf = new Win32ColorDrawBuf(1,1);
533         LOGFONTA lf;
534         lf.lfCharSet = ANSI_CHARSET; //DEFAULT_CHARSET;
535         lf.lfFaceName[0] = 0;
536         HDC dc = drawbuf.dc;
537         int res = 
538             EnumFontFamiliesExA(
539                                 dc,                  // handle to DC
540                                 &lf,                              // font information
541                                 cast(FONTENUMPROCA)&LVWin32FontEnumFontFamExProc, // callback function (FONTENUMPROC)
542                                 cast(LPARAM)(cast(void*)this),                    // additional data
543                                 0U                     // not used; must be 0
544                                     );
545         destroy(drawbuf);
546         Log.i("Found ", _fontFaces.length, " font faces");
547         return res!=0;
548     }
549 
550     /// for returning of not found font
551     FontRef _emptyFontRef;
552 
553     /// get font by properties
554     override ref FontRef getFont(int size, int weight, bool italic, FontFamily family, string face) {
555         //Log.i("getFont()");
556         FontDef * def = findFace(family, face);
557         //Log.i("getFont() found face ", def.face, " by requested face ", face);
558         if (def !is null) {
559             int index = _activeFonts.find(size, weight, italic, def.family, def.face);
560             if (index >= 0)
561                 return _activeFonts.get(index);
562             debug(FontResources) Log.d("Creating new font");
563             Win32Font item = new Win32Font();
564             if (!item.create(def, size, weight, italic))
565                 return _emptyFontRef;
566             debug(FontResources) Log.d("Adding to list of active fonts");
567             return _activeFonts.add(item);
568         } else {
569             return _emptyFontRef;
570         }
571     }
572 
573     /// find font face definition by family only (try to get one of defaults for family if possible)
574     FontDef * findFace(FontFamily family) {
575         FontDef * res = null;
576         switch(family) {
577             case FontFamily.SansSerif:
578                 res = findFace("Arial"); if (res !is null) return res;
579                 res = findFace("Tahoma"); if (res !is null) return res;
580                 res = findFace("Calibri"); if (res !is null) return res;
581                 res = findFace("Verdana"); if (res !is null) return res;
582                 res = findFace("Lucida Sans"); if (res !is null) return res;
583                 break;
584             case FontFamily.Serif:
585                 res = findFace("Times New Roman"); if (res !is null) return res;
586                 res = findFace("Georgia"); if (res !is null) return res;
587                 res = findFace("Century Schoolbook"); if (res !is null) return res;
588                 res = findFace("Bookman Old Style"); if (res !is null) return res;
589                 break;
590             case FontFamily.MonoSpace:
591                 res = findFace("Courier New"); if (res !is null) return res;
592                 res = findFace("Lucida Console"); if (res !is null) return res;
593                 res = findFace("Century Schoolbook"); if (res !is null) return res;
594                 res = findFace("Bookman Old Style"); if (res !is null) return res;
595                 break;
596             case FontFamily.Cursive:
597                 res = findFace("Comic Sans MS"); if (res !is null) return res;
598                 res = findFace("Lucida Handwriting"); if (res !is null) return res;
599                 res = findFace("Monotype Corsiva"); if (res !is null) return res;
600                 break;
601             default:
602                 break;
603         }
604         return null;
605     }
606 
607     /// find font face definition by face only
608     FontDef * findFace(string face) {
609         if (face.length == 0)
610             return null;
611         string[] faces = split(face, ",");
612         foreach(f; faces) {
613             if (f in _faceByName)
614                 return _faceByName[f];
615         }
616         return null;
617     }
618 
619     /// find font face definition by family and face
620     FontDef * findFace(FontFamily family, string face) {
621         // by face only
622         FontDef * res = findFace(face);
623         if (res !is null)
624             return res;
625         // best for family
626         res = findFace(family);
627         if (res !is null)
628             return res;
629         for (int i = 0; i < _fontFaces.length; i++) {
630             res = &_fontFaces[i];
631             if (res.family == family)
632                 return res;
633         }
634         res = findFace(FontFamily.SansSerif);
635         if (res !is null)
636             return res;
637         return &_fontFaces[0];
638     }
639 
640     /// register enumerated font
641     bool registerFont(FontFamily family, string fontFace, ubyte pitchAndFamily) {
642         Log.d("registerFont(", family, ",", fontFace, ")");
643         _fontFaces ~= FontDef(family, fontFace, pitchAndFamily);
644         _faceByName[fontFace] = &_fontFaces[$ - 1];
645         return true;
646     }
647 
648     /// clear usage flags for all entries
649     override void checkpoint() {
650         _activeFonts.checkpoint();
651     }
652 
653     /// removes entries not used after last call of checkpoint() or cleanup()
654     override void cleanup() {
655         _activeFonts.cleanup();
656         //_list.cleanup();
657     }
658 
659     /// clears glyph cache
660     override void clearGlyphCaches() {
661         _activeFonts.clearGlyphCache();
662     }
663 }
664 
665 FontFamily pitchAndFamilyToFontFamily(ubyte flags) {
666     if ((flags & FF_DECORATIVE) == FF_DECORATIVE)
667         return FontFamily.Fantasy;
668     else if ((flags & (FIXED_PITCH)) != 0) // | | MONO_FONT
669         return FontFamily.MonoSpace;
670     else if ((flags & (FF_ROMAN)) != 0)
671         return FontFamily.Serif;
672     else if ((flags & (FF_SCRIPT)) != 0)
673         return FontFamily.Cursive;
674     return FontFamily.SansSerif;
675 }
676 
677 // definition
678 extern(Windows) {
679     int LVWin32FontEnumFontFamExProc(
680                                      const (LOGFONTA) *lf,    // logical-font data
681                                      const (TEXTMETRICA) *lpntme,  // physical-font data
682                                      //ENUMLOGFONTEX *lpelfe,    // logical-font data
683                                      //NEWTEXTMETRICEX *lpntme,  // physical-font data
684                                      DWORD fontType,           // type of font
685                                      LPARAM lParam             // application-defined data
686                                          )
687     {
688         //
689         //Log.d("LVWin32FontEnumFontFamExProc fontType=", fontType);
690         if (fontType == TRUETYPE_FONTTYPE)
691         {
692             void * p = cast(void*)lParam;
693             Win32FontManager fontman = cast(Win32FontManager)p;
694             string face = fromStringz(lf.lfFaceName.ptr).dup;
695             FontFamily family = pitchAndFamilyToFontFamily(lf.lfPitchAndFamily);
696             if (face.length < 2 || face[0] == '@')
697                 return 1;
698             //Log.d("face:", face);
699             fontman.registerFont(family, face, lf.lfPitchAndFamily);
700         }
701         return 1;
702     }
703 }