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