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