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 }