windowscodecs: Protect from invalid input some metadata handler methods.
[wine.git] / dlls / gdiplus / font.c
blob51733f4360dbf0ed8a3b13781707a6813da3f1fd
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 const REAL mm_per_inch = 25.4;
122 static const REAL inch_per_point = 1.0/72.0;
124 static GpFontCollection installedFontCollection = {0};
126 static LONG em_size_to_pixel(REAL em_size, Unit unit, LONG dpi)
128 switch (unit)
130 default:
131 FIXME("Unhandled unit type: %d\n", unit);
132 return 0;
134 case UnitPixel:
135 case UnitWorld:
136 /* FIXME: Figure out when World != Pixel */
137 return em_size;
138 case UnitDisplay:
139 FIXME("Unknown behavior for UnitDisplay! Please report!\n");
140 /* FIXME: Figure out how this works...
141 * MSDN says that if "DISPLAY" is a monitor, then pixel should be
142 * used. That's not what I got. Tests on Windows revealed no output,
143 * and the tests in tests/font crash windows */
144 return 0;
145 case UnitPoint:
146 return em_size * dpi * inch_per_point;
147 case UnitInch:
148 return em_size * dpi;
149 case UnitDocument:
150 return em_size * dpi / 300.0; /* Per MSDN */
151 case UnitMillimeter:
152 return em_size * dpi / mm_per_inch;
156 /*******************************************************************************
157 * GdipCreateFont [GDIPLUS.@]
159 * Create a new font based off of a FontFamily
161 * PARAMS
162 * *fontFamily [I] Family to base the font off of
163 * emSize [I] Size of the font
164 * style [I] Bitwise OR of FontStyle enumeration
165 * unit [I] Unit emSize is measured in
166 * **font [I] the resulting Font object
168 * RETURNS
169 * SUCCESS: Ok
170 * FAILURE: InvalidParameter if fontfamily or font is NULL.
171 * FAILURE: FontFamilyNotFound if an invalid FontFamily is given
173 * NOTES
174 * UnitDisplay is unsupported.
175 * emSize is stored separately from lfHeight, to hold the fraction.
177 GpStatus WINGDIPAPI GdipCreateFont(GDIPCONST GpFontFamily *fontFamily,
178 REAL emSize, INT style, Unit unit, GpFont **font)
180 HFONT hfont;
181 OUTLINETEXTMETRICW otm;
182 LOGFONTW lfw;
183 HDC hdc;
184 GpStatus stat;
185 int ret;
187 if (!fontFamily || !font || emSize < 0.0)
188 return InvalidParameter;
190 TRACE("%p (%s), %f, %d, %d, %p\n", fontFamily,
191 debugstr_w(fontFamily->FamilyName), emSize, style, unit, font);
193 memset(&lfw, 0, sizeof(lfw));
195 stat = GdipGetFamilyName(fontFamily, lfw.lfFaceName, LANG_NEUTRAL);
196 if (stat != Ok) return stat;
198 lfw.lfHeight = -em_size_to_pixel(emSize, unit, fontFamily->dpi);
199 lfw.lfWeight = style & FontStyleBold ? FW_BOLD : FW_REGULAR;
200 lfw.lfItalic = style & FontStyleItalic;
201 lfw.lfUnderline = style & FontStyleUnderline;
202 lfw.lfStrikeOut = style & FontStyleStrikeout;
204 hfont = CreateFontIndirectW(&lfw);
205 hdc = CreateCompatibleDC(0);
206 SelectObject(hdc, hfont);
207 otm.otmSize = sizeof(otm);
208 ret = GetOutlineTextMetricsW(hdc, otm.otmSize, &otm);
209 DeleteDC(hdc);
210 DeleteObject(hfont);
212 if (!ret) return NotTrueTypeFont;
214 *font = GdipAlloc(sizeof(GpFont));
215 if (!*font) return OutOfMemory;
217 (*font)->unit = unit;
218 (*font)->emSize = emSize;
219 (*font)->otm = otm;
221 stat = clone_font_family(fontFamily, &(*font)->family);
222 if (stat != Ok)
224 GdipFree(*font);
225 return stat;
228 TRACE("<-- %p\n", *font);
230 return Ok;
233 /*******************************************************************************
234 * GdipCreateFontFromLogfontW [GDIPLUS.@]
236 GpStatus WINGDIPAPI GdipCreateFontFromLogfontW(HDC hdc,
237 GDIPCONST LOGFONTW *logfont, GpFont **font)
239 HFONT hfont, oldfont;
240 OUTLINETEXTMETRICW otm;
241 GpStatus stat;
242 int ret;
244 TRACE("(%p, %p, %p)\n", hdc, logfont, font);
246 if (!hdc || !logfont || !font)
247 return InvalidParameter;
249 hfont = CreateFontIndirectW(logfont);
250 oldfont = SelectObject(hdc, hfont);
251 otm.otmSize = sizeof(otm);
252 ret = GetOutlineTextMetricsW(hdc, otm.otmSize, &otm);
253 SelectObject(hdc, oldfont);
254 DeleteObject(hfont);
256 if (!ret) return NotTrueTypeFont;
258 *font = GdipAlloc(sizeof(GpFont));
259 if (!*font) return OutOfMemory;
261 (*font)->unit = UnitWorld;
262 (*font)->emSize = otm.otmTextMetrics.tmAscent;
263 (*font)->otm = otm;
265 stat = GdipCreateFontFamilyFromName(logfont->lfFaceName, NULL, &(*font)->family);
266 if (stat != Ok)
268 GdipFree(*font);
269 return NotTrueTypeFont;
272 TRACE("<-- %p\n", *font);
274 return Ok;
277 /*******************************************************************************
278 * GdipCreateFontFromLogfontA [GDIPLUS.@]
280 GpStatus WINGDIPAPI GdipCreateFontFromLogfontA(HDC hdc,
281 GDIPCONST LOGFONTA *lfa, GpFont **font)
283 LOGFONTW lfw;
285 TRACE("(%p, %p, %p)\n", hdc, lfa, font);
287 if(!lfa || !font)
288 return InvalidParameter;
290 memcpy(&lfw, lfa, FIELD_OFFSET(LOGFONTA,lfFaceName) );
292 if(!MultiByteToWideChar(CP_ACP, 0, lfa->lfFaceName, -1, lfw.lfFaceName, LF_FACESIZE))
293 return GenericError;
295 return GdipCreateFontFromLogfontW(hdc, &lfw, font);
298 /*******************************************************************************
299 * GdipDeleteFont [GDIPLUS.@]
301 GpStatus WINGDIPAPI GdipDeleteFont(GpFont* font)
303 TRACE("(%p)\n", font);
305 if(!font)
306 return InvalidParameter;
308 GdipDeleteFontFamily(font->family);
309 GdipFree(font);
311 return Ok;
314 /*******************************************************************************
315 * GdipCreateFontFromDC [GDIPLUS.@]
317 GpStatus WINGDIPAPI GdipCreateFontFromDC(HDC hdc, GpFont **font)
319 HFONT hfont;
320 LOGFONTW lfw;
322 TRACE("(%p, %p)\n", hdc, font);
324 if(!font)
325 return InvalidParameter;
327 hfont = GetCurrentObject(hdc, OBJ_FONT);
328 if(!hfont)
329 return GenericError;
331 if(!GetObjectW(hfont, sizeof(LOGFONTW), &lfw))
332 return GenericError;
334 return GdipCreateFontFromLogfontW(hdc, &lfw, font);
337 /*******************************************************************************
338 * GdipGetFamily [GDIPLUS.@]
340 * Returns the FontFamily for the specified Font
342 * PARAMS
343 * font [I] Font to request from
344 * family [O] Resulting FontFamily object
346 * RETURNS
347 * SUCCESS: Ok
348 * FAILURE: An element of GpStatus
350 GpStatus WINGDIPAPI GdipGetFamily(GpFont *font, GpFontFamily **family)
352 TRACE("%p %p\n", font, family);
354 if (!(font && family))
355 return InvalidParameter;
357 return GdipCloneFontFamily(font->family, family);
360 static REAL get_font_size(const GpFont *font)
362 return font->emSize;
365 /******************************************************************************
366 * GdipGetFontSize [GDIPLUS.@]
368 * Returns the size of the font in Units
370 * PARAMS
371 * *font [I] The font to retrieve size from
372 * *size [O] Pointer to hold retrieved value
374 * RETURNS
375 * SUCCESS: Ok
376 * FAILURE: InvalidParameter (font or size was NULL)
378 * NOTES
379 * Size returned is actually emSize -- not internal size used for drawing.
381 GpStatus WINGDIPAPI GdipGetFontSize(GpFont *font, REAL *size)
383 TRACE("(%p, %p)\n", font, size);
385 if (!(font && size)) return InvalidParameter;
387 *size = get_font_size(font);
388 TRACE("%s,%d => %f\n", debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *size);
390 return Ok;
393 static INT get_font_style(const GpFont *font)
395 INT style;
397 if (font->otm.otmTextMetrics.tmWeight > FW_REGULAR)
398 style = FontStyleBold;
399 else
400 style = FontStyleRegular;
401 if (font->otm.otmTextMetrics.tmItalic)
402 style |= FontStyleItalic;
403 if (font->otm.otmTextMetrics.tmUnderlined)
404 style |= FontStyleUnderline;
405 if (font->otm.otmTextMetrics.tmStruckOut)
406 style |= FontStyleStrikeout;
408 return style;
411 /*******************************************************************************
412 * GdipGetFontStyle [GDIPLUS.@]
414 * Gets the font's style, returned in bitwise OR of FontStyle enumeration
416 * PARAMS
417 * font [I] font to request from
418 * style [O] resulting pointer to a FontStyle enumeration
420 * RETURNS
421 * SUCCESS: Ok
422 * FAILURE: InvalidParameter
424 GpStatus WINGDIPAPI GdipGetFontStyle(GpFont *font, INT *style)
426 TRACE("%p %p\n", font, style);
428 if (!(font && style))
429 return InvalidParameter;
431 *style = get_font_style(font);
432 TRACE("%s,%d => %d\n", debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *style);
434 return Ok;
437 /*******************************************************************************
438 * GdipGetFontUnit [GDIPLUS.@]
440 * PARAMS
441 * font [I] Font to retrieve from
442 * unit [O] Return value
444 * RETURNS
445 * FAILURE: font or unit was NULL
446 * OK: otherwise
448 GpStatus WINGDIPAPI GdipGetFontUnit(GpFont *font, Unit *unit)
450 TRACE("(%p, %p)\n", font, unit);
452 if (!(font && unit)) return InvalidParameter;
454 *unit = font->unit;
455 TRACE("%s,%d => %d\n", debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *unit);
457 return Ok;
460 /*******************************************************************************
461 * GdipGetLogFontA [GDIPLUS.@]
463 GpStatus WINGDIPAPI GdipGetLogFontA(GpFont *font, GpGraphics *graphics,
464 LOGFONTA *lfa)
466 GpStatus status;
467 LOGFONTW lfw;
469 TRACE("(%p, %p, %p)\n", font, graphics, lfa);
471 status = GdipGetLogFontW(font, graphics, &lfw);
472 if(status != Ok)
473 return status;
475 memcpy(lfa, &lfw, FIELD_OFFSET(LOGFONTA,lfFaceName) );
477 if(!WideCharToMultiByte(CP_ACP, 0, lfw.lfFaceName, -1, lfa->lfFaceName, LF_FACESIZE, NULL, NULL))
478 return GenericError;
480 return Ok;
483 void get_log_fontW(const GpFont *font, GpGraphics *graphics, LOGFONTW *lf)
485 /* FIXME: use graphics */
486 lf->lfHeight = -em_size_to_pixel(font->emSize, font->unit, font->family->dpi);
487 lf->lfWidth = 0;
488 lf->lfEscapement = 0;
489 lf->lfOrientation = 0;
490 lf->lfWeight = font->otm.otmTextMetrics.tmWeight;
491 lf->lfItalic = font->otm.otmTextMetrics.tmItalic ? 1 : 0;
492 lf->lfUnderline = font->otm.otmTextMetrics.tmUnderlined ? 1 : 0;
493 lf->lfStrikeOut = font->otm.otmTextMetrics.tmStruckOut ? 1 : 0;
494 lf->lfCharSet = font->otm.otmTextMetrics.tmCharSet;
495 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
496 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
497 lf->lfQuality = DEFAULT_QUALITY;
498 lf->lfPitchAndFamily = 0;
499 strcpyW(lf->lfFaceName, font->family->FamilyName);
502 /*******************************************************************************
503 * GdipGetLogFontW [GDIPLUS.@]
505 GpStatus WINGDIPAPI GdipGetLogFontW(GpFont *font, GpGraphics *graphics,
506 LOGFONTW *lfw)
508 TRACE("(%p, %p, %p)\n", font, graphics, lfw);
510 if(!font || !graphics || !lfw)
511 return InvalidParameter;
513 get_log_fontW(font, graphics, lfw);
514 TRACE("=> %s,%d\n", debugstr_w(lfw->lfFaceName), lfw->lfHeight);
516 return Ok;
519 /*******************************************************************************
520 * GdipCloneFont [GDIPLUS.@]
522 GpStatus WINGDIPAPI GdipCloneFont(GpFont *font, GpFont **cloneFont)
524 GpStatus stat;
526 TRACE("(%p, %p)\n", font, cloneFont);
528 if(!font || !cloneFont)
529 return InvalidParameter;
531 *cloneFont = GdipAlloc(sizeof(GpFont));
532 if(!*cloneFont) return OutOfMemory;
534 **cloneFont = *font;
535 stat = GdipCloneFontFamily(font->family, &(*cloneFont)->family);
536 if (stat != Ok) GdipFree(*cloneFont);
538 return stat;
541 /*******************************************************************************
542 * GdipGetFontHeight [GDIPLUS.@]
543 * PARAMS
544 * font [I] Font to retrieve height from
545 * graphics [I] The current graphics context
546 * height [O] Resulting height
547 * RETURNS
548 * SUCCESS: Ok
549 * FAILURE: Another element of GpStatus
551 * NOTES
552 * Forwards to GdipGetFontHeightGivenDPI
554 GpStatus WINGDIPAPI GdipGetFontHeight(GDIPCONST GpFont *font,
555 GDIPCONST GpGraphics *graphics, REAL *height)
557 REAL dpi;
558 GpStatus stat;
560 TRACE("%p %p %p\n", font, graphics, height);
562 if (graphics)
564 stat = GdipGetDpiY((GpGraphics*)graphics, &dpi);
565 if (stat != Ok) return stat;
567 else
568 dpi = font->family->dpi;
570 return GdipGetFontHeightGivenDPI(font, dpi, height);
573 /*******************************************************************************
574 * GdipGetFontHeightGivenDPI [GDIPLUS.@]
575 * PARAMS
576 * font [I] Font to retrieve DPI from
577 * dpi [I] DPI to assume
578 * height [O] Return value
580 * RETURNS
581 * SUCCESS: Ok
582 * FAILURE: InvalidParameter if font or height is NULL
584 * NOTES
585 * According to MSDN, the result is (lineSpacing)*(fontSize / emHeight)*dpi
586 * (for anything other than unit Pixel)
588 GpStatus WINGDIPAPI GdipGetFontHeightGivenDPI(GDIPCONST GpFont *font, REAL dpi, REAL *height)
590 GpStatus stat;
591 INT style;
592 UINT16 line_spacing, em_height;
593 REAL font_height, font_size;
595 if (!font || !height) return InvalidParameter;
597 TRACE("%p (%s), %f, %p\n", font,
598 debugstr_w(font->family->FamilyName), dpi, height);
600 font_size = get_font_size(font);
601 style = get_font_style(font);
602 stat = GdipGetLineSpacing(font->family, style, &line_spacing);
603 if (stat != Ok) return stat;
604 stat = GdipGetEmHeight(font->family, style, &em_height);
605 if (stat != Ok) return stat;
607 font_height = (REAL)line_spacing * font_size / (REAL)em_height;
609 switch (font->unit)
611 case UnitPixel:
612 case UnitWorld:
613 *height = font_height;
614 break;
615 case UnitPoint:
616 *height = font_height * dpi * inch_per_point;
617 break;
618 case UnitInch:
619 *height = font_height * dpi;
620 break;
621 case UnitDocument:
622 *height = font_height * (dpi / 300.0);
623 break;
624 case UnitMillimeter:
625 *height = font_height * (dpi / mm_per_inch);
626 break;
627 default:
628 FIXME("Unhandled unit type: %d\n", font->unit);
629 return NotImplemented;
632 TRACE("%s,%d(unit %d) => %f\n",
633 debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, font->unit, *height);
635 return Ok;
638 /***********************************************************************
639 * Borrowed from GDI32:
641 * Elf is really an ENUMLOGFONTEXW, and ntm is a NEWTEXTMETRICEXW.
642 * We have to use other types because of the FONTENUMPROCW definition.
644 static INT CALLBACK is_font_installed_proc(const LOGFONTW *elf,
645 const TEXTMETRICW *ntm, DWORD type, LPARAM lParam)
647 if (type != TRUETYPE_FONTTYPE)
648 return 1;
650 *(LOGFONTW *)lParam = *elf;
652 return 0;
655 struct font_metrics
657 UINT16 em_height, ascent, descent, line_spacing; /* in font units */
658 int dpi;
661 static BOOL get_font_metrics(HDC hdc, struct font_metrics *fm)
663 OUTLINETEXTMETRICW otm;
664 TT_OS2_V2 tt_os2;
665 TT_HHEA tt_hori;
666 LONG size;
667 UINT16 line_gap;
669 otm.otmSize = sizeof(otm);
670 if (!GetOutlineTextMetricsW(hdc, otm.otmSize, &otm)) return FALSE;
672 fm->em_height = otm.otmEMSquare;
673 fm->dpi = GetDeviceCaps(hdc, LOGPIXELSY);
675 memset(&tt_hori, 0, sizeof(tt_hori));
676 if (GetFontData(hdc, MS_HHEA_TAG, 0, &tt_hori, sizeof(tt_hori)) != GDI_ERROR)
678 fm->ascent = GET_BE_WORD(tt_hori.Ascender);
679 fm->descent = -GET_BE_WORD(tt_hori.Descender);
680 TRACE("hhea: ascent %d, descent %d\n", fm->ascent, fm->descent);
681 line_gap = GET_BE_WORD(tt_hori.LineGap);
682 fm->line_spacing = fm->ascent + fm->descent + line_gap;
683 TRACE("line_gap %u, line_spacing %u\n", line_gap, fm->line_spacing);
684 if (fm->ascent + fm->descent != 0) return TRUE;
687 size = GetFontData(hdc, MS_OS2_TAG, 0, NULL, 0);
688 if (size == GDI_ERROR) return FALSE;
690 if (size > sizeof(tt_os2)) size = sizeof(tt_os2);
692 memset(&tt_os2, 0, sizeof(tt_os2));
693 if (GetFontData(hdc, MS_OS2_TAG, 0, &tt_os2, size) != size) return FALSE;
695 fm->ascent = GET_BE_WORD(tt_os2.usWinAscent);
696 fm->descent = GET_BE_WORD(tt_os2.usWinDescent);
697 TRACE("usWinAscent %u, usWinDescent %u\n", fm->ascent, fm->descent);
698 if (fm->ascent + fm->descent == 0)
700 fm->ascent = GET_BE_WORD(tt_os2.sTypoAscender);
701 fm->descent = GET_BE_WORD(tt_os2.sTypoDescender);
702 TRACE("sTypoAscender %u, sTypoDescender %u\n", fm->ascent, fm->descent);
704 line_gap = GET_BE_WORD(tt_os2.sTypoLineGap);
705 fm->line_spacing = fm->ascent + fm->descent + line_gap;
706 TRACE("line_gap %u, line_spacing %u\n", line_gap, fm->line_spacing);
707 return TRUE;
710 static GpStatus find_installed_font(const WCHAR *name, struct font_metrics *fm)
712 LOGFONTW lf;
713 HDC hdc = CreateCompatibleDC(0);
714 GpStatus ret = FontFamilyNotFound;
716 if(!EnumFontFamiliesW(hdc, name, is_font_installed_proc, (LPARAM)&lf))
718 HFONT hfont, old_font;
720 hfont = CreateFontIndirectW(&lf);
721 old_font = SelectObject(hdc, hfont);
722 ret = get_font_metrics(hdc, fm) ? Ok : NotTrueTypeFont;
723 SelectObject(hdc, old_font);
724 DeleteObject(hfont);
727 DeleteDC(hdc);
728 return ret;
731 /*******************************************************************************
732 * GdipCreateFontFamilyFromName [GDIPLUS.@]
734 * Creates a font family object based on a supplied name
736 * PARAMS
737 * name [I] Name of the font
738 * fontCollection [I] What font collection (if any) the font belongs to (may be NULL)
739 * FontFamily [O] Pointer to the resulting FontFamily object
741 * RETURNS
742 * SUCCESS: Ok
743 * FAILURE: FamilyNotFound if the requested FontFamily does not exist on the system
744 * FAILURE: Invalid parameter if FontFamily or name is NULL
746 * NOTES
747 * If fontCollection is NULL then the object is not part of any collection
751 GpStatus WINGDIPAPI GdipCreateFontFamilyFromName(GDIPCONST WCHAR *name,
752 GpFontCollection *fontCollection,
753 GpFontFamily **FontFamily)
755 GpStatus stat;
756 GpFontFamily* ffamily;
757 struct font_metrics fm;
759 TRACE("%s, %p %p\n", debugstr_w(name), fontCollection, FontFamily);
761 if (!(name && FontFamily))
762 return InvalidParameter;
763 if (fontCollection)
764 FIXME("No support for FontCollections yet!\n");
766 stat = find_installed_font(name, &fm);
767 if (stat != Ok) return stat;
769 ffamily = GdipAlloc(sizeof (GpFontFamily));
770 if (!ffamily) return OutOfMemory;
772 lstrcpynW(ffamily->FamilyName, name, LF_FACESIZE);
773 ffamily->em_height = fm.em_height;
774 ffamily->ascent = fm.ascent;
775 ffamily->descent = fm.descent;
776 ffamily->line_spacing = fm.line_spacing;
777 ffamily->dpi = fm.dpi;
779 *FontFamily = ffamily;
781 TRACE("<-- %p\n", ffamily);
783 return Ok;
786 static GpStatus clone_font_family(const GpFontFamily *family, GpFontFamily **clone)
788 *clone = GdipAlloc(sizeof(GpFontFamily));
789 if (!*clone) return OutOfMemory;
791 **clone = *family;
793 return Ok;
796 /*******************************************************************************
797 * GdipCloneFontFamily [GDIPLUS.@]
799 * Creates a deep copy of a Font Family object
801 * PARAMS
802 * FontFamily [I] Font to clone
803 * clonedFontFamily [O] The resulting cloned font
805 * RETURNS
806 * SUCCESS: Ok
808 GpStatus WINGDIPAPI GdipCloneFontFamily(GpFontFamily* FontFamily, GpFontFamily** clonedFontFamily)
810 GpStatus status;
812 if (!(FontFamily && clonedFontFamily)) return InvalidParameter;
814 TRACE("%p (%s), %p\n", FontFamily,
815 debugstr_w(FontFamily->FamilyName), clonedFontFamily);
817 status = clone_font_family(FontFamily, clonedFontFamily);
818 if (status != Ok) return status;
820 TRACE("<-- %p\n", *clonedFontFamily);
822 return Ok;
825 /*******************************************************************************
826 * GdipGetFamilyName [GDIPLUS.@]
828 * Returns the family name into name
830 * PARAMS
831 * *family [I] Family to retrieve from
832 * *name [O] WCHARS of the family name
833 * LANGID [I] charset
835 * RETURNS
836 * SUCCESS: Ok
837 * FAILURE: InvalidParameter if family is NULL
839 * NOTES
840 * If name is a NULL ptr, then both XP and Vista will crash (so we do as well)
842 GpStatus WINGDIPAPI GdipGetFamilyName (GDIPCONST GpFontFamily *family,
843 WCHAR *name, LANGID language)
845 static int lang_fixme;
847 if (family == NULL)
848 return InvalidParameter;
850 TRACE("%p, %p, %d\n", family, name, language);
852 if (language != LANG_NEUTRAL && !lang_fixme++)
853 FIXME("No support for handling of multiple languages!\n");
855 lstrcpynW (name, family->FamilyName, LF_FACESIZE);
857 return Ok;
861 /*****************************************************************************
862 * GdipDeleteFontFamily [GDIPLUS.@]
864 * Removes the specified FontFamily
866 * PARAMS
867 * *FontFamily [I] The family to delete
869 * RETURNS
870 * SUCCESS: Ok
871 * FAILURE: InvalidParameter if FontFamily is NULL.
874 GpStatus WINGDIPAPI GdipDeleteFontFamily(GpFontFamily *FontFamily)
876 if (!FontFamily)
877 return InvalidParameter;
878 TRACE("Deleting %p (%s)\n", FontFamily, debugstr_w(FontFamily->FamilyName));
880 GdipFree (FontFamily);
882 return Ok;
885 GpStatus WINGDIPAPI GdipGetCellAscent(GDIPCONST GpFontFamily *family,
886 INT style, UINT16* CellAscent)
888 if (!(family && CellAscent)) return InvalidParameter;
890 *CellAscent = family->ascent;
891 TRACE("%s => %u\n", debugstr_w(family->FamilyName), *CellAscent);
893 return Ok;
896 GpStatus WINGDIPAPI GdipGetCellDescent(GDIPCONST GpFontFamily *family,
897 INT style, UINT16* CellDescent)
899 TRACE("(%p, %d, %p)\n", family, style, CellDescent);
901 if (!(family && CellDescent)) return InvalidParameter;
903 *CellDescent = family->descent;
904 TRACE("%s => %u\n", debugstr_w(family->FamilyName), *CellDescent);
906 return Ok;
909 /*******************************************************************************
910 * GdipGetEmHeight [GDIPLUS.@]
912 * Gets the height of the specified family in EmHeights
914 * PARAMS
915 * family [I] Family to retrieve from
916 * style [I] (optional) style
917 * EmHeight [O] return value
919 * RETURNS
920 * SUCCESS: Ok
921 * FAILURE: InvalidParameter
923 GpStatus WINGDIPAPI GdipGetEmHeight(GDIPCONST GpFontFamily *family, INT style, UINT16* EmHeight)
925 if (!(family && EmHeight)) return InvalidParameter;
927 TRACE("%p (%s), %d, %p\n", family, debugstr_w(family->FamilyName), style, EmHeight);
929 *EmHeight = family->em_height;
930 TRACE("%s => %u\n", debugstr_w(family->FamilyName), *EmHeight);
932 return Ok;
936 /*******************************************************************************
937 * GdipGetLineSpacing [GDIPLUS.@]
939 * Returns the line spacing in design units
941 * PARAMS
942 * family [I] Family to retrieve from
943 * style [I] (Optional) font style
944 * LineSpacing [O] Return value
946 * RETURNS
947 * SUCCESS: Ok
948 * FAILURE: InvalidParameter (family or LineSpacing was NULL)
950 GpStatus WINGDIPAPI GdipGetLineSpacing(GDIPCONST GpFontFamily *family,
951 INT style, UINT16* LineSpacing)
953 TRACE("%p, %d, %p\n", family, style, LineSpacing);
955 if (!(family && LineSpacing))
956 return InvalidParameter;
958 if (style) FIXME("ignoring style\n");
960 *LineSpacing = family->line_spacing;
961 TRACE("%s => %u\n", debugstr_w(family->FamilyName), *LineSpacing);
963 return Ok;
966 static INT CALLBACK font_has_style_proc(const LOGFONTW *elf,
967 const TEXTMETRICW *ntm, DWORD type, LPARAM lParam)
969 INT fontstyle = FontStyleRegular;
971 if (!ntm) return 1;
973 if (ntm->tmWeight >= FW_BOLD) fontstyle |= FontStyleBold;
974 if (ntm->tmItalic) fontstyle |= FontStyleItalic;
975 if (ntm->tmUnderlined) fontstyle |= FontStyleUnderline;
976 if (ntm->tmStruckOut) fontstyle |= FontStyleStrikeout;
978 return (INT)lParam != fontstyle;
981 GpStatus WINGDIPAPI GdipIsStyleAvailable(GDIPCONST GpFontFamily* family,
982 INT style, BOOL* IsStyleAvailable)
984 HDC hdc;
986 TRACE("%p %d %p\n", family, style, IsStyleAvailable);
988 if (!(family && IsStyleAvailable))
989 return InvalidParameter;
991 *IsStyleAvailable = FALSE;
993 hdc = GetDC(0);
995 if(!EnumFontFamiliesW(hdc, family->FamilyName, font_has_style_proc, (LPARAM)style))
996 *IsStyleAvailable = TRUE;
998 ReleaseDC(0, hdc);
1000 return Ok;
1003 /*****************************************************************************
1004 * GdipGetGenericFontFamilyMonospace [GDIPLUS.@]
1006 * Obtains a serif family (Courier New on Windows)
1008 * PARAMS
1009 * **nativeFamily [I] Where the font will be stored
1011 * RETURNS
1012 * InvalidParameter if nativeFamily is NULL.
1013 * Ok otherwise.
1015 GpStatus WINGDIPAPI GdipGetGenericFontFamilyMonospace(GpFontFamily **nativeFamily)
1017 static const WCHAR CourierNew[] = {'C','o','u','r','i','e','r',' ','N','e','w','\0'};
1018 static const WCHAR LiberationMono[] = {'L','i','b','e','r','a','t','i','o','n',' ','M','o','n','o','\0'};
1019 GpStatus stat;
1021 if (nativeFamily == NULL) return InvalidParameter;
1023 stat = GdipCreateFontFamilyFromName(CourierNew, NULL, nativeFamily);
1025 if (stat == FontFamilyNotFound)
1026 stat = GdipCreateFontFamilyFromName(LiberationMono, NULL, nativeFamily);
1028 if (stat == FontFamilyNotFound)
1029 ERR("Missing 'Courier New' font\n");
1031 return stat;
1034 /*****************************************************************************
1035 * GdipGetGenericFontFamilySerif [GDIPLUS.@]
1037 * Obtains a serif family (Times New Roman on Windows)
1039 * PARAMS
1040 * **nativeFamily [I] Where the font will be stored
1042 * RETURNS
1043 * InvalidParameter if nativeFamily is NULL.
1044 * Ok otherwise.
1046 GpStatus WINGDIPAPI GdipGetGenericFontFamilySerif(GpFontFamily **nativeFamily)
1048 static const WCHAR TimesNewRoman[] = {'T','i','m','e','s',' ','N','e','w',' ','R','o','m','a','n','\0'};
1049 static const WCHAR LiberationSerif[] = {'L','i','b','e','r','a','t','i','o','n',' ','S','e','r','i','f','\0'};
1050 GpStatus stat;
1052 TRACE("(%p)\n", nativeFamily);
1054 if (nativeFamily == NULL) return InvalidParameter;
1056 stat = GdipCreateFontFamilyFromName(TimesNewRoman, NULL, nativeFamily);
1058 if (stat == FontFamilyNotFound)
1059 stat = GdipCreateFontFamilyFromName(LiberationSerif, NULL, nativeFamily);
1061 if (stat == FontFamilyNotFound)
1062 ERR("Missing 'Times New Roman' font\n");
1064 return stat;
1067 /*****************************************************************************
1068 * GdipGetGenericFontFamilySansSerif [GDIPLUS.@]
1070 * Obtains a serif family (Microsoft Sans Serif on Windows)
1072 * PARAMS
1073 * **nativeFamily [I] Where the font will be stored
1075 * RETURNS
1076 * InvalidParameter if nativeFamily is NULL.
1077 * Ok otherwise.
1079 GpStatus WINGDIPAPI GdipGetGenericFontFamilySansSerif(GpFontFamily **nativeFamily)
1081 GpStatus stat;
1082 static const WCHAR MicrosoftSansSerif[] = {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f','\0'};
1083 static const WCHAR Tahoma[] = {'T','a','h','o','m','a','\0'};
1085 TRACE("(%p)\n", nativeFamily);
1087 if (nativeFamily == NULL) return InvalidParameter;
1089 stat = GdipCreateFontFamilyFromName(MicrosoftSansSerif, NULL, nativeFamily);
1091 if (stat == FontFamilyNotFound)
1092 /* FIXME: Microsoft Sans Serif is not installed on Wine. */
1093 stat = GdipCreateFontFamilyFromName(Tahoma, NULL, nativeFamily);
1095 return stat;
1098 /*****************************************************************************
1099 * GdipGetGenericFontFamilySansSerif [GDIPLUS.@]
1101 GpStatus WINGDIPAPI GdipNewPrivateFontCollection(GpFontCollection** fontCollection)
1103 TRACE("%p\n", fontCollection);
1105 if (!fontCollection)
1106 return InvalidParameter;
1108 *fontCollection = GdipAlloc(sizeof(GpFontCollection));
1109 if (!*fontCollection) return OutOfMemory;
1111 (*fontCollection)->FontFamilies = NULL;
1112 (*fontCollection)->count = 0;
1113 (*fontCollection)->allocated = 0;
1115 TRACE("<-- %p\n", *fontCollection);
1117 return Ok;
1120 /*****************************************************************************
1121 * GdipDeletePrivateFontCollection [GDIPLUS.@]
1123 GpStatus WINGDIPAPI GdipDeletePrivateFontCollection(GpFontCollection **fontCollection)
1125 INT i;
1127 TRACE("%p\n", fontCollection);
1129 if (!fontCollection)
1130 return InvalidParameter;
1132 for (i = 0; i < (*fontCollection)->count; i++) GdipFree((*fontCollection)->FontFamilies[i]);
1133 GdipFree(*fontCollection);
1135 return Ok;
1138 /*****************************************************************************
1139 * GdipPrivateAddFontFile [GDIPLUS.@]
1141 GpStatus WINGDIPAPI GdipPrivateAddFontFile(GpFontCollection* fontCollection,
1142 GDIPCONST WCHAR* filename)
1144 FIXME("stub: %p, %s\n", fontCollection, debugstr_w(filename));
1146 if (!(fontCollection && filename))
1147 return InvalidParameter;
1149 return NotImplemented;
1152 /* Copied from msi/font.c */
1154 typedef struct _tagTT_OFFSET_TABLE {
1155 USHORT uMajorVersion;
1156 USHORT uMinorVersion;
1157 USHORT uNumOfTables;
1158 USHORT uSearchRange;
1159 USHORT uEntrySelector;
1160 USHORT uRangeShift;
1161 } TT_OFFSET_TABLE;
1163 typedef struct _tagTT_TABLE_DIRECTORY {
1164 char szTag[4]; /* table name */
1165 ULONG uCheckSum; /* Check sum */
1166 ULONG uOffset; /* Offset from beginning of file */
1167 ULONG uLength; /* length of the table in bytes */
1168 } TT_TABLE_DIRECTORY;
1170 typedef struct _tagTT_NAME_TABLE_HEADER {
1171 USHORT uFSelector; /* format selector. Always 0 */
1172 USHORT uNRCount; /* Name Records count */
1173 USHORT uStorageOffset; /* Offset for strings storage,
1174 * from start of the table */
1175 } TT_NAME_TABLE_HEADER;
1177 #define NAME_ID_FULL_FONT_NAME 4
1178 #define NAME_ID_VERSION 5
1180 typedef struct _tagTT_NAME_RECORD {
1181 USHORT uPlatformID;
1182 USHORT uEncodingID;
1183 USHORT uLanguageID;
1184 USHORT uNameID;
1185 USHORT uStringLength;
1186 USHORT uStringOffset; /* from start of storage area */
1187 } TT_NAME_RECORD;
1189 #define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
1190 #define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))
1193 * Code based off of code located here
1194 * http://www.codeproject.com/gdi/fontnamefromfile.asp
1196 static WCHAR *load_ttf_name_id( const char *mem, DWORD_PTR size, DWORD id, WCHAR *ret, DWORD len )
1198 const TT_TABLE_DIRECTORY *tblDir;
1199 TT_OFFSET_TABLE ttOffsetTable;
1200 TT_NAME_TABLE_HEADER ttNTHeader;
1201 TT_NAME_RECORD ttRecord;
1202 DWORD ofs, pos;
1203 int i;
1205 if (sizeof(TT_OFFSET_TABLE) > size)
1206 return NULL;
1207 ttOffsetTable = *(TT_OFFSET_TABLE*)mem;
1208 ttOffsetTable.uNumOfTables = SWAPWORD(ttOffsetTable.uNumOfTables);
1209 ttOffsetTable.uMajorVersion = SWAPWORD(ttOffsetTable.uMajorVersion);
1210 ttOffsetTable.uMinorVersion = SWAPWORD(ttOffsetTable.uMinorVersion);
1212 if (ttOffsetTable.uMajorVersion != 1 || ttOffsetTable.uMinorVersion != 0)
1213 return NULL;
1215 pos = sizeof(ttOffsetTable);
1216 for (i = 0; i < ttOffsetTable.uNumOfTables; i++)
1218 tblDir = (const TT_TABLE_DIRECTORY*)&mem[pos];
1219 pos += sizeof(*tblDir);
1220 if (memcmp(tblDir->szTag,"name",4)==0)
1222 ofs = SWAPLONG(tblDir->uOffset);
1223 break;
1226 if (i >= ttOffsetTable.uNumOfTables)
1227 return NULL;
1229 pos = ofs + sizeof(ttNTHeader);
1230 if (pos > size)
1231 return NULL;
1232 ttNTHeader = *(TT_NAME_TABLE_HEADER*)&mem[ofs];
1233 ttNTHeader.uNRCount = SWAPWORD(ttNTHeader.uNRCount);
1234 ttNTHeader.uStorageOffset = SWAPWORD(ttNTHeader.uStorageOffset);
1235 for(i=0; i<ttNTHeader.uNRCount; i++)
1237 ttRecord = *(TT_NAME_RECORD*)&mem[pos];
1238 pos += sizeof(ttRecord);
1239 if (pos > size)
1240 return NULL;
1242 ttRecord.uNameID = SWAPWORD(ttRecord.uNameID);
1243 if (ttRecord.uNameID == id)
1245 const char *buf;
1247 ttRecord.uStringLength = SWAPWORD(ttRecord.uStringLength);
1248 ttRecord.uStringOffset = SWAPWORD(ttRecord.uStringOffset);
1249 if (ofs + ttRecord.uStringOffset + ttNTHeader.uStorageOffset + ttRecord.uStringLength > size)
1250 return NULL;
1251 buf = mem + ofs + ttRecord.uStringOffset + ttNTHeader.uStorageOffset;
1252 len = MultiByteToWideChar(CP_ACP, 0, buf, ttRecord.uStringLength, ret, len-1);
1253 ret[len] = 0;
1254 return ret;
1257 return NULL;
1260 static INT CALLBACK add_font_proc(const LOGFONTW *lfw, const TEXTMETRICW *ntm, DWORD type, LPARAM lParam);
1262 /*****************************************************************************
1263 * GdipPrivateAddMemoryFont [GDIPLUS.@]
1265 GpStatus WINGDIPAPI GdipPrivateAddMemoryFont(GpFontCollection* fontCollection,
1266 GDIPCONST void* memory, INT length)
1268 WCHAR buf[32], *name;
1269 DWORD count = 0;
1270 HANDLE font;
1271 TRACE("%p, %p, %d\n", fontCollection, memory, length);
1273 if (!fontCollection || !memory || !length)
1274 return InvalidParameter;
1276 name = load_ttf_name_id(memory, length, NAME_ID_FULL_FONT_NAME, buf, sizeof(buf)/sizeof(*buf));
1277 if (!name)
1278 return OutOfMemory;
1280 font = AddFontMemResourceEx((void*)memory, length, NULL, &count);
1281 TRACE("%s: %p/%u\n", debugstr_w(name), font, count);
1282 if (!font || !count)
1283 return InvalidParameter;
1285 if (count)
1287 HDC hdc;
1288 LOGFONTW lfw;
1290 hdc = GetDC(0);
1292 lfw.lfCharSet = DEFAULT_CHARSET;
1293 lstrcpyW(lfw.lfFaceName, name);
1294 lfw.lfPitchAndFamily = 0;
1296 if (!EnumFontFamiliesExW(hdc, &lfw, add_font_proc, (LPARAM)fontCollection, 0))
1298 ReleaseDC(0, hdc);
1299 return OutOfMemory;
1302 ReleaseDC(0, hdc);
1304 return Ok;
1307 /*****************************************************************************
1308 * GdipGetFontCollectionFamilyCount [GDIPLUS.@]
1310 GpStatus WINGDIPAPI GdipGetFontCollectionFamilyCount(
1311 GpFontCollection* fontCollection, INT* numFound)
1313 TRACE("%p, %p\n", fontCollection, numFound);
1315 if (!(fontCollection && numFound))
1316 return InvalidParameter;
1318 *numFound = fontCollection->count;
1319 return Ok;
1322 /*****************************************************************************
1323 * GdipGetFontCollectionFamilyList [GDIPLUS.@]
1325 GpStatus WINGDIPAPI GdipGetFontCollectionFamilyList(
1326 GpFontCollection* fontCollection, INT numSought,
1327 GpFontFamily* gpfamilies[], INT* numFound)
1329 INT i;
1330 GpStatus stat=Ok;
1332 TRACE("%p, %d, %p, %p\n", fontCollection, numSought, gpfamilies, numFound);
1334 if (!(fontCollection && gpfamilies && numFound))
1335 return InvalidParameter;
1337 memset(gpfamilies, 0, sizeof(*gpfamilies) * numSought);
1339 for (i = 0; i < numSought && i < fontCollection->count && stat == Ok; i++)
1341 stat = GdipCloneFontFamily(fontCollection->FontFamilies[i], &gpfamilies[i]);
1344 if (stat == Ok)
1345 *numFound = i;
1346 else
1348 int numToFree=i;
1349 for (i=0; i<numToFree; i++)
1351 GdipDeleteFontFamily(gpfamilies[i]);
1352 gpfamilies[i] = NULL;
1356 return stat;
1359 void free_installed_fonts(void)
1361 while (installedFontCollection.count)
1362 GdipDeleteFontFamily(installedFontCollection.FontFamilies[--installedFontCollection.count]);
1363 HeapFree(GetProcessHeap(), 0, installedFontCollection.FontFamilies);
1364 installedFontCollection.FontFamilies = NULL;
1365 installedFontCollection.allocated = 0;
1368 static INT CALLBACK add_font_proc(const LOGFONTW *lfw, const TEXTMETRICW *ntm,
1369 DWORD type, LPARAM lParam)
1371 GpFontCollection* fonts = (GpFontCollection*)lParam;
1372 int i;
1374 if (type == RASTER_FONTTYPE)
1375 return 1;
1377 /* skip duplicates */
1378 for (i=0; i<fonts->count; i++)
1379 if (strcmpiW(lfw->lfFaceName, fonts->FontFamilies[i]->FamilyName) == 0)
1380 return 1;
1382 if (fonts->allocated == fonts->count)
1384 INT new_alloc_count = fonts->allocated+50;
1385 GpFontFamily** new_family_list = HeapAlloc(GetProcessHeap(), 0, new_alloc_count*sizeof(void*));
1387 if (!new_family_list)
1388 return 0;
1390 memcpy(new_family_list, fonts->FontFamilies, fonts->count*sizeof(void*));
1391 HeapFree(GetProcessHeap(), 0, fonts->FontFamilies);
1392 fonts->FontFamilies = new_family_list;
1393 fonts->allocated = new_alloc_count;
1396 if (GdipCreateFontFamilyFromName(lfw->lfFaceName, NULL, &fonts->FontFamilies[fonts->count]) == Ok)
1397 fonts->count++;
1398 else
1399 return 0;
1401 return 1;
1404 GpStatus WINGDIPAPI GdipNewInstalledFontCollection(
1405 GpFontCollection** fontCollection)
1407 TRACE("(%p)\n",fontCollection);
1409 if (!fontCollection)
1410 return InvalidParameter;
1412 if (installedFontCollection.count == 0)
1414 HDC hdc;
1415 LOGFONTW lfw;
1417 hdc = GetDC(0);
1419 lfw.lfCharSet = DEFAULT_CHARSET;
1420 lfw.lfFaceName[0] = 0;
1421 lfw.lfPitchAndFamily = 0;
1423 if (!EnumFontFamiliesExW(hdc, &lfw, add_font_proc, (LPARAM)&installedFontCollection, 0))
1425 free_installed_fonts();
1426 ReleaseDC(0, hdc);
1427 return OutOfMemory;
1430 ReleaseDC(0, hdc);
1433 *fontCollection = &installedFontCollection;
1435 return Ok;