1 module dlangui.platforms.windows.win32fonts; 2 3 version (Windows) { 4 5 import win32.windows; 6 import dlangui.graphics.fonts; 7 import dlangui.platforms.windows.win32drawbuf; 8 import std.string; 9 import std.utf; 10 11 //auto toUTF16z(S)(S s) 12 //{ 13 //return toUTFz!(const(wchar)*)(s); 14 //} 15 16 private struct FontDef { 17 immutable FontFamily _family; 18 immutable string _face; 19 immutable ubyte _pitchAndFamily; 20 @property FontFamily family() { return _family; } 21 @property string face() { return _face; } 22 @property ubyte pitchAndFamily() { return _pitchAndFamily; } 23 this(FontFamily family, string face, ubyte putchAndFamily) { 24 _family = family; 25 _face = face; 26 _pitchAndFamily = pitchAndFamily; 27 } 28 } 29 30 31 /** 32 * Font implementation based on Win32 API system fonts. 33 */ 34 class Win32Font : Font { 35 HFONT _hfont; 36 int _size; 37 int _height; 38 int _weight; 39 int _baseline; 40 bool _italic; 41 string _face; 42 FontFamily _family; 43 LOGFONTA _logfont; 44 Win32ColorDrawBuf _drawbuf; 45 GlyphCache _glyphCache; 46 47 /// need to call create() after construction to initialize font 48 this() { 49 } 50 51 /// do cleanup 52 ~this() { 53 clear(); 54 } 55 56 /// cleanup resources 57 override void clear() { 58 if (_hfont !is null) 59 { 60 DeleteObject(_hfont); 61 _hfont = NULL; 62 _height = 0; 63 _baseline = 0; 64 _size = 0; 65 } 66 if (_drawbuf !is null) { 67 destroy(_drawbuf); 68 _drawbuf = null; 69 } 70 } 71 72 uint getGlyphIndex(dchar code) 73 { 74 if (_drawbuf is null) 75 return 0; 76 wchar[2] s; 77 wchar[2] g; 78 s[0] = cast(wchar)code; 79 s[1] = 0; 80 g[0] = 0; 81 GCP_RESULTSW gcp; 82 gcp.lStructSize = GCP_RESULTSW.sizeof; 83 gcp.lpOutString = null; 84 gcp.lpOrder = null; 85 gcp.lpDx = null; 86 gcp.lpCaretPos = null; 87 gcp.lpClass = null; 88 gcp.lpGlyphs = g.ptr; 89 gcp.nGlyphs = 2; 90 gcp.nMaxFit = 2; 91 92 DWORD res = GetCharacterPlacementW( 93 _drawbuf.dc, s.ptr, 1, 94 1000, 95 &gcp, 96 0 97 ); 98 if (!res) 99 return 0; 100 return g[0]; 101 } 102 103 override Glyph * getCharGlyph(dchar ch, bool withImage = true) { 104 uint glyphIndex = getGlyphIndex(ch); 105 if (!glyphIndex) 106 return null; 107 if (glyphIndex >= 0xFFFF) 108 return null; 109 Glyph * found = _glyphCache.find(cast(ushort)glyphIndex); 110 if (found !is null) 111 return found; 112 GLYPHMETRICS metrics; 113 114 MAT2 identity = { {0,1}, {0,0}, {0,0}, {0,1} }; 115 uint res; 116 res = GetGlyphOutlineW( _drawbuf.dc, cast(wchar)ch, 117 GGO_METRICS, 118 &metrics, 119 0, 120 null, 121 &identity ); 122 if (res==GDI_ERROR) 123 return null; 124 int gs = GetGlyphOutlineW( _drawbuf.dc, cast(wchar)ch, 125 GGO_GRAY8_BITMAP, //GGO_METRICS 126 &metrics, 127 0, 128 NULL, 129 &identity ); 130 if (gs >= 0x10000 || gs < 0) 131 return null; 132 133 Glyph g; 134 version (USE_OPENGL) { 135 g.id = nextGlyphId(); 136 } 137 g.blackBoxX = cast(ubyte)metrics.gmBlackBoxX; 138 g.blackBoxY = cast(ubyte)metrics.gmBlackBoxY; 139 g.originX = cast(byte)metrics.gmptGlyphOrigin.x; 140 g.originY = cast(byte)metrics.gmptGlyphOrigin.y; 141 g.width = cast(ubyte)metrics.gmCellIncX; 142 g.glyphIndex = cast(ushort)glyphIndex; 143 144 if (g.blackBoxX>0 && g.blackBoxY>0) 145 { 146 g.glyph = new ubyte[g.blackBoxX * g.blackBoxY]; 147 if (gs>0) 148 { 149 ubyte glyph[] = new ubyte[gs]; 150 res = GetGlyphOutlineW( _drawbuf.dc, cast(wchar)ch, 151 GGO_GRAY8_BITMAP, //GGO_METRICS 152 &metrics, 153 gs, 154 glyph.ptr, 155 &identity ); 156 if (res==GDI_ERROR) 157 { 158 return null; 159 } 160 int glyph_row_size = (g.blackBoxX + 3) / 4 * 4; 161 ubyte * src = glyph.ptr; 162 ubyte * dst = g.glyph.ptr; 163 for (int y = 0; y < g.blackBoxY; y++) 164 { 165 for (int x = 0; x < g.blackBoxX; x++) 166 { 167 ubyte b = src[x]; 168 if (b>=64) 169 b = 63; 170 b = (b<<2) & 0xFC; 171 dst[x] = b; 172 } 173 src += glyph_row_size; 174 dst += g.blackBoxX; 175 } 176 } 177 else 178 { 179 // empty glyph 180 for (int i = g.blackBoxX * g.blackBoxY - 1; i >= 0; i--) 181 g.glyph[i] = 0; 182 } 183 } 184 // found! 185 return _glyphCache.put(cast(ushort)glyphIndex, &g); 186 } 187 188 // draw text string to buffer 189 override void drawText(DrawBuf buf, int x, int y, const dchar[] text, uint color) { 190 int[] widths; 191 int charsMeasured = measureText(text, widths, 3000); 192 Rect clip = buf.clipOrFullRect; 193 if (y + height < clip.top || y >= clip.bottom) 194 return; 195 for (int i = 0; i < charsMeasured; i++) { 196 int xx = (i > 0) ? widths[i - 1] : 0; 197 if (x + xx > clip.right) 198 break; 199 Glyph * glyph = getCharGlyph(text[i]); 200 if (glyph is null) 201 continue; 202 if ( glyph.blackBoxX && glyph.blackBoxY ) { 203 int gx = x + xx + glyph.originX; 204 if (gx + glyph.blackBoxX < clip.left) 205 continue; 206 buf.drawGlyph( gx, 207 y + _baseline - glyph.originY, 208 glyph, 209 color); 210 } 211 } 212 } 213 214 static if (true) { 215 override int measureText(const dchar[] text, ref int[] widths, int maxWidth) { 216 if (text.length == 0) 217 return 0; 218 const dchar * pstr = text.ptr; 219 uint len = cast(uint)text.length; 220 if (widths.length < len) 221 widths.length = len; 222 int x = 0; 223 int charsMeasured = 0; 224 for (int i = 0; i < len; i++) { 225 Glyph * glyph = getCharGlyph(text[i], true); // TODO: what is better 226 if (glyph is null) { 227 // if no glyph, use previous width - treat as zero width 228 widths[i] = i > 0 ? widths[i-1] : 0; 229 continue; 230 } 231 int w = x + glyph.width; // using advance 232 int w2 = x + glyph.originX + glyph.blackBoxX; // using black box 233 if (w < w2) // choose bigger value 234 w = w2; 235 widths[i] = w; 236 x += glyph.width; 237 charsMeasured = i + 1; 238 if (x > maxWidth) 239 break; 240 } 241 return charsMeasured; 242 } 243 } else { 244 245 override int measureText(const dchar[] text, ref int[] widths, int maxWidth) { 246 if (_hfont is null || _drawbuf is null || text.length == 0) 247 return 0; 248 wstring utf16text = toUTF16(text); 249 const wchar * pstr = utf16text.ptr; 250 uint len = cast(uint)utf16text.length; 251 GCP_RESULTSW gcpres; 252 gcpres.lStructSize = gcpres.sizeof; 253 if (widths.length < len + 1) 254 widths.length = len + 1; 255 gcpres.lpDx = widths.ptr; 256 gcpres.nMaxFit = len; 257 gcpres.nGlyphs = len; 258 uint res = GetCharacterPlacementW( 259 _drawbuf.dc, 260 pstr, 261 len, 262 maxWidth, 263 &gcpres, 264 GCP_MAXEXTENT); //|GCP_USEKERNING 265 if (!res) { 266 widths[0] = 0; 267 return 0; 268 } 269 uint measured = gcpres.nMaxFit; 270 int total = 0; 271 for (int i = 0; i < measured; i++) { 272 int w = widths[i]; 273 total += w; 274 widths[i] = total; 275 } 276 return measured; 277 } 278 } 279 280 bool create(FontDef * def, int size, int weight, bool italic) { 281 if (!isNull()) 282 clear(); 283 LOGFONTA lf; 284 lf.lfCharSet = ANSI_CHARSET; //DEFAULT_CHARSET; 285 lf.lfFaceName[0..def.face.length] = def.face; 286 lf.lfFaceName[def.face.length] = 0; 287 lf.lfHeight = -size; 288 lf.lfItalic = italic; 289 lf.lfOutPrecision = OUT_OUTLINE_PRECIS; //OUT_TT_ONLY_PRECIS; 290 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; 291 //lf.lfQuality = NONANTIALIASED_QUALITY; //ANTIALIASED_QUALITY; 292 //lf.lfQuality = PROOF_QUALITY; //ANTIALIASED_QUALITY; 293 lf.lfQuality = size < 18 ? NONANTIALIASED_QUALITY : PROOF_QUALITY; //ANTIALIASED_QUALITY; 294 lf.lfPitchAndFamily = def.pitchAndFamily; 295 _hfont = CreateFontIndirectA(&lf); 296 _drawbuf = new Win32ColorDrawBuf(1, 1); 297 SelectObject(_drawbuf.dc, _hfont); 298 299 TEXTMETRICW tm; 300 GetTextMetricsW(_drawbuf.dc, &tm); 301 302 _size = size; 303 _height = tm.tmHeight; 304 _baseline = _height - tm.tmDescent; 305 _weight = weight; 306 _italic = italic; 307 _face = def.face; 308 _family = def.family; 309 Log.d("Created font ", _face, " ", _size); 310 return true; 311 } 312 313 // clear usage flags for all entries 314 override void checkpoint() { 315 _glyphCache.checkpoint(); 316 } 317 318 // removes entries not used after last call of checkpoint() or cleanup() 319 override void cleanup() { 320 _glyphCache.cleanup(); 321 } 322 323 @property override int size() { return _size; } 324 @property override int height() { return _height; } 325 @property override int weight() { return _weight; } 326 @property override int baseline() { return _baseline; } 327 @property override bool italic() { return _italic; } 328 @property override string face() { return _face; } 329 @property override FontFamily family() { return _family; } 330 @property override bool isNull() { return _hfont is null; } 331 } 332 333 334 /** 335 * Font manager implementation based on Win32 API system fonts. 336 */ 337 class Win32FontManager : FontManager { 338 private FontList _activeFonts; 339 private FontDef[] _fontFaces; 340 private FontDef*[string] _faceByName; 341 342 /// initialize in constructor 343 this() { 344 Log.i("Creating Win32FontManager"); 345 //instance = this; 346 init(); 347 } 348 ~this() { 349 Log.i("Destroying Win32FontManager"); 350 } 351 352 /// initialize font manager by enumerating of system fonts 353 bool init() { 354 Log.i("Win32FontManager.init()"); 355 Win32ColorDrawBuf drawbuf = new Win32ColorDrawBuf(1,1); 356 LOGFONTA lf; 357 lf.lfCharSet = ANSI_CHARSET; //DEFAULT_CHARSET; 358 lf.lfFaceName[0] = 0; 359 HDC dc = drawbuf.dc; 360 int res = 361 EnumFontFamiliesExA( 362 dc, // handle to DC 363 &lf, // font information 364 &LVWin32FontEnumFontFamExProc, // callback function (FONTENUMPROC) 365 cast(LPARAM)(cast(void*)this), // additional data 366 0U // not used; must be 0 367 ); 368 destroy(drawbuf); 369 Log.i("Found ", _fontFaces.length, " font faces"); 370 return res!=0; 371 } 372 373 /// for returning of not found font 374 FontRef _emptyFontRef; 375 376 /// get font by properties 377 override ref FontRef getFont(int size, int weight, bool italic, FontFamily family, string face) { 378 //Log.i("getFont()"); 379 FontDef * def = findFace(family, face); 380 if (def !is null) { 381 int index = _activeFonts.find(size, weight, italic, def.family, def.face); 382 if (index >= 0) 383 return _activeFonts.get(index); 384 Log.d("Creating new font"); 385 Win32Font item = new Win32Font(); 386 if (!item.create(def, size, weight, italic)) 387 return _emptyFontRef; 388 Log.d("Adding to list of active fonts"); 389 return _activeFonts.add(item); 390 } else { 391 return _emptyFontRef; 392 } 393 } 394 395 /// find font face definition by family only (try to get one of defaults for family if possible) 396 FontDef * findFace(FontFamily family) { 397 FontDef * res = null; 398 switch(family) { 399 case FontFamily.SansSerif: 400 res = findFace("Arial"); if (res !is null) return res; 401 res = findFace("Tahoma"); if (res !is null) return res; 402 res = findFace("Calibri"); if (res !is null) return res; 403 res = findFace("Verdana"); if (res !is null) return res; 404 res = findFace("Lucida Sans"); if (res !is null) return res; 405 break; 406 case FontFamily.Serif: 407 res = findFace("Times New Roman"); if (res !is null) return res; 408 res = findFace("Georgia"); if (res !is null) return res; 409 res = findFace("Century Schoolbook"); if (res !is null) return res; 410 res = findFace("Bookman Old Style"); if (res !is null) return res; 411 break; 412 case FontFamily.MonoSpace: 413 res = findFace("Courier New"); if (res !is null) return res; 414 res = findFace("Lucida Console"); if (res !is null) return res; 415 res = findFace("Century Schoolbook"); if (res !is null) return res; 416 res = findFace("Bookman Old Style"); if (res !is null) return res; 417 break; 418 case FontFamily.Cursive: 419 res = findFace("Comic Sans MS"); if (res !is null) return res; 420 res = findFace("Lucida Handwriting"); if (res !is null) return res; 421 res = findFace("Monotype Corsiva"); if (res !is null) return res; 422 break; 423 default: 424 break; 425 } 426 return null; 427 } 428 429 /// find font face definition by face only 430 FontDef * findFace(string face) { 431 if (face.length == 0) 432 return null; 433 if (face in _faceByName) 434 return _faceByName[face]; 435 return null; 436 } 437 438 /// find font face definition by family and face 439 FontDef * findFace(FontFamily family, string face) { 440 // by face only 441 FontDef * res = findFace(face); 442 if (res !is null) 443 return res; 444 // best for family 445 res = findFace(family); 446 if (res !is null) 447 return res; 448 for (int i = 0; i < _fontFaces.length; i++) { 449 res = &_fontFaces[i]; 450 if (res.family == family) 451 return res; 452 } 453 res = findFace(FontFamily.SansSerif); 454 if (res !is null) 455 return res; 456 return &_fontFaces[0]; 457 } 458 459 /// register enumerated font 460 bool registerFont(FontFamily family, string fontFace, ubyte pitchAndFamily) { 461 Log.d("registerFont(", family, ",", fontFace, ")"); 462 _fontFaces ~= FontDef(family, fontFace, pitchAndFamily); 463 _faceByName[fontFace] = &_fontFaces[$ - 1]; 464 return true; 465 } 466 467 /// clear usage flags for all entries 468 override void checkpoint() { 469 _activeFonts.checkpoint(); 470 } 471 472 /// removes entries not used after last call of checkpoint() or cleanup() 473 override void cleanup() { 474 _activeFonts.cleanup(); 475 //_list.cleanup(); 476 } 477 } 478 479 FontFamily pitchAndFamilyToFontFamily(ubyte flags) { 480 if ((flags & FF_DECORATIVE) == FF_DECORATIVE) 481 return FontFamily.Fantasy; 482 else if ((flags & (FIXED_PITCH)) != 0) // | | MONO_FONT 483 return FontFamily.MonoSpace; 484 else if ((flags & (FF_ROMAN)) != 0) 485 return FontFamily.Serif; 486 else if ((flags & (FF_SCRIPT)) != 0) 487 return FontFamily.Cursive; 488 return FontFamily.SansSerif; 489 } 490 491 // definition 492 extern(Windows) { 493 int LVWin32FontEnumFontFamExProc( 494 const (LOGFONTA) *lf, // logical-font data 495 const (TEXTMETRICA) *lpntme, // physical-font data 496 //ENUMLOGFONTEX *lpelfe, // logical-font data 497 //NEWTEXTMETRICEX *lpntme, // physical-font data 498 DWORD fontType, // type of font 499 LPARAM lParam // application-defined data 500 ) 501 { 502 // 503 //Log.d("LVWin32FontEnumFontFamExProc fontType=", fontType); 504 if (fontType == TRUETYPE_FONTTYPE) 505 { 506 void * p = cast(void*)lParam; 507 Win32FontManager fontman = cast(Win32FontManager)p; 508 string face = fromStringz(lf.lfFaceName.ptr); 509 FontFamily family = pitchAndFamilyToFontFamily(lf.lfPitchAndFamily); 510 if (face.length < 2 || face[0] == '@') 511 return 1; 512 //Log.d("face:", face); 513 fontman.registerFont(family, face, lf.lfPitchAndFamily); 514 } 515 return 1; 516 } 517 } 518 519 }