gdiplus: GdipGetLogFont should use device scale and transform when appropriate.
[wine.git] / dlls / gdiplus / font.c
blobf6a5d1c4a12dda45cb45535cbf3afceca9ca85aa
1 /*
2 * Copyright (C) 2007 Google (Evan Stade)
3 * Copyright (C) 2012 Dmitry Timoshkov
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 #include <stdarg.h>
22 #include "windef.h"
23 #include "winbase.h"
24 #include "wingdi.h"
25 #include "winnls.h"
26 #include "winreg.h"
27 #include "wine/debug.h"
28 #include "wine/unicode.h"
30 WINE_DEFAULT_DEBUG_CHANNEL (gdiplus);
32 #include "objbase.h"
34 #include "gdiplus.h"
35 #include "gdiplus_private.h"
37 /* PANOSE is 10 bytes in size, need to pack the structure properly */
38 #include "pshpack2.h"
39 typedef struct
41 USHORT version;
42 SHORT xAvgCharWidth;
43 USHORT usWeightClass;
44 USHORT usWidthClass;
45 SHORT fsType;
46 SHORT ySubscriptXSize;
47 SHORT ySubscriptYSize;
48 SHORT ySubscriptXOffset;
49 SHORT ySubscriptYOffset;
50 SHORT ySuperscriptXSize;
51 SHORT ySuperscriptYSize;
52 SHORT ySuperscriptXOffset;
53 SHORT ySuperscriptYOffset;
54 SHORT yStrikeoutSize;
55 SHORT yStrikeoutPosition;
56 SHORT sFamilyClass;
57 PANOSE panose;
58 ULONG ulUnicodeRange1;
59 ULONG ulUnicodeRange2;
60 ULONG ulUnicodeRange3;
61 ULONG ulUnicodeRange4;
62 CHAR achVendID[4];
63 USHORT fsSelection;
64 USHORT usFirstCharIndex;
65 USHORT usLastCharIndex;
66 /* According to the Apple spec, original version didn't have the below fields,
67 * version numbers were taken from the OpenType spec.
69 /* version 0 (TrueType 1.5) */
70 USHORT sTypoAscender;
71 USHORT sTypoDescender;
72 USHORT sTypoLineGap;
73 USHORT usWinAscent;
74 USHORT usWinDescent;
75 /* version 1 (TrueType 1.66) */
76 ULONG ulCodePageRange1;
77 ULONG ulCodePageRange2;
78 /* version 2 (OpenType 1.2) */
79 SHORT sxHeight;
80 SHORT sCapHeight;
81 USHORT usDefaultChar;
82 USHORT usBreakChar;
83 USHORT usMaxContext;
84 } TT_OS2_V2;
86 typedef struct
88 ULONG Version;
89 SHORT Ascender;
90 SHORT Descender;
91 SHORT LineGap;
92 USHORT advanceWidthMax;
93 SHORT minLeftSideBearing;
94 SHORT minRightSideBearing;
95 SHORT xMaxExtent;
96 SHORT caretSlopeRise;
97 SHORT caretSlopeRun;
98 SHORT caretOffset;
99 SHORT reserved[4];
100 SHORT metricDataFormat;
101 USHORT numberOfHMetrics;
102 } TT_HHEA;
103 #include "poppack.h"
105 #ifdef WORDS_BIGENDIAN
106 #define GET_BE_WORD(x) (x)
107 #define GET_BE_DWORD(x) (x)
108 #else
109 #define GET_BE_WORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
110 #define GET_BE_DWORD(x) MAKELONG(GET_BE_WORD(HIWORD(x)), GET_BE_WORD(LOWORD(x)));
111 #endif
113 #define MS_MAKE_TAG(ch0, ch1, ch2, ch3) \
114 ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \
115 ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24))
116 #define MS_OS2_TAG MS_MAKE_TAG('O','S','/','2')
117 #define MS_HHEA_TAG MS_MAKE_TAG('h','h','e','a')
119 static GpStatus clone_font_family(const GpFontFamily *, GpFontFamily **);
121 static GpFontCollection installedFontCollection = {0};
123 /*******************************************************************************
124 * GdipCreateFont [GDIPLUS.@]
126 * Create a new font based off of a FontFamily
128 * PARAMS
129 * *fontFamily [I] Family to base the font off of
130 * emSize [I] Size of the font
131 * style [I] Bitwise OR of FontStyle enumeration
132 * unit [I] Unit emSize is measured in
133 * **font [I] the resulting Font object
135 * RETURNS
136 * SUCCESS: Ok
137 * FAILURE: InvalidParameter if fontfamily or font is NULL.
138 * FAILURE: FontFamilyNotFound if an invalid FontFamily is given
140 * NOTES
141 * UnitDisplay is unsupported.
142 * emSize is stored separately from lfHeight, to hold the fraction.
144 GpStatus WINGDIPAPI GdipCreateFont(GDIPCONST GpFontFamily *fontFamily,
145 REAL emSize, INT style, Unit unit, GpFont **font)
147 HFONT hfont;
148 OUTLINETEXTMETRICW otm;
149 LOGFONTW lfw;
150 HDC hdc;
151 GpStatus stat;
152 int ret;
154 if (!fontFamily || !font || emSize < 0.0)
155 return InvalidParameter;
157 TRACE("%p (%s), %f, %d, %d, %p\n", fontFamily,
158 debugstr_w(fontFamily->FamilyName), emSize, style, unit, font);
160 memset(&lfw, 0, sizeof(lfw));
162 stat = GdipGetFamilyName(fontFamily, lfw.lfFaceName, LANG_NEUTRAL);
163 if (stat != Ok) return stat;
165 lfw.lfHeight = -units_to_pixels(emSize, unit, fontFamily->dpi);
166 lfw.lfWeight = style & FontStyleBold ? FW_BOLD : FW_REGULAR;
167 lfw.lfItalic = style & FontStyleItalic;
168 lfw.lfUnderline = style & FontStyleUnderline;
169 lfw.lfStrikeOut = style & FontStyleStrikeout;
171 hfont = CreateFontIndirectW(&lfw);
172 hdc = CreateCompatibleDC(0);
173 SelectObject(hdc, hfont);
174 otm.otmSize = sizeof(otm);
175 ret = GetOutlineTextMetricsW(hdc, otm.otmSize, &otm);
176 DeleteDC(hdc);
177 DeleteObject(hfont);
179 if (!ret) return NotTrueTypeFont;
181 *font = GdipAlloc(sizeof(GpFont));
182 if (!*font) return OutOfMemory;
184 (*font)->unit = unit;
185 (*font)->emSize = emSize;
186 (*font)->otm = otm;
188 stat = clone_font_family(fontFamily, &(*font)->family);
189 if (stat != Ok)
191 GdipFree(*font);
192 return stat;
195 TRACE("<-- %p\n", *font);
197 return Ok;
200 /*******************************************************************************
201 * GdipCreateFontFromLogfontW [GDIPLUS.@]
203 GpStatus WINGDIPAPI GdipCreateFontFromLogfontW(HDC hdc,
204 GDIPCONST LOGFONTW *logfont, GpFont **font)
206 HFONT hfont, oldfont;
207 OUTLINETEXTMETRICW otm;
208 WCHAR facename[LF_FACESIZE];
209 GpStatus stat;
210 int ret;
212 TRACE("(%p, %p, %p)\n", hdc, logfont, font);
214 if (!hdc || !logfont || !font)
215 return InvalidParameter;
217 hfont = CreateFontIndirectW(logfont);
218 oldfont = SelectObject(hdc, hfont);
219 otm.otmSize = sizeof(otm);
220 ret = GetOutlineTextMetricsW(hdc, otm.otmSize, &otm);
221 GetTextFaceW(hdc, LF_FACESIZE, facename);
222 SelectObject(hdc, oldfont);
223 DeleteObject(hfont);
225 if (!ret) return NotTrueTypeFont;
227 *font = GdipAlloc(sizeof(GpFont));
228 if (!*font) return OutOfMemory;
230 (*font)->unit = UnitWorld;
231 (*font)->emSize = otm.otmTextMetrics.tmAscent;
232 (*font)->otm = otm;
234 stat = GdipCreateFontFamilyFromName(facename, NULL, &(*font)->family);
235 if (stat != Ok)
237 GdipFree(*font);
238 return NotTrueTypeFont;
241 TRACE("<-- %p\n", *font);
243 return Ok;
246 /*******************************************************************************
247 * GdipCreateFontFromLogfontA [GDIPLUS.@]
249 GpStatus WINGDIPAPI GdipCreateFontFromLogfontA(HDC hdc,
250 GDIPCONST LOGFONTA *lfa, GpFont **font)
252 LOGFONTW lfw;
254 TRACE("(%p, %p, %p)\n", hdc, lfa, font);
256 if(!lfa || !font)
257 return InvalidParameter;
259 memcpy(&lfw, lfa, FIELD_OFFSET(LOGFONTA,lfFaceName) );
261 if(!MultiByteToWideChar(CP_ACP, 0, lfa->lfFaceName, -1, lfw.lfFaceName, LF_FACESIZE))
262 return GenericError;
264 return GdipCreateFontFromLogfontW(hdc, &lfw, font);
267 /*******************************************************************************
268 * GdipDeleteFont [GDIPLUS.@]
270 GpStatus WINGDIPAPI GdipDeleteFont(GpFont* font)
272 TRACE("(%p)\n", font);
274 if(!font)
275 return InvalidParameter;
277 GdipDeleteFontFamily(font->family);
278 GdipFree(font);
280 return Ok;
283 /*******************************************************************************
284 * GdipCreateFontFromDC [GDIPLUS.@]
286 GpStatus WINGDIPAPI GdipCreateFontFromDC(HDC hdc, GpFont **font)
288 HFONT hfont;
289 LOGFONTW lfw;
291 TRACE("(%p, %p)\n", hdc, font);
293 if(!font)
294 return InvalidParameter;
296 hfont = GetCurrentObject(hdc, OBJ_FONT);
297 if(!hfont)
298 return GenericError;
300 if(!GetObjectW(hfont, sizeof(LOGFONTW), &lfw))
301 return GenericError;
303 return GdipCreateFontFromLogfontW(hdc, &lfw, font);
306 /*******************************************************************************
307 * GdipGetFamily [GDIPLUS.@]
309 * Returns the FontFamily for the specified Font
311 * PARAMS
312 * font [I] Font to request from
313 * family [O] Resulting FontFamily object
315 * RETURNS
316 * SUCCESS: Ok
317 * FAILURE: An element of GpStatus
319 GpStatus WINGDIPAPI GdipGetFamily(GpFont *font, GpFontFamily **family)
321 TRACE("%p %p\n", font, family);
323 if (!(font && family))
324 return InvalidParameter;
326 return GdipCloneFontFamily(font->family, family);
329 static REAL get_font_size(const GpFont *font)
331 return font->emSize;
334 /******************************************************************************
335 * GdipGetFontSize [GDIPLUS.@]
337 * Returns the size of the font in Units
339 * PARAMS
340 * *font [I] The font to retrieve size from
341 * *size [O] Pointer to hold retrieved value
343 * RETURNS
344 * SUCCESS: Ok
345 * FAILURE: InvalidParameter (font or size was NULL)
347 * NOTES
348 * Size returned is actually emSize -- not internal size used for drawing.
350 GpStatus WINGDIPAPI GdipGetFontSize(GpFont *font, REAL *size)
352 TRACE("(%p, %p)\n", font, size);
354 if (!(font && size)) return InvalidParameter;
356 *size = get_font_size(font);
357 TRACE("%s,%d => %f\n", debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *size);
359 return Ok;
362 static INT get_font_style(const GpFont *font)
364 INT style;
366 if (font->otm.otmTextMetrics.tmWeight > FW_REGULAR)
367 style = FontStyleBold;
368 else
369 style = FontStyleRegular;
370 if (font->otm.otmTextMetrics.tmItalic)
371 style |= FontStyleItalic;
372 if (font->otm.otmTextMetrics.tmUnderlined)
373 style |= FontStyleUnderline;
374 if (font->otm.otmTextMetrics.tmStruckOut)
375 style |= FontStyleStrikeout;
377 return style;
380 /*******************************************************************************
381 * GdipGetFontStyle [GDIPLUS.@]
383 * Gets the font's style, returned in bitwise OR of FontStyle enumeration
385 * PARAMS
386 * font [I] font to request from
387 * style [O] resulting pointer to a FontStyle enumeration
389 * RETURNS
390 * SUCCESS: Ok
391 * FAILURE: InvalidParameter
393 GpStatus WINGDIPAPI GdipGetFontStyle(GpFont *font, INT *style)
395 TRACE("%p %p\n", font, style);
397 if (!(font && style))
398 return InvalidParameter;
400 *style = get_font_style(font);
401 TRACE("%s,%d => %d\n", debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *style);
403 return Ok;
406 /*******************************************************************************
407 * GdipGetFontUnit [GDIPLUS.@]
409 * PARAMS
410 * font [I] Font to retrieve from
411 * unit [O] Return value
413 * RETURNS
414 * FAILURE: font or unit was NULL
415 * OK: otherwise
417 GpStatus WINGDIPAPI GdipGetFontUnit(GpFont *font, Unit *unit)
419 TRACE("(%p, %p)\n", font, unit);
421 if (!(font && unit)) return InvalidParameter;
423 *unit = font->unit;
424 TRACE("%s,%d => %d\n", debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *unit);
426 return Ok;
429 /*******************************************************************************
430 * GdipGetLogFontA [GDIPLUS.@]
432 GpStatus WINGDIPAPI GdipGetLogFontA(GpFont *font, GpGraphics *graphics,
433 LOGFONTA *lfa)
435 GpStatus status;
436 LOGFONTW lfw;
438 TRACE("(%p, %p, %p)\n", font, graphics, lfa);
440 status = GdipGetLogFontW(font, graphics, &lfw);
441 if(status != Ok)
442 return status;
444 memcpy(lfa, &lfw, FIELD_OFFSET(LOGFONTA,lfFaceName) );
446 if(!WideCharToMultiByte(CP_ACP, 0, lfw.lfFaceName, -1, lfa->lfFaceName, LF_FACESIZE, NULL, NULL))
447 return GenericError;
449 return Ok;
452 /*******************************************************************************
453 * GdipGetLogFontW [GDIPLUS.@]
455 GpStatus WINGDIPAPI GdipGetLogFontW(GpFont *font, GpGraphics *graphics, LOGFONTW *lf)
457 REAL angle, rel_height, height;
458 GpMatrix *matrix;
459 GpPointF pt[3];
461 TRACE("(%p, %p, %p)\n", font, graphics, lf);
463 if (!font || !graphics || !lf)
464 return InvalidParameter;
466 GdipCloneMatrix(graphics->worldtrans, &matrix);
468 if (font->unit == UnitPixel)
470 height = units_to_pixels(font->emSize, graphics->unit, graphics->yres);
471 if (graphics->unit != UnitDisplay)
472 GdipScaleMatrix(matrix, graphics->scale, graphics->scale, MatrixOrderAppend);
474 else
476 if (graphics->unit == UnitDisplay || graphics->unit == UnitPixel)
477 height = units_to_pixels(font->emSize, font->unit, graphics->xres);
478 else
479 height = units_to_pixels(font->emSize, font->unit, graphics->yres);
482 pt[0].X = 0.0;
483 pt[0].Y = 0.0;
484 pt[1].X = 1.0;
485 pt[1].Y = 0.0;
486 pt[2].X = 0.0;
487 pt[2].Y = 1.0;
488 GdipTransformMatrixPoints(matrix, pt, 3);
489 angle = -gdiplus_atan2((pt[1].Y - pt[0].Y), (pt[1].X - pt[0].X));
490 rel_height = sqrt((pt[2].Y - pt[0].Y) * (pt[2].Y - pt[0].Y)+
491 (pt[2].X - pt[0].X) * (pt[2].X - pt[0].X));
492 GdipDeleteMatrix(matrix);
494 lf->lfHeight = -gdip_round(height * rel_height);
495 lf->lfWidth = 0;
496 lf->lfEscapement = lf->lfOrientation = gdip_round((angle / M_PI) * 1800.0);
497 if (lf->lfEscapement < 0)
499 lf->lfEscapement += 3600;
500 lf->lfOrientation += 3600;
502 lf->lfWeight = font->otm.otmTextMetrics.tmWeight;
503 lf->lfItalic = font->otm.otmTextMetrics.tmItalic ? 1 : 0;
504 lf->lfUnderline = font->otm.otmTextMetrics.tmUnderlined ? 1 : 0;
505 lf->lfStrikeOut = font->otm.otmTextMetrics.tmStruckOut ? 1 : 0;
506 lf->lfCharSet = font->otm.otmTextMetrics.tmCharSet;
507 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
508 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
509 lf->lfQuality = DEFAULT_QUALITY;
510 lf->lfPitchAndFamily = 0;
511 strcpyW(lf->lfFaceName, font->family->FamilyName);
513 TRACE("=> %s,%d\n", debugstr_w(lf->lfFaceName), lf->lfHeight);
515 return Ok;
518 /*******************************************************************************
519 * GdipCloneFont [GDIPLUS.@]
521 GpStatus WINGDIPAPI GdipCloneFont(GpFont *font, GpFont **cloneFont)
523 GpStatus stat;
525 TRACE("(%p, %p)\n", font, cloneFont);
527 if(!font || !cloneFont)
528 return InvalidParameter;
530 *cloneFont = GdipAlloc(sizeof(GpFont));
531 if(!*cloneFont) return OutOfMemory;
533 **cloneFont = *font;
534 stat = GdipCloneFontFamily(font->family, &(*cloneFont)->family);
535 if (stat != Ok) GdipFree(*cloneFont);
537 return stat;
540 /*******************************************************************************
541 * GdipGetFontHeight [GDIPLUS.@]
542 * PARAMS
543 * font [I] Font to retrieve height from
544 * graphics [I] The current graphics context
545 * height [O] Resulting height
546 * RETURNS
547 * SUCCESS: Ok
548 * FAILURE: Another element of GpStatus
550 * NOTES
551 * Forwards to GdipGetFontHeightGivenDPI
553 GpStatus WINGDIPAPI GdipGetFontHeight(GDIPCONST GpFont *font,
554 GDIPCONST GpGraphics *graphics, REAL *height)
556 REAL dpi;
557 GpStatus stat;
558 REAL font_height;
560 TRACE("%p %p %p\n", font, graphics, height);
562 stat = GdipGetFontHeightGivenDPI(font, font->family->dpi, &font_height);
563 if (stat != Ok) return stat;
565 if (!graphics)
567 *height = font_height;
568 TRACE("%s,%d => %f\n",
569 debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *height);
570 return Ok;
573 stat = GdipGetDpiY((GpGraphics *)graphics, &dpi);
574 if (stat != Ok) return stat;
576 *height = pixels_to_units(font_height, graphics->unit, dpi);
578 TRACE("%s,%d(unit %d) => %f\n",
579 debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, graphics->unit, *height);
580 return Ok;
583 /*******************************************************************************
584 * GdipGetFontHeightGivenDPI [GDIPLUS.@]
585 * PARAMS
586 * font [I] Font to retrieve DPI from
587 * dpi [I] DPI to assume
588 * height [O] Return value
590 * RETURNS
591 * SUCCESS: Ok
592 * FAILURE: InvalidParameter if font or height is NULL
594 * NOTES
595 * According to MSDN, the result is (lineSpacing)*(fontSize / emHeight)*dpi
596 * (for anything other than unit Pixel)
598 GpStatus WINGDIPAPI GdipGetFontHeightGivenDPI(GDIPCONST GpFont *font, REAL dpi, REAL *height)
600 GpStatus stat;
601 INT style;
602 UINT16 line_spacing, em_height;
603 REAL font_size;
605 if (!font || !height) return InvalidParameter;
607 TRACE("%p (%s), %f, %p\n", font,
608 debugstr_w(font->family->FamilyName), dpi, height);
610 font_size = units_to_pixels(get_font_size(font), font->unit, dpi);
611 style = get_font_style(font);
612 stat = GdipGetLineSpacing(font->family, style, &line_spacing);
613 if (stat != Ok) return stat;
614 stat = GdipGetEmHeight(font->family, style, &em_height);
615 if (stat != Ok) return stat;
617 *height = (REAL)line_spacing * font_size / (REAL)em_height;
619 TRACE("%s,%d => %f\n",
620 debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *height);
622 return Ok;
625 /***********************************************************************
626 * Borrowed from GDI32:
628 * Elf is really an ENUMLOGFONTEXW, and ntm is a NEWTEXTMETRICEXW.
629 * We have to use other types because of the FONTENUMPROCW definition.
631 static INT CALLBACK is_font_installed_proc(const LOGFONTW *elf,
632 const TEXTMETRICW *ntm, DWORD type, LPARAM lParam)
634 if (type & RASTER_FONTTYPE)
635 return 1;
637 *(LOGFONTW *)lParam = *elf;
639 return 0;
642 struct font_metrics
644 WCHAR facename[LF_FACESIZE];
645 UINT16 em_height, ascent, descent, line_spacing; /* in font units */
646 int dpi;
649 static BOOL get_font_metrics(HDC hdc, struct font_metrics *fm)
651 OUTLINETEXTMETRICW otm;
652 TT_OS2_V2 tt_os2;
653 TT_HHEA tt_hori;
654 LONG size;
655 UINT16 line_gap;
657 otm.otmSize = sizeof(otm);
658 if (!GetOutlineTextMetricsW(hdc, otm.otmSize, &otm)) return FALSE;
660 GetTextFaceW(hdc, LF_FACESIZE, fm->facename);
662 fm->em_height = otm.otmEMSquare;
663 fm->dpi = GetDeviceCaps(hdc, LOGPIXELSY);
665 memset(&tt_hori, 0, sizeof(tt_hori));
666 if (GetFontData(hdc, MS_HHEA_TAG, 0, &tt_hori, sizeof(tt_hori)) != GDI_ERROR)
668 fm->ascent = GET_BE_WORD(tt_hori.Ascender);
669 fm->descent = -GET_BE_WORD(tt_hori.Descender);
670 TRACE("hhea: ascent %d, descent %d\n", fm->ascent, fm->descent);
671 line_gap = GET_BE_WORD(tt_hori.LineGap);
672 fm->line_spacing = fm->ascent + fm->descent + line_gap;
673 TRACE("line_gap %u, line_spacing %u\n", line_gap, fm->line_spacing);
674 if (fm->ascent + fm->descent != 0) return TRUE;
677 size = GetFontData(hdc, MS_OS2_TAG, 0, NULL, 0);
678 if (size == GDI_ERROR) return FALSE;
680 if (size > sizeof(tt_os2)) size = sizeof(tt_os2);
682 memset(&tt_os2, 0, sizeof(tt_os2));
683 if (GetFontData(hdc, MS_OS2_TAG, 0, &tt_os2, size) != size) return FALSE;
685 fm->ascent = GET_BE_WORD(tt_os2.usWinAscent);
686 fm->descent = GET_BE_WORD(tt_os2.usWinDescent);
687 TRACE("usWinAscent %u, usWinDescent %u\n", fm->ascent, fm->descent);
688 if (fm->ascent + fm->descent == 0)
690 fm->ascent = GET_BE_WORD(tt_os2.sTypoAscender);
691 fm->descent = GET_BE_WORD(tt_os2.sTypoDescender);
692 TRACE("sTypoAscender %u, sTypoDescender %u\n", fm->ascent, fm->descent);
694 line_gap = GET_BE_WORD(tt_os2.sTypoLineGap);
695 fm->line_spacing = fm->ascent + fm->descent + line_gap;
696 TRACE("line_gap %u, line_spacing %u\n", line_gap, fm->line_spacing);
697 return TRUE;
700 static GpStatus find_installed_font(const WCHAR *name, struct font_metrics *fm)
702 LOGFONTW lf;
703 HDC hdc = CreateCompatibleDC(0);
704 GpStatus ret = FontFamilyNotFound;
706 if(!EnumFontFamiliesW(hdc, name, is_font_installed_proc, (LPARAM)&lf))
708 HFONT hfont, old_font;
710 hfont = CreateFontIndirectW(&lf);
711 old_font = SelectObject(hdc, hfont);
712 ret = get_font_metrics(hdc, fm) ? Ok : NotTrueTypeFont;
713 SelectObject(hdc, old_font);
714 DeleteObject(hfont);
717 DeleteDC(hdc);
718 return ret;
721 /*******************************************************************************
722 * GdipCreateFontFamilyFromName [GDIPLUS.@]
724 * Creates a font family object based on a supplied name
726 * PARAMS
727 * name [I] Name of the font
728 * fontCollection [I] What font collection (if any) the font belongs to (may be NULL)
729 * FontFamily [O] Pointer to the resulting FontFamily object
731 * RETURNS
732 * SUCCESS: Ok
733 * FAILURE: FamilyNotFound if the requested FontFamily does not exist on the system
734 * FAILURE: Invalid parameter if FontFamily or name is NULL
736 * NOTES
737 * If fontCollection is NULL then the object is not part of any collection
741 GpStatus WINGDIPAPI GdipCreateFontFamilyFromName(GDIPCONST WCHAR *name,
742 GpFontCollection *fontCollection,
743 GpFontFamily **FontFamily)
745 GpStatus stat;
746 GpFontFamily* ffamily;
747 struct font_metrics fm;
749 TRACE("%s, %p %p\n", debugstr_w(name), fontCollection, FontFamily);
751 if (!(name && FontFamily))
752 return InvalidParameter;
753 if (fontCollection)
754 FIXME("No support for FontCollections yet!\n");
756 stat = find_installed_font(name, &fm);
757 if (stat != Ok) return stat;
759 ffamily = GdipAlloc(sizeof (GpFontFamily));
760 if (!ffamily) return OutOfMemory;
762 lstrcpyW(ffamily->FamilyName, fm.facename);
763 ffamily->em_height = fm.em_height;
764 ffamily->ascent = fm.ascent;
765 ffamily->descent = fm.descent;
766 ffamily->line_spacing = fm.line_spacing;
767 ffamily->dpi = fm.dpi;
769 *FontFamily = ffamily;
771 TRACE("<-- %p\n", ffamily);
773 return Ok;
776 static GpStatus clone_font_family(const GpFontFamily *family, GpFontFamily **clone)
778 *clone = GdipAlloc(sizeof(GpFontFamily));
779 if (!*clone) return OutOfMemory;
781 **clone = *family;
783 return Ok;
786 /*******************************************************************************
787 * GdipCloneFontFamily [GDIPLUS.@]
789 * Creates a deep copy of a Font Family object
791 * PARAMS
792 * FontFamily [I] Font to clone
793 * clonedFontFamily [O] The resulting cloned font
795 * RETURNS
796 * SUCCESS: Ok
798 GpStatus WINGDIPAPI GdipCloneFontFamily(GpFontFamily* FontFamily, GpFontFamily** clonedFontFamily)
800 GpStatus status;
802 if (!(FontFamily && clonedFontFamily)) return InvalidParameter;
804 TRACE("%p (%s), %p\n", FontFamily,
805 debugstr_w(FontFamily->FamilyName), clonedFontFamily);
807 status = clone_font_family(FontFamily, clonedFontFamily);
808 if (status != Ok) return status;
810 TRACE("<-- %p\n", *clonedFontFamily);
812 return Ok;
815 /*******************************************************************************
816 * GdipGetFamilyName [GDIPLUS.@]
818 * Returns the family name into name
820 * PARAMS
821 * *family [I] Family to retrieve from
822 * *name [O] WCHARS of the family name
823 * LANGID [I] charset
825 * RETURNS
826 * SUCCESS: Ok
827 * FAILURE: InvalidParameter if family is NULL
829 * NOTES
830 * If name is a NULL ptr, then both XP and Vista will crash (so we do as well)
832 GpStatus WINGDIPAPI GdipGetFamilyName (GDIPCONST GpFontFamily *family,
833 WCHAR *name, LANGID language)
835 static int lang_fixme;
837 if (family == NULL)
838 return InvalidParameter;
840 TRACE("%p, %p, %d\n", family, name, language);
842 if (language != LANG_NEUTRAL && !lang_fixme++)
843 FIXME("No support for handling of multiple languages!\n");
845 lstrcpynW (name, family->FamilyName, LF_FACESIZE);
847 return Ok;
851 /*****************************************************************************
852 * GdipDeleteFontFamily [GDIPLUS.@]
854 * Removes the specified FontFamily
856 * PARAMS
857 * *FontFamily [I] The family to delete
859 * RETURNS
860 * SUCCESS: Ok
861 * FAILURE: InvalidParameter if FontFamily is NULL.
864 GpStatus WINGDIPAPI GdipDeleteFontFamily(GpFontFamily *FontFamily)
866 if (!FontFamily)
867 return InvalidParameter;
868 TRACE("Deleting %p (%s)\n", FontFamily, debugstr_w(FontFamily->FamilyName));
870 GdipFree (FontFamily);
872 return Ok;
875 GpStatus WINGDIPAPI GdipGetCellAscent(GDIPCONST GpFontFamily *family,
876 INT style, UINT16* CellAscent)
878 if (!(family && CellAscent)) return InvalidParameter;
880 *CellAscent = family->ascent;
881 TRACE("%s => %u\n", debugstr_w(family->FamilyName), *CellAscent);
883 return Ok;
886 GpStatus WINGDIPAPI GdipGetCellDescent(GDIPCONST GpFontFamily *family,
887 INT style, UINT16* CellDescent)
889 TRACE("(%p, %d, %p)\n", family, style, CellDescent);
891 if (!(family && CellDescent)) return InvalidParameter;
893 *CellDescent = family->descent;
894 TRACE("%s => %u\n", debugstr_w(family->FamilyName), *CellDescent);
896 return Ok;
899 /*******************************************************************************
900 * GdipGetEmHeight [GDIPLUS.@]
902 * Gets the height of the specified family in EmHeights
904 * PARAMS
905 * family [I] Family to retrieve from
906 * style [I] (optional) style
907 * EmHeight [O] return value
909 * RETURNS
910 * SUCCESS: Ok
911 * FAILURE: InvalidParameter
913 GpStatus WINGDIPAPI GdipGetEmHeight(GDIPCONST GpFontFamily *family, INT style, UINT16* EmHeight)
915 if (!(family && EmHeight)) return InvalidParameter;
917 TRACE("%p (%s), %d, %p\n", family, debugstr_w(family->FamilyName), style, EmHeight);
919 *EmHeight = family->em_height;
920 TRACE("%s => %u\n", debugstr_w(family->FamilyName), *EmHeight);
922 return Ok;
926 /*******************************************************************************
927 * GdipGetLineSpacing [GDIPLUS.@]
929 * Returns the line spacing in design units
931 * PARAMS
932 * family [I] Family to retrieve from
933 * style [I] (Optional) font style
934 * LineSpacing [O] Return value
936 * RETURNS
937 * SUCCESS: Ok
938 * FAILURE: InvalidParameter (family or LineSpacing was NULL)
940 GpStatus WINGDIPAPI GdipGetLineSpacing(GDIPCONST GpFontFamily *family,
941 INT style, UINT16* LineSpacing)
943 TRACE("%p, %d, %p\n", family, style, LineSpacing);
945 if (!(family && LineSpacing))
946 return InvalidParameter;
948 if (style) FIXME("ignoring style\n");
950 *LineSpacing = family->line_spacing;
951 TRACE("%s => %u\n", debugstr_w(family->FamilyName), *LineSpacing);
953 return Ok;
956 static INT CALLBACK font_has_style_proc(const LOGFONTW *elf,
957 const TEXTMETRICW *ntm, DWORD type, LPARAM lParam)
959 INT fontstyle = FontStyleRegular;
961 if (!ntm) return 1;
963 if (ntm->tmWeight >= FW_BOLD) fontstyle |= FontStyleBold;
964 if (ntm->tmItalic) fontstyle |= FontStyleItalic;
965 if (ntm->tmUnderlined) fontstyle |= FontStyleUnderline;
966 if (ntm->tmStruckOut) fontstyle |= FontStyleStrikeout;
968 return (INT)lParam != fontstyle;
971 GpStatus WINGDIPAPI GdipIsStyleAvailable(GDIPCONST GpFontFamily* family,
972 INT style, BOOL* IsStyleAvailable)
974 HDC hdc;
976 TRACE("%p %d %p\n", family, style, IsStyleAvailable);
978 if (!(family && IsStyleAvailable))
979 return InvalidParameter;
981 *IsStyleAvailable = FALSE;
983 hdc = GetDC(0);
985 if(!EnumFontFamiliesW(hdc, family->FamilyName, font_has_style_proc, (LPARAM)style))
986 *IsStyleAvailable = TRUE;
988 ReleaseDC(0, hdc);
990 return Ok;
993 /*****************************************************************************
994 * GdipGetGenericFontFamilyMonospace [GDIPLUS.@]
996 * Obtains a serif family (Courier New on Windows)
998 * PARAMS
999 * **nativeFamily [I] Where the font will be stored
1001 * RETURNS
1002 * InvalidParameter if nativeFamily is NULL.
1003 * Ok otherwise.
1005 GpStatus WINGDIPAPI GdipGetGenericFontFamilyMonospace(GpFontFamily **nativeFamily)
1007 static const WCHAR CourierNew[] = {'C','o','u','r','i','e','r',' ','N','e','w','\0'};
1008 static const WCHAR LiberationMono[] = {'L','i','b','e','r','a','t','i','o','n',' ','M','o','n','o','\0'};
1009 GpStatus stat;
1011 if (nativeFamily == NULL) return InvalidParameter;
1013 stat = GdipCreateFontFamilyFromName(CourierNew, NULL, nativeFamily);
1015 if (stat == FontFamilyNotFound)
1016 stat = GdipCreateFontFamilyFromName(LiberationMono, NULL, nativeFamily);
1018 if (stat == FontFamilyNotFound)
1019 ERR("Missing 'Courier New' font\n");
1021 return stat;
1024 /*****************************************************************************
1025 * GdipGetGenericFontFamilySerif [GDIPLUS.@]
1027 * Obtains a serif family (Times New Roman on Windows)
1029 * PARAMS
1030 * **nativeFamily [I] Where the font will be stored
1032 * RETURNS
1033 * InvalidParameter if nativeFamily is NULL.
1034 * Ok otherwise.
1036 GpStatus WINGDIPAPI GdipGetGenericFontFamilySerif(GpFontFamily **nativeFamily)
1038 static const WCHAR TimesNewRoman[] = {'T','i','m','e','s',' ','N','e','w',' ','R','o','m','a','n','\0'};
1039 static const WCHAR LiberationSerif[] = {'L','i','b','e','r','a','t','i','o','n',' ','S','e','r','i','f','\0'};
1040 GpStatus stat;
1042 TRACE("(%p)\n", nativeFamily);
1044 if (nativeFamily == NULL) return InvalidParameter;
1046 stat = GdipCreateFontFamilyFromName(TimesNewRoman, NULL, nativeFamily);
1048 if (stat == FontFamilyNotFound)
1049 stat = GdipCreateFontFamilyFromName(LiberationSerif, NULL, nativeFamily);
1051 if (stat == FontFamilyNotFound)
1052 ERR("Missing 'Times New Roman' font\n");
1054 return stat;
1057 /*****************************************************************************
1058 * GdipGetGenericFontFamilySansSerif [GDIPLUS.@]
1060 * Obtains a serif family (Microsoft Sans Serif on Windows)
1062 * PARAMS
1063 * **nativeFamily [I] Where the font will be stored
1065 * RETURNS
1066 * InvalidParameter if nativeFamily is NULL.
1067 * Ok otherwise.
1069 GpStatus WINGDIPAPI GdipGetGenericFontFamilySansSerif(GpFontFamily **nativeFamily)
1071 GpStatus stat;
1072 static const WCHAR MicrosoftSansSerif[] = {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f','\0'};
1073 static const WCHAR Tahoma[] = {'T','a','h','o','m','a','\0'};
1075 TRACE("(%p)\n", nativeFamily);
1077 if (nativeFamily == NULL) return InvalidParameter;
1079 stat = GdipCreateFontFamilyFromName(MicrosoftSansSerif, NULL, nativeFamily);
1081 if (stat == FontFamilyNotFound)
1082 /* FIXME: Microsoft Sans Serif is not installed on Wine. */
1083 stat = GdipCreateFontFamilyFromName(Tahoma, NULL, nativeFamily);
1085 return stat;
1088 /*****************************************************************************
1089 * GdipGetGenericFontFamilySansSerif [GDIPLUS.@]
1091 GpStatus WINGDIPAPI GdipNewPrivateFontCollection(GpFontCollection** fontCollection)
1093 TRACE("%p\n", fontCollection);
1095 if (!fontCollection)
1096 return InvalidParameter;
1098 *fontCollection = GdipAlloc(sizeof(GpFontCollection));
1099 if (!*fontCollection) return OutOfMemory;
1101 (*fontCollection)->FontFamilies = NULL;
1102 (*fontCollection)->count = 0;
1103 (*fontCollection)->allocated = 0;
1105 TRACE("<-- %p\n", *fontCollection);
1107 return Ok;
1110 /*****************************************************************************
1111 * GdipDeletePrivateFontCollection [GDIPLUS.@]
1113 GpStatus WINGDIPAPI GdipDeletePrivateFontCollection(GpFontCollection **fontCollection)
1115 INT i;
1117 TRACE("%p\n", fontCollection);
1119 if (!fontCollection)
1120 return InvalidParameter;
1122 for (i = 0; i < (*fontCollection)->count; i++) GdipFree((*fontCollection)->FontFamilies[i]);
1123 GdipFree(*fontCollection);
1125 return Ok;
1128 /*****************************************************************************
1129 * GdipPrivateAddFontFile [GDIPLUS.@]
1131 GpStatus WINGDIPAPI GdipPrivateAddFontFile(GpFontCollection* fontCollection,
1132 GDIPCONST WCHAR* filename)
1134 FIXME("stub: %p, %s\n", fontCollection, debugstr_w(filename));
1136 if (!(fontCollection && filename))
1137 return InvalidParameter;
1139 return NotImplemented;
1142 /* Copied from msi/font.c */
1144 typedef struct _tagTT_OFFSET_TABLE {
1145 USHORT uMajorVersion;
1146 USHORT uMinorVersion;
1147 USHORT uNumOfTables;
1148 USHORT uSearchRange;
1149 USHORT uEntrySelector;
1150 USHORT uRangeShift;
1151 } TT_OFFSET_TABLE;
1153 typedef struct _tagTT_TABLE_DIRECTORY {
1154 char szTag[4]; /* table name */
1155 ULONG uCheckSum; /* Check sum */
1156 ULONG uOffset; /* Offset from beginning of file */
1157 ULONG uLength; /* length of the table in bytes */
1158 } TT_TABLE_DIRECTORY;
1160 typedef struct _tagTT_NAME_TABLE_HEADER {
1161 USHORT uFSelector; /* format selector. Always 0 */
1162 USHORT uNRCount; /* Name Records count */
1163 USHORT uStorageOffset; /* Offset for strings storage,
1164 * from start of the table */
1165 } TT_NAME_TABLE_HEADER;
1167 #define NAME_ID_FULL_FONT_NAME 4
1168 #define NAME_ID_VERSION 5
1170 typedef struct _tagTT_NAME_RECORD {
1171 USHORT uPlatformID;
1172 USHORT uEncodingID;
1173 USHORT uLanguageID;
1174 USHORT uNameID;
1175 USHORT uStringLength;
1176 USHORT uStringOffset; /* from start of storage area */
1177 } TT_NAME_RECORD;
1179 #define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
1180 #define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))
1183 * Code based off of code located here
1184 * http://www.codeproject.com/gdi/fontnamefromfile.asp
1186 static WCHAR *load_ttf_name_id( const char *mem, DWORD_PTR size, DWORD id, WCHAR *ret, DWORD len )
1188 const TT_TABLE_DIRECTORY *tblDir;
1189 TT_OFFSET_TABLE ttOffsetTable;
1190 TT_NAME_TABLE_HEADER ttNTHeader;
1191 TT_NAME_RECORD ttRecord;
1192 DWORD ofs, pos;
1193 int i;
1195 if (sizeof(TT_OFFSET_TABLE) > size)
1196 return NULL;
1197 ttOffsetTable = *(TT_OFFSET_TABLE*)mem;
1198 ttOffsetTable.uNumOfTables = SWAPWORD(ttOffsetTable.uNumOfTables);
1199 ttOffsetTable.uMajorVersion = SWAPWORD(ttOffsetTable.uMajorVersion);
1200 ttOffsetTable.uMinorVersion = SWAPWORD(ttOffsetTable.uMinorVersion);
1202 if (ttOffsetTable.uMajorVersion != 1 || ttOffsetTable.uMinorVersion != 0)
1203 return NULL;
1205 pos = sizeof(ttOffsetTable);
1206 for (i = 0; i < ttOffsetTable.uNumOfTables; i++)
1208 tblDir = (const TT_TABLE_DIRECTORY*)&mem[pos];
1209 pos += sizeof(*tblDir);
1210 if (memcmp(tblDir->szTag,"name",4)==0)
1212 ofs = SWAPLONG(tblDir->uOffset);
1213 break;
1216 if (i >= ttOffsetTable.uNumOfTables)
1217 return NULL;
1219 pos = ofs + sizeof(ttNTHeader);
1220 if (pos > size)
1221 return NULL;
1222 ttNTHeader = *(TT_NAME_TABLE_HEADER*)&mem[ofs];
1223 ttNTHeader.uNRCount = SWAPWORD(ttNTHeader.uNRCount);
1224 ttNTHeader.uStorageOffset = SWAPWORD(ttNTHeader.uStorageOffset);
1225 for(i=0; i<ttNTHeader.uNRCount; i++)
1227 ttRecord = *(TT_NAME_RECORD*)&mem[pos];
1228 pos += sizeof(ttRecord);
1229 if (pos > size)
1230 return NULL;
1232 ttRecord.uNameID = SWAPWORD(ttRecord.uNameID);
1233 if (ttRecord.uNameID == id)
1235 const char *buf;
1237 ttRecord.uStringLength = SWAPWORD(ttRecord.uStringLength);
1238 ttRecord.uStringOffset = SWAPWORD(ttRecord.uStringOffset);
1239 if (ofs + ttRecord.uStringOffset + ttNTHeader.uStorageOffset + ttRecord.uStringLength > size)
1240 return NULL;
1241 buf = mem + ofs + ttRecord.uStringOffset + ttNTHeader.uStorageOffset;
1242 len = MultiByteToWideChar(CP_ACP, 0, buf, ttRecord.uStringLength, ret, len-1);
1243 ret[len] = 0;
1244 return ret;
1247 return NULL;
1250 static INT CALLBACK add_font_proc(const LOGFONTW *lfw, const TEXTMETRICW *ntm, DWORD type, LPARAM lParam);
1252 /*****************************************************************************
1253 * GdipPrivateAddMemoryFont [GDIPLUS.@]
1255 GpStatus WINGDIPAPI GdipPrivateAddMemoryFont(GpFontCollection* fontCollection,
1256 GDIPCONST void* memory, INT length)
1258 WCHAR buf[32], *name;
1259 DWORD count = 0;
1260 HANDLE font;
1261 TRACE("%p, %p, %d\n", fontCollection, memory, length);
1263 if (!fontCollection || !memory || !length)
1264 return InvalidParameter;
1266 name = load_ttf_name_id(memory, length, NAME_ID_FULL_FONT_NAME, buf, sizeof(buf)/sizeof(*buf));
1267 if (!name)
1268 return OutOfMemory;
1270 font = AddFontMemResourceEx((void*)memory, length, NULL, &count);
1271 TRACE("%s: %p/%u\n", debugstr_w(name), font, count);
1272 if (!font || !count)
1273 return InvalidParameter;
1275 if (count)
1277 HDC hdc;
1278 LOGFONTW lfw;
1280 hdc = GetDC(0);
1282 lfw.lfCharSet = DEFAULT_CHARSET;
1283 lstrcpyW(lfw.lfFaceName, name);
1284 lfw.lfPitchAndFamily = 0;
1286 if (!EnumFontFamiliesExW(hdc, &lfw, add_font_proc, (LPARAM)fontCollection, 0))
1288 ReleaseDC(0, hdc);
1289 return OutOfMemory;
1292 ReleaseDC(0, hdc);
1294 return Ok;
1297 /*****************************************************************************
1298 * GdipGetFontCollectionFamilyCount [GDIPLUS.@]
1300 GpStatus WINGDIPAPI GdipGetFontCollectionFamilyCount(
1301 GpFontCollection* fontCollection, INT* numFound)
1303 TRACE("%p, %p\n", fontCollection, numFound);
1305 if (!(fontCollection && numFound))
1306 return InvalidParameter;
1308 *numFound = fontCollection->count;
1309 return Ok;
1312 /*****************************************************************************
1313 * GdipGetFontCollectionFamilyList [GDIPLUS.@]
1315 GpStatus WINGDIPAPI GdipGetFontCollectionFamilyList(
1316 GpFontCollection* fontCollection, INT numSought,
1317 GpFontFamily* gpfamilies[], INT* numFound)
1319 INT i;
1320 GpStatus stat=Ok;
1322 TRACE("%p, %d, %p, %p\n", fontCollection, numSought, gpfamilies, numFound);
1324 if (!(fontCollection && gpfamilies && numFound))
1325 return InvalidParameter;
1327 memset(gpfamilies, 0, sizeof(*gpfamilies) * numSought);
1329 for (i = 0; i < numSought && i < fontCollection->count && stat == Ok; i++)
1331 stat = GdipCloneFontFamily(fontCollection->FontFamilies[i], &gpfamilies[i]);
1334 if (stat == Ok)
1335 *numFound = i;
1336 else
1338 int numToFree=i;
1339 for (i=0; i<numToFree; i++)
1341 GdipDeleteFontFamily(gpfamilies[i]);
1342 gpfamilies[i] = NULL;
1346 return stat;
1349 void free_installed_fonts(void)
1351 while (installedFontCollection.count)
1352 GdipDeleteFontFamily(installedFontCollection.FontFamilies[--installedFontCollection.count]);
1353 HeapFree(GetProcessHeap(), 0, installedFontCollection.FontFamilies);
1354 installedFontCollection.FontFamilies = NULL;
1355 installedFontCollection.allocated = 0;
1358 static INT CALLBACK add_font_proc(const LOGFONTW *lfw, const TEXTMETRICW *ntm,
1359 DWORD type, LPARAM lParam)
1361 GpFontCollection* fonts = (GpFontCollection*)lParam;
1362 int i;
1364 if (type == RASTER_FONTTYPE)
1365 return 1;
1367 /* skip duplicates */
1368 for (i=0; i<fonts->count; i++)
1369 if (strcmpiW(lfw->lfFaceName, fonts->FontFamilies[i]->FamilyName) == 0)
1370 return 1;
1372 if (fonts->allocated == fonts->count)
1374 INT new_alloc_count = fonts->allocated+50;
1375 GpFontFamily** new_family_list = HeapAlloc(GetProcessHeap(), 0, new_alloc_count*sizeof(void*));
1377 if (!new_family_list)
1378 return 0;
1380 memcpy(new_family_list, fonts->FontFamilies, fonts->count*sizeof(void*));
1381 HeapFree(GetProcessHeap(), 0, fonts->FontFamilies);
1382 fonts->FontFamilies = new_family_list;
1383 fonts->allocated = new_alloc_count;
1386 if (GdipCreateFontFamilyFromName(lfw->lfFaceName, NULL, &fonts->FontFamilies[fonts->count]) == Ok)
1387 fonts->count++;
1388 else
1389 return 0;
1391 return 1;
1394 GpStatus WINGDIPAPI GdipNewInstalledFontCollection(
1395 GpFontCollection** fontCollection)
1397 TRACE("(%p)\n",fontCollection);
1399 if (!fontCollection)
1400 return InvalidParameter;
1402 if (installedFontCollection.count == 0)
1404 HDC hdc;
1405 LOGFONTW lfw;
1407 hdc = GetDC(0);
1409 lfw.lfCharSet = DEFAULT_CHARSET;
1410 lfw.lfFaceName[0] = 0;
1411 lfw.lfPitchAndFamily = 0;
1413 if (!EnumFontFamiliesExW(hdc, &lfw, add_font_proc, (LPARAM)&installedFontCollection, 0))
1415 free_installed_fonts();
1416 ReleaseDC(0, hdc);
1417 return OutOfMemory;
1420 ReleaseDC(0, hdc);
1423 *fontCollection = &installedFontCollection;
1425 return Ok;