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