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 ch = getReplacementChar(ch); 275 if (!ch) 276 return null; 277 glyphIndex = getGlyphIndex(ch); 278 if (!glyphIndex) { 279 ch = getReplacementChar(ch); 280 if (!ch) 281 return null; 282 glyphIndex = getGlyphIndex(ch); 283 } 284 } 285 if (!glyphIndex) 286 return null; 287 if (glyphIndex >= 0xFFFF) 288 return null; 289 GLYPHMETRICS metrics; 290 291 bool needSubpixelRendering = FontManager.subpixelRenderingMode && antialiased; 292 MAT2 scaleMatrix = { {0,1}, {0,0}, {0,0}, {0,1} }; 293 294 uint res; 295 res = GetGlyphOutlineW( _drawbuf.dc, cast(wchar)ch, 296 GGO_METRICS, 297 &metrics, 298 0, 299 null, 300 &scaleMatrix ); 301 if (res == GDI_ERROR) 302 return null; 303 304 Glyph * g = new Glyph; 305 static if (ENABLE_OPENGL) { 306 g.id = nextGlyphId(); 307 } 308 //g.blackBoxX = cast(ushort)metrics.gmBlackBoxX; 309 //g.blackBoxY = cast(ubyte)metrics.gmBlackBoxY; 310 //g.originX = cast(byte)metrics.gmptGlyphOrigin.x; 311 //g.originY = cast(byte)metrics.gmptGlyphOrigin.y; 312 //g.width = cast(ubyte)metrics.gmCellIncX; 313 g.subpixelMode = SubpixelRenderingMode.None; 314 315 if (needSubpixelRendering) { 316 scaleMatrix.eM11.value = 3; // request glyph 3 times wider for subpixel antialiasing 317 } 318 319 int gs = 0; 320 // calculate bitmap size 321 if (antialiased) { 322 gs = GetGlyphOutlineW( _drawbuf.dc, cast(wchar)ch, 323 GGO_GRAY8_BITMAP, 324 &metrics, 325 0, 326 NULL, 327 &scaleMatrix ); 328 } else { 329 gs = GetGlyphOutlineW( _drawbuf.dc, cast(wchar)ch, 330 GGO_BITMAP, 331 &metrics, 332 0, 333 NULL, 334 &scaleMatrix ); 335 } 336 337 if (gs >= 0x10000 || gs < 0) 338 return null; 339 340 if (needSubpixelRendering) { 341 //Log.d("ch=", ch); 342 //Log.d("NORMAL: blackBoxX=", g.blackBoxX, " \tblackBoxY=", g.blackBoxY, " \torigin.x=", g.originX, " \torigin.y=", g.originY, "\tgmCellIncX=", g.width); 343 g.blackBoxX = cast(ushort)metrics.gmBlackBoxX; 344 g.blackBoxY = cast(ubyte)metrics.gmBlackBoxY; 345 g.originX = cast(byte)((metrics.gmptGlyphOrigin.x + 0) / 3); 346 g.originY = cast(byte)metrics.gmptGlyphOrigin.y; 347 g.widthPixels = cast(ubyte)((metrics.gmCellIncX + 2) / 3); 348 g.widthScaled = g.widthPixels << 6; 349 g.subpixelMode = FontManager.subpixelRenderingMode; 350 //Log.d(" *3 : blackBoxX=", metrics.gmBlackBoxX, " \tblackBoxY=", metrics.gmBlackBoxY, " \torigin.x=", metrics.gmptGlyphOrigin.x, " \torigin.y=", metrics.gmptGlyphOrigin.y, " \tgmCellIncX=", metrics.gmCellIncX); 351 //Log.d(" /3 : blackBoxX=", g.blackBoxX, " \tblackBoxY=", g.blackBoxY, " \torigin.x=", g.originX, " \torigin.y=", g.originY, "\tgmCellIncX=", g.width); 352 } else { 353 g.blackBoxX = cast(ushort)metrics.gmBlackBoxX; 354 g.blackBoxY = cast(ubyte)metrics.gmBlackBoxY; 355 g.originX = cast(byte)metrics.gmptGlyphOrigin.x; 356 g.originY = cast(byte)metrics.gmptGlyphOrigin.y; 357 g.widthPixels = cast(ubyte)metrics.gmCellIncX; 358 g.widthScaled = g.widthPixels << 6; 359 } 360 361 if (g.blackBoxX > 0 && g.blackBoxY > 0) { 362 g.glyph = new ubyte[g.blackBoxX * g.blackBoxY]; 363 if (gs>0) 364 { 365 if (antialiased) { 366 // antialiased glyph 367 ubyte[] glyph = new ubyte[gs]; 368 res = GetGlyphOutlineW( _drawbuf.dc, cast(wchar)ch, 369 GGO_GRAY8_BITMAP, //GGO_METRICS 370 &metrics, 371 gs, 372 glyph.ptr, 373 &scaleMatrix); 374 if (res==GDI_ERROR) 375 { 376 return null; 377 } 378 if (needSubpixelRendering) { 379 ubyte[] newglyph; 380 int shiftedBy = 0; 381 g.blackBoxX = prepare_lcd_glyph(glyph.ptr, 382 metrics, 383 newglyph, 384 shiftedBy); 385 g.glyph = newglyph; 386 //g.originX = cast(ubyte)((metrics.gmptGlyphOrigin.x + 2 - shiftedBy) / 3); 387 //g.width = cast(ubyte)((metrics.gmCellIncX + 2 - shiftedBy) / 3); 388 } else { 389 int glyph_row_size = (g.blackBoxX + 3) / 4 * 4; 390 ubyte * src = glyph.ptr; 391 ubyte * dst = g.glyph.ptr; 392 for (int y = 0; y < g.blackBoxY; y++) 393 { 394 for (int x = 0; x < g.blackBoxX; x++) 395 { 396 dst[x] = _gamma65.correct(src[x]); 397 } 398 src += glyph_row_size; 399 dst += g.blackBoxX; 400 } 401 } 402 } else { 403 // bitmap glyph 404 ubyte[] glyph = new ubyte[gs]; 405 res = GetGlyphOutlineW( _drawbuf.dc, cast(wchar)ch, 406 GGO_BITMAP, //GGO_METRICS 407 &metrics, 408 gs, 409 glyph.ptr, 410 &scaleMatrix ); 411 if (res==GDI_ERROR) 412 { 413 return null; 414 } 415 int glyph_row_bytes = ((g.blackBoxX + 7) / 8); 416 int glyph_row_size = (glyph_row_bytes + 3) / 4 * 4; 417 ubyte * src = glyph.ptr; 418 ubyte * dst = g.glyph.ptr; 419 for (int y = 0; y < g.blackBoxY; y++) 420 { 421 for (int x = 0; x < g.blackBoxX; x++) 422 { 423 int offset = x >> 3; 424 int shift = 7 - (x & 7); 425 ubyte b = ((src[offset] >> shift) & 1) ? 255 : 0; 426 dst[x] = b; 427 } 428 src += glyph_row_size; 429 dst += g.blackBoxX; 430 } 431 } 432 } 433 else 434 { 435 // empty glyph 436 for (int i = g.blackBoxX * g.blackBoxY - 1; i >= 0; i--) 437 g.glyph[i] = 0; 438 } 439 } 440 // found! 441 return _glyphCache.put(ch, g); 442 } 443 444 /// init from font definition 445 bool create(FontDef * def, int size, int weight, bool italic) { 446 if (!isNull()) 447 clear(); 448 LOGFONTA lf; 449 lf.lfCharSet = ANSI_CHARSET; //DEFAULT_CHARSET; 450 lf.lfFaceName[0..def.face.length] = def.face; 451 lf.lfFaceName[def.face.length] = 0; 452 lf.lfHeight = -size; //pixelsToPoints(size); 453 lf.lfItalic = italic; 454 lf.lfWeight = weight; 455 lf.lfOutPrecision = OUT_TT_ONLY_PRECIS; //OUT_OUTLINE_PRECIS; //OUT_TT_ONLY_PRECIS; 456 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; 457 //lf.lfQuality = NONANTIALIASED_QUALITY; //ANTIALIASED_QUALITY; 458 //lf.lfQuality = PROOF_QUALITY; //ANTIALIASED_QUALITY; 459 lf.lfQuality = antialiased ? NONANTIALIASED_QUALITY : ANTIALIASED_QUALITY; //PROOF_QUALITY; //ANTIALIASED_QUALITY; //size < 18 ? NONANTIALIASED_QUALITY : PROOF_QUALITY; //ANTIALIASED_QUALITY; 460 lf.lfPitchAndFamily = def.pitchAndFamily; 461 _hfont = CreateFontIndirectA(&lf); 462 _drawbuf = new Win32ColorDrawBuf(1, 1); 463 SelectObject(_drawbuf.dc, _hfont); 464 465 TEXTMETRICW tm; 466 GetTextMetricsW(_drawbuf.dc, &tm); 467 468 _size = size; 469 _height = tm.tmHeight; 470 debug(FontResources) Log.d("Win32Font.create: height=", _height, " for size=", _size, " points=", lf.lfHeight, " dpi=", _dpi); 471 _baseline = _height - tm.tmDescent; 472 _weight = weight; 473 _italic = italic; 474 _face = def.face; 475 _family = def.family; 476 debug(FontResources) Log.d("Created font ", _face, " ", _size); 477 return true; 478 } 479 480 /// clear usage flags for all entries 481 override void checkpoint() { 482 _glyphCache.checkpoint(); 483 } 484 485 /// removes entries not used after last call of checkpoint() or cleanup() 486 override void cleanup() { 487 _glyphCache.cleanup(); 488 } 489 490 /// clears glyph cache 491 override void clearGlyphCache() { 492 _glyphCache.clear(); 493 } 494 495 @property override int size() { return _size; } 496 @property override int height() { return _height; } 497 @property override int weight() { return _weight; } 498 @property override int baseline() { return _baseline; } 499 @property override bool italic() { return _italic; } 500 @property override string face() { return _face; } 501 @property override FontFamily family() { return _family; } 502 @property override bool isNull() { return _hfont is null; } 503 } 504 505 506 /** 507 * Font manager implementation based on Win32 API system fonts. 508 */ 509 class Win32FontManager : FontManager { 510 private FontList _activeFonts; 511 private FontDef[] _fontFaces; 512 private FontDef*[string] _faceByName; 513 514 /// override to return list of font faces available 515 override FontFaceProps[] getFaces() { 516 FontFaceProps[] res; 517 for (int i = 0; i < _fontFaces.length; i++) { 518 FontFaceProps item = FontFaceProps(_fontFaces[i].face, _fontFaces[i].family); 519 bool found = false; 520 for (int j = 0; j < res.length; j++) { 521 if (res[j].face == item.face) { 522 found = true; 523 break; 524 } 525 } 526 if (!found) 527 res ~= item; 528 } 529 return res; 530 } 531 532 533 /// initialize in constructor 534 this() { 535 debug Log.i("Creating Win32FontManager"); 536 //instance = this; 537 initialize(); 538 } 539 ~this() { 540 debug Log.i("Destroying Win32FontManager"); 541 } 542 543 /// initialize font manager by enumerating of system fonts 544 bool initialize() { 545 debug Log.i("Win32FontManager.initialize()"); 546 Win32ColorDrawBuf drawbuf = new Win32ColorDrawBuf(1,1); 547 LOGFONTA lf; 548 lf.lfCharSet = ANSI_CHARSET; //DEFAULT_CHARSET; 549 lf.lfFaceName[0] = 0; 550 HDC dc = drawbuf.dc; 551 int res = 552 EnumFontFamiliesExA( 553 dc, // handle to DC 554 &lf, // font information 555 cast(FONTENUMPROCA)&LVWin32FontEnumFontFamExProc, // callback function (FONTENUMPROC) 556 cast(LPARAM)(cast(void*)this), // additional data 557 0U // not used; must be 0 558 ); 559 destroy(drawbuf); 560 Log.i("Found ", _fontFaces.length, " font faces"); 561 return res!=0; 562 } 563 564 /// for returning of not found font 565 FontRef _emptyFontRef; 566 567 /// get font by properties 568 override ref FontRef getFont(int size, int weight, bool italic, FontFamily family, string face) { 569 //Log.i("getFont()"); 570 FontDef * def = findFace(family, face); 571 //Log.i("getFont() found face ", def.face, " by requested face ", face); 572 if (def !is null) { 573 int index = _activeFonts.find(size, weight, italic, def.family, def.face); 574 if (index >= 0) 575 return _activeFonts.get(index); 576 debug(FontResources) Log.d("Creating new font"); 577 Win32Font item = new Win32Font(); 578 if (!item.create(def, size, weight, italic)) 579 return _emptyFontRef; 580 debug(FontResources) Log.d("Adding to list of active fonts"); 581 return _activeFonts.add(item); 582 } else { 583 return _emptyFontRef; 584 } 585 } 586 587 /// find font face definition by family only (try to get one of defaults for family if possible) 588 FontDef * findFace(FontFamily family) { 589 FontDef * res = null; 590 switch(family) { 591 case FontFamily.SansSerif: 592 res = findFace("Arial"); if (res !is null) return res; 593 res = findFace("Tahoma"); if (res !is null) return res; 594 res = findFace("Calibri"); if (res !is null) return res; 595 res = findFace("Verdana"); if (res !is null) return res; 596 res = findFace("Lucida Sans"); if (res !is null) return res; 597 break; 598 case FontFamily.Serif: 599 res = findFace("Times New Roman"); if (res !is null) return res; 600 res = findFace("Georgia"); if (res !is null) return res; 601 res = findFace("Century Schoolbook"); if (res !is null) return res; 602 res = findFace("Bookman Old Style"); if (res !is null) return res; 603 break; 604 case FontFamily.MonoSpace: 605 res = findFace("Courier New"); if (res !is null) return res; 606 res = findFace("Lucida Console"); if (res !is null) return res; 607 res = findFace("Century Schoolbook"); if (res !is null) return res; 608 res = findFace("Bookman Old Style"); if (res !is null) return res; 609 break; 610 case FontFamily.Cursive: 611 res = findFace("Comic Sans MS"); if (res !is null) return res; 612 res = findFace("Lucida Handwriting"); if (res !is null) return res; 613 res = findFace("Monotype Corsiva"); if (res !is null) return res; 614 break; 615 default: 616 break; 617 } 618 return null; 619 } 620 621 /// find font face definition by face only 622 FontDef * findFace(string face) { 623 if (face.length == 0) 624 return null; 625 string[] faces = split(face, ","); 626 foreach(f; faces) { 627 if (f in _faceByName) 628 return _faceByName[f]; 629 } 630 return null; 631 } 632 633 /// find font face definition by family and face 634 FontDef * findFace(FontFamily family, string face) { 635 // by face only 636 FontDef * res = findFace(face); 637 if (res !is null) 638 return res; 639 // best for family 640 res = findFace(family); 641 if (res !is null) 642 return res; 643 for (int i = 0; i < _fontFaces.length; i++) { 644 res = &_fontFaces[i]; 645 if (res.family == family) 646 return res; 647 } 648 res = findFace(FontFamily.SansSerif); 649 if (res !is null) 650 return res; 651 return &_fontFaces[0]; 652 } 653 654 /// register enumerated font 655 bool registerFont(FontFamily family, string fontFace, ubyte pitchAndFamily) { 656 Log.d("registerFont(", family, ",", fontFace, ")"); 657 _fontFaces ~= FontDef(family, fontFace, pitchAndFamily); 658 _faceByName[fontFace] = &_fontFaces[$ - 1]; 659 return true; 660 } 661 662 /// clear usage flags for all entries 663 override void checkpoint() { 664 _activeFonts.checkpoint(); 665 } 666 667 /// removes entries not used after last call of checkpoint() or cleanup() 668 override void cleanup() { 669 _activeFonts.cleanup(); 670 //_list.cleanup(); 671 } 672 673 /// clears glyph cache 674 override void clearGlyphCaches() { 675 _activeFonts.clearGlyphCache(); 676 } 677 } 678 679 FontFamily pitchAndFamilyToFontFamily(ubyte flags) { 680 if ((flags & FF_DECORATIVE) == FF_DECORATIVE) 681 return FontFamily.Fantasy; 682 else if ((flags & (FIXED_PITCH)) != 0) // | | MONO_FONT 683 return FontFamily.MonoSpace; 684 else if ((flags & (FF_ROMAN)) != 0) 685 return FontFamily.Serif; 686 else if ((flags & (FF_SCRIPT)) != 0) 687 return FontFamily.Cursive; 688 return FontFamily.SansSerif; 689 } 690 691 // definition 692 extern(Windows) { 693 int LVWin32FontEnumFontFamExProc( 694 const (LOGFONTA) *lf, // logical-font data 695 const (TEXTMETRICA) *lpntme, // physical-font data 696 //ENUMLOGFONTEX *lpelfe, // logical-font data 697 //NEWTEXTMETRICEX *lpntme, // physical-font data 698 DWORD fontType, // type of font 699 LPARAM lParam // application-defined data 700 ) 701 { 702 // 703 //Log.d("LVWin32FontEnumFontFamExProc fontType=", fontType); 704 if (fontType == TRUETYPE_FONTTYPE) 705 { 706 void * p = cast(void*)lParam; 707 Win32FontManager fontman = cast(Win32FontManager)p; 708 string face = fromStringz(lf.lfFaceName.ptr).dup; 709 FontFamily family = pitchAndFamilyToFontFamily(lf.lfPitchAndFamily); 710 if (face.length < 2 || face[0] == '@') 711 return 1; 712 //Log.d("face:", face); 713 fontman.registerFont(family, face, lf.lfPitchAndFamily); 714 } 715 return 1; 716 } 717 }