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