gdiplus: Use font emSize to map to pixel size instead of using gdi32 font metrics.
[wine] / dlls / gdiplus / font.c
1 /*
2  * Copyright (C) 2007 Google (Evan Stade)
3  * Copyright (C) 2012 Dmitry Timoshkov
4  *
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.
9  *
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.
14  *
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
18  */
19
20 #include <stdarg.h>
21
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"
29
30 WINE_DEFAULT_DEBUG_CHANNEL (gdiplus);
31
32 #include "objbase.h"
33
34 #include "gdiplus.h"
35 #include "gdiplus_private.h"
36
37 /* PANOSE is 10 bytes in size, need to pack the structure properly */
38 #include "pshpack2.h"
39 typedef struct
40 {
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.
68      */
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;
85
86 typedef struct
87 {
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"
104
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
112
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')
118
119 static GpStatus clone_font_family(const GpFontFamily *, GpFontFamily **);
120
121 static const REAL mm_per_inch = 25.4;
122 static const REAL inch_per_point = 1.0/72.0;
123
124 static GpFontCollection installedFontCollection = {0};
125
126 static LONG em_size_to_pixel(REAL em_size, Unit unit, LONG dpi)
127 {
128     switch (unit)
129     {
130     default:
131         FIXME("Unhandled unit type: %d\n", unit);
132         return 0;
133
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;
153     }
154 }
155
156 /*******************************************************************************
157  * GdipCreateFont [GDIPLUS.@]
158  *
159  * Create a new font based off of a FontFamily
160  *
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
167  *
168  * RETURNS
169  *  SUCCESS: Ok
170  *  FAILURE: InvalidParameter if fontfamily or font is NULL.
171  *  FAILURE: FontFamilyNotFound if an invalid FontFamily is given
172  *
173  * NOTES
174  *  UnitDisplay is unsupported.
175  *  emSize is stored separately from lfHeight, to hold the fraction.
176  */
177 GpStatus WINGDIPAPI GdipCreateFont(GDIPCONST GpFontFamily *fontFamily,
178                         REAL emSize, INT style, Unit unit, GpFont **font)
179 {
180     HFONT hfont;
181     OUTLINETEXTMETRICW otm;
182     LOGFONTW lfw;
183     HDC hdc;
184     GpStatus stat;
185     int ret;
186
187     if (!fontFamily || !font || emSize < 0.0)
188         return InvalidParameter;
189
190     TRACE("%p (%s), %f, %d, %d, %p\n", fontFamily,
191             debugstr_w(fontFamily->FamilyName), emSize, style, unit, font);
192
193     memset(&lfw, 0, sizeof(lfw));
194
195     stat = GdipGetFamilyName(fontFamily, lfw.lfFaceName, LANG_NEUTRAL);
196     if (stat != Ok) return stat;
197
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;
203
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);
211
212     if (!ret) return NotTrueTypeFont;
213
214     *font = GdipAlloc(sizeof(GpFont));
215     if (!*font) return OutOfMemory;
216
217     (*font)->unit = unit;
218     (*font)->emSize = emSize;
219     (*font)->otm = otm;
220
221     stat = clone_font_family(fontFamily, &(*font)->family);
222     if (stat != Ok)
223     {
224         GdipFree(*font);
225         return stat;
226     }
227
228     TRACE("<-- %p\n", *font);
229
230     return Ok;
231 }
232
233 /*******************************************************************************
234  * GdipCreateFontFromLogfontW [GDIPLUS.@]
235  */
236 GpStatus WINGDIPAPI GdipCreateFontFromLogfontW(HDC hdc,
237     GDIPCONST LOGFONTW *logfont, GpFont **font)
238 {
239     HFONT hfont, oldfont;
240     OUTLINETEXTMETRICW otm;
241     GpStatus stat;
242     int ret;
243
244     TRACE("(%p, %p, %p)\n", hdc, logfont, font);
245
246     if (!hdc || !logfont || !font)
247         return InvalidParameter;
248
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);
255
256     if (!ret) return NotTrueTypeFont;
257
258     *font = GdipAlloc(sizeof(GpFont));
259     if (!*font) return OutOfMemory;
260
261     (*font)->unit = UnitWorld;
262     (*font)->emSize = otm.otmTextMetrics.tmAscent;
263     (*font)->otm = otm;
264
265     stat = GdipCreateFontFamilyFromName(logfont->lfFaceName, NULL, &(*font)->family);
266     if (stat != Ok)
267     {
268         GdipFree(*font);
269         return NotTrueTypeFont;
270     }
271
272     TRACE("<-- %p\n", *font);
273
274     return Ok;
275 }
276
277 /*******************************************************************************
278  * GdipCreateFontFromLogfontA [GDIPLUS.@]
279  */
280 GpStatus WINGDIPAPI GdipCreateFontFromLogfontA(HDC hdc,
281     GDIPCONST LOGFONTA *lfa, GpFont **font)
282 {
283     LOGFONTW lfw;
284
285     TRACE("(%p, %p, %p)\n", hdc, lfa, font);
286
287     if(!lfa || !font)
288         return InvalidParameter;
289
290     memcpy(&lfw, lfa, FIELD_OFFSET(LOGFONTA,lfFaceName) );
291
292     if(!MultiByteToWideChar(CP_ACP, 0, lfa->lfFaceName, -1, lfw.lfFaceName, LF_FACESIZE))
293         return GenericError;
294
295     return GdipCreateFontFromLogfontW(hdc, &lfw, font);
296 }
297
298 /*******************************************************************************
299  * GdipDeleteFont [GDIPLUS.@]
300  */
301 GpStatus WINGDIPAPI GdipDeleteFont(GpFont* font)
302 {
303     TRACE("(%p)\n", font);
304
305     if(!font)
306         return InvalidParameter;
307
308     GdipDeleteFontFamily(font->family);
309     GdipFree(font);
310
311     return Ok;
312 }
313
314 /*******************************************************************************
315  * GdipCreateFontFromDC [GDIPLUS.@]
316  */
317 GpStatus WINGDIPAPI GdipCreateFontFromDC(HDC hdc, GpFont **font)
318 {
319     HFONT hfont;
320     LOGFONTW lfw;
321
322     TRACE("(%p, %p)\n", hdc, font);
323
324     if(!font)
325         return InvalidParameter;
326
327     hfont = GetCurrentObject(hdc, OBJ_FONT);
328     if(!hfont)
329         return GenericError;
330
331     if(!GetObjectW(hfont, sizeof(LOGFONTW), &lfw))
332         return GenericError;
333
334     return GdipCreateFontFromLogfontW(hdc, &lfw, font);
335 }
336
337 /*******************************************************************************
338  * GdipGetFamily [GDIPLUS.@]
339  *
340  * Returns the FontFamily for the specified Font
341  *
342  * PARAMS
343  *  font    [I] Font to request from
344  *  family  [O] Resulting FontFamily object
345  *
346  * RETURNS
347  *  SUCCESS: Ok
348  *  FAILURE: An element of GpStatus
349  */
350 GpStatus WINGDIPAPI GdipGetFamily(GpFont *font, GpFontFamily **family)
351 {
352     TRACE("%p %p\n", font, family);
353
354     if (!(font && family))
355         return InvalidParameter;
356
357     return GdipCloneFontFamily(font->family, family);
358 }
359
360 static REAL get_font_size(const GpFont *font)
361 {
362     return font->emSize;
363 }
364
365 /******************************************************************************
366  * GdipGetFontSize [GDIPLUS.@]
367  *
368  * Returns the size of the font in Units
369  *
370  * PARAMS
371  *  *font       [I] The font to retrieve size from
372  *  *size       [O] Pointer to hold retrieved value
373  *
374  * RETURNS
375  *  SUCCESS: Ok
376  *  FAILURE: InvalidParameter (font or size was NULL)
377  *
378  * NOTES
379  *  Size returned is actually emSize -- not internal size used for drawing.
380  */
381 GpStatus WINGDIPAPI GdipGetFontSize(GpFont *font, REAL *size)
382 {
383     TRACE("(%p, %p)\n", font, size);
384
385     if (!(font && size)) return InvalidParameter;
386
387     *size = get_font_size(font);
388     TRACE("%s,%d => %f\n", debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *size);
389
390     return Ok;
391 }
392
393 static INT get_font_style(const GpFont *font)
394 {
395     INT style;
396
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;
407
408     return style;
409 }
410
411 /*******************************************************************************
412  * GdipGetFontStyle [GDIPLUS.@]
413  *
414  * Gets the font's style, returned in bitwise OR of FontStyle enumeration
415  *
416  * PARAMS
417  *  font    [I] font to request from
418  *  style   [O] resulting pointer to a FontStyle enumeration
419  *
420  * RETURNS
421  *  SUCCESS: Ok
422  *  FAILURE: InvalidParameter
423  */
424 GpStatus WINGDIPAPI GdipGetFontStyle(GpFont *font, INT *style)
425 {
426     TRACE("%p %p\n", font, style);
427
428     if (!(font && style))
429         return InvalidParameter;
430
431     *style = get_font_style(font);
432     TRACE("%s,%d => %d\n", debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *style);
433
434     return Ok;
435 }
436
437 /*******************************************************************************
438  * GdipGetFontUnit  [GDIPLUS.@]
439  *
440  * PARAMS
441  *  font    [I] Font to retrieve from
442  *  unit    [O] Return value
443  *
444  * RETURNS
445  *  FAILURE: font or unit was NULL
446  *  OK: otherwise
447  */
448 GpStatus WINGDIPAPI GdipGetFontUnit(GpFont *font, Unit *unit)
449 {
450     TRACE("(%p, %p)\n", font, unit);
451
452     if (!(font && unit)) return InvalidParameter;
453
454     *unit = font->unit;
455     TRACE("%s,%d => %d\n", debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *unit);
456
457     return Ok;
458 }
459
460 /*******************************************************************************
461  * GdipGetLogFontA [GDIPLUS.@]
462  */
463 GpStatus WINGDIPAPI GdipGetLogFontA(GpFont *font, GpGraphics *graphics,
464     LOGFONTA *lfa)
465 {
466     GpStatus status;
467     LOGFONTW lfw;
468
469     TRACE("(%p, %p, %p)\n", font, graphics, lfa);
470
471     status = GdipGetLogFontW(font, graphics, &lfw);
472     if(status != Ok)
473         return status;
474
475     memcpy(lfa, &lfw, FIELD_OFFSET(LOGFONTA,lfFaceName) );
476
477     if(!WideCharToMultiByte(CP_ACP, 0, lfw.lfFaceName, -1, lfa->lfFaceName, LF_FACESIZE, NULL, NULL))
478         return GenericError;
479
480     return Ok;
481 }
482
483 void get_log_fontW(const GpFont *font, GpGraphics *graphics, LOGFONTW *lf)
484 {
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);
500 }
501
502 /*******************************************************************************
503  * GdipGetLogFontW [GDIPLUS.@]
504  */
505 GpStatus WINGDIPAPI GdipGetLogFontW(GpFont *font, GpGraphics *graphics,
506     LOGFONTW *lfw)
507 {
508     TRACE("(%p, %p, %p)\n", font, graphics, lfw);
509
510     if(!font || !graphics || !lfw)
511         return InvalidParameter;
512
513     get_log_fontW(font, graphics, lfw);
514     TRACE("=> %s,%d\n", debugstr_w(lfw->lfFaceName), lfw->lfHeight);
515
516     return Ok;
517 }
518
519 /*******************************************************************************
520  * GdipCloneFont [GDIPLUS.@]
521  */
522 GpStatus WINGDIPAPI GdipCloneFont(GpFont *font, GpFont **cloneFont)
523 {
524     GpStatus stat;
525
526     TRACE("(%p, %p)\n", font, cloneFont);
527
528     if(!font || !cloneFont)
529         return InvalidParameter;
530
531     *cloneFont = GdipAlloc(sizeof(GpFont));
532     if(!*cloneFont)    return OutOfMemory;
533
534     **cloneFont = *font;
535     stat = GdipCloneFontFamily(font->family, &(*cloneFont)->family);
536     if (stat != Ok) GdipFree(*cloneFont);
537
538     return stat;
539 }
540
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
550  *
551  * NOTES
552  *  Forwards to GdipGetFontHeightGivenDPI
553  */
554 GpStatus WINGDIPAPI GdipGetFontHeight(GDIPCONST GpFont *font,
555         GDIPCONST GpGraphics *graphics, REAL *height)
556 {
557     REAL dpi;
558     GpStatus stat;
559
560     TRACE("%p %p %p\n", font, graphics, height);
561
562     if (graphics)
563     {
564         stat = GdipGetDpiY((GpGraphics*)graphics, &dpi);
565         if (stat != Ok) return stat;
566     }
567     else
568         dpi = font->family->dpi;
569
570     return GdipGetFontHeightGivenDPI(font, dpi, height);
571 }
572
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
579  *
580  * RETURNS
581  *  SUCCESS: Ok
582  *  FAILURE: InvalidParameter if font or height is NULL
583  *
584  * NOTES
585  *  According to MSDN, the result is (lineSpacing)*(fontSize / emHeight)*dpi
586  *  (for anything other than unit Pixel)
587  */
588 GpStatus WINGDIPAPI GdipGetFontHeightGivenDPI(GDIPCONST GpFont *font, REAL dpi, REAL *height)
589 {
590     GpStatus stat;
591     INT style;
592     UINT16 line_spacing, em_height;
593     REAL font_height, font_size;
594
595     if (!font || !height) return InvalidParameter;
596
597     TRACE("%p (%s), %f, %p\n", font,
598             debugstr_w(font->family->FamilyName), dpi, height);
599
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;
606
607     font_height = (REAL)line_spacing * font_size / (REAL)em_height;
608
609     switch (font->unit)
610     {
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;
630     }
631
632     TRACE("%s,%d(unit %d) => %f\n",
633           debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, font->unit, *height);
634
635     return Ok;
636 }
637
638 /***********************************************************************
639  * Borrowed from GDI32:
640  *
641  * Elf is really an ENUMLOGFONTEXW, and ntm is a NEWTEXTMETRICEXW.
642  *     We have to use other types because of the FONTENUMPROCW definition.
643  */
644 static INT CALLBACK is_font_installed_proc(const LOGFONTW *elf,
645                             const TEXTMETRICW *ntm, DWORD type, LPARAM lParam)
646 {
647     if (type != TRUETYPE_FONTTYPE)
648         return 1;
649
650     *(LOGFONTW *)lParam = *elf;
651
652     return 0;
653 }
654
655 struct font_metrics
656 {
657     UINT16 em_height, ascent, descent, line_spacing; /* in font units */
658     int dpi;
659 };
660
661 static BOOL get_font_metrics(HDC hdc, struct font_metrics *fm)
662 {
663     OUTLINETEXTMETRICW otm;
664     TT_OS2_V2 tt_os2;
665     TT_HHEA tt_hori;
666     LONG size;
667     UINT16 line_gap;
668
669     otm.otmSize = sizeof(otm);
670     if (!GetOutlineTextMetricsW(hdc, otm.otmSize, &otm)) return FALSE;
671
672     fm->em_height = otm.otmEMSquare;
673     fm->dpi = GetDeviceCaps(hdc, LOGPIXELSY);
674
675     memset(&tt_hori, 0, sizeof(tt_hori));
676     if (GetFontData(hdc, MS_HHEA_TAG, 0, &tt_hori, sizeof(tt_hori)) != GDI_ERROR)
677     {
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;
685     }
686
687     size = GetFontData(hdc, MS_OS2_TAG, 0, NULL, 0);
688     if (size == GDI_ERROR) return FALSE;
689
690     if (size > sizeof(tt_os2)) size = sizeof(tt_os2);
691
692     memset(&tt_os2, 0, sizeof(tt_os2));
693     if (GetFontData(hdc, MS_OS2_TAG, 0, &tt_os2, size) != size) return FALSE;
694
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)
699     {
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);
703     }
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;
708 }
709
710 static GpStatus find_installed_font(const WCHAR *name, struct font_metrics *fm)
711 {
712     LOGFONTW lf;
713     HDC hdc = CreateCompatibleDC(0);
714     GpStatus ret = FontFamilyNotFound;
715
716     if(!EnumFontFamiliesW(hdc, name, is_font_installed_proc, (LPARAM)&lf))
717     {
718         HFONT hfont, old_font;
719
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);
725     }
726
727     DeleteDC(hdc);
728     return ret;
729 }
730
731 /*******************************************************************************
732  * GdipCreateFontFamilyFromName [GDIPLUS.@]
733  *
734  * Creates a font family object based on a supplied name
735  *
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
740  *
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
745  *
746  * NOTES
747  *   If fontCollection is NULL then the object is not part of any collection
748  *
749  */
750
751 GpStatus WINGDIPAPI GdipCreateFontFamilyFromName(GDIPCONST WCHAR *name,
752                                         GpFontCollection *fontCollection,
753                                         GpFontFamily **FontFamily)
754 {
755     GpStatus stat;
756     GpFontFamily* ffamily;
757     struct font_metrics fm;
758
759     TRACE("%s, %p %p\n", debugstr_w(name), fontCollection, FontFamily);
760
761     if (!(name && FontFamily))
762         return InvalidParameter;
763     if (fontCollection)
764         FIXME("No support for FontCollections yet!\n");
765
766     stat = find_installed_font(name, &fm);
767     if (stat != Ok) return stat;
768
769     ffamily = GdipAlloc(sizeof (GpFontFamily));
770     if (!ffamily) return OutOfMemory;
771
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;
778
779     *FontFamily = ffamily;
780
781     TRACE("<-- %p\n", ffamily);
782
783     return Ok;
784 }
785
786 static GpStatus clone_font_family(const GpFontFamily *family, GpFontFamily **clone)
787 {
788     *clone = GdipAlloc(sizeof(GpFontFamily));
789     if (!*clone) return OutOfMemory;
790
791     **clone = *family;
792
793     return Ok;
794 }
795
796 /*******************************************************************************
797  * GdipCloneFontFamily [GDIPLUS.@]
798  *
799  * Creates a deep copy of a Font Family object
800  *
801  * PARAMS
802  *  FontFamily          [I] Font to clone
803  *  clonedFontFamily    [O] The resulting cloned font
804  *
805  * RETURNS
806  *  SUCCESS: Ok
807  */
808 GpStatus WINGDIPAPI GdipCloneFontFamily(GpFontFamily* FontFamily, GpFontFamily** clonedFontFamily)
809 {
810     GpStatus status;
811
812     if (!(FontFamily && clonedFontFamily)) return InvalidParameter;
813
814     TRACE("%p (%s), %p\n", FontFamily,
815             debugstr_w(FontFamily->FamilyName), clonedFontFamily);
816
817     status = clone_font_family(FontFamily, clonedFontFamily);
818     if (status != Ok) return status;
819
820     TRACE("<-- %p\n", *clonedFontFamily);
821
822     return Ok;
823 }
824
825 /*******************************************************************************
826  * GdipGetFamilyName [GDIPLUS.@]
827  *
828  * Returns the family name into name
829  *
830  * PARAMS
831  *  *family     [I] Family to retrieve from
832  *  *name       [O] WCHARS of the family name
833  *  LANGID      [I] charset
834  *
835  * RETURNS
836  *  SUCCESS: Ok
837  *  FAILURE: InvalidParameter if family is NULL
838  *
839  * NOTES
840  *   If name is a NULL ptr, then both XP and Vista will crash (so we do as well)
841  */
842 GpStatus WINGDIPAPI GdipGetFamilyName (GDIPCONST GpFontFamily *family,
843                                        WCHAR *name, LANGID language)
844 {
845     static int lang_fixme;
846
847     if (family == NULL)
848          return InvalidParameter;
849
850     TRACE("%p, %p, %d\n", family, name, language);
851
852     if (language != LANG_NEUTRAL && !lang_fixme++)
853         FIXME("No support for handling of multiple languages!\n");
854
855     lstrcpynW (name, family->FamilyName, LF_FACESIZE);
856
857     return Ok;
858 }
859
860
861 /*****************************************************************************
862  * GdipDeleteFontFamily [GDIPLUS.@]
863  *
864  * Removes the specified FontFamily
865  *
866  * PARAMS
867  *  *FontFamily         [I] The family to delete
868  *
869  * RETURNS
870  *  SUCCESS: Ok
871  *  FAILURE: InvalidParameter if FontFamily is NULL.
872  *
873  */
874 GpStatus WINGDIPAPI GdipDeleteFontFamily(GpFontFamily *FontFamily)
875 {
876     if (!FontFamily)
877         return InvalidParameter;
878     TRACE("Deleting %p (%s)\n", FontFamily, debugstr_w(FontFamily->FamilyName));
879
880     GdipFree (FontFamily);
881
882     return Ok;
883 }
884
885 GpStatus WINGDIPAPI GdipGetCellAscent(GDIPCONST GpFontFamily *family,
886         INT style, UINT16* CellAscent)
887 {
888     if (!(family && CellAscent)) return InvalidParameter;
889
890     *CellAscent = family->ascent;
891     TRACE("%s => %u\n", debugstr_w(family->FamilyName), *CellAscent);
892
893     return Ok;
894 }
895
896 GpStatus WINGDIPAPI GdipGetCellDescent(GDIPCONST GpFontFamily *family,
897         INT style, UINT16* CellDescent)
898 {
899     TRACE("(%p, %d, %p)\n", family, style, CellDescent);
900
901     if (!(family && CellDescent)) return InvalidParameter;
902
903     *CellDescent = family->descent;
904     TRACE("%s => %u\n", debugstr_w(family->FamilyName), *CellDescent);
905
906     return Ok;
907 }
908
909 /*******************************************************************************
910  * GdipGetEmHeight [GDIPLUS.@]
911  *
912  * Gets the height of the specified family in EmHeights
913  *
914  * PARAMS
915  *  family      [I] Family to retrieve from
916  *  style       [I] (optional) style
917  *  EmHeight    [O] return value
918  *
919  * RETURNS
920  *  SUCCESS: Ok
921  *  FAILURE: InvalidParameter
922  */
923 GpStatus WINGDIPAPI GdipGetEmHeight(GDIPCONST GpFontFamily *family, INT style, UINT16* EmHeight)
924 {
925     if (!(family && EmHeight)) return InvalidParameter;
926
927     TRACE("%p (%s), %d, %p\n", family, debugstr_w(family->FamilyName), style, EmHeight);
928
929     *EmHeight = family->em_height;
930     TRACE("%s => %u\n", debugstr_w(family->FamilyName), *EmHeight);
931
932     return Ok;
933 }
934
935
936 /*******************************************************************************
937  * GdipGetLineSpacing [GDIPLUS.@]
938  *
939  * Returns the line spacing in design units
940  *
941  * PARAMS
942  *  family      [I] Family to retrieve from
943  *  style       [I] (Optional) font style
944  *  LineSpacing [O] Return value
945  *
946  * RETURNS
947  *  SUCCESS: Ok
948  *  FAILURE: InvalidParameter (family or LineSpacing was NULL)
949  */
950 GpStatus WINGDIPAPI GdipGetLineSpacing(GDIPCONST GpFontFamily *family,
951         INT style, UINT16* LineSpacing)
952 {
953     TRACE("%p, %d, %p\n", family, style, LineSpacing);
954
955     if (!(family && LineSpacing))
956         return InvalidParameter;
957
958     if (style) FIXME("ignoring style\n");
959
960     *LineSpacing = family->line_spacing;
961     TRACE("%s => %u\n", debugstr_w(family->FamilyName), *LineSpacing);
962
963     return Ok;
964 }
965
966 static INT CALLBACK font_has_style_proc(const LOGFONTW *elf,
967                             const TEXTMETRICW *ntm, DWORD type, LPARAM lParam)
968 {
969     INT fontstyle = FontStyleRegular;
970
971     if (!ntm) return 1;
972
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;
977
978     return (INT)lParam != fontstyle;
979 }
980
981 GpStatus WINGDIPAPI GdipIsStyleAvailable(GDIPCONST GpFontFamily* family,
982         INT style, BOOL* IsStyleAvailable)
983 {
984     HDC hdc;
985
986     TRACE("%p %d %p\n", family, style, IsStyleAvailable);
987
988     if (!(family && IsStyleAvailable))
989         return InvalidParameter;
990
991     *IsStyleAvailable = FALSE;
992
993     hdc = GetDC(0);
994
995     if(!EnumFontFamiliesW(hdc, family->FamilyName, font_has_style_proc, (LPARAM)style))
996         *IsStyleAvailable = TRUE;
997
998     ReleaseDC(0, hdc);
999
1000     return Ok;
1001 }
1002
1003 /*****************************************************************************
1004  * GdipGetGenericFontFamilyMonospace [GDIPLUS.@]
1005  *
1006  * Obtains a serif family (Courier New on Windows)
1007  *
1008  * PARAMS
1009  *  **nativeFamily         [I] Where the font will be stored
1010  *
1011  * RETURNS
1012  *  InvalidParameter if nativeFamily is NULL.
1013  *  Ok otherwise.
1014  */
1015 GpStatus WINGDIPAPI GdipGetGenericFontFamilyMonospace(GpFontFamily **nativeFamily)
1016 {
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;
1020
1021     if (nativeFamily == NULL) return InvalidParameter;
1022
1023     stat = GdipCreateFontFamilyFromName(CourierNew, NULL, nativeFamily);
1024
1025     if (stat == FontFamilyNotFound)
1026         stat = GdipCreateFontFamilyFromName(LiberationMono, NULL, nativeFamily);
1027
1028     if (stat == FontFamilyNotFound)
1029         ERR("Missing 'Courier New' font\n");
1030
1031     return stat;
1032 }
1033
1034 /*****************************************************************************
1035  * GdipGetGenericFontFamilySerif [GDIPLUS.@]
1036  *
1037  * Obtains a serif family (Times New Roman on Windows)
1038  *
1039  * PARAMS
1040  *  **nativeFamily         [I] Where the font will be stored
1041  *
1042  * RETURNS
1043  *  InvalidParameter if nativeFamily is NULL.
1044  *  Ok otherwise.
1045  */
1046 GpStatus WINGDIPAPI GdipGetGenericFontFamilySerif(GpFontFamily **nativeFamily)
1047 {
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;
1051
1052     TRACE("(%p)\n", nativeFamily);
1053
1054     if (nativeFamily == NULL) return InvalidParameter;
1055
1056     stat = GdipCreateFontFamilyFromName(TimesNewRoman, NULL, nativeFamily);
1057
1058     if (stat == FontFamilyNotFound)
1059         stat = GdipCreateFontFamilyFromName(LiberationSerif, NULL, nativeFamily);
1060
1061     if (stat == FontFamilyNotFound)
1062         ERR("Missing 'Times New Roman' font\n");
1063
1064     return stat;
1065 }
1066
1067 /*****************************************************************************
1068  * GdipGetGenericFontFamilySansSerif [GDIPLUS.@]
1069  *
1070  * Obtains a serif family (Microsoft Sans Serif on Windows)
1071  *
1072  * PARAMS
1073  *  **nativeFamily         [I] Where the font will be stored
1074  *
1075  * RETURNS
1076  *  InvalidParameter if nativeFamily is NULL.
1077  *  Ok otherwise.
1078  */
1079 GpStatus WINGDIPAPI GdipGetGenericFontFamilySansSerif(GpFontFamily **nativeFamily)
1080 {
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'};
1084
1085     TRACE("(%p)\n", nativeFamily);
1086
1087     if (nativeFamily == NULL) return InvalidParameter;
1088
1089     stat = GdipCreateFontFamilyFromName(MicrosoftSansSerif, NULL, nativeFamily);
1090
1091     if (stat == FontFamilyNotFound)
1092         /* FIXME: Microsoft Sans Serif is not installed on Wine. */
1093         stat = GdipCreateFontFamilyFromName(Tahoma, NULL, nativeFamily);
1094
1095     return stat;
1096 }
1097
1098 /*****************************************************************************
1099  * GdipGetGenericFontFamilySansSerif [GDIPLUS.@]
1100  */
1101 GpStatus WINGDIPAPI GdipNewPrivateFontCollection(GpFontCollection** fontCollection)
1102 {
1103     TRACE("%p\n", fontCollection);
1104
1105     if (!fontCollection)
1106         return InvalidParameter;
1107
1108     *fontCollection = GdipAlloc(sizeof(GpFontCollection));
1109     if (!*fontCollection) return OutOfMemory;
1110
1111     (*fontCollection)->FontFamilies = NULL;
1112     (*fontCollection)->count = 0;
1113     (*fontCollection)->allocated = 0;
1114
1115     TRACE("<-- %p\n", *fontCollection);
1116
1117     return Ok;
1118 }
1119
1120 /*****************************************************************************
1121  * GdipDeletePrivateFontCollection [GDIPLUS.@]
1122  */
1123 GpStatus WINGDIPAPI GdipDeletePrivateFontCollection(GpFontCollection **fontCollection)
1124 {
1125     INT i;
1126
1127     TRACE("%p\n", fontCollection);
1128
1129     if (!fontCollection)
1130         return InvalidParameter;
1131
1132     for (i = 0; i < (*fontCollection)->count; i++) GdipFree((*fontCollection)->FontFamilies[i]);
1133     GdipFree(*fontCollection);
1134
1135     return Ok;
1136 }
1137
1138 /*****************************************************************************
1139  * GdipPrivateAddFontFile [GDIPLUS.@]
1140  */
1141 GpStatus WINGDIPAPI GdipPrivateAddFontFile(GpFontCollection* fontCollection,
1142         GDIPCONST WCHAR* filename)
1143 {
1144     FIXME("stub: %p, %s\n", fontCollection, debugstr_w(filename));
1145
1146     if (!(fontCollection && filename))
1147         return InvalidParameter;
1148
1149     return NotImplemented;
1150 }
1151
1152 /* Copied from msi/font.c */
1153
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;
1162
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;
1169
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;
1176
1177 #define NAME_ID_FULL_FONT_NAME  4
1178 #define NAME_ID_VERSION         5
1179
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;
1188
1189 #define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
1190 #define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))
1191
1192 /*
1193  * Code based off of code located here
1194  * http://www.codeproject.com/gdi/fontnamefromfile.asp
1195  */
1196 static WCHAR *load_ttf_name_id( const char *mem, DWORD_PTR size, DWORD id, WCHAR *ret, DWORD len )
1197 {
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;
1204
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);
1211
1212     if (ttOffsetTable.uMajorVersion != 1 || ttOffsetTable.uMinorVersion != 0)
1213         return NULL;
1214
1215     pos = sizeof(ttOffsetTable);
1216     for (i = 0; i < ttOffsetTable.uNumOfTables; i++)
1217     {
1218         tblDir = (const TT_TABLE_DIRECTORY*)&mem[pos];
1219         pos += sizeof(*tblDir);
1220         if (memcmp(tblDir->szTag,"name",4)==0)
1221         {
1222             ofs = SWAPLONG(tblDir->uOffset);
1223             break;
1224         }
1225     }
1226     if (i >= ttOffsetTable.uNumOfTables)
1227         return NULL;
1228
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++)
1236     {
1237         ttRecord = *(TT_NAME_RECORD*)&mem[pos];
1238         pos += sizeof(ttRecord);
1239         if (pos > size)
1240             return NULL;
1241
1242         ttRecord.uNameID = SWAPWORD(ttRecord.uNameID);
1243         if (ttRecord.uNameID == id)
1244         {
1245             const char *buf;
1246
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;
1255         }
1256     }
1257     return NULL;
1258 }
1259
1260 static INT CALLBACK add_font_proc(const LOGFONTW *lfw, const TEXTMETRICW *ntm, DWORD type, LPARAM lParam);
1261
1262 /*****************************************************************************
1263  * GdipPrivateAddMemoryFont [GDIPLUS.@]
1264  */
1265 GpStatus WINGDIPAPI GdipPrivateAddMemoryFont(GpFontCollection* fontCollection,
1266         GDIPCONST void* memory, INT length)
1267 {
1268     WCHAR buf[32], *name;
1269     DWORD count = 0;
1270     HANDLE font;
1271     TRACE("%p, %p, %d\n", fontCollection, memory, length);
1272
1273     if (!fontCollection || !memory || !length)
1274         return InvalidParameter;
1275
1276     name = load_ttf_name_id(memory, length, NAME_ID_FULL_FONT_NAME, buf, sizeof(buf)/sizeof(*buf));
1277     if (!name)
1278         return OutOfMemory;
1279
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;
1284
1285     if (count)
1286     {
1287         HDC hdc;
1288         LOGFONTW lfw;
1289
1290         hdc = GetDC(0);
1291
1292         lfw.lfCharSet = DEFAULT_CHARSET;
1293         lstrcpyW(lfw.lfFaceName, name);
1294         lfw.lfPitchAndFamily = 0;
1295
1296         if (!EnumFontFamiliesExW(hdc, &lfw, add_font_proc, (LPARAM)fontCollection, 0))
1297         {
1298             ReleaseDC(0, hdc);
1299             return OutOfMemory;
1300         }
1301
1302         ReleaseDC(0, hdc);
1303     }
1304     return Ok;
1305 }
1306
1307 /*****************************************************************************
1308  * GdipGetFontCollectionFamilyCount [GDIPLUS.@]
1309  */
1310 GpStatus WINGDIPAPI GdipGetFontCollectionFamilyCount(
1311         GpFontCollection* fontCollection, INT* numFound)
1312 {
1313     TRACE("%p, %p\n", fontCollection, numFound);
1314
1315     if (!(fontCollection && numFound))
1316         return InvalidParameter;
1317
1318     *numFound = fontCollection->count;
1319     return Ok;
1320 }
1321
1322 /*****************************************************************************
1323  * GdipGetFontCollectionFamilyList [GDIPLUS.@]
1324  */
1325 GpStatus WINGDIPAPI GdipGetFontCollectionFamilyList(
1326         GpFontCollection* fontCollection, INT numSought,
1327         GpFontFamily* gpfamilies[], INT* numFound)
1328 {
1329     INT i;
1330     GpStatus stat=Ok;
1331
1332     TRACE("%p, %d, %p, %p\n", fontCollection, numSought, gpfamilies, numFound);
1333
1334     if (!(fontCollection && gpfamilies && numFound))
1335         return InvalidParameter;
1336
1337     memset(gpfamilies, 0, sizeof(*gpfamilies) * numSought);
1338
1339     for (i = 0; i < numSought && i < fontCollection->count && stat == Ok; i++)
1340     {
1341         stat = GdipCloneFontFamily(fontCollection->FontFamilies[i], &gpfamilies[i]);
1342     }
1343
1344     if (stat == Ok)
1345         *numFound = i;
1346     else
1347     {
1348         int numToFree=i;
1349         for (i=0; i<numToFree; i++)
1350         {
1351             GdipDeleteFontFamily(gpfamilies[i]);
1352             gpfamilies[i] = NULL;
1353         }
1354     }
1355
1356     return stat;
1357 }
1358
1359 void free_installed_fonts(void)
1360 {
1361     while (installedFontCollection.count)
1362         GdipDeleteFontFamily(installedFontCollection.FontFamilies[--installedFontCollection.count]);
1363     HeapFree(GetProcessHeap(), 0, installedFontCollection.FontFamilies);
1364     installedFontCollection.FontFamilies = NULL;
1365     installedFontCollection.allocated = 0;
1366 }
1367
1368 static INT CALLBACK add_font_proc(const LOGFONTW *lfw, const TEXTMETRICW *ntm,
1369         DWORD type, LPARAM lParam)
1370 {
1371     GpFontCollection* fonts = (GpFontCollection*)lParam;
1372     int i;
1373
1374     if (type == RASTER_FONTTYPE)
1375         return 1;
1376
1377     /* skip duplicates */
1378     for (i=0; i<fonts->count; i++)
1379         if (strcmpiW(lfw->lfFaceName, fonts->FontFamilies[i]->FamilyName) == 0)
1380             return 1;
1381
1382     if (fonts->allocated == fonts->count)
1383     {
1384         INT new_alloc_count = fonts->allocated+50;
1385         GpFontFamily** new_family_list = HeapAlloc(GetProcessHeap(), 0, new_alloc_count*sizeof(void*));
1386
1387         if (!new_family_list)
1388             return 0;
1389
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;
1394     }
1395
1396     if (GdipCreateFontFamilyFromName(lfw->lfFaceName, NULL, &fonts->FontFamilies[fonts->count]) == Ok)
1397         fonts->count++;
1398     else
1399         return 0;
1400
1401     return 1;
1402 }
1403
1404 GpStatus WINGDIPAPI GdipNewInstalledFontCollection(
1405         GpFontCollection** fontCollection)
1406 {
1407     TRACE("(%p)\n",fontCollection);
1408
1409     if (!fontCollection)
1410         return InvalidParameter;
1411
1412     if (installedFontCollection.count == 0)
1413     {
1414         HDC hdc;
1415         LOGFONTW lfw;
1416
1417         hdc = GetDC(0);
1418
1419         lfw.lfCharSet = DEFAULT_CHARSET;
1420         lfw.lfFaceName[0] = 0;
1421         lfw.lfPitchAndFamily = 0;
1422
1423         if (!EnumFontFamiliesExW(hdc, &lfw, add_font_proc, (LPARAM)&installedFontCollection, 0))
1424         {
1425             free_installed_fonts();
1426             ReleaseDC(0, hdc);
1427             return OutOfMemory;
1428         }
1429
1430         ReleaseDC(0, hdc);
1431     }
1432
1433     *fontCollection = &installedFontCollection;
1434
1435     return Ok;
1436 }