msvcrt/tests: Add a trailing '\n' to a win_skip().
[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 GpFontCollection installedFontCollection = {0};
122
123 /*******************************************************************************
124  * GdipCreateFont [GDIPLUS.@]
125  *
126  * Create a new font based off of a FontFamily
127  *
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
134  *
135  * RETURNS
136  *  SUCCESS: Ok
137  *  FAILURE: InvalidParameter if fontfamily or font is NULL.
138  *  FAILURE: FontFamilyNotFound if an invalid FontFamily is given
139  *
140  * NOTES
141  *  UnitDisplay is unsupported.
142  *  emSize is stored separately from lfHeight, to hold the fraction.
143  */
144 GpStatus WINGDIPAPI GdipCreateFont(GDIPCONST GpFontFamily *fontFamily,
145                         REAL emSize, INT style, Unit unit, GpFont **font)
146 {
147     HFONT hfont;
148     OUTLINETEXTMETRICW otm;
149     LOGFONTW lfw;
150     HDC hdc;
151     GpStatus stat;
152     int ret;
153
154     if (!fontFamily || !font || emSize < 0.0)
155         return InvalidParameter;
156
157     TRACE("%p (%s), %f, %d, %d, %p\n", fontFamily,
158             debugstr_w(fontFamily->FamilyName), emSize, style, unit, font);
159
160     memset(&lfw, 0, sizeof(lfw));
161
162     stat = GdipGetFamilyName(fontFamily, lfw.lfFaceName, LANG_NEUTRAL);
163     if (stat != Ok) return stat;
164
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;
170
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);
178
179     if (!ret) return NotTrueTypeFont;
180
181     *font = GdipAlloc(sizeof(GpFont));
182     if (!*font) return OutOfMemory;
183
184     (*font)->unit = unit;
185     (*font)->emSize = emSize;
186     (*font)->otm = otm;
187
188     stat = clone_font_family(fontFamily, &(*font)->family);
189     if (stat != Ok)
190     {
191         GdipFree(*font);
192         return stat;
193     }
194
195     TRACE("<-- %p\n", *font);
196
197     return Ok;
198 }
199
200 /*******************************************************************************
201  * GdipCreateFontFromLogfontW [GDIPLUS.@]
202  */
203 GpStatus WINGDIPAPI GdipCreateFontFromLogfontW(HDC hdc,
204     GDIPCONST LOGFONTW *logfont, GpFont **font)
205 {
206     HFONT hfont, oldfont;
207     OUTLINETEXTMETRICW otm;
208     GpStatus stat;
209     int ret;
210
211     TRACE("(%p, %p, %p)\n", hdc, logfont, font);
212
213     if (!hdc || !logfont || !font)
214         return InvalidParameter;
215
216     hfont = CreateFontIndirectW(logfont);
217     oldfont = SelectObject(hdc, hfont);
218     otm.otmSize = sizeof(otm);
219     ret = GetOutlineTextMetricsW(hdc, otm.otmSize, &otm);
220     SelectObject(hdc, oldfont);
221     DeleteObject(hfont);
222
223     if (!ret) return NotTrueTypeFont;
224
225     *font = GdipAlloc(sizeof(GpFont));
226     if (!*font) return OutOfMemory;
227
228     (*font)->unit = UnitWorld;
229     (*font)->emSize = otm.otmTextMetrics.tmAscent;
230     (*font)->otm = otm;
231
232     stat = GdipCreateFontFamilyFromName(logfont->lfFaceName, NULL, &(*font)->family);
233     if (stat != Ok)
234     {
235         GdipFree(*font);
236         return NotTrueTypeFont;
237     }
238
239     TRACE("<-- %p\n", *font);
240
241     return Ok;
242 }
243
244 /*******************************************************************************
245  * GdipCreateFontFromLogfontA [GDIPLUS.@]
246  */
247 GpStatus WINGDIPAPI GdipCreateFontFromLogfontA(HDC hdc,
248     GDIPCONST LOGFONTA *lfa, GpFont **font)
249 {
250     LOGFONTW lfw;
251
252     TRACE("(%p, %p, %p)\n", hdc, lfa, font);
253
254     if(!lfa || !font)
255         return InvalidParameter;
256
257     memcpy(&lfw, lfa, FIELD_OFFSET(LOGFONTA,lfFaceName) );
258
259     if(!MultiByteToWideChar(CP_ACP, 0, lfa->lfFaceName, -1, lfw.lfFaceName, LF_FACESIZE))
260         return GenericError;
261
262     return GdipCreateFontFromLogfontW(hdc, &lfw, font);
263 }
264
265 /*******************************************************************************
266  * GdipDeleteFont [GDIPLUS.@]
267  */
268 GpStatus WINGDIPAPI GdipDeleteFont(GpFont* font)
269 {
270     TRACE("(%p)\n", font);
271
272     if(!font)
273         return InvalidParameter;
274
275     GdipDeleteFontFamily(font->family);
276     GdipFree(font);
277
278     return Ok;
279 }
280
281 /*******************************************************************************
282  * GdipCreateFontFromDC [GDIPLUS.@]
283  */
284 GpStatus WINGDIPAPI GdipCreateFontFromDC(HDC hdc, GpFont **font)
285 {
286     HFONT hfont;
287     LOGFONTW lfw;
288
289     TRACE("(%p, %p)\n", hdc, font);
290
291     if(!font)
292         return InvalidParameter;
293
294     hfont = GetCurrentObject(hdc, OBJ_FONT);
295     if(!hfont)
296         return GenericError;
297
298     if(!GetObjectW(hfont, sizeof(LOGFONTW), &lfw))
299         return GenericError;
300
301     return GdipCreateFontFromLogfontW(hdc, &lfw, font);
302 }
303
304 /*******************************************************************************
305  * GdipGetFamily [GDIPLUS.@]
306  *
307  * Returns the FontFamily for the specified Font
308  *
309  * PARAMS
310  *  font    [I] Font to request from
311  *  family  [O] Resulting FontFamily object
312  *
313  * RETURNS
314  *  SUCCESS: Ok
315  *  FAILURE: An element of GpStatus
316  */
317 GpStatus WINGDIPAPI GdipGetFamily(GpFont *font, GpFontFamily **family)
318 {
319     TRACE("%p %p\n", font, family);
320
321     if (!(font && family))
322         return InvalidParameter;
323
324     return GdipCloneFontFamily(font->family, family);
325 }
326
327 static REAL get_font_size(const GpFont *font)
328 {
329     return font->emSize;
330 }
331
332 /******************************************************************************
333  * GdipGetFontSize [GDIPLUS.@]
334  *
335  * Returns the size of the font in Units
336  *
337  * PARAMS
338  *  *font       [I] The font to retrieve size from
339  *  *size       [O] Pointer to hold retrieved value
340  *
341  * RETURNS
342  *  SUCCESS: Ok
343  *  FAILURE: InvalidParameter (font or size was NULL)
344  *
345  * NOTES
346  *  Size returned is actually emSize -- not internal size used for drawing.
347  */
348 GpStatus WINGDIPAPI GdipGetFontSize(GpFont *font, REAL *size)
349 {
350     TRACE("(%p, %p)\n", font, size);
351
352     if (!(font && size)) return InvalidParameter;
353
354     *size = get_font_size(font);
355     TRACE("%s,%d => %f\n", debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *size);
356
357     return Ok;
358 }
359
360 static INT get_font_style(const GpFont *font)
361 {
362     INT style;
363
364     if (font->otm.otmTextMetrics.tmWeight > FW_REGULAR)
365         style = FontStyleBold;
366     else
367         style = FontStyleRegular;
368     if (font->otm.otmTextMetrics.tmItalic)
369         style |= FontStyleItalic;
370     if (font->otm.otmTextMetrics.tmUnderlined)
371         style |= FontStyleUnderline;
372     if (font->otm.otmTextMetrics.tmStruckOut)
373         style |= FontStyleStrikeout;
374
375     return style;
376 }
377
378 /*******************************************************************************
379  * GdipGetFontStyle [GDIPLUS.@]
380  *
381  * Gets the font's style, returned in bitwise OR of FontStyle enumeration
382  *
383  * PARAMS
384  *  font    [I] font to request from
385  *  style   [O] resulting pointer to a FontStyle enumeration
386  *
387  * RETURNS
388  *  SUCCESS: Ok
389  *  FAILURE: InvalidParameter
390  */
391 GpStatus WINGDIPAPI GdipGetFontStyle(GpFont *font, INT *style)
392 {
393     TRACE("%p %p\n", font, style);
394
395     if (!(font && style))
396         return InvalidParameter;
397
398     *style = get_font_style(font);
399     TRACE("%s,%d => %d\n", debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *style);
400
401     return Ok;
402 }
403
404 /*******************************************************************************
405  * GdipGetFontUnit  [GDIPLUS.@]
406  *
407  * PARAMS
408  *  font    [I] Font to retrieve from
409  *  unit    [O] Return value
410  *
411  * RETURNS
412  *  FAILURE: font or unit was NULL
413  *  OK: otherwise
414  */
415 GpStatus WINGDIPAPI GdipGetFontUnit(GpFont *font, Unit *unit)
416 {
417     TRACE("(%p, %p)\n", font, unit);
418
419     if (!(font && unit)) return InvalidParameter;
420
421     *unit = font->unit;
422     TRACE("%s,%d => %d\n", debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *unit);
423
424     return Ok;
425 }
426
427 /*******************************************************************************
428  * GdipGetLogFontA [GDIPLUS.@]
429  */
430 GpStatus WINGDIPAPI GdipGetLogFontA(GpFont *font, GpGraphics *graphics,
431     LOGFONTA *lfa)
432 {
433     GpStatus status;
434     LOGFONTW lfw;
435
436     TRACE("(%p, %p, %p)\n", font, graphics, lfa);
437
438     status = GdipGetLogFontW(font, graphics, &lfw);
439     if(status != Ok)
440         return status;
441
442     memcpy(lfa, &lfw, FIELD_OFFSET(LOGFONTA,lfFaceName) );
443
444     if(!WideCharToMultiByte(CP_ACP, 0, lfw.lfFaceName, -1, lfa->lfFaceName, LF_FACESIZE, NULL, NULL))
445         return GenericError;
446
447     return Ok;
448 }
449
450 void get_log_fontW(const GpFont *font, GpGraphics *graphics, LOGFONTW *lf)
451 {
452     REAL height;
453
454     if (font->unit == UnitPixel)
455     {
456         height = units_to_pixels(font->emSize, graphics->unit, graphics->yres);
457         if (graphics->unit != UnitDisplay)
458             height *= graphics->scale;
459     }
460     else
461     {
462         if (graphics->unit == UnitDisplay || graphics->unit == UnitPixel)
463             height = units_to_pixels(font->emSize, font->unit, graphics->xres);
464         else
465             height = units_to_pixels(font->emSize, font->unit, graphics->yres);
466     }
467
468     lf->lfHeight = -(height + 0.5);
469     lf->lfWidth = 0;
470     lf->lfEscapement = 0;
471     lf->lfOrientation = 0;
472     lf->lfWeight = font->otm.otmTextMetrics.tmWeight;
473     lf->lfItalic = font->otm.otmTextMetrics.tmItalic ? 1 : 0;
474     lf->lfUnderline = font->otm.otmTextMetrics.tmUnderlined ? 1 : 0;
475     lf->lfStrikeOut = font->otm.otmTextMetrics.tmStruckOut ? 1 : 0;
476     lf->lfCharSet = font->otm.otmTextMetrics.tmCharSet;
477     lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
478     lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
479     lf->lfQuality = DEFAULT_QUALITY;
480     lf->lfPitchAndFamily = 0;
481     strcpyW(lf->lfFaceName, font->family->FamilyName);
482 }
483
484 /*******************************************************************************
485  * GdipGetLogFontW [GDIPLUS.@]
486  */
487 GpStatus WINGDIPAPI GdipGetLogFontW(GpFont *font, GpGraphics *graphics,
488     LOGFONTW *lfw)
489 {
490     TRACE("(%p, %p, %p)\n", font, graphics, lfw);
491
492     if(!font || !graphics || !lfw)
493         return InvalidParameter;
494
495     get_log_fontW(font, graphics, lfw);
496     TRACE("=> %s,%d\n", debugstr_w(lfw->lfFaceName), lfw->lfHeight);
497
498     return Ok;
499 }
500
501 /*******************************************************************************
502  * GdipCloneFont [GDIPLUS.@]
503  */
504 GpStatus WINGDIPAPI GdipCloneFont(GpFont *font, GpFont **cloneFont)
505 {
506     GpStatus stat;
507
508     TRACE("(%p, %p)\n", font, cloneFont);
509
510     if(!font || !cloneFont)
511         return InvalidParameter;
512
513     *cloneFont = GdipAlloc(sizeof(GpFont));
514     if(!*cloneFont)    return OutOfMemory;
515
516     **cloneFont = *font;
517     stat = GdipCloneFontFamily(font->family, &(*cloneFont)->family);
518     if (stat != Ok) GdipFree(*cloneFont);
519
520     return stat;
521 }
522
523 /*******************************************************************************
524  * GdipGetFontHeight [GDIPLUS.@]
525  * PARAMS
526  *  font        [I] Font to retrieve height from
527  *  graphics    [I] The current graphics context
528  *  height      [O] Resulting height
529  * RETURNS
530  *  SUCCESS: Ok
531  *  FAILURE: Another element of GpStatus
532  *
533  * NOTES
534  *  Forwards to GdipGetFontHeightGivenDPI
535  */
536 GpStatus WINGDIPAPI GdipGetFontHeight(GDIPCONST GpFont *font,
537         GDIPCONST GpGraphics *graphics, REAL *height)
538 {
539     REAL dpi;
540     GpStatus stat;
541     REAL font_height;
542
543     TRACE("%p %p %p\n", font, graphics, height);
544
545     stat = GdipGetFontHeightGivenDPI(font, font->family->dpi, &font_height);
546     if (stat != Ok) return stat;
547
548     if (!graphics)
549     {
550         *height = font_height;
551         TRACE("%s,%d => %f\n",
552               debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *height);
553         return Ok;
554     }
555
556     stat = GdipGetDpiY((GpGraphics *)graphics, &dpi);
557     if (stat != Ok) return stat;
558
559     *height = pixels_to_units(font_height, graphics->unit, dpi);
560
561     TRACE("%s,%d(unit %d) => %f\n",
562           debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, graphics->unit, *height);
563     return Ok;
564 }
565
566 /*******************************************************************************
567  * GdipGetFontHeightGivenDPI [GDIPLUS.@]
568  * PARAMS
569  *  font        [I] Font to retrieve DPI from
570  *  dpi         [I] DPI to assume
571  *  height      [O] Return value
572  *
573  * RETURNS
574  *  SUCCESS: Ok
575  *  FAILURE: InvalidParameter if font or height is NULL
576  *
577  * NOTES
578  *  According to MSDN, the result is (lineSpacing)*(fontSize / emHeight)*dpi
579  *  (for anything other than unit Pixel)
580  */
581 GpStatus WINGDIPAPI GdipGetFontHeightGivenDPI(GDIPCONST GpFont *font, REAL dpi, REAL *height)
582 {
583     GpStatus stat;
584     INT style;
585     UINT16 line_spacing, em_height;
586     REAL font_size;
587
588     if (!font || !height) return InvalidParameter;
589
590     TRACE("%p (%s), %f, %p\n", font,
591             debugstr_w(font->family->FamilyName), dpi, height);
592
593     font_size = units_to_pixels(get_font_size(font), font->unit, dpi);
594     style = get_font_style(font);
595     stat = GdipGetLineSpacing(font->family, style, &line_spacing);
596     if (stat != Ok) return stat;
597     stat = GdipGetEmHeight(font->family, style, &em_height);
598     if (stat != Ok) return stat;
599
600     *height = (REAL)line_spacing * font_size / (REAL)em_height;
601
602     TRACE("%s,%d => %f\n",
603           debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *height);
604
605     return Ok;
606 }
607
608 /***********************************************************************
609  * Borrowed from GDI32:
610  *
611  * Elf is really an ENUMLOGFONTEXW, and ntm is a NEWTEXTMETRICEXW.
612  *     We have to use other types because of the FONTENUMPROCW definition.
613  */
614 static INT CALLBACK is_font_installed_proc(const LOGFONTW *elf,
615                             const TEXTMETRICW *ntm, DWORD type, LPARAM lParam)
616 {
617     if (type & RASTER_FONTTYPE)
618         return 1;
619
620     *(LOGFONTW *)lParam = *elf;
621
622     return 0;
623 }
624
625 struct font_metrics
626 {
627     UINT16 em_height, ascent, descent, line_spacing; /* in font units */
628     int dpi;
629 };
630
631 static BOOL get_font_metrics(HDC hdc, struct font_metrics *fm)
632 {
633     OUTLINETEXTMETRICW otm;
634     TT_OS2_V2 tt_os2;
635     TT_HHEA tt_hori;
636     LONG size;
637     UINT16 line_gap;
638
639     otm.otmSize = sizeof(otm);
640     if (!GetOutlineTextMetricsW(hdc, otm.otmSize, &otm)) return FALSE;
641
642     fm->em_height = otm.otmEMSquare;
643     fm->dpi = GetDeviceCaps(hdc, LOGPIXELSY);
644
645     memset(&tt_hori, 0, sizeof(tt_hori));
646     if (GetFontData(hdc, MS_HHEA_TAG, 0, &tt_hori, sizeof(tt_hori)) != GDI_ERROR)
647     {
648         fm->ascent = GET_BE_WORD(tt_hori.Ascender);
649         fm->descent = -GET_BE_WORD(tt_hori.Descender);
650         TRACE("hhea: ascent %d, descent %d\n", fm->ascent, fm->descent);
651         line_gap = GET_BE_WORD(tt_hori.LineGap);
652         fm->line_spacing = fm->ascent + fm->descent + line_gap;
653         TRACE("line_gap %u, line_spacing %u\n", line_gap, fm->line_spacing);
654         if (fm->ascent + fm->descent != 0) return TRUE;
655     }
656
657     size = GetFontData(hdc, MS_OS2_TAG, 0, NULL, 0);
658     if (size == GDI_ERROR) return FALSE;
659
660     if (size > sizeof(tt_os2)) size = sizeof(tt_os2);
661
662     memset(&tt_os2, 0, sizeof(tt_os2));
663     if (GetFontData(hdc, MS_OS2_TAG, 0, &tt_os2, size) != size) return FALSE;
664
665     fm->ascent = GET_BE_WORD(tt_os2.usWinAscent);
666     fm->descent = GET_BE_WORD(tt_os2.usWinDescent);
667     TRACE("usWinAscent %u, usWinDescent %u\n", fm->ascent, fm->descent);
668     if (fm->ascent + fm->descent == 0)
669     {
670         fm->ascent = GET_BE_WORD(tt_os2.sTypoAscender);
671         fm->descent = GET_BE_WORD(tt_os2.sTypoDescender);
672         TRACE("sTypoAscender %u, sTypoDescender %u\n", fm->ascent, fm->descent);
673     }
674     line_gap = GET_BE_WORD(tt_os2.sTypoLineGap);
675     fm->line_spacing = fm->ascent + fm->descent + line_gap;
676     TRACE("line_gap %u, line_spacing %u\n", line_gap, fm->line_spacing);
677     return TRUE;
678 }
679
680 static GpStatus find_installed_font(const WCHAR *name, struct font_metrics *fm)
681 {
682     LOGFONTW lf;
683     HDC hdc = CreateCompatibleDC(0);
684     GpStatus ret = FontFamilyNotFound;
685
686     if(!EnumFontFamiliesW(hdc, name, is_font_installed_proc, (LPARAM)&lf))
687     {
688         HFONT hfont, old_font;
689
690         hfont = CreateFontIndirectW(&lf);
691         old_font = SelectObject(hdc, hfont);
692         ret = get_font_metrics(hdc, fm) ? Ok : NotTrueTypeFont;
693         SelectObject(hdc, old_font);
694         DeleteObject(hfont);
695     }
696
697     DeleteDC(hdc);
698     return ret;
699 }
700
701 /*******************************************************************************
702  * GdipCreateFontFamilyFromName [GDIPLUS.@]
703  *
704  * Creates a font family object based on a supplied name
705  *
706  * PARAMS
707  *  name               [I] Name of the font
708  *  fontCollection     [I] What font collection (if any) the font belongs to (may be NULL)
709  *  FontFamily         [O] Pointer to the resulting FontFamily object
710  *
711  * RETURNS
712  *  SUCCESS: Ok
713  *  FAILURE: FamilyNotFound if the requested FontFamily does not exist on the system
714  *  FAILURE: Invalid parameter if FontFamily or name is NULL
715  *
716  * NOTES
717  *   If fontCollection is NULL then the object is not part of any collection
718  *
719  */
720
721 GpStatus WINGDIPAPI GdipCreateFontFamilyFromName(GDIPCONST WCHAR *name,
722                                         GpFontCollection *fontCollection,
723                                         GpFontFamily **FontFamily)
724 {
725     GpStatus stat;
726     GpFontFamily* ffamily;
727     struct font_metrics fm;
728
729     TRACE("%s, %p %p\n", debugstr_w(name), fontCollection, FontFamily);
730
731     if (!(name && FontFamily))
732         return InvalidParameter;
733     if (fontCollection)
734         FIXME("No support for FontCollections yet!\n");
735
736     stat = find_installed_font(name, &fm);
737     if (stat != Ok) return stat;
738
739     ffamily = GdipAlloc(sizeof (GpFontFamily));
740     if (!ffamily) return OutOfMemory;
741
742     lstrcpynW(ffamily->FamilyName, name, LF_FACESIZE);
743     ffamily->em_height = fm.em_height;
744     ffamily->ascent = fm.ascent;
745     ffamily->descent = fm.descent;
746     ffamily->line_spacing = fm.line_spacing;
747     ffamily->dpi = fm.dpi;
748
749     *FontFamily = ffamily;
750
751     TRACE("<-- %p\n", ffamily);
752
753     return Ok;
754 }
755
756 static GpStatus clone_font_family(const GpFontFamily *family, GpFontFamily **clone)
757 {
758     *clone = GdipAlloc(sizeof(GpFontFamily));
759     if (!*clone) return OutOfMemory;
760
761     **clone = *family;
762
763     return Ok;
764 }
765
766 /*******************************************************************************
767  * GdipCloneFontFamily [GDIPLUS.@]
768  *
769  * Creates a deep copy of a Font Family object
770  *
771  * PARAMS
772  *  FontFamily          [I] Font to clone
773  *  clonedFontFamily    [O] The resulting cloned font
774  *
775  * RETURNS
776  *  SUCCESS: Ok
777  */
778 GpStatus WINGDIPAPI GdipCloneFontFamily(GpFontFamily* FontFamily, GpFontFamily** clonedFontFamily)
779 {
780     GpStatus status;
781
782     if (!(FontFamily && clonedFontFamily)) return InvalidParameter;
783
784     TRACE("%p (%s), %p\n", FontFamily,
785             debugstr_w(FontFamily->FamilyName), clonedFontFamily);
786
787     status = clone_font_family(FontFamily, clonedFontFamily);
788     if (status != Ok) return status;
789
790     TRACE("<-- %p\n", *clonedFontFamily);
791
792     return Ok;
793 }
794
795 /*******************************************************************************
796  * GdipGetFamilyName [GDIPLUS.@]
797  *
798  * Returns the family name into name
799  *
800  * PARAMS
801  *  *family     [I] Family to retrieve from
802  *  *name       [O] WCHARS of the family name
803  *  LANGID      [I] charset
804  *
805  * RETURNS
806  *  SUCCESS: Ok
807  *  FAILURE: InvalidParameter if family is NULL
808  *
809  * NOTES
810  *   If name is a NULL ptr, then both XP and Vista will crash (so we do as well)
811  */
812 GpStatus WINGDIPAPI GdipGetFamilyName (GDIPCONST GpFontFamily *family,
813                                        WCHAR *name, LANGID language)
814 {
815     static int lang_fixme;
816
817     if (family == NULL)
818          return InvalidParameter;
819
820     TRACE("%p, %p, %d\n", family, name, language);
821
822     if (language != LANG_NEUTRAL && !lang_fixme++)
823         FIXME("No support for handling of multiple languages!\n");
824
825     lstrcpynW (name, family->FamilyName, LF_FACESIZE);
826
827     return Ok;
828 }
829
830
831 /*****************************************************************************
832  * GdipDeleteFontFamily [GDIPLUS.@]
833  *
834  * Removes the specified FontFamily
835  *
836  * PARAMS
837  *  *FontFamily         [I] The family to delete
838  *
839  * RETURNS
840  *  SUCCESS: Ok
841  *  FAILURE: InvalidParameter if FontFamily is NULL.
842  *
843  */
844 GpStatus WINGDIPAPI GdipDeleteFontFamily(GpFontFamily *FontFamily)
845 {
846     if (!FontFamily)
847         return InvalidParameter;
848     TRACE("Deleting %p (%s)\n", FontFamily, debugstr_w(FontFamily->FamilyName));
849
850     GdipFree (FontFamily);
851
852     return Ok;
853 }
854
855 GpStatus WINGDIPAPI GdipGetCellAscent(GDIPCONST GpFontFamily *family,
856         INT style, UINT16* CellAscent)
857 {
858     if (!(family && CellAscent)) return InvalidParameter;
859
860     *CellAscent = family->ascent;
861     TRACE("%s => %u\n", debugstr_w(family->FamilyName), *CellAscent);
862
863     return Ok;
864 }
865
866 GpStatus WINGDIPAPI GdipGetCellDescent(GDIPCONST GpFontFamily *family,
867         INT style, UINT16* CellDescent)
868 {
869     TRACE("(%p, %d, %p)\n", family, style, CellDescent);
870
871     if (!(family && CellDescent)) return InvalidParameter;
872
873     *CellDescent = family->descent;
874     TRACE("%s => %u\n", debugstr_w(family->FamilyName), *CellDescent);
875
876     return Ok;
877 }
878
879 /*******************************************************************************
880  * GdipGetEmHeight [GDIPLUS.@]
881  *
882  * Gets the height of the specified family in EmHeights
883  *
884  * PARAMS
885  *  family      [I] Family to retrieve from
886  *  style       [I] (optional) style
887  *  EmHeight    [O] return value
888  *
889  * RETURNS
890  *  SUCCESS: Ok
891  *  FAILURE: InvalidParameter
892  */
893 GpStatus WINGDIPAPI GdipGetEmHeight(GDIPCONST GpFontFamily *family, INT style, UINT16* EmHeight)
894 {
895     if (!(family && EmHeight)) return InvalidParameter;
896
897     TRACE("%p (%s), %d, %p\n", family, debugstr_w(family->FamilyName), style, EmHeight);
898
899     *EmHeight = family->em_height;
900     TRACE("%s => %u\n", debugstr_w(family->FamilyName), *EmHeight);
901
902     return Ok;
903 }
904
905
906 /*******************************************************************************
907  * GdipGetLineSpacing [GDIPLUS.@]
908  *
909  * Returns the line spacing in design units
910  *
911  * PARAMS
912  *  family      [I] Family to retrieve from
913  *  style       [I] (Optional) font style
914  *  LineSpacing [O] Return value
915  *
916  * RETURNS
917  *  SUCCESS: Ok
918  *  FAILURE: InvalidParameter (family or LineSpacing was NULL)
919  */
920 GpStatus WINGDIPAPI GdipGetLineSpacing(GDIPCONST GpFontFamily *family,
921         INT style, UINT16* LineSpacing)
922 {
923     TRACE("%p, %d, %p\n", family, style, LineSpacing);
924
925     if (!(family && LineSpacing))
926         return InvalidParameter;
927
928     if (style) FIXME("ignoring style\n");
929
930     *LineSpacing = family->line_spacing;
931     TRACE("%s => %u\n", debugstr_w(family->FamilyName), *LineSpacing);
932
933     return Ok;
934 }
935
936 static INT CALLBACK font_has_style_proc(const LOGFONTW *elf,
937                             const TEXTMETRICW *ntm, DWORD type, LPARAM lParam)
938 {
939     INT fontstyle = FontStyleRegular;
940
941     if (!ntm) return 1;
942
943     if (ntm->tmWeight >= FW_BOLD) fontstyle |= FontStyleBold;
944     if (ntm->tmItalic) fontstyle |= FontStyleItalic;
945     if (ntm->tmUnderlined) fontstyle |= FontStyleUnderline;
946     if (ntm->tmStruckOut) fontstyle |= FontStyleStrikeout;
947
948     return (INT)lParam != fontstyle;
949 }
950
951 GpStatus WINGDIPAPI GdipIsStyleAvailable(GDIPCONST GpFontFamily* family,
952         INT style, BOOL* IsStyleAvailable)
953 {
954     HDC hdc;
955
956     TRACE("%p %d %p\n", family, style, IsStyleAvailable);
957
958     if (!(family && IsStyleAvailable))
959         return InvalidParameter;
960
961     *IsStyleAvailable = FALSE;
962
963     hdc = GetDC(0);
964
965     if(!EnumFontFamiliesW(hdc, family->FamilyName, font_has_style_proc, (LPARAM)style))
966         *IsStyleAvailable = TRUE;
967
968     ReleaseDC(0, hdc);
969
970     return Ok;
971 }
972
973 /*****************************************************************************
974  * GdipGetGenericFontFamilyMonospace [GDIPLUS.@]
975  *
976  * Obtains a serif family (Courier New on Windows)
977  *
978  * PARAMS
979  *  **nativeFamily         [I] Where the font will be stored
980  *
981  * RETURNS
982  *  InvalidParameter if nativeFamily is NULL.
983  *  Ok otherwise.
984  */
985 GpStatus WINGDIPAPI GdipGetGenericFontFamilyMonospace(GpFontFamily **nativeFamily)
986 {
987     static const WCHAR CourierNew[] = {'C','o','u','r','i','e','r',' ','N','e','w','\0'};
988     static const WCHAR LiberationMono[] = {'L','i','b','e','r','a','t','i','o','n',' ','M','o','n','o','\0'};
989     GpStatus stat;
990
991     if (nativeFamily == NULL) return InvalidParameter;
992
993     stat = GdipCreateFontFamilyFromName(CourierNew, NULL, nativeFamily);
994
995     if (stat == FontFamilyNotFound)
996         stat = GdipCreateFontFamilyFromName(LiberationMono, NULL, nativeFamily);
997
998     if (stat == FontFamilyNotFound)
999         ERR("Missing 'Courier New' font\n");
1000
1001     return stat;
1002 }
1003
1004 /*****************************************************************************
1005  * GdipGetGenericFontFamilySerif [GDIPLUS.@]
1006  *
1007  * Obtains a serif family (Times New Roman on Windows)
1008  *
1009  * PARAMS
1010  *  **nativeFamily         [I] Where the font will be stored
1011  *
1012  * RETURNS
1013  *  InvalidParameter if nativeFamily is NULL.
1014  *  Ok otherwise.
1015  */
1016 GpStatus WINGDIPAPI GdipGetGenericFontFamilySerif(GpFontFamily **nativeFamily)
1017 {
1018     static const WCHAR TimesNewRoman[] = {'T','i','m','e','s',' ','N','e','w',' ','R','o','m','a','n','\0'};
1019     static const WCHAR LiberationSerif[] = {'L','i','b','e','r','a','t','i','o','n',' ','S','e','r','i','f','\0'};
1020     GpStatus stat;
1021
1022     TRACE("(%p)\n", nativeFamily);
1023
1024     if (nativeFamily == NULL) return InvalidParameter;
1025
1026     stat = GdipCreateFontFamilyFromName(TimesNewRoman, NULL, nativeFamily);
1027
1028     if (stat == FontFamilyNotFound)
1029         stat = GdipCreateFontFamilyFromName(LiberationSerif, NULL, nativeFamily);
1030
1031     if (stat == FontFamilyNotFound)
1032         ERR("Missing 'Times New Roman' font\n");
1033
1034     return stat;
1035 }
1036
1037 /*****************************************************************************
1038  * GdipGetGenericFontFamilySansSerif [GDIPLUS.@]
1039  *
1040  * Obtains a serif family (Microsoft Sans Serif on Windows)
1041  *
1042  * PARAMS
1043  *  **nativeFamily         [I] Where the font will be stored
1044  *
1045  * RETURNS
1046  *  InvalidParameter if nativeFamily is NULL.
1047  *  Ok otherwise.
1048  */
1049 GpStatus WINGDIPAPI GdipGetGenericFontFamilySansSerif(GpFontFamily **nativeFamily)
1050 {
1051     GpStatus stat;
1052     static const WCHAR MicrosoftSansSerif[] = {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f','\0'};
1053     static const WCHAR Tahoma[] = {'T','a','h','o','m','a','\0'};
1054
1055     TRACE("(%p)\n", nativeFamily);
1056
1057     if (nativeFamily == NULL) return InvalidParameter;
1058
1059     stat = GdipCreateFontFamilyFromName(MicrosoftSansSerif, NULL, nativeFamily);
1060
1061     if (stat == FontFamilyNotFound)
1062         /* FIXME: Microsoft Sans Serif is not installed on Wine. */
1063         stat = GdipCreateFontFamilyFromName(Tahoma, NULL, nativeFamily);
1064
1065     return stat;
1066 }
1067
1068 /*****************************************************************************
1069  * GdipGetGenericFontFamilySansSerif [GDIPLUS.@]
1070  */
1071 GpStatus WINGDIPAPI GdipNewPrivateFontCollection(GpFontCollection** fontCollection)
1072 {
1073     TRACE("%p\n", fontCollection);
1074
1075     if (!fontCollection)
1076         return InvalidParameter;
1077
1078     *fontCollection = GdipAlloc(sizeof(GpFontCollection));
1079     if (!*fontCollection) return OutOfMemory;
1080
1081     (*fontCollection)->FontFamilies = NULL;
1082     (*fontCollection)->count = 0;
1083     (*fontCollection)->allocated = 0;
1084
1085     TRACE("<-- %p\n", *fontCollection);
1086
1087     return Ok;
1088 }
1089
1090 /*****************************************************************************
1091  * GdipDeletePrivateFontCollection [GDIPLUS.@]
1092  */
1093 GpStatus WINGDIPAPI GdipDeletePrivateFontCollection(GpFontCollection **fontCollection)
1094 {
1095     INT i;
1096
1097     TRACE("%p\n", fontCollection);
1098
1099     if (!fontCollection)
1100         return InvalidParameter;
1101
1102     for (i = 0; i < (*fontCollection)->count; i++) GdipFree((*fontCollection)->FontFamilies[i]);
1103     GdipFree(*fontCollection);
1104
1105     return Ok;
1106 }
1107
1108 /*****************************************************************************
1109  * GdipPrivateAddFontFile [GDIPLUS.@]
1110  */
1111 GpStatus WINGDIPAPI GdipPrivateAddFontFile(GpFontCollection* fontCollection,
1112         GDIPCONST WCHAR* filename)
1113 {
1114     FIXME("stub: %p, %s\n", fontCollection, debugstr_w(filename));
1115
1116     if (!(fontCollection && filename))
1117         return InvalidParameter;
1118
1119     return NotImplemented;
1120 }
1121
1122 /* Copied from msi/font.c */
1123
1124 typedef struct _tagTT_OFFSET_TABLE {
1125     USHORT uMajorVersion;
1126     USHORT uMinorVersion;
1127     USHORT uNumOfTables;
1128     USHORT uSearchRange;
1129     USHORT uEntrySelector;
1130     USHORT uRangeShift;
1131 } TT_OFFSET_TABLE;
1132
1133 typedef struct _tagTT_TABLE_DIRECTORY {
1134     char szTag[4]; /* table name */
1135     ULONG uCheckSum; /* Check sum */
1136     ULONG uOffset; /* Offset from beginning of file */
1137     ULONG uLength; /* length of the table in bytes */
1138 } TT_TABLE_DIRECTORY;
1139
1140 typedef struct _tagTT_NAME_TABLE_HEADER {
1141     USHORT uFSelector; /* format selector. Always 0 */
1142     USHORT uNRCount; /* Name Records count */
1143     USHORT uStorageOffset; /* Offset for strings storage,
1144                             * from start of the table */
1145 } TT_NAME_TABLE_HEADER;
1146
1147 #define NAME_ID_FULL_FONT_NAME  4
1148 #define NAME_ID_VERSION         5
1149
1150 typedef struct _tagTT_NAME_RECORD {
1151     USHORT uPlatformID;
1152     USHORT uEncodingID;
1153     USHORT uLanguageID;
1154     USHORT uNameID;
1155     USHORT uStringLength;
1156     USHORT uStringOffset; /* from start of storage area */
1157 } TT_NAME_RECORD;
1158
1159 #define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
1160 #define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))
1161
1162 /*
1163  * Code based off of code located here
1164  * http://www.codeproject.com/gdi/fontnamefromfile.asp
1165  */
1166 static WCHAR *load_ttf_name_id( const char *mem, DWORD_PTR size, DWORD id, WCHAR *ret, DWORD len )
1167 {
1168     const TT_TABLE_DIRECTORY *tblDir;
1169     TT_OFFSET_TABLE ttOffsetTable;
1170     TT_NAME_TABLE_HEADER ttNTHeader;
1171     TT_NAME_RECORD ttRecord;
1172     DWORD ofs, pos;
1173     int i;
1174
1175     if (sizeof(TT_OFFSET_TABLE) > size)
1176         return NULL;
1177     ttOffsetTable = *(TT_OFFSET_TABLE*)mem;
1178     ttOffsetTable.uNumOfTables = SWAPWORD(ttOffsetTable.uNumOfTables);
1179     ttOffsetTable.uMajorVersion = SWAPWORD(ttOffsetTable.uMajorVersion);
1180     ttOffsetTable.uMinorVersion = SWAPWORD(ttOffsetTable.uMinorVersion);
1181
1182     if (ttOffsetTable.uMajorVersion != 1 || ttOffsetTable.uMinorVersion != 0)
1183         return NULL;
1184
1185     pos = sizeof(ttOffsetTable);
1186     for (i = 0; i < ttOffsetTable.uNumOfTables; i++)
1187     {
1188         tblDir = (const TT_TABLE_DIRECTORY*)&mem[pos];
1189         pos += sizeof(*tblDir);
1190         if (memcmp(tblDir->szTag,"name",4)==0)
1191         {
1192             ofs = SWAPLONG(tblDir->uOffset);
1193             break;
1194         }
1195     }
1196     if (i >= ttOffsetTable.uNumOfTables)
1197         return NULL;
1198
1199     pos = ofs + sizeof(ttNTHeader);
1200     if (pos > size)
1201         return NULL;
1202     ttNTHeader = *(TT_NAME_TABLE_HEADER*)&mem[ofs];
1203     ttNTHeader.uNRCount = SWAPWORD(ttNTHeader.uNRCount);
1204     ttNTHeader.uStorageOffset = SWAPWORD(ttNTHeader.uStorageOffset);
1205     for(i=0; i<ttNTHeader.uNRCount; i++)
1206     {
1207         ttRecord = *(TT_NAME_RECORD*)&mem[pos];
1208         pos += sizeof(ttRecord);
1209         if (pos > size)
1210             return NULL;
1211
1212         ttRecord.uNameID = SWAPWORD(ttRecord.uNameID);
1213         if (ttRecord.uNameID == id)
1214         {
1215             const char *buf;
1216
1217             ttRecord.uStringLength = SWAPWORD(ttRecord.uStringLength);
1218             ttRecord.uStringOffset = SWAPWORD(ttRecord.uStringOffset);
1219             if (ofs + ttRecord.uStringOffset + ttNTHeader.uStorageOffset + ttRecord.uStringLength > size)
1220                 return NULL;
1221             buf = mem + ofs + ttRecord.uStringOffset + ttNTHeader.uStorageOffset;
1222             len = MultiByteToWideChar(CP_ACP, 0, buf, ttRecord.uStringLength, ret, len-1);
1223             ret[len] = 0;
1224             return ret;
1225         }
1226     }
1227     return NULL;
1228 }
1229
1230 static INT CALLBACK add_font_proc(const LOGFONTW *lfw, const TEXTMETRICW *ntm, DWORD type, LPARAM lParam);
1231
1232 /*****************************************************************************
1233  * GdipPrivateAddMemoryFont [GDIPLUS.@]
1234  */
1235 GpStatus WINGDIPAPI GdipPrivateAddMemoryFont(GpFontCollection* fontCollection,
1236         GDIPCONST void* memory, INT length)
1237 {
1238     WCHAR buf[32], *name;
1239     DWORD count = 0;
1240     HANDLE font;
1241     TRACE("%p, %p, %d\n", fontCollection, memory, length);
1242
1243     if (!fontCollection || !memory || !length)
1244         return InvalidParameter;
1245
1246     name = load_ttf_name_id(memory, length, NAME_ID_FULL_FONT_NAME, buf, sizeof(buf)/sizeof(*buf));
1247     if (!name)
1248         return OutOfMemory;
1249
1250     font = AddFontMemResourceEx((void*)memory, length, NULL, &count);
1251     TRACE("%s: %p/%u\n", debugstr_w(name), font, count);
1252     if (!font || !count)
1253         return InvalidParameter;
1254
1255     if (count)
1256     {
1257         HDC hdc;
1258         LOGFONTW lfw;
1259
1260         hdc = GetDC(0);
1261
1262         lfw.lfCharSet = DEFAULT_CHARSET;
1263         lstrcpyW(lfw.lfFaceName, name);
1264         lfw.lfPitchAndFamily = 0;
1265
1266         if (!EnumFontFamiliesExW(hdc, &lfw, add_font_proc, (LPARAM)fontCollection, 0))
1267         {
1268             ReleaseDC(0, hdc);
1269             return OutOfMemory;
1270         }
1271
1272         ReleaseDC(0, hdc);
1273     }
1274     return Ok;
1275 }
1276
1277 /*****************************************************************************
1278  * GdipGetFontCollectionFamilyCount [GDIPLUS.@]
1279  */
1280 GpStatus WINGDIPAPI GdipGetFontCollectionFamilyCount(
1281         GpFontCollection* fontCollection, INT* numFound)
1282 {
1283     TRACE("%p, %p\n", fontCollection, numFound);
1284
1285     if (!(fontCollection && numFound))
1286         return InvalidParameter;
1287
1288     *numFound = fontCollection->count;
1289     return Ok;
1290 }
1291
1292 /*****************************************************************************
1293  * GdipGetFontCollectionFamilyList [GDIPLUS.@]
1294  */
1295 GpStatus WINGDIPAPI GdipGetFontCollectionFamilyList(
1296         GpFontCollection* fontCollection, INT numSought,
1297         GpFontFamily* gpfamilies[], INT* numFound)
1298 {
1299     INT i;
1300     GpStatus stat=Ok;
1301
1302     TRACE("%p, %d, %p, %p\n", fontCollection, numSought, gpfamilies, numFound);
1303
1304     if (!(fontCollection && gpfamilies && numFound))
1305         return InvalidParameter;
1306
1307     memset(gpfamilies, 0, sizeof(*gpfamilies) * numSought);
1308
1309     for (i = 0; i < numSought && i < fontCollection->count && stat == Ok; i++)
1310     {
1311         stat = GdipCloneFontFamily(fontCollection->FontFamilies[i], &gpfamilies[i]);
1312     }
1313
1314     if (stat == Ok)
1315         *numFound = i;
1316     else
1317     {
1318         int numToFree=i;
1319         for (i=0; i<numToFree; i++)
1320         {
1321             GdipDeleteFontFamily(gpfamilies[i]);
1322             gpfamilies[i] = NULL;
1323         }
1324     }
1325
1326     return stat;
1327 }
1328
1329 void free_installed_fonts(void)
1330 {
1331     while (installedFontCollection.count)
1332         GdipDeleteFontFamily(installedFontCollection.FontFamilies[--installedFontCollection.count]);
1333     HeapFree(GetProcessHeap(), 0, installedFontCollection.FontFamilies);
1334     installedFontCollection.FontFamilies = NULL;
1335     installedFontCollection.allocated = 0;
1336 }
1337
1338 static INT CALLBACK add_font_proc(const LOGFONTW *lfw, const TEXTMETRICW *ntm,
1339         DWORD type, LPARAM lParam)
1340 {
1341     GpFontCollection* fonts = (GpFontCollection*)lParam;
1342     int i;
1343
1344     if (type == RASTER_FONTTYPE)
1345         return 1;
1346
1347     /* skip duplicates */
1348     for (i=0; i<fonts->count; i++)
1349         if (strcmpiW(lfw->lfFaceName, fonts->FontFamilies[i]->FamilyName) == 0)
1350             return 1;
1351
1352     if (fonts->allocated == fonts->count)
1353     {
1354         INT new_alloc_count = fonts->allocated+50;
1355         GpFontFamily** new_family_list = HeapAlloc(GetProcessHeap(), 0, new_alloc_count*sizeof(void*));
1356
1357         if (!new_family_list)
1358             return 0;
1359
1360         memcpy(new_family_list, fonts->FontFamilies, fonts->count*sizeof(void*));
1361         HeapFree(GetProcessHeap(), 0, fonts->FontFamilies);
1362         fonts->FontFamilies = new_family_list;
1363         fonts->allocated = new_alloc_count;
1364     }
1365
1366     if (GdipCreateFontFamilyFromName(lfw->lfFaceName, NULL, &fonts->FontFamilies[fonts->count]) == Ok)
1367         fonts->count++;
1368     else
1369         return 0;
1370
1371     return 1;
1372 }
1373
1374 GpStatus WINGDIPAPI GdipNewInstalledFontCollection(
1375         GpFontCollection** fontCollection)
1376 {
1377     TRACE("(%p)\n",fontCollection);
1378
1379     if (!fontCollection)
1380         return InvalidParameter;
1381
1382     if (installedFontCollection.count == 0)
1383     {
1384         HDC hdc;
1385         LOGFONTW lfw;
1386
1387         hdc = GetDC(0);
1388
1389         lfw.lfCharSet = DEFAULT_CHARSET;
1390         lfw.lfFaceName[0] = 0;
1391         lfw.lfPitchAndFamily = 0;
1392
1393         if (!EnumFontFamiliesExW(hdc, &lfw, add_font_proc, (LPARAM)&installedFontCollection, 0))
1394         {
1395             free_installed_fonts();
1396             ReleaseDC(0, hdc);
1397             return OutOfMemory;
1398         }
1399
1400         ReleaseDC(0, hdc);
1401     }
1402
1403     *fontCollection = &installedFontCollection;
1404
1405     return Ok;
1406 }