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