gdiplus: Return OutOfMemory when creating a path gradient from a NULL path.
[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
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     *(LOGFONTW *)lParam = *elf;
533
534     return 0;
535 }
536
537 static BOOL find_installed_font(const WCHAR *name, OUTLINETEXTMETRICW *otm)
538 {
539     LOGFONTW lf;
540     HDC hdc = CreateCompatibleDC(0);
541     BOOL ret = FALSE;
542
543     if(!EnumFontFamiliesW(hdc, name, is_font_installed_proc, (LPARAM)&lf))
544     {
545         HFONT hfont;
546
547         lf.lfHeight = -2048;
548         hfont = CreateFontIndirectW(&lf);
549         hfont = SelectObject(hdc, hfont);
550
551         otm->otmSize = sizeof(*otm);
552         if (GetOutlineTextMetricsW(hdc, otm->otmSize, otm))
553             ret = TRUE;
554
555         DeleteObject(SelectObject(hdc, hfont));
556     }
557
558     DeleteDC(hdc);
559     return ret;
560 }
561
562 /*******************************************************************************
563  * GdipCreateFontFamilyFromName [GDIPLUS.@]
564  *
565  * Creates a font family object based on a supplied name
566  *
567  * PARAMS
568  *  name               [I] Name of the font
569  *  fontCollection     [I] What font collection (if any) the font belongs to (may be NULL)
570  *  FontFamily         [O] Pointer to the resulting FontFamily object
571  *
572  * RETURNS
573  *  SUCCESS: Ok
574  *  FAILURE: FamilyNotFound if the requested FontFamily does not exist on the system
575  *  FAILURE: Invalid parameter if FontFamily or name is NULL
576  *
577  * NOTES
578  *   If fontCollection is NULL then the object is not part of any collection
579  *
580  */
581
582 GpStatus WINGDIPAPI GdipCreateFontFamilyFromName(GDIPCONST WCHAR *name,
583                                         GpFontCollection *fontCollection,
584                                         GpFontFamily **FontFamily)
585 {
586     GpFontFamily* ffamily;
587     OUTLINETEXTMETRICW otm;
588
589     TRACE("%s, %p %p\n", debugstr_w(name), fontCollection, FontFamily);
590
591     if (!(name && FontFamily))
592         return InvalidParameter;
593     if (fontCollection)
594         FIXME("No support for FontCollections yet!\n");
595
596     if (!find_installed_font(name, &otm))
597         return FontFamilyNotFound;
598
599     ffamily = GdipAlloc(sizeof (GpFontFamily));
600     if (!ffamily) return OutOfMemory;
601
602     ffamily->otm = otm;
603     lstrcpynW(ffamily->FamilyName, name, LF_FACESIZE);
604
605     *FontFamily = ffamily;
606
607     TRACE("<-- %p\n", ffamily);
608
609     return Ok;
610 }
611
612 /*******************************************************************************
613  * GdipCloneFontFamily [GDIPLUS.@]
614  *
615  * Creates a deep copy of a Font Family object
616  *
617  * PARAMS
618  *  FontFamily          [I] Font to clone
619  *  clonedFontFamily    [O] The resulting cloned font
620  *
621  * RETURNS
622  *  SUCCESS: Ok
623  */
624 GpStatus WINGDIPAPI GdipCloneFontFamily(GpFontFamily* FontFamily, GpFontFamily** clonedFontFamily)
625 {
626     if (!(FontFamily && clonedFontFamily)) return InvalidParameter;
627
628     TRACE("stub: %p (%s), %p\n", FontFamily,
629             debugstr_w(FontFamily->FamilyName), clonedFontFamily);
630
631     *clonedFontFamily = GdipAlloc(sizeof(GpFontFamily));
632     if (!*clonedFontFamily) return OutOfMemory;
633
634     (*clonedFontFamily)->otm = FontFamily->otm;
635     lstrcpyW((*clonedFontFamily)->FamilyName, FontFamily->FamilyName);
636
637     TRACE("<-- %p\n", *clonedFontFamily);
638
639     return Ok;
640 }
641
642 /*******************************************************************************
643  * GdipGetFamilyName [GDIPLUS.@]
644  *
645  * Returns the family name into name
646  *
647  * PARAMS
648  *  *family     [I] Family to retrieve from
649  *  *name       [O] WCHARS of the family name
650  *  LANGID      [I] charset
651  *
652  * RETURNS
653  *  SUCCESS: Ok
654  *  FAILURE: InvalidParameter if family is NULL
655  *
656  * NOTES
657  *   If name is a NULL ptr, then both XP and Vista will crash (so we do as well)
658  */
659 GpStatus WINGDIPAPI GdipGetFamilyName (GDIPCONST GpFontFamily *family,
660                                        WCHAR *name, LANGID language)
661 {
662     static int lang_fixme;
663
664     if (family == NULL)
665          return InvalidParameter;
666
667     TRACE("%p, %p, %d\n", family, name, language);
668
669     if (language != LANG_NEUTRAL && !lang_fixme++)
670         FIXME("No support for handling of multiple languages!\n");
671
672     lstrcpynW (name, family->FamilyName, LF_FACESIZE);
673
674     return Ok;
675 }
676
677
678 /*****************************************************************************
679  * GdipDeleteFontFamily [GDIPLUS.@]
680  *
681  * Removes the specified FontFamily
682  *
683  * PARAMS
684  *  *FontFamily         [I] The family to delete
685  *
686  * RETURNS
687  *  SUCCESS: Ok
688  *  FAILURE: InvalidParameter if FontFamily is NULL.
689  *
690  */
691 GpStatus WINGDIPAPI GdipDeleteFontFamily(GpFontFamily *FontFamily)
692 {
693     if (!FontFamily)
694         return InvalidParameter;
695     TRACE("Deleting %p (%s)\n", FontFamily, debugstr_w(FontFamily->FamilyName));
696
697     GdipFree (FontFamily);
698
699     return Ok;
700 }
701
702 GpStatus WINGDIPAPI GdipGetCellAscent(GDIPCONST GpFontFamily *family,
703         INT style, UINT16* CellAscent)
704 {
705     if (!(family && CellAscent)) return InvalidParameter;
706
707     *CellAscent = family->otm.otmTextMetrics.tmAscent;
708
709     return Ok;
710 }
711
712 GpStatus WINGDIPAPI GdipGetCellDescent(GDIPCONST GpFontFamily *family,
713         INT style, UINT16* CellDescent)
714 {
715     TRACE("(%p, %d, %p)\n", family, style, CellDescent);
716
717     if (!(family && CellDescent)) return InvalidParameter;
718
719     *CellDescent = family->otm.otmTextMetrics.tmDescent;
720
721     return Ok;
722 }
723
724 /*******************************************************************************
725  * GdipGetEmHeight [GDIPLUS.@]
726  *
727  * Gets the height of the specified family in EmHeights
728  *
729  * PARAMS
730  *  family      [I] Family to retrieve from
731  *  style       [I] (optional) style
732  *  EmHeight    [O] return value
733  *
734  * RETURNS
735  *  SUCCESS: Ok
736  *  FAILURE: InvalidParameter
737  */
738 GpStatus WINGDIPAPI GdipGetEmHeight(GDIPCONST GpFontFamily *family, INT style, UINT16* EmHeight)
739 {
740     if (!(family && EmHeight)) return InvalidParameter;
741
742     TRACE("%p (%s), %d, %p\n", family, debugstr_w(family->FamilyName), style, EmHeight);
743
744     *EmHeight = family->otm.otmEMSquare;
745
746     return Ok;
747 }
748
749
750 /*******************************************************************************
751  * GdipGetLineSpacing [GDIPLUS.@]
752  *
753  * Returns the line spacing in design units
754  *
755  * PARAMS
756  *  family      [I] Family to retrieve from
757  *  style       [I] (Optional) font style
758  *  LineSpacing [O] Return value
759  *
760  * RETURNS
761  *  SUCCESS: Ok
762  *  FAILURE: InvalidParameter (family or LineSpacing was NULL)
763  */
764 GpStatus WINGDIPAPI GdipGetLineSpacing(GDIPCONST GpFontFamily *family,
765         INT style, UINT16* LineSpacing)
766 {
767     TRACE("%p, %d, %p\n", family, style, LineSpacing);
768
769     if (!(family && LineSpacing))
770         return InvalidParameter;
771
772     if (style) FIXME("ignoring style\n");
773
774     *LineSpacing = family->otm.otmTextMetrics.tmAscent + family->otm.otmTextMetrics.tmDescent + family->otm.otmTextMetrics.tmExternalLeading;
775
776     return Ok;
777 }
778
779 static INT CALLBACK font_has_style_proc(const LOGFONTW *elf,
780                             const TEXTMETRICW *ntm, DWORD type, LPARAM lParam)
781 {
782     INT fontstyle = FontStyleRegular;
783
784     if (!ntm) return 1;
785
786     if (ntm->tmWeight >= FW_BOLD) fontstyle |= FontStyleBold;
787     if (ntm->tmItalic) fontstyle |= FontStyleItalic;
788     if (ntm->tmUnderlined) fontstyle |= FontStyleUnderline;
789     if (ntm->tmStruckOut) fontstyle |= FontStyleStrikeout;
790
791     return (INT)lParam != fontstyle;
792 }
793
794 GpStatus WINGDIPAPI GdipIsStyleAvailable(GDIPCONST GpFontFamily* family,
795         INT style, BOOL* IsStyleAvailable)
796 {
797     HDC hdc;
798
799     TRACE("%p %d %p\n", family, style, IsStyleAvailable);
800
801     if (!(family && IsStyleAvailable))
802         return InvalidParameter;
803
804     *IsStyleAvailable = FALSE;
805
806     hdc = GetDC(0);
807
808     if(!EnumFontFamiliesW(hdc, family->FamilyName, font_has_style_proc, (LPARAM)style))
809         *IsStyleAvailable = TRUE;
810
811     ReleaseDC(0, hdc);
812
813     return Ok;
814 }
815
816 /*****************************************************************************
817  * GdipGetGenericFontFamilyMonospace [GDIPLUS.@]
818  *
819  * Obtains a serif family (Courier New on Windows)
820  *
821  * PARAMS
822  *  **nativeFamily         [I] Where the font will be stored
823  *
824  * RETURNS
825  *  InvalidParameter if nativeFamily is NULL.
826  *  Ok otherwise.
827  */
828 GpStatus WINGDIPAPI GdipGetGenericFontFamilyMonospace(GpFontFamily **nativeFamily)
829 {
830     static const WCHAR CourierNew[] = {'C','o','u','r','i','e','r',' ','N','e','w','\0'};
831     static const WCHAR LiberationMono[] = {'L','i','b','e','r','a','t','i','o','n',' ','M','o','n','o','\0'};
832     GpStatus stat;
833
834     if (nativeFamily == NULL) return InvalidParameter;
835
836     stat = GdipCreateFontFamilyFromName(CourierNew, NULL, nativeFamily);
837
838     if (stat == FontFamilyNotFound)
839         stat = GdipCreateFontFamilyFromName(LiberationMono, NULL, nativeFamily);
840
841     if (stat == FontFamilyNotFound)
842         ERR("Missing 'Courier New' font\n");
843
844     return stat;
845 }
846
847 /*****************************************************************************
848  * GdipGetGenericFontFamilySerif [GDIPLUS.@]
849  *
850  * Obtains a serif family (Times New Roman on Windows)
851  *
852  * PARAMS
853  *  **nativeFamily         [I] Where the font will be stored
854  *
855  * RETURNS
856  *  InvalidParameter if nativeFamily is NULL.
857  *  Ok otherwise.
858  */
859 GpStatus WINGDIPAPI GdipGetGenericFontFamilySerif(GpFontFamily **nativeFamily)
860 {
861     static const WCHAR TimesNewRoman[] = {'T','i','m','e','s',' ','N','e','w',' ','R','o','m','a','n','\0'};
862     static const WCHAR LiberationSerif[] = {'L','i','b','e','r','a','t','i','o','n',' ','S','e','r','i','f','\0'};
863     GpStatus stat;
864
865     TRACE("(%p)\n", nativeFamily);
866
867     if (nativeFamily == NULL) return InvalidParameter;
868
869     stat = GdipCreateFontFamilyFromName(TimesNewRoman, NULL, nativeFamily);
870
871     if (stat == FontFamilyNotFound)
872         stat = GdipCreateFontFamilyFromName(LiberationSerif, NULL, nativeFamily);
873
874     if (stat == FontFamilyNotFound)
875         ERR("Missing 'Times New Roman' font\n");
876
877     return stat;
878 }
879
880 /*****************************************************************************
881  * GdipGetGenericFontFamilySansSerif [GDIPLUS.@]
882  *
883  * Obtains a serif family (Microsoft Sans Serif on Windows)
884  *
885  * PARAMS
886  *  **nativeFamily         [I] Where the font will be stored
887  *
888  * RETURNS
889  *  InvalidParameter if nativeFamily is NULL.
890  *  Ok otherwise.
891  */
892 GpStatus WINGDIPAPI GdipGetGenericFontFamilySansSerif(GpFontFamily **nativeFamily)
893 {
894     GpStatus stat;
895     static const WCHAR MicrosoftSansSerif[] = {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f','\0'};
896     static const WCHAR Tahoma[] = {'T','a','h','o','m','a','\0'};
897
898     TRACE("(%p)\n", nativeFamily);
899
900     if (nativeFamily == NULL) return InvalidParameter;
901
902     stat = GdipCreateFontFamilyFromName(MicrosoftSansSerif, NULL, nativeFamily);
903
904     if (stat == FontFamilyNotFound)
905         /* FIXME: Microsoft Sans Serif is not installed on Wine. */
906         stat = GdipCreateFontFamilyFromName(Tahoma, NULL, nativeFamily);
907
908     return stat;
909 }
910
911 /*****************************************************************************
912  * GdipGetGenericFontFamilySansSerif [GDIPLUS.@]
913  */
914 GpStatus WINGDIPAPI GdipNewPrivateFontCollection(GpFontCollection** fontCollection)
915 {
916     TRACE("%p\n", fontCollection);
917
918     if (!fontCollection)
919         return InvalidParameter;
920
921     *fontCollection = GdipAlloc(sizeof(GpFontCollection));
922     if (!*fontCollection) return OutOfMemory;
923
924     (*fontCollection)->FontFamilies = NULL;
925     (*fontCollection)->count = 0;
926     (*fontCollection)->allocated = 0;
927
928     TRACE("<-- %p\n", *fontCollection);
929
930     return Ok;
931 }
932
933 /*****************************************************************************
934  * GdipDeletePrivateFontCollection [GDIPLUS.@]
935  */
936 GpStatus WINGDIPAPI GdipDeletePrivateFontCollection(GpFontCollection **fontCollection)
937 {
938     INT i;
939
940     TRACE("%p\n", fontCollection);
941
942     if (!fontCollection)
943         return InvalidParameter;
944
945     for (i = 0; i < (*fontCollection)->count; i++) GdipFree((*fontCollection)->FontFamilies[i]);
946     GdipFree(*fontCollection);
947
948     return Ok;
949 }
950
951 /*****************************************************************************
952  * GdipPrivateAddFontFile [GDIPLUS.@]
953  */
954 GpStatus WINGDIPAPI GdipPrivateAddFontFile(GpFontCollection* fontCollection,
955         GDIPCONST WCHAR* filename)
956 {
957     FIXME("stub: %p, %s\n", fontCollection, debugstr_w(filename));
958
959     if (!(fontCollection && filename))
960         return InvalidParameter;
961
962     return NotImplemented;
963 }
964
965 /* Copied from msi/font.c */
966
967 typedef struct _tagTT_OFFSET_TABLE {
968     USHORT uMajorVersion;
969     USHORT uMinorVersion;
970     USHORT uNumOfTables;
971     USHORT uSearchRange;
972     USHORT uEntrySelector;
973     USHORT uRangeShift;
974 } TT_OFFSET_TABLE;
975
976 typedef struct _tagTT_TABLE_DIRECTORY {
977     char szTag[4]; /* table name */
978     ULONG uCheckSum; /* Check sum */
979     ULONG uOffset; /* Offset from beginning of file */
980     ULONG uLength; /* length of the table in bytes */
981 } TT_TABLE_DIRECTORY;
982
983 typedef struct _tagTT_NAME_TABLE_HEADER {
984     USHORT uFSelector; /* format selector. Always 0 */
985     USHORT uNRCount; /* Name Records count */
986     USHORT uStorageOffset; /* Offset for strings storage,
987                             * from start of the table */
988 } TT_NAME_TABLE_HEADER;
989
990 #define NAME_ID_FULL_FONT_NAME  4
991 #define NAME_ID_VERSION         5
992
993 typedef struct _tagTT_NAME_RECORD {
994     USHORT uPlatformID;
995     USHORT uEncodingID;
996     USHORT uLanguageID;
997     USHORT uNameID;
998     USHORT uStringLength;
999     USHORT uStringOffset; /* from start of storage area */
1000 } TT_NAME_RECORD;
1001
1002 #define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
1003 #define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))
1004
1005 /*
1006  * Code based off of code located here
1007  * http://www.codeproject.com/gdi/fontnamefromfile.asp
1008  */
1009 static WCHAR *load_ttf_name_id( const char *mem, DWORD_PTR size, DWORD id, WCHAR *ret, DWORD len )
1010 {
1011     const TT_TABLE_DIRECTORY *tblDir;
1012     TT_OFFSET_TABLE ttOffsetTable;
1013     TT_NAME_TABLE_HEADER ttNTHeader;
1014     TT_NAME_RECORD ttRecord;
1015     DWORD ofs, pos;
1016     int i;
1017
1018     if (sizeof(TT_OFFSET_TABLE) > size)
1019         return NULL;
1020     ttOffsetTable = *(TT_OFFSET_TABLE*)mem;
1021     ttOffsetTable.uNumOfTables = SWAPWORD(ttOffsetTable.uNumOfTables);
1022     ttOffsetTable.uMajorVersion = SWAPWORD(ttOffsetTable.uMajorVersion);
1023     ttOffsetTable.uMinorVersion = SWAPWORD(ttOffsetTable.uMinorVersion);
1024
1025     if (ttOffsetTable.uMajorVersion != 1 || ttOffsetTable.uMinorVersion != 0)
1026         return NULL;
1027
1028     pos = sizeof(ttOffsetTable);
1029     for (i = 0; i < ttOffsetTable.uNumOfTables; i++)
1030     {
1031         tblDir = (const TT_TABLE_DIRECTORY*)&mem[pos];
1032         pos += sizeof(*tblDir);
1033         if (memcmp(tblDir->szTag,"name",4)==0)
1034         {
1035             ofs = SWAPLONG(tblDir->uOffset);
1036             break;
1037         }
1038     }
1039     if (i >= ttOffsetTable.uNumOfTables)
1040         return NULL;
1041
1042     pos = ofs + sizeof(ttNTHeader);
1043     if (pos > size)
1044         return NULL;
1045     ttNTHeader = *(TT_NAME_TABLE_HEADER*)&mem[ofs];
1046     ttNTHeader.uNRCount = SWAPWORD(ttNTHeader.uNRCount);
1047     ttNTHeader.uStorageOffset = SWAPWORD(ttNTHeader.uStorageOffset);
1048     for(i=0; i<ttNTHeader.uNRCount; i++)
1049     {
1050         ttRecord = *(TT_NAME_RECORD*)&mem[pos];
1051         pos += sizeof(ttRecord);
1052         if (pos > size)
1053             return NULL;
1054
1055         ttRecord.uNameID = SWAPWORD(ttRecord.uNameID);
1056         if (ttRecord.uNameID == id)
1057         {
1058             const char *buf;
1059
1060             ttRecord.uStringLength = SWAPWORD(ttRecord.uStringLength);
1061             ttRecord.uStringOffset = SWAPWORD(ttRecord.uStringOffset);
1062             if (ofs + ttRecord.uStringOffset + ttNTHeader.uStorageOffset + ttRecord.uStringLength > size)
1063                 return NULL;
1064             buf = mem + ofs + ttRecord.uStringOffset + ttNTHeader.uStorageOffset;
1065             len = MultiByteToWideChar(CP_ACP, 0, buf, ttRecord.uStringLength, ret, len-1);
1066             ret[len] = 0;
1067             return ret;
1068         }
1069     }
1070     return NULL;
1071 }
1072
1073 static INT CALLBACK add_font_proc(const LOGFONTW *lfw, const TEXTMETRICW *ntm, DWORD type, LPARAM lParam);
1074
1075 /*****************************************************************************
1076  * GdipPrivateAddMemoryFont [GDIPLUS.@]
1077  */
1078 GpStatus WINGDIPAPI GdipPrivateAddMemoryFont(GpFontCollection* fontCollection,
1079         GDIPCONST void* memory, INT length)
1080 {
1081     WCHAR buf[32], *name;
1082     DWORD count = 0;
1083     HANDLE font;
1084     TRACE("%p, %p, %d\n", fontCollection, memory, length);
1085
1086     if (!fontCollection || !memory || !length)
1087         return InvalidParameter;
1088
1089     name = load_ttf_name_id(memory, length, NAME_ID_FULL_FONT_NAME, buf, sizeof(buf)/sizeof(*buf));
1090     if (!name)
1091         return OutOfMemory;
1092
1093     font = AddFontMemResourceEx((void*)memory, length, NULL, &count);
1094     TRACE("%s: %p/%u\n", debugstr_w(name), font, count);
1095     if (!font || !count)
1096         return InvalidParameter;
1097
1098     if (count)
1099     {
1100         HDC hdc;
1101         LOGFONTW lfw;
1102
1103         hdc = GetDC(0);
1104
1105         lfw.lfCharSet = DEFAULT_CHARSET;
1106         lstrcpyW(lfw.lfFaceName, name);
1107         lfw.lfPitchAndFamily = 0;
1108
1109         if (!EnumFontFamiliesExW(hdc, &lfw, add_font_proc, (LPARAM)fontCollection, 0))
1110         {
1111             ReleaseDC(0, hdc);
1112             return OutOfMemory;
1113         }
1114
1115         ReleaseDC(0, hdc);
1116     }
1117     return Ok;
1118 }
1119
1120 /*****************************************************************************
1121  * GdipGetFontCollectionFamilyCount [GDIPLUS.@]
1122  */
1123 GpStatus WINGDIPAPI GdipGetFontCollectionFamilyCount(
1124         GpFontCollection* fontCollection, INT* numFound)
1125 {
1126     TRACE("%p, %p\n", fontCollection, numFound);
1127
1128     if (!(fontCollection && numFound))
1129         return InvalidParameter;
1130
1131     *numFound = fontCollection->count;
1132     return Ok;
1133 }
1134
1135 /*****************************************************************************
1136  * GdipGetFontCollectionFamilyList [GDIPLUS.@]
1137  */
1138 GpStatus WINGDIPAPI GdipGetFontCollectionFamilyList(
1139         GpFontCollection* fontCollection, INT numSought,
1140         GpFontFamily* gpfamilies[], INT* numFound)
1141 {
1142     INT i;
1143     GpStatus stat=Ok;
1144
1145     TRACE("%p, %d, %p, %p\n", fontCollection, numSought, gpfamilies, numFound);
1146
1147     if (!(fontCollection && gpfamilies && numFound))
1148         return InvalidParameter;
1149
1150     memset(gpfamilies, 0, sizeof(*gpfamilies) * numSought);
1151
1152     for (i = 0; i < numSought && i < fontCollection->count && stat == Ok; i++)
1153     {
1154         stat = GdipCloneFontFamily(fontCollection->FontFamilies[i], &gpfamilies[i]);
1155     }
1156
1157     if (stat == Ok)
1158         *numFound = i;
1159     else
1160     {
1161         int numToFree=i;
1162         for (i=0; i<numToFree; i++)
1163         {
1164             GdipDeleteFontFamily(gpfamilies[i]);
1165             gpfamilies[i] = NULL;
1166         }
1167     }
1168
1169     return stat;
1170 }
1171
1172 void free_installed_fonts(void)
1173 {
1174     while (installedFontCollection.count)
1175         GdipDeleteFontFamily(installedFontCollection.FontFamilies[--installedFontCollection.count]);
1176     HeapFree(GetProcessHeap(), 0, installedFontCollection.FontFamilies);
1177     installedFontCollection.FontFamilies = NULL;
1178     installedFontCollection.allocated = 0;
1179 }
1180
1181 static INT CALLBACK add_font_proc(const LOGFONTW *lfw, const TEXTMETRICW *ntm,
1182         DWORD type, LPARAM lParam)
1183 {
1184     GpFontCollection* fonts = (GpFontCollection*)lParam;
1185     int i;
1186
1187     if (type == RASTER_FONTTYPE)
1188         return 1;
1189
1190     /* skip duplicates */
1191     for (i=0; i<fonts->count; i++)
1192         if (strcmpiW(lfw->lfFaceName, fonts->FontFamilies[i]->FamilyName) == 0)
1193             return 1;
1194
1195     if (fonts->allocated == fonts->count)
1196     {
1197         INT new_alloc_count = fonts->allocated+50;
1198         GpFontFamily** new_family_list = HeapAlloc(GetProcessHeap(), 0, new_alloc_count*sizeof(void*));
1199
1200         if (!new_family_list)
1201             return 0;
1202
1203         memcpy(new_family_list, fonts->FontFamilies, fonts->count*sizeof(void*));
1204         HeapFree(GetProcessHeap(), 0, fonts->FontFamilies);
1205         fonts->FontFamilies = new_family_list;
1206         fonts->allocated = new_alloc_count;
1207     }
1208
1209     if (GdipCreateFontFamilyFromName(lfw->lfFaceName, NULL, &fonts->FontFamilies[fonts->count]) == Ok)
1210         fonts->count++;
1211     else
1212         return 0;
1213
1214     return 1;
1215 }
1216
1217 GpStatus WINGDIPAPI GdipNewInstalledFontCollection(
1218         GpFontCollection** fontCollection)
1219 {
1220     TRACE("(%p)\n",fontCollection);
1221
1222     if (!fontCollection)
1223         return InvalidParameter;
1224
1225     if (installedFontCollection.count == 0)
1226     {
1227         HDC hdc;
1228         LOGFONTW lfw;
1229
1230         hdc = GetDC(0);
1231
1232         lfw.lfCharSet = DEFAULT_CHARSET;
1233         lfw.lfFaceName[0] = 0;
1234         lfw.lfPitchAndFamily = 0;
1235
1236         if (!EnumFontFamiliesExW(hdc, &lfw, add_font_proc, (LPARAM)&installedFontCollection, 0))
1237         {
1238             free_installed_fonts();
1239             ReleaseDC(0, hdc);
1240             return OutOfMemory;
1241         }
1242
1243         ReleaseDC(0, hdc);
1244     }
1245
1246     *fontCollection = &installedFontCollection;
1247
1248     return Ok;
1249 }