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