dmusic: Pass creation parameters to DMUSIC_CreateDirectMusicBufferImpl then allocate...
[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 NEWTEXTMETRICW* tmw;
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     tmw = &fontFamily->tmw;
114     lfw = &((*font)->lfw);
115     ZeroMemory(&(*lfw), sizeof(*lfw));
116
117     lfw->lfWeight = tmw->tmWeight;
118     lfw->lfItalic = tmw->tmItalic;
119     lfw->lfUnderline = tmw->tmUnderlined;
120     lfw->lfStrikeOut = tmw->tmStruckOut;
121     lfw->lfCharSet = tmw->tmCharSet;
122     lfw->lfPitchAndFamily = tmw->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 = tmw->ntmSizeEM;
159     (*font)->line_spacing = tmw->tmAscent + tmw->tmDescent + tmw->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
320     return Ok;
321 }
322
323 /*******************************************************************************
324  * GdipGetFontStyle [GDIPLUS.@]
325  *
326  * Gets the font's style, returned in bitwise OR of FontStyle enumeration
327  *
328  * PARAMS
329  *  font    [I] font to request from
330  *  style   [O] resulting pointer to a FontStyle enumeration
331  *
332  * RETURNS
333  *  SUCCESS: Ok
334  *  FAILURE: InvalidParameter
335  */
336 GpStatus WINGDIPAPI GdipGetFontStyle(GpFont *font, INT *style)
337 {
338     TRACE("%p %p\n", font, style);
339
340     if (!(font && style))
341         return InvalidParameter;
342
343     if (font->lfw.lfWeight > FW_REGULAR)
344         *style = FontStyleBold;
345     else
346         *style = FontStyleRegular;
347     if (font->lfw.lfItalic)
348         *style |= FontStyleItalic;
349     if (font->lfw.lfUnderline)
350         *style |= FontStyleUnderline;
351     if (font->lfw.lfStrikeOut)
352         *style |= FontStyleStrikeout;
353
354     return Ok;
355 }
356
357 /*******************************************************************************
358  * GdipGetFontUnit  [GDIPLUS.@]
359  *
360  * PARAMS
361  *  font    [I] Font to retrieve from
362  *  unit    [O] Return value
363  *
364  * RETURNS
365  *  FAILURE: font or unit was NULL
366  *  OK: otherwise
367  */
368 GpStatus WINGDIPAPI GdipGetFontUnit(GpFont *font, Unit *unit)
369 {
370     TRACE("(%p, %p)\n", font, unit);
371
372     if (!(font && unit)) return InvalidParameter;
373
374     *unit = font->unit;
375
376     return Ok;
377 }
378
379 /*******************************************************************************
380  * GdipGetLogFontA [GDIPLUS.@]
381  */
382 GpStatus WINGDIPAPI GdipGetLogFontA(GpFont *font, GpGraphics *graphics,
383     LOGFONTA *lfa)
384 {
385     GpStatus status;
386     LOGFONTW lfw;
387
388     TRACE("(%p, %p, %p)\n", font, graphics, lfa);
389
390     status = GdipGetLogFontW(font, graphics, &lfw);
391     if(status != Ok)
392         return status;
393
394     memcpy(lfa, &lfw, FIELD_OFFSET(LOGFONTA,lfFaceName) );
395
396     if(!WideCharToMultiByte(CP_ACP, 0, lfw.lfFaceName, -1, lfa->lfFaceName, LF_FACESIZE, NULL, NULL))
397         return GenericError;
398
399     return Ok;
400 }
401
402 /*******************************************************************************
403  * GdipGetLogFontW [GDIPLUS.@]
404  */
405 GpStatus WINGDIPAPI GdipGetLogFontW(GpFont *font, GpGraphics *graphics,
406     LOGFONTW *lfw)
407 {
408     TRACE("(%p, %p, %p)\n", font, graphics, lfw);
409
410     /* FIXME: use graphics */
411     if(!font || !graphics || !lfw)
412         return InvalidParameter;
413
414     *lfw = font->lfw;
415
416     return Ok;
417 }
418
419 /*******************************************************************************
420  * GdipCloneFont [GDIPLUS.@]
421  */
422 GpStatus WINGDIPAPI GdipCloneFont(GpFont *font, GpFont **cloneFont)
423 {
424     TRACE("(%p, %p)\n", font, cloneFont);
425
426     if(!font || !cloneFont)
427         return InvalidParameter;
428
429     *cloneFont = GdipAlloc(sizeof(GpFont));
430     if(!*cloneFont)    return OutOfMemory;
431
432     **cloneFont = *font;
433
434     return Ok;
435 }
436
437 /*******************************************************************************
438  * GdipGetFontHeight [GDIPLUS.@]
439  * PARAMS
440  *  font        [I] Font to retrieve height from
441  *  graphics    [I] The current graphics context
442  *  height      [O] Resulting height
443  * RETURNS
444  *  SUCCESS: Ok
445  *  FAILURE: Another element of GpStatus
446  *
447  * NOTES
448  *  Forwards to GdipGetFontHeightGivenDPI
449  */
450 GpStatus WINGDIPAPI GdipGetFontHeight(GDIPCONST GpFont *font,
451         GDIPCONST GpGraphics *graphics, REAL *height)
452 {
453     REAL dpi;
454     GpStatus stat;
455
456     TRACE("%p %p %p\n", font, graphics, height);
457
458     stat = GdipGetDpiY((GpGraphics*)graphics, &dpi);
459
460     if (stat == Ok)
461         stat = GdipGetFontHeightGivenDPI(font, dpi, height);
462
463     return stat;
464 }
465
466 /*******************************************************************************
467  * GdipGetFontHeightGivenDPI [GDIPLUS.@]
468  * PARAMS
469  *  font        [I] Font to retrieve DPI from
470  *  dpi         [I] DPI to assume
471  *  height      [O] Return value
472  *
473  * RETURNS
474  *  SUCCESS: Ok
475  *  FAILURE: InvalidParameter if font or height is NULL
476  *
477  * NOTES
478  *  According to MSDN, the result is (lineSpacing)*(fontSize / emHeight)*dpi
479  *  (for anything other than unit Pixel)
480  */
481 GpStatus WINGDIPAPI GdipGetFontHeightGivenDPI(GDIPCONST GpFont *font, REAL dpi, REAL *height)
482 {
483     REAL font_height;
484
485     TRACE("%p (%s), %f, %p\n", font,
486             debugstr_w(font->lfw.lfFaceName), dpi, height);
487
488     if (!(font && height)) return InvalidParameter;
489
490     font_height = font->line_spacing * (font->emSize / font->height);
491
492     switch (font->unit)
493     {
494         case UnitPixel:
495         case UnitWorld:
496             *height = font_height;
497             break;
498         case UnitPoint:
499             *height = font_height * dpi * inch_per_point;
500             break;
501         case UnitInch:
502             *height = font_height * dpi;
503             break;
504         case UnitDocument:
505             *height = font_height * (dpi / 300.0);
506             break;
507         case UnitMillimeter:
508             *height = font_height * (dpi / mm_per_inch);
509             break;
510         default:
511             FIXME("Unhandled unit type: %d\n", font->unit);
512             return NotImplemented;
513     }
514
515     return Ok;
516 }
517
518 /***********************************************************************
519  * Borrowed from GDI32:
520  *
521  * Elf is really an ENUMLOGFONTEXW, and ntm is a NEWTEXTMETRICEXW.
522  *     We have to use other types because of the FONTENUMPROCW definition.
523  */
524 static INT CALLBACK is_font_installed_proc(const LOGFONTW *elf,
525                             const TEXTMETRICW *ntm, DWORD type, LPARAM lParam)
526 {
527     if (!ntm || type == RASTER_FONTTYPE)
528     {
529         return 1;
530     }
531
532     *(NEWTEXTMETRICW*)lParam = *(const NEWTEXTMETRICW*)ntm;
533
534     return 0;
535 }
536
537 static BOOL find_installed_font(const WCHAR *name, NEWTEXTMETRICW *ntm)
538 {
539     HDC hdc = GetDC(0);
540     BOOL ret = FALSE;
541
542     if(!EnumFontFamiliesW(hdc, name, is_font_installed_proc, (LPARAM)ntm))
543         ret = TRUE;
544
545     ReleaseDC(0, hdc);
546     return ret;
547 }
548
549 /*******************************************************************************
550  * GdipCreateFontFamilyFromName [GDIPLUS.@]
551  *
552  * Creates a font family object based on a supplied name
553  *
554  * PARAMS
555  *  name               [I] Name of the font
556  *  fontCollection     [I] What font collection (if any) the font belongs to (may be NULL)
557  *  FontFamily         [O] Pointer to the resulting FontFamily object
558  *
559  * RETURNS
560  *  SUCCESS: Ok
561  *  FAILURE: FamilyNotFound if the requested FontFamily does not exist on the system
562  *  FAILURE: Invalid parameter if FontFamily or name is NULL
563  *
564  * NOTES
565  *   If fontCollection is NULL then the object is not part of any collection
566  *
567  */
568
569 GpStatus WINGDIPAPI GdipCreateFontFamilyFromName(GDIPCONST WCHAR *name,
570                                         GpFontCollection *fontCollection,
571                                         GpFontFamily **FontFamily)
572 {
573     GpFontFamily* ffamily;
574     NEWTEXTMETRICW ntm;
575
576     TRACE("%s, %p %p\n", debugstr_w(name), fontCollection, FontFamily);
577
578     if (!(name && FontFamily))
579         return InvalidParameter;
580     if (fontCollection)
581         FIXME("No support for FontCollections yet!\n");
582
583     if (!find_installed_font(name, &ntm))
584         return FontFamilyNotFound;
585
586     ffamily = GdipAlloc(sizeof (GpFontFamily));
587     if (!ffamily) return OutOfMemory;
588
589     ffamily->tmw = ntm;
590     lstrcpynW(ffamily->FamilyName, name, LF_FACESIZE);
591
592     *FontFamily = ffamily;
593
594     TRACE("<-- %p\n", ffamily);
595
596     return Ok;
597 }
598
599 /*******************************************************************************
600  * GdipCloneFontFamily [GDIPLUS.@]
601  *
602  * Creates a deep copy of a Font Family object
603  *
604  * PARAMS
605  *  FontFamily          [I] Font to clone
606  *  clonedFontFamily    [O] The resulting cloned font
607  *
608  * RETURNS
609  *  SUCCESS: Ok
610  */
611 GpStatus WINGDIPAPI GdipCloneFontFamily(GpFontFamily* FontFamily, GpFontFamily** clonedFontFamily)
612 {
613     if (!(FontFamily && clonedFontFamily)) return InvalidParameter;
614
615     TRACE("stub: %p (%s), %p\n", FontFamily,
616             debugstr_w(FontFamily->FamilyName), clonedFontFamily);
617
618     *clonedFontFamily = GdipAlloc(sizeof(GpFontFamily));
619     if (!*clonedFontFamily) return OutOfMemory;
620
621     (*clonedFontFamily)->tmw = FontFamily->tmw;
622     lstrcpyW((*clonedFontFamily)->FamilyName, FontFamily->FamilyName);
623
624     TRACE("<-- %p\n", *clonedFontFamily);
625
626     return Ok;
627 }
628
629 /*******************************************************************************
630  * GdipGetFamilyName [GDIPLUS.@]
631  *
632  * Returns the family name into name
633  *
634  * PARAMS
635  *  *family     [I] Family to retrieve from
636  *  *name       [O] WCHARS of the family name
637  *  LANGID      [I] charset
638  *
639  * RETURNS
640  *  SUCCESS: Ok
641  *  FAILURE: InvalidParameter if family is NULL
642  *
643  * NOTES
644  *   If name is a NULL ptr, then both XP and Vista will crash (so we do as well)
645  */
646 GpStatus WINGDIPAPI GdipGetFamilyName (GDIPCONST GpFontFamily *family,
647                                        WCHAR *name, LANGID language)
648 {
649     static int lang_fixme;
650
651     if (family == NULL)
652          return InvalidParameter;
653
654     TRACE("%p, %p, %d\n", family, name, language);
655
656     if (language != LANG_NEUTRAL && !lang_fixme++)
657         FIXME("No support for handling of multiple languages!\n");
658
659     lstrcpynW (name, family->FamilyName, LF_FACESIZE);
660
661     return Ok;
662 }
663
664
665 /*****************************************************************************
666  * GdipDeleteFontFamily [GDIPLUS.@]
667  *
668  * Removes the specified FontFamily
669  *
670  * PARAMS
671  *  *FontFamily         [I] The family to delete
672  *
673  * RETURNS
674  *  SUCCESS: Ok
675  *  FAILURE: InvalidParameter if FontFamily is NULL.
676  *
677  */
678 GpStatus WINGDIPAPI GdipDeleteFontFamily(GpFontFamily *FontFamily)
679 {
680     if (!FontFamily)
681         return InvalidParameter;
682     TRACE("Deleting %p (%s)\n", FontFamily, debugstr_w(FontFamily->FamilyName));
683
684     GdipFree (FontFamily);
685
686     return Ok;
687 }
688
689 GpStatus WINGDIPAPI GdipGetCellAscent(GDIPCONST GpFontFamily *family,
690         INT style, UINT16* CellAscent)
691 {
692     if (!(family && CellAscent)) return InvalidParameter;
693
694     *CellAscent = family->tmw.tmAscent;
695
696     return Ok;
697 }
698
699 GpStatus WINGDIPAPI GdipGetCellDescent(GDIPCONST GpFontFamily *family,
700         INT style, UINT16* CellDescent)
701 {
702     TRACE("(%p, %d, %p)\n", family, style, CellDescent);
703
704     if (!(family && CellDescent)) return InvalidParameter;
705
706     *CellDescent = family->tmw.tmDescent;
707
708     return Ok;
709 }
710
711 /*******************************************************************************
712  * GdipGetEmHeight [GDIPLUS.@]
713  *
714  * Gets the height of the specified family in EmHeights
715  *
716  * PARAMS
717  *  family      [I] Family to retrieve from
718  *  style       [I] (optional) style
719  *  EmHeight    [O] return value
720  *
721  * RETURNS
722  *  SUCCESS: Ok
723  *  FAILURE: InvalidParameter
724  */
725 GpStatus WINGDIPAPI GdipGetEmHeight(GDIPCONST GpFontFamily *family, INT style, UINT16* EmHeight)
726 {
727     if (!(family && EmHeight)) return InvalidParameter;
728
729     TRACE("%p (%s), %d, %p\n", family, debugstr_w(family->FamilyName), style, EmHeight);
730
731     *EmHeight = family->tmw.ntmSizeEM;
732
733     return Ok;
734 }
735
736
737 /*******************************************************************************
738  * GdipGetLineSpacing [GDIPLUS.@]
739  *
740  * Returns the line spacing in design units
741  *
742  * PARAMS
743  *  family      [I] Family to retrieve from
744  *  style       [I] (Optional) font style
745  *  LineSpacing [O] Return value
746  *
747  * RETURNS
748  *  SUCCESS: Ok
749  *  FAILURE: InvalidParameter (family or LineSpacing was NULL)
750  */
751 GpStatus WINGDIPAPI GdipGetLineSpacing(GDIPCONST GpFontFamily *family,
752         INT style, UINT16* LineSpacing)
753 {
754     TRACE("%p, %d, %p\n", family, style, LineSpacing);
755
756     if (!(family && LineSpacing))
757         return InvalidParameter;
758
759     if (style) FIXME("ignoring style\n");
760
761     *LineSpacing = family->tmw.tmAscent + family->tmw.tmDescent + family->tmw.tmExternalLeading;
762
763     return Ok;
764 }
765
766 static INT CALLBACK font_has_style_proc(const LOGFONTW *elf,
767                             const TEXTMETRICW *ntm, DWORD type, LPARAM lParam)
768 {
769     INT fontstyle=0;
770
771     if (!ntm) return 1;
772
773     if (ntm->tmWeight >= FW_BOLD) fontstyle |= FontStyleBold;
774     if (ntm->tmItalic) fontstyle |= FontStyleItalic;
775     if (ntm->tmUnderlined) fontstyle |= FontStyleUnderline;
776     if (ntm->tmStruckOut) fontstyle |= FontStyleStrikeout;
777
778     return (INT)lParam != fontstyle;
779 }
780
781 GpStatus WINGDIPAPI GdipIsStyleAvailable(GDIPCONST GpFontFamily* family,
782         INT style, BOOL* IsStyleAvailable)
783 {
784     HDC hdc;
785
786     TRACE("%p %d %p\n", family, style, IsStyleAvailable);
787
788     if (!(family && IsStyleAvailable))
789         return InvalidParameter;
790
791     *IsStyleAvailable = FALSE;
792
793     hdc = GetDC(0);
794
795     if(!EnumFontFamiliesW(hdc, family->FamilyName, font_has_style_proc, (LPARAM)style))
796         *IsStyleAvailable = TRUE;
797
798     ReleaseDC(0, hdc);
799
800     return Ok;
801 }
802
803 /*****************************************************************************
804  * GdipGetGenericFontFamilyMonospace [GDIPLUS.@]
805  *
806  * Obtains a serif family (Courier New on Windows)
807  *
808  * PARAMS
809  *  **nativeFamily         [I] Where the font will be stored
810  *
811  * RETURNS
812  *  InvalidParameter if nativeFamily is NULL.
813  *  Ok otherwise.
814  */
815 GpStatus WINGDIPAPI GdipGetGenericFontFamilyMonospace(GpFontFamily **nativeFamily)
816 {
817     static const WCHAR CourierNew[] = {'C','o','u','r','i','e','r',' ','N','e','w','\0'};
818     static const WCHAR LiberationMono[] = {'L','i','b','e','r','a','t','i','o','n',' ','M','o','n','o','\0'};
819     GpStatus stat;
820
821     if (nativeFamily == NULL) return InvalidParameter;
822
823     stat = GdipCreateFontFamilyFromName(CourierNew, NULL, nativeFamily);
824
825     if (stat == FontFamilyNotFound)
826         stat = GdipCreateFontFamilyFromName(LiberationMono, NULL, nativeFamily);
827
828     if (stat == FontFamilyNotFound)
829         ERR("Missing 'Courier New' font\n");
830
831     return stat;
832 }
833
834 /*****************************************************************************
835  * GdipGetGenericFontFamilySerif [GDIPLUS.@]
836  *
837  * Obtains a serif family (Times New Roman on Windows)
838  *
839  * PARAMS
840  *  **nativeFamily         [I] Where the font will be stored
841  *
842  * RETURNS
843  *  InvalidParameter if nativeFamily is NULL.
844  *  Ok otherwise.
845  */
846 GpStatus WINGDIPAPI GdipGetGenericFontFamilySerif(GpFontFamily **nativeFamily)
847 {
848     static const WCHAR TimesNewRoman[] = {'T','i','m','e','s',' ','N','e','w',' ','R','o','m','a','n','\0'};
849     static const WCHAR LiberationSerif[] = {'L','i','b','e','r','a','t','i','o','n',' ','S','e','r','i','f','\0'};
850     GpStatus stat;
851
852     TRACE("(%p)\n", nativeFamily);
853
854     if (nativeFamily == NULL) return InvalidParameter;
855
856     stat = GdipCreateFontFamilyFromName(TimesNewRoman, NULL, nativeFamily);
857
858     if (stat == FontFamilyNotFound)
859         stat = GdipCreateFontFamilyFromName(LiberationSerif, NULL, nativeFamily);
860
861     if (stat == FontFamilyNotFound)
862         ERR("Missing 'Times New Roman' font\n");
863
864     return stat;
865 }
866
867 /*****************************************************************************
868  * GdipGetGenericFontFamilySansSerif [GDIPLUS.@]
869  *
870  * Obtains a serif family (Microsoft Sans Serif on Windows)
871  *
872  * PARAMS
873  *  **nativeFamily         [I] Where the font will be stored
874  *
875  * RETURNS
876  *  InvalidParameter if nativeFamily is NULL.
877  *  Ok otherwise.
878  */
879 GpStatus WINGDIPAPI GdipGetGenericFontFamilySansSerif(GpFontFamily **nativeFamily)
880 {
881     GpStatus stat;
882     static const WCHAR MicrosoftSansSerif[] = {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f','\0'};
883     static const WCHAR Tahoma[] = {'T','a','h','o','m','a','\0'};
884
885     TRACE("(%p)\n", nativeFamily);
886
887     if (nativeFamily == NULL) return InvalidParameter;
888
889     stat = GdipCreateFontFamilyFromName(MicrosoftSansSerif, NULL, nativeFamily);
890
891     if (stat == FontFamilyNotFound)
892         /* FIXME: Microsoft Sans Serif is not installed on Wine. */
893         stat = GdipCreateFontFamilyFromName(Tahoma, NULL, nativeFamily);
894
895     return stat;
896 }
897
898 /*****************************************************************************
899  * GdipGetGenericFontFamilySansSerif [GDIPLUS.@]
900  */
901 GpStatus WINGDIPAPI GdipNewPrivateFontCollection(GpFontCollection** fontCollection)
902 {
903     TRACE("%p\n", fontCollection);
904
905     if (!fontCollection)
906         return InvalidParameter;
907
908     *fontCollection = GdipAlloc(sizeof(GpFontCollection));
909     if (!*fontCollection) return OutOfMemory;
910
911     (*fontCollection)->FontFamilies = NULL;
912     (*fontCollection)->count = 0;
913     (*fontCollection)->allocated = 0;
914
915     TRACE("<-- %p\n", *fontCollection);
916
917     return Ok;
918 }
919
920 /*****************************************************************************
921  * GdipDeletePrivateFontCollection [GDIPLUS.@]
922  */
923 GpStatus WINGDIPAPI GdipDeletePrivateFontCollection(GpFontCollection **fontCollection)
924 {
925     INT i;
926
927     TRACE("%p\n", fontCollection);
928
929     if (!fontCollection)
930         return InvalidParameter;
931
932     for (i = 0; i < (*fontCollection)->count; i++) GdipFree((*fontCollection)->FontFamilies[i]);
933     GdipFree(*fontCollection);
934
935     return Ok;
936 }
937
938 /*****************************************************************************
939  * GdipPrivateAddFontFile [GDIPLUS.@]
940  */
941 GpStatus WINGDIPAPI GdipPrivateAddFontFile(GpFontCollection* fontCollection,
942         GDIPCONST WCHAR* filename)
943 {
944     FIXME("stub: %p, %s\n", fontCollection, debugstr_w(filename));
945
946     if (!(fontCollection && filename))
947         return InvalidParameter;
948
949     return NotImplemented;
950 }
951
952 /* Copied from msi/font.c */
953
954 typedef struct _tagTT_OFFSET_TABLE {
955     USHORT uMajorVersion;
956     USHORT uMinorVersion;
957     USHORT uNumOfTables;
958     USHORT uSearchRange;
959     USHORT uEntrySelector;
960     USHORT uRangeShift;
961 } TT_OFFSET_TABLE;
962
963 typedef struct _tagTT_TABLE_DIRECTORY {
964     char szTag[4]; /* table name */
965     ULONG uCheckSum; /* Check sum */
966     ULONG uOffset; /* Offset from beginning of file */
967     ULONG uLength; /* length of the table in bytes */
968 } TT_TABLE_DIRECTORY;
969
970 typedef struct _tagTT_NAME_TABLE_HEADER {
971     USHORT uFSelector; /* format selector. Always 0 */
972     USHORT uNRCount; /* Name Records count */
973     USHORT uStorageOffset; /* Offset for strings storage,
974                             * from start of the table */
975 } TT_NAME_TABLE_HEADER;
976
977 #define NAME_ID_FULL_FONT_NAME  4
978 #define NAME_ID_VERSION         5
979
980 typedef struct _tagTT_NAME_RECORD {
981     USHORT uPlatformID;
982     USHORT uEncodingID;
983     USHORT uLanguageID;
984     USHORT uNameID;
985     USHORT uStringLength;
986     USHORT uStringOffset; /* from start of storage area */
987 } TT_NAME_RECORD;
988
989 #define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
990 #define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))
991
992 /*
993  * Code based off of code located here
994  * http://www.codeproject.com/gdi/fontnamefromfile.asp
995  */
996 static WCHAR *load_ttf_name_id( const char *mem, DWORD_PTR size, DWORD id, WCHAR *ret, DWORD len )
997 {
998     const TT_TABLE_DIRECTORY *tblDir;
999     TT_OFFSET_TABLE ttOffsetTable;
1000     TT_NAME_TABLE_HEADER ttNTHeader;
1001     TT_NAME_RECORD ttRecord;
1002     DWORD ofs, pos;
1003     int i;
1004
1005     if (sizeof(TT_OFFSET_TABLE) > size)
1006         return NULL;
1007     ttOffsetTable = *(TT_OFFSET_TABLE*)mem;
1008     ttOffsetTable.uNumOfTables = SWAPWORD(ttOffsetTable.uNumOfTables);
1009     ttOffsetTable.uMajorVersion = SWAPWORD(ttOffsetTable.uMajorVersion);
1010     ttOffsetTable.uMinorVersion = SWAPWORD(ttOffsetTable.uMinorVersion);
1011
1012     if (ttOffsetTable.uMajorVersion != 1 || ttOffsetTable.uMinorVersion != 0)
1013         return NULL;
1014
1015     pos = sizeof(ttOffsetTable);
1016     for (i = 0; i < ttOffsetTable.uNumOfTables; i++)
1017     {
1018         tblDir = (const TT_TABLE_DIRECTORY*)&mem[pos];
1019         pos += sizeof(*tblDir);
1020         if (memcmp(tblDir->szTag,"name",4)==0)
1021         {
1022             ofs = SWAPLONG(tblDir->uOffset);
1023             break;
1024         }
1025     }
1026     if (i >= ttOffsetTable.uNumOfTables)
1027         return NULL;
1028
1029     pos = ofs + sizeof(ttNTHeader);
1030     if (pos > size)
1031         return NULL;
1032     ttNTHeader = *(TT_NAME_TABLE_HEADER*)&mem[ofs];
1033     ttNTHeader.uNRCount = SWAPWORD(ttNTHeader.uNRCount);
1034     ttNTHeader.uStorageOffset = SWAPWORD(ttNTHeader.uStorageOffset);
1035     for(i=0; i<ttNTHeader.uNRCount; i++)
1036     {
1037         ttRecord = *(TT_NAME_RECORD*)&mem[pos];
1038         pos += sizeof(ttRecord);
1039         if (pos > size)
1040             return NULL;
1041
1042         ttRecord.uNameID = SWAPWORD(ttRecord.uNameID);
1043         if (ttRecord.uNameID == id)
1044         {
1045             const char *buf;
1046
1047             ttRecord.uStringLength = SWAPWORD(ttRecord.uStringLength);
1048             ttRecord.uStringOffset = SWAPWORD(ttRecord.uStringOffset);
1049             if (ofs + ttRecord.uStringOffset + ttNTHeader.uStorageOffset + ttRecord.uStringLength > size)
1050                 return NULL;
1051             buf = mem + ofs + ttRecord.uStringOffset + ttNTHeader.uStorageOffset;
1052             len = MultiByteToWideChar(CP_ACP, 0, buf, ttRecord.uStringLength, ret, len-1);
1053             ret[len] = 0;
1054             return ret;
1055         }
1056     }
1057     return NULL;
1058 }
1059
1060 static INT CALLBACK add_font_proc(const LOGFONTW *lfw, const TEXTMETRICW *ntm, DWORD type, LPARAM lParam);
1061
1062 /*****************************************************************************
1063  * GdipPrivateAddMemoryFont [GDIPLUS.@]
1064  */
1065 GpStatus WINGDIPAPI GdipPrivateAddMemoryFont(GpFontCollection* fontCollection,
1066         GDIPCONST void* memory, INT length)
1067 {
1068     WCHAR buf[32], *name;
1069     DWORD count = 0;
1070     HANDLE font;
1071     TRACE("%p, %p, %d\n", fontCollection, memory, length);
1072
1073     if (!fontCollection || !memory || !length)
1074         return InvalidParameter;
1075
1076     name = load_ttf_name_id(memory, length, NAME_ID_FULL_FONT_NAME, buf, sizeof(buf)/sizeof(*buf));
1077     if (!name)
1078         return OutOfMemory;
1079
1080     font = AddFontMemResourceEx((void*)memory, length, NULL, &count);
1081     TRACE("%s: %p/%u\n", debugstr_w(name), font, count);
1082     if (!font || !count)
1083         return InvalidParameter;
1084
1085     if (count)
1086     {
1087         HDC hdc;
1088         LOGFONTW lfw;
1089
1090         hdc = GetDC(0);
1091
1092         lfw.lfCharSet = DEFAULT_CHARSET;
1093         lstrcpyW(lfw.lfFaceName, name);
1094         lfw.lfPitchAndFamily = 0;
1095
1096         if (!EnumFontFamiliesExW(hdc, &lfw, add_font_proc, (LPARAM)fontCollection, 0))
1097         {
1098             ReleaseDC(0, hdc);
1099             return OutOfMemory;
1100         }
1101
1102         ReleaseDC(0, hdc);
1103     }
1104     return Ok;
1105 }
1106
1107 /*****************************************************************************
1108  * GdipGetFontCollectionFamilyCount [GDIPLUS.@]
1109  */
1110 GpStatus WINGDIPAPI GdipGetFontCollectionFamilyCount(
1111         GpFontCollection* fontCollection, INT* numFound)
1112 {
1113     TRACE("%p, %p\n", fontCollection, numFound);
1114
1115     if (!(fontCollection && numFound))
1116         return InvalidParameter;
1117
1118     *numFound = fontCollection->count;
1119     return Ok;
1120 }
1121
1122 /*****************************************************************************
1123  * GdipGetFontCollectionFamilyList [GDIPLUS.@]
1124  */
1125 GpStatus WINGDIPAPI GdipGetFontCollectionFamilyList(
1126         GpFontCollection* fontCollection, INT numSought,
1127         GpFontFamily* gpfamilies[], INT* numFound)
1128 {
1129     INT i;
1130     GpStatus stat=Ok;
1131
1132     TRACE("%p, %d, %p, %p\n", fontCollection, numSought, gpfamilies, numFound);
1133
1134     if (!(fontCollection && gpfamilies && numFound))
1135         return InvalidParameter;
1136
1137     memset(gpfamilies, 0, sizeof(*gpfamilies) * numSought);
1138
1139     for (i = 0; i < numSought && i < fontCollection->count && stat == Ok; i++)
1140     {
1141         stat = GdipCloneFontFamily(fontCollection->FontFamilies[i], &gpfamilies[i]);
1142     }
1143
1144     if (stat == Ok)
1145         *numFound = i;
1146     else
1147     {
1148         int numToFree=i;
1149         for (i=0; i<numToFree; i++)
1150         {
1151             GdipDeleteFontFamily(gpfamilies[i]);
1152             gpfamilies[i] = NULL;
1153         }
1154     }
1155
1156     return stat;
1157 }
1158
1159 void free_installed_fonts(void)
1160 {
1161     while (installedFontCollection.count)
1162         GdipDeleteFontFamily(installedFontCollection.FontFamilies[--installedFontCollection.count]);
1163     HeapFree(GetProcessHeap(), 0, installedFontCollection.FontFamilies);
1164     installedFontCollection.FontFamilies = NULL;
1165     installedFontCollection.allocated = 0;
1166 }
1167
1168 static INT CALLBACK add_font_proc(const LOGFONTW *lfw, const TEXTMETRICW *ntm,
1169         DWORD type, LPARAM lParam)
1170 {
1171     GpFontCollection* fonts = (GpFontCollection*)lParam;
1172     int i;
1173
1174     if (type == RASTER_FONTTYPE)
1175         return 1;
1176
1177     /* skip duplicates */
1178     for (i=0; i<fonts->count; i++)
1179         if (strcmpiW(lfw->lfFaceName, fonts->FontFamilies[i]->FamilyName) == 0)
1180             return 1;
1181
1182     if (fonts->allocated == fonts->count)
1183     {
1184         INT new_alloc_count = fonts->allocated+50;
1185         GpFontFamily** new_family_list = HeapAlloc(GetProcessHeap(), 0, new_alloc_count*sizeof(void*));
1186
1187         if (!new_family_list)
1188             return 0;
1189
1190         memcpy(new_family_list, fonts->FontFamilies, fonts->count*sizeof(void*));
1191         HeapFree(GetProcessHeap(), 0, fonts->FontFamilies);
1192         fonts->FontFamilies = new_family_list;
1193         fonts->allocated = new_alloc_count;
1194     }
1195
1196     if (GdipCreateFontFamilyFromName(lfw->lfFaceName, NULL, &fonts->FontFamilies[fonts->count]) == Ok)
1197         fonts->count++;
1198     else
1199         return 0;
1200
1201     return 1;
1202 }
1203
1204 GpStatus WINGDIPAPI GdipNewInstalledFontCollection(
1205         GpFontCollection** fontCollection)
1206 {
1207     TRACE("(%p)\n",fontCollection);
1208
1209     if (!fontCollection)
1210         return InvalidParameter;
1211
1212     if (installedFontCollection.count == 0)
1213     {
1214         HDC hdc;
1215         LOGFONTW lfw;
1216
1217         hdc = GetDC(0);
1218
1219         lfw.lfCharSet = DEFAULT_CHARSET;
1220         lfw.lfFaceName[0] = 0;
1221         lfw.lfPitchAndFamily = 0;
1222
1223         if (!EnumFontFamiliesExW(hdc, &lfw, add_font_proc, (LPARAM)&installedFontCollection, 0))
1224         {
1225             free_installed_fonts();
1226             ReleaseDC(0, hdc);
1227             return OutOfMemory;
1228         }
1229
1230         ReleaseDC(0, hdc);
1231     }
1232
1233     *fontCollection = &installedFontCollection;
1234
1235     return Ok;
1236 }