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 }