ntdll: Move retrieving the startup info to the Unix library.
[wine.git] / dlls / gdiplus / font.c
bloba7719a94583e820d4b9b842ef23342b05084084a
1 /*
2 * Copyright (C) 2007 Google (Evan Stade)
3 * Copyright (C) 2012 Dmitry Timoshkov
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.
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.
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
20 #include <stdarg.h>
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"
29 WINE_DEFAULT_DEBUG_CHANNEL (gdiplus);
31 #include "objbase.h"
33 #include "gdiplus.h"
34 #include "gdiplus_private.h"
36 /* PANOSE is 10 bytes in size, need to pack the structure properly */
37 #include "pshpack2.h"
38 typedef struct
40 USHORT version;
41 SHORT xAvgCharWidth;
42 USHORT usWeightClass;
43 USHORT usWidthClass;
44 SHORT fsType;
45 SHORT ySubscriptXSize;
46 SHORT ySubscriptYSize;
47 SHORT ySubscriptXOffset;
48 SHORT ySubscriptYOffset;
49 SHORT ySuperscriptXSize;
50 SHORT ySuperscriptYSize;
51 SHORT ySuperscriptXOffset;
52 SHORT ySuperscriptYOffset;
53 SHORT yStrikeoutSize;
54 SHORT yStrikeoutPosition;
55 SHORT sFamilyClass;
56 PANOSE panose;
57 ULONG ulUnicodeRange1;
58 ULONG ulUnicodeRange2;
59 ULONG ulUnicodeRange3;
60 ULONG ulUnicodeRange4;
61 CHAR achVendID[4];
62 USHORT fsSelection;
63 USHORT usFirstCharIndex;
64 USHORT usLastCharIndex;
65 /* According to the Apple spec, original version didn't have the below fields,
66 * version numbers were taken from the OpenType spec.
68 /* version 0 (TrueType 1.5) */
69 USHORT sTypoAscender;
70 USHORT sTypoDescender;
71 USHORT sTypoLineGap;
72 USHORT usWinAscent;
73 USHORT usWinDescent;
74 /* version 1 (TrueType 1.66) */
75 ULONG ulCodePageRange1;
76 ULONG ulCodePageRange2;
77 /* version 2 (OpenType 1.2) */
78 SHORT sxHeight;
79 SHORT sCapHeight;
80 USHORT usDefaultChar;
81 USHORT usBreakChar;
82 USHORT usMaxContext;
83 } TT_OS2_V2;
85 typedef struct
87 ULONG Version;
88 SHORT Ascender;
89 SHORT Descender;
90 SHORT LineGap;
91 USHORT advanceWidthMax;
92 SHORT minLeftSideBearing;
93 SHORT minRightSideBearing;
94 SHORT xMaxExtent;
95 SHORT caretSlopeRise;
96 SHORT caretSlopeRun;
97 SHORT caretOffset;
98 SHORT reserved[4];
99 SHORT metricDataFormat;
100 USHORT numberOfHMetrics;
101 } TT_HHEA;
102 #include "poppack.h"
104 #ifdef WORDS_BIGENDIAN
105 #define GET_BE_WORD(x) (x)
106 #define GET_BE_DWORD(x) (x)
107 #else
108 #define GET_BE_WORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
109 #define GET_BE_DWORD(x) MAKELONG(GET_BE_WORD(HIWORD(x)), GET_BE_WORD(LOWORD(x)));
110 #endif
112 #define MS_MAKE_TAG(ch0, ch1, ch2, ch3) \
113 ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \
114 ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24))
115 #define MS_OS2_TAG MS_MAKE_TAG('O','S','/','2')
116 #define MS_HHEA_TAG MS_MAKE_TAG('h','h','e','a')
118 static GpFontCollection installedFontCollection = {0};
120 /*******************************************************************************
121 * GdipCreateFont [GDIPLUS.@]
123 * Create a new font based off of a FontFamily
125 * PARAMS
126 * *fontFamily [I] Family to base the font off of
127 * emSize [I] Size of the font
128 * style [I] Bitwise OR of FontStyle enumeration
129 * unit [I] Unit emSize is measured in
130 * **font [I] the resulting Font object
132 * RETURNS
133 * SUCCESS: Ok
134 * FAILURE: InvalidParameter if fontfamily or font is NULL.
135 * FAILURE: FontFamilyNotFound if an invalid FontFamily is given
137 * NOTES
138 * UnitDisplay is unsupported.
139 * emSize is stored separately from lfHeight, to hold the fraction.
141 GpStatus WINGDIPAPI GdipCreateFont(GDIPCONST GpFontFamily *fontFamily,
142 REAL emSize, INT style, Unit unit, GpFont **font)
144 HFONT hfont;
145 OUTLINETEXTMETRICW otm;
146 LOGFONTW lfw;
147 HDC hdc;
148 GpStatus stat;
149 int ret;
151 if (!fontFamily || !font || emSize < 0.0)
152 return InvalidParameter;
154 TRACE("%p (%s), %f, %d, %d, %p\n", fontFamily,
155 debugstr_w(fontFamily->FamilyName), emSize, style, unit, font);
157 memset(&lfw, 0, sizeof(lfw));
159 stat = GdipGetFamilyName(fontFamily, lfw.lfFaceName, LANG_NEUTRAL);
160 if (stat != Ok) return stat;
162 lfw.lfHeight = -units_to_pixels(emSize, unit, fontFamily->dpi);
163 lfw.lfWeight = style & FontStyleBold ? FW_BOLD : FW_REGULAR;
164 lfw.lfItalic = style & FontStyleItalic;
165 lfw.lfUnderline = style & FontStyleUnderline;
166 lfw.lfStrikeOut = style & FontStyleStrikeout;
168 hfont = CreateFontIndirectW(&lfw);
169 hdc = CreateCompatibleDC(0);
170 SelectObject(hdc, hfont);
171 otm.otmSize = sizeof(otm);
172 ret = GetOutlineTextMetricsW(hdc, otm.otmSize, &otm);
173 DeleteDC(hdc);
174 DeleteObject(hfont);
176 if (!ret) return NotTrueTypeFont;
178 *font = heap_alloc_zero(sizeof(GpFont));
179 if (!*font) return OutOfMemory;
181 (*font)->unit = unit;
182 (*font)->emSize = emSize;
183 (*font)->otm = otm;
184 (*font)->family = (GpFontFamily *)fontFamily;
186 TRACE("<-- %p\n", *font);
188 return Ok;
191 /*******************************************************************************
192 * GdipCreateFontFromLogfontW [GDIPLUS.@]
194 GpStatus WINGDIPAPI GdipCreateFontFromLogfontW(HDC hdc,
195 GDIPCONST LOGFONTW *logfont, GpFont **font)
197 HFONT hfont, oldfont;
198 OUTLINETEXTMETRICW otm;
199 WCHAR facename[LF_FACESIZE];
200 GpStatus stat;
201 int ret;
203 TRACE("(%p, %p, %p)\n", hdc, logfont, font);
205 if (!hdc || !logfont || !font)
206 return InvalidParameter;
208 hfont = CreateFontIndirectW(logfont);
209 oldfont = SelectObject(hdc, hfont);
210 otm.otmSize = sizeof(otm);
211 ret = GetOutlineTextMetricsW(hdc, otm.otmSize, &otm);
212 GetTextFaceW(hdc, LF_FACESIZE, facename);
213 SelectObject(hdc, oldfont);
214 DeleteObject(hfont);
216 if (!ret) return NotTrueTypeFont;
218 *font = heap_alloc_zero(sizeof(GpFont));
219 if (!*font) return OutOfMemory;
221 (*font)->unit = UnitWorld;
222 (*font)->emSize = otm.otmTextMetrics.tmAscent;
223 (*font)->otm = otm;
225 stat = GdipCreateFontFamilyFromName(facename, NULL, &(*font)->family);
226 if (stat != Ok)
228 heap_free(*font);
229 return NotTrueTypeFont;
232 TRACE("<-- %p\n", *font);
234 return Ok;
237 /*******************************************************************************
238 * GdipCreateFontFromLogfontA [GDIPLUS.@]
240 GpStatus WINGDIPAPI GdipCreateFontFromLogfontA(HDC hdc,
241 GDIPCONST LOGFONTA *lfa, GpFont **font)
243 LOGFONTW lfw;
245 TRACE("(%p, %p, %p)\n", hdc, lfa, font);
247 if(!lfa || !font)
248 return InvalidParameter;
250 memcpy(&lfw, lfa, FIELD_OFFSET(LOGFONTA,lfFaceName) );
252 if(!MultiByteToWideChar(CP_ACP, 0, lfa->lfFaceName, -1, lfw.lfFaceName, LF_FACESIZE))
253 return GenericError;
255 return GdipCreateFontFromLogfontW(hdc, &lfw, font);
258 /*******************************************************************************
259 * GdipDeleteFont [GDIPLUS.@]
261 GpStatus WINGDIPAPI GdipDeleteFont(GpFont* font)
263 TRACE("(%p)\n", font);
265 if(!font)
266 return InvalidParameter;
268 GdipDeleteFontFamily(font->family);
269 heap_free(font);
271 return Ok;
274 /*******************************************************************************
275 * GdipCreateFontFromDC [GDIPLUS.@]
277 GpStatus WINGDIPAPI GdipCreateFontFromDC(HDC hdc, GpFont **font)
279 HFONT hfont;
280 LOGFONTW lfw;
282 TRACE("(%p, %p)\n", hdc, font);
284 if(!font)
285 return InvalidParameter;
287 hfont = GetCurrentObject(hdc, OBJ_FONT);
288 if(!hfont)
289 return GenericError;
291 if(!GetObjectW(hfont, sizeof(LOGFONTW), &lfw))
292 return GenericError;
294 return GdipCreateFontFromLogfontW(hdc, &lfw, font);
297 /*******************************************************************************
298 * GdipGetFamily [GDIPLUS.@]
300 * Returns the FontFamily for the specified Font
302 * PARAMS
303 * font [I] Font to request from
304 * family [O] Resulting FontFamily object
306 * RETURNS
307 * SUCCESS: Ok
308 * FAILURE: An element of GpStatus
310 GpStatus WINGDIPAPI GdipGetFamily(GpFont *font, GpFontFamily **family)
312 TRACE("%p %p\n", font, family);
314 if (!(font && family))
315 return InvalidParameter;
317 *family = font->family;
318 return Ok;
321 static REAL get_font_size(const GpFont *font)
323 return font->emSize;
326 /******************************************************************************
327 * GdipGetFontSize [GDIPLUS.@]
329 * Returns the size of the font in Units
331 * PARAMS
332 * *font [I] The font to retrieve size from
333 * *size [O] Pointer to hold retrieved value
335 * RETURNS
336 * SUCCESS: Ok
337 * FAILURE: InvalidParameter (font or size was NULL)
339 * NOTES
340 * Size returned is actually emSize -- not internal size used for drawing.
342 GpStatus WINGDIPAPI GdipGetFontSize(GpFont *font, REAL *size)
344 TRACE("(%p, %p)\n", font, size);
346 if (!(font && size)) return InvalidParameter;
348 *size = get_font_size(font);
349 TRACE("%s,%d => %f\n", debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *size);
351 return Ok;
354 static INT get_font_style(const GpFont *font)
356 INT style;
358 if (font->otm.otmTextMetrics.tmWeight > FW_REGULAR)
359 style = FontStyleBold;
360 else
361 style = FontStyleRegular;
362 if (font->otm.otmTextMetrics.tmItalic)
363 style |= FontStyleItalic;
364 if (font->otm.otmTextMetrics.tmUnderlined)
365 style |= FontStyleUnderline;
366 if (font->otm.otmTextMetrics.tmStruckOut)
367 style |= FontStyleStrikeout;
369 return style;
372 /*******************************************************************************
373 * GdipGetFontStyle [GDIPLUS.@]
375 * Gets the font's style, returned in bitwise OR of FontStyle enumeration
377 * PARAMS
378 * font [I] font to request from
379 * style [O] resulting pointer to a FontStyle enumeration
381 * RETURNS
382 * SUCCESS: Ok
383 * FAILURE: InvalidParameter
385 GpStatus WINGDIPAPI GdipGetFontStyle(GpFont *font, INT *style)
387 TRACE("%p %p\n", font, style);
389 if (!(font && style))
390 return InvalidParameter;
392 *style = get_font_style(font);
393 TRACE("%s,%d => %d\n", debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *style);
395 return Ok;
398 /*******************************************************************************
399 * GdipGetFontUnit [GDIPLUS.@]
401 * PARAMS
402 * font [I] Font to retrieve from
403 * unit [O] Return value
405 * RETURNS
406 * FAILURE: font or unit was NULL
407 * OK: otherwise
409 GpStatus WINGDIPAPI GdipGetFontUnit(GpFont *font, Unit *unit)
411 TRACE("(%p, %p)\n", font, unit);
413 if (!(font && unit)) return InvalidParameter;
415 *unit = font->unit;
416 TRACE("%s,%d => %d\n", debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *unit);
418 return Ok;
421 /*******************************************************************************
422 * GdipGetLogFontA [GDIPLUS.@]
424 GpStatus WINGDIPAPI GdipGetLogFontA(GpFont *font, GpGraphics *graphics,
425 LOGFONTA *lfa)
427 GpStatus status;
428 LOGFONTW lfw;
430 TRACE("(%p, %p, %p)\n", font, graphics, lfa);
432 status = GdipGetLogFontW(font, graphics, &lfw);
433 if(status != Ok)
434 return status;
436 memcpy(lfa, &lfw, FIELD_OFFSET(LOGFONTA,lfFaceName) );
438 if(!WideCharToMultiByte(CP_ACP, 0, lfw.lfFaceName, -1, lfa->lfFaceName, LF_FACESIZE, NULL, NULL))
439 return GenericError;
441 return Ok;
444 /*******************************************************************************
445 * GdipGetLogFontW [GDIPLUS.@]
447 GpStatus WINGDIPAPI GdipGetLogFontW(GpFont *font, GpGraphics *graphics, LOGFONTW *lf)
449 REAL angle, rel_height, height;
450 GpMatrix matrix;
451 GpPointF pt[3];
453 TRACE("(%p, %p, %p)\n", font, graphics, lf);
455 if (!font || !graphics || !lf)
456 return InvalidParameter;
458 matrix = graphics->worldtrans;
460 if (font->unit == UnitPixel || font->unit == UnitWorld)
462 height = units_to_pixels(font->emSize, graphics->unit, graphics->yres);
463 if (graphics->unit != UnitDisplay)
464 GdipScaleMatrix(&matrix, graphics->scale, graphics->scale, MatrixOrderAppend);
466 else
468 if (graphics->unit == UnitDisplay || graphics->unit == UnitPixel)
469 height = units_to_pixels(font->emSize, font->unit, graphics->xres);
470 else
471 height = units_to_pixels(font->emSize, font->unit, graphics->yres);
474 pt[0].X = 0.0;
475 pt[0].Y = 0.0;
476 pt[1].X = 1.0;
477 pt[1].Y = 0.0;
478 pt[2].X = 0.0;
479 pt[2].Y = 1.0;
480 GdipTransformMatrixPoints(&matrix, pt, 3);
481 angle = -gdiplus_atan2((pt[1].Y - pt[0].Y), (pt[1].X - pt[0].X));
482 rel_height = sqrt((pt[2].Y - pt[0].Y) * (pt[2].Y - pt[0].Y)+
483 (pt[2].X - pt[0].X) * (pt[2].X - pt[0].X));
485 lf->lfHeight = -gdip_round(height * rel_height);
486 lf->lfWidth = 0;
487 lf->lfEscapement = lf->lfOrientation = gdip_round((angle / M_PI) * 1800.0);
488 if (lf->lfEscapement < 0)
490 lf->lfEscapement += 3600;
491 lf->lfOrientation += 3600;
493 lf->lfWeight = font->otm.otmTextMetrics.tmWeight;
494 lf->lfItalic = font->otm.otmTextMetrics.tmItalic ? 1 : 0;
495 lf->lfUnderline = font->otm.otmTextMetrics.tmUnderlined ? 1 : 0;
496 lf->lfStrikeOut = font->otm.otmTextMetrics.tmStruckOut ? 1 : 0;
497 lf->lfCharSet = font->otm.otmTextMetrics.tmCharSet;
498 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
499 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
500 lf->lfQuality = DEFAULT_QUALITY;
501 lf->lfPitchAndFamily = 0;
502 lstrcpyW(lf->lfFaceName, font->family->FamilyName);
504 TRACE("=> %s,%d\n", debugstr_w(lf->lfFaceName), lf->lfHeight);
506 return Ok;
509 /*******************************************************************************
510 * GdipCloneFont [GDIPLUS.@]
512 GpStatus WINGDIPAPI GdipCloneFont(GpFont *font, GpFont **cloneFont)
514 TRACE("(%p, %p)\n", font, cloneFont);
516 if(!font || !cloneFont)
517 return InvalidParameter;
519 *cloneFont = heap_alloc_zero(sizeof(GpFont));
520 if(!*cloneFont) return OutOfMemory;
522 **cloneFont = *font;
523 return Ok;
526 /*******************************************************************************
527 * GdipGetFontHeight [GDIPLUS.@]
528 * PARAMS
529 * font [I] Font to retrieve height from
530 * graphics [I] The current graphics context
531 * height [O] Resulting height
532 * RETURNS
533 * SUCCESS: Ok
534 * FAILURE: Another element of GpStatus
536 * NOTES
537 * Forwards to GdipGetFontHeightGivenDPI
539 GpStatus WINGDIPAPI GdipGetFontHeight(GDIPCONST GpFont *font,
540 GDIPCONST GpGraphics *graphics, REAL *height)
542 REAL dpi;
543 GpStatus stat;
544 REAL font_height;
546 TRACE("%p %p %p\n", font, graphics, height);
548 if (!font || !height) return InvalidParameter;
550 stat = GdipGetFontHeightGivenDPI(font, font->family->dpi, &font_height);
551 if (stat != Ok) return stat;
553 if (!graphics)
555 *height = font_height;
556 TRACE("%s,%d => %f\n",
557 debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *height);
558 return Ok;
561 stat = GdipGetDpiY((GpGraphics *)graphics, &dpi);
562 if (stat != Ok) return stat;
564 *height = pixels_to_units(font_height, graphics->unit, dpi);
566 TRACE("%s,%d(unit %d) => %f\n",
567 debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, graphics->unit, *height);
568 return Ok;
571 /*******************************************************************************
572 * GdipGetFontHeightGivenDPI [GDIPLUS.@]
573 * PARAMS
574 * font [I] Font to retrieve DPI from
575 * dpi [I] DPI to assume
576 * height [O] Return value
578 * RETURNS
579 * SUCCESS: Ok
580 * FAILURE: InvalidParameter if font or height is NULL
582 * NOTES
583 * According to MSDN, the result is (lineSpacing)*(fontSize / emHeight)*dpi
584 * (for anything other than unit Pixel)
586 GpStatus WINGDIPAPI GdipGetFontHeightGivenDPI(GDIPCONST GpFont *font, REAL dpi, REAL *height)
588 GpStatus stat;
589 INT style;
590 UINT16 line_spacing, em_height;
591 REAL font_size;
593 if (!font || !height) return InvalidParameter;
595 TRACE("%p (%s), %f, %p\n", font,
596 debugstr_w(font->family->FamilyName), dpi, height);
598 font_size = units_to_pixels(get_font_size(font), font->unit, dpi);
599 style = get_font_style(font);
600 stat = GdipGetLineSpacing(font->family, style, &line_spacing);
601 if (stat != Ok) return stat;
602 stat = GdipGetEmHeight(font->family, style, &em_height);
603 if (stat != Ok) return stat;
605 *height = (REAL)line_spacing * font_size / (REAL)em_height;
607 TRACE("%s,%d => %f\n",
608 debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *height);
610 return Ok;
613 /***********************************************************************
614 * Borrowed from GDI32:
616 * Elf is really an ENUMLOGFONTEXW, and ntm is a NEWTEXTMETRICEXW.
617 * We have to use other types because of the FONTENUMPROCW definition.
619 static INT CALLBACK is_font_installed_proc(const LOGFONTW *elf,
620 const TEXTMETRICW *ntm, DWORD type, LPARAM lParam)
622 const ENUMLOGFONTW *elfW = (const ENUMLOGFONTW *)elf;
623 LOGFONTW *lf = (LOGFONTW *)lParam;
625 if (type & RASTER_FONTTYPE)
626 return 1;
628 *lf = *elf;
629 /* replace substituted font name by a real one */
630 lstrcpynW(lf->lfFaceName, elfW->elfFullName, LF_FACESIZE);
631 return 0;
634 struct font_metrics
636 WCHAR facename[LF_FACESIZE];
637 UINT16 em_height, ascent, descent, line_spacing; /* in font units */
638 int dpi;
641 static BOOL get_font_metrics(HDC hdc, struct font_metrics *fm)
643 OUTLINETEXTMETRICW otm;
644 TT_OS2_V2 tt_os2;
645 TT_HHEA tt_hori;
646 LONG size;
647 UINT16 line_gap;
649 otm.otmSize = sizeof(otm);
650 if (!GetOutlineTextMetricsW(hdc, otm.otmSize, &otm)) return FALSE;
652 fm->em_height = otm.otmEMSquare;
653 fm->dpi = GetDeviceCaps(hdc, LOGPIXELSY);
655 memset(&tt_hori, 0, sizeof(tt_hori));
656 if (GetFontData(hdc, MS_HHEA_TAG, 0, &tt_hori, sizeof(tt_hori)) != GDI_ERROR)
658 fm->ascent = GET_BE_WORD(tt_hori.Ascender);
659 fm->descent = -GET_BE_WORD(tt_hori.Descender);
660 TRACE("hhea: ascent %d, descent %d\n", fm->ascent, fm->descent);
661 line_gap = GET_BE_WORD(tt_hori.LineGap);
662 fm->line_spacing = fm->ascent + fm->descent + line_gap;
663 TRACE("line_gap %u, line_spacing %u\n", line_gap, fm->line_spacing);
664 if (fm->ascent + fm->descent != 0) return TRUE;
667 size = GetFontData(hdc, MS_OS2_TAG, 0, NULL, 0);
668 if (size == GDI_ERROR) return FALSE;
670 if (size > sizeof(tt_os2)) size = sizeof(tt_os2);
672 memset(&tt_os2, 0, sizeof(tt_os2));
673 if (GetFontData(hdc, MS_OS2_TAG, 0, &tt_os2, size) != size) return FALSE;
675 fm->ascent = GET_BE_WORD(tt_os2.usWinAscent);
676 fm->descent = GET_BE_WORD(tt_os2.usWinDescent);
677 TRACE("usWinAscent %u, usWinDescent %u\n", fm->ascent, fm->descent);
678 if (fm->ascent + fm->descent == 0)
680 fm->ascent = GET_BE_WORD(tt_os2.sTypoAscender);
681 fm->descent = GET_BE_WORD(tt_os2.sTypoDescender);
682 TRACE("sTypoAscender %u, sTypoDescender %u\n", fm->ascent, fm->descent);
684 line_gap = GET_BE_WORD(tt_os2.sTypoLineGap);
685 fm->line_spacing = fm->ascent + fm->descent + line_gap;
686 TRACE("line_gap %u, line_spacing %u\n", line_gap, fm->line_spacing);
687 return TRUE;
690 /*******************************************************************************
691 * GdipCreateFontFamilyFromName [GDIPLUS.@]
693 * Creates a font family object based on a supplied name
695 * PARAMS
696 * name [I] Name of the font
697 * fontCollection [I] What font collection (if any) the font belongs to (may be NULL)
698 * FontFamily [O] Pointer to the resulting FontFamily object
700 * RETURNS
701 * SUCCESS: Ok
702 * FAILURE: FamilyNotFound if the requested FontFamily does not exist on the system
703 * FAILURE: Invalid parameter if FontFamily or name is NULL
705 * NOTES
706 * If fontCollection is NULL then the object is not part of any collection
710 GpStatus WINGDIPAPI GdipCreateFontFamilyFromName(GDIPCONST WCHAR *name,
711 GpFontCollection *collection,
712 GpFontFamily **family)
714 HDC hdc;
715 LOGFONTW lf;
716 GpStatus status;
717 int i;
719 TRACE("%s, %p %p\n", debugstr_w(name), collection, family);
721 if (!name || !family)
722 return InvalidParameter;
724 if (!collection)
726 status = GdipNewInstalledFontCollection(&collection);
727 if (status != Ok) return status;
730 status = FontFamilyNotFound;
732 hdc = CreateCompatibleDC(0);
734 if (!EnumFontFamiliesW(hdc, name, is_font_installed_proc, (LPARAM)&lf))
736 for (i = 0; i < collection->count; i++)
738 if (!wcsicmp(lf.lfFaceName, collection->FontFamilies[i]->FamilyName))
740 *family = collection->FontFamilies[i];
741 TRACE("<-- %p\n", *family);
742 status = Ok;
743 break;
748 DeleteDC(hdc);
749 return status;
752 /*******************************************************************************
753 * GdipCloneFontFamily [GDIPLUS.@]
755 * Creates a deep copy of a Font Family object
757 * PARAMS
758 * FontFamily [I] Font to clone
759 * clonedFontFamily [O] The resulting cloned font
761 * RETURNS
762 * SUCCESS: Ok
764 GpStatus WINGDIPAPI GdipCloneFontFamily(GpFontFamily *family, GpFontFamily **clone)
766 if (!family || !clone)
767 return InvalidParameter;
769 TRACE("%p (%s), %p\n", family, debugstr_w(family->FamilyName), clone);
771 *clone = family;
772 return Ok;
775 /*******************************************************************************
776 * GdipGetFamilyName [GDIPLUS.@]
778 * Returns the family name into name
780 * PARAMS
781 * *family [I] Family to retrieve from
782 * *name [O] WCHARS of the family name
783 * LANGID [I] charset
785 * RETURNS
786 * SUCCESS: Ok
787 * FAILURE: InvalidParameter if family is NULL
789 * NOTES
790 * If name is a NULL ptr, then both XP and Vista will crash (so we do as well)
792 GpStatus WINGDIPAPI GdipGetFamilyName (GDIPCONST GpFontFamily *family,
793 WCHAR *name, LANGID language)
795 static int lang_fixme;
797 if (family == NULL)
798 return InvalidParameter;
800 TRACE("%p, %p, %d\n", family, name, language);
802 if (language != LANG_NEUTRAL && !lang_fixme++)
803 FIXME("No support for handling of multiple languages!\n");
805 lstrcpynW (name, family->FamilyName, LF_FACESIZE);
807 return Ok;
811 /*****************************************************************************
812 * GdipDeleteFontFamily [GDIPLUS.@]
814 * Removes the specified FontFamily
816 * PARAMS
817 * *FontFamily [I] The family to delete
819 * RETURNS
820 * SUCCESS: Ok
821 * FAILURE: InvalidParameter if FontFamily is NULL.
824 GpStatus WINGDIPAPI GdipDeleteFontFamily(GpFontFamily *FontFamily)
826 if (!FontFamily)
827 return InvalidParameter;
829 return Ok;
832 GpStatus WINGDIPAPI GdipGetCellAscent(GDIPCONST GpFontFamily *family,
833 INT style, UINT16* CellAscent)
835 if (!(family && CellAscent)) return InvalidParameter;
837 *CellAscent = family->ascent;
838 TRACE("%s => %u\n", debugstr_w(family->FamilyName), *CellAscent);
840 return Ok;
843 GpStatus WINGDIPAPI GdipGetCellDescent(GDIPCONST GpFontFamily *family,
844 INT style, UINT16* CellDescent)
846 TRACE("(%p, %d, %p)\n", family, style, CellDescent);
848 if (!(family && CellDescent)) return InvalidParameter;
850 *CellDescent = family->descent;
851 TRACE("%s => %u\n", debugstr_w(family->FamilyName), *CellDescent);
853 return Ok;
856 /*******************************************************************************
857 * GdipGetEmHeight [GDIPLUS.@]
859 * Gets the height of the specified family in EmHeights
861 * PARAMS
862 * family [I] Family to retrieve from
863 * style [I] (optional) style
864 * EmHeight [O] return value
866 * RETURNS
867 * SUCCESS: Ok
868 * FAILURE: InvalidParameter
870 GpStatus WINGDIPAPI GdipGetEmHeight(GDIPCONST GpFontFamily *family, INT style, UINT16* EmHeight)
872 if (!(family && EmHeight)) return InvalidParameter;
874 TRACE("%p (%s), %d, %p\n", family, debugstr_w(family->FamilyName), style, EmHeight);
876 *EmHeight = family->em_height;
877 TRACE("%s => %u\n", debugstr_w(family->FamilyName), *EmHeight);
879 return Ok;
883 /*******************************************************************************
884 * GdipGetLineSpacing [GDIPLUS.@]
886 * Returns the line spacing in design units
888 * PARAMS
889 * family [I] Family to retrieve from
890 * style [I] (Optional) font style
891 * LineSpacing [O] Return value
893 * RETURNS
894 * SUCCESS: Ok
895 * FAILURE: InvalidParameter (family or LineSpacing was NULL)
897 GpStatus WINGDIPAPI GdipGetLineSpacing(GDIPCONST GpFontFamily *family,
898 INT style, UINT16* LineSpacing)
900 TRACE("%p, %d, %p\n", family, style, LineSpacing);
902 if (!(family && LineSpacing))
903 return InvalidParameter;
905 if (style) FIXME("ignoring style\n");
907 *LineSpacing = family->line_spacing;
908 TRACE("%s => %u\n", debugstr_w(family->FamilyName), *LineSpacing);
910 return Ok;
913 static INT CALLBACK font_has_style_proc(const LOGFONTW *elf,
914 const TEXTMETRICW *ntm, DWORD type, LPARAM lParam)
916 INT fontstyle = FontStyleRegular;
918 if (!ntm) return 1;
920 if (ntm->tmWeight >= FW_BOLD) fontstyle |= FontStyleBold;
921 if (ntm->tmItalic) fontstyle |= FontStyleItalic;
922 if (ntm->tmUnderlined) fontstyle |= FontStyleUnderline;
923 if (ntm->tmStruckOut) fontstyle |= FontStyleStrikeout;
925 return (INT)lParam != fontstyle;
928 GpStatus WINGDIPAPI GdipIsStyleAvailable(GDIPCONST GpFontFamily* family,
929 INT style, BOOL* IsStyleAvailable)
931 HDC hdc;
933 TRACE("%p %d %p\n", family, style, IsStyleAvailable);
935 if (!(family && IsStyleAvailable))
936 return InvalidParameter;
938 *IsStyleAvailable = FALSE;
940 hdc = CreateCompatibleDC(0);
942 if(!EnumFontFamiliesW(hdc, family->FamilyName, font_has_style_proc, (LPARAM)style))
943 *IsStyleAvailable = TRUE;
945 DeleteDC(hdc);
947 return Ok;
950 /*****************************************************************************
951 * GdipGetGenericFontFamilyMonospace [GDIPLUS.@]
953 * Obtains a serif family (Courier New on Windows)
955 * PARAMS
956 * **nativeFamily [I] Where the font will be stored
958 * RETURNS
959 * InvalidParameter if nativeFamily is NULL.
960 * Ok otherwise.
962 GpStatus WINGDIPAPI GdipGetGenericFontFamilyMonospace(GpFontFamily **nativeFamily)
964 static const WCHAR CourierNew[] = {'C','o','u','r','i','e','r',' ','N','e','w','\0'};
965 static const WCHAR LiberationMono[] = {'L','i','b','e','r','a','t','i','o','n',' ','M','o','n','o','\0'};
966 GpStatus stat;
968 if (nativeFamily == NULL) return InvalidParameter;
970 stat = GdipCreateFontFamilyFromName(CourierNew, NULL, nativeFamily);
972 if (stat == FontFamilyNotFound)
973 stat = GdipCreateFontFamilyFromName(LiberationMono, NULL, nativeFamily);
975 if (stat == FontFamilyNotFound)
976 ERR("Missing 'Courier New' font\n");
978 return stat;
981 /*****************************************************************************
982 * GdipGetGenericFontFamilySerif [GDIPLUS.@]
984 * Obtains a serif family (Times New Roman on Windows)
986 * PARAMS
987 * **nativeFamily [I] Where the font will be stored
989 * RETURNS
990 * InvalidParameter if nativeFamily is NULL.
991 * Ok otherwise.
993 GpStatus WINGDIPAPI GdipGetGenericFontFamilySerif(GpFontFamily **nativeFamily)
995 static const WCHAR TimesNewRoman[] = {'T','i','m','e','s',' ','N','e','w',' ','R','o','m','a','n','\0'};
996 static const WCHAR LiberationSerif[] = {'L','i','b','e','r','a','t','i','o','n',' ','S','e','r','i','f','\0'};
997 GpStatus stat;
999 TRACE("(%p)\n", nativeFamily);
1001 if (nativeFamily == NULL) return InvalidParameter;
1003 stat = GdipCreateFontFamilyFromName(TimesNewRoman, NULL, nativeFamily);
1005 if (stat == FontFamilyNotFound)
1006 stat = GdipCreateFontFamilyFromName(LiberationSerif, NULL, nativeFamily);
1008 if (stat == FontFamilyNotFound)
1009 ERR("Missing 'Times New Roman' font\n");
1011 return stat;
1014 /*****************************************************************************
1015 * GdipGetGenericFontFamilySansSerif [GDIPLUS.@]
1017 * Obtains a serif family (Microsoft Sans Serif on Windows)
1019 * PARAMS
1020 * **nativeFamily [I] Where the font will be stored
1022 * RETURNS
1023 * InvalidParameter if nativeFamily is NULL.
1024 * Ok otherwise.
1026 GpStatus WINGDIPAPI GdipGetGenericFontFamilySansSerif(GpFontFamily **nativeFamily)
1028 GpStatus stat;
1029 static const WCHAR MicrosoftSansSerif[] = {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f','\0'};
1030 static const WCHAR Tahoma[] = {'T','a','h','o','m','a','\0'};
1032 TRACE("(%p)\n", nativeFamily);
1034 if (nativeFamily == NULL) return InvalidParameter;
1036 stat = GdipCreateFontFamilyFromName(MicrosoftSansSerif, NULL, nativeFamily);
1038 if (stat == FontFamilyNotFound)
1039 /* FIXME: Microsoft Sans Serif is not installed on Wine. */
1040 stat = GdipCreateFontFamilyFromName(Tahoma, NULL, nativeFamily);
1042 return stat;
1045 /*****************************************************************************
1046 * GdipGetGenericFontFamilySansSerif [GDIPLUS.@]
1048 GpStatus WINGDIPAPI GdipNewPrivateFontCollection(GpFontCollection** fontCollection)
1050 TRACE("%p\n", fontCollection);
1052 if (!fontCollection)
1053 return InvalidParameter;
1055 *fontCollection = heap_alloc_zero(sizeof(GpFontCollection));
1056 if (!*fontCollection) return OutOfMemory;
1058 (*fontCollection)->FontFamilies = NULL;
1059 (*fontCollection)->count = 0;
1060 (*fontCollection)->allocated = 0;
1062 TRACE("<-- %p\n", *fontCollection);
1064 return Ok;
1067 /*****************************************************************************
1068 * GdipDeletePrivateFontCollection [GDIPLUS.@]
1070 GpStatus WINGDIPAPI GdipDeletePrivateFontCollection(GpFontCollection **fontCollection)
1072 INT i;
1074 TRACE("%p\n", fontCollection);
1076 if (!fontCollection)
1077 return InvalidParameter;
1079 for (i = 0; i < (*fontCollection)->count; i++) heap_free((*fontCollection)->FontFamilies[i]);
1080 heap_free((*fontCollection)->FontFamilies);
1081 heap_free(*fontCollection);
1083 return Ok;
1086 /*****************************************************************************
1087 * GdipPrivateAddFontFile [GDIPLUS.@]
1089 GpStatus WINGDIPAPI GdipPrivateAddFontFile(GpFontCollection *collection, GDIPCONST WCHAR *name)
1091 HANDLE file, mapping;
1092 LARGE_INTEGER size;
1093 void *mem;
1094 GpStatus status;
1096 TRACE("%p, %s\n", collection, debugstr_w(name));
1098 if (!collection || !name) return InvalidParameter;
1100 file = CreateFileW(name, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
1101 if (file == INVALID_HANDLE_VALUE) return InvalidParameter;
1103 if (!GetFileSizeEx(file, &size) || size.u.HighPart)
1105 CloseHandle(file);
1106 return InvalidParameter;
1109 mapping = CreateFileMappingW(file, NULL, PAGE_READONLY, 0, 0, NULL);
1110 CloseHandle(file);
1111 if (!mapping) return InvalidParameter;
1113 mem = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
1114 CloseHandle(mapping);
1115 if (!mem) return InvalidParameter;
1117 /* GdipPrivateAddMemoryFont creates a copy of the memory block */
1118 status = GdipPrivateAddMemoryFont(collection, mem, size.u.LowPart);
1119 UnmapViewOfFile(mem);
1121 return status;
1124 #define TT_PLATFORM_APPLE_UNICODE 0
1125 #define TT_PLATFORM_MACINTOSH 1
1126 #define TT_PLATFORM_MICROSOFT 3
1128 #define TT_APPLE_ID_DEFAULT 0
1129 #define TT_APPLE_ID_ISO_10646 2
1130 #define TT_APPLE_ID_UNICODE_2_0 3
1132 #define TT_MS_ID_SYMBOL_CS 0
1133 #define TT_MS_ID_UNICODE_CS 1
1135 #define TT_MAC_ID_SIMPLIFIED_CHINESE 25
1137 #define NAME_ID_FULL_FONT_NAME 4
1139 typedef struct {
1140 USHORT major_version;
1141 USHORT minor_version;
1142 USHORT tables_no;
1143 USHORT search_range;
1144 USHORT entry_selector;
1145 USHORT range_shift;
1146 } tt_header;
1148 typedef struct {
1149 char tag[4]; /* table name */
1150 ULONG check_sum; /* Check sum */
1151 ULONG offset; /* Offset from beginning of file */
1152 ULONG length; /* length of the table in bytes */
1153 } tt_table_directory;
1155 typedef struct {
1156 USHORT format; /* format selector. Always 0 */
1157 USHORT count; /* Name Records count */
1158 USHORT string_offset; /* Offset for strings storage, * from start of the table */
1159 } tt_name_table;
1161 typedef struct {
1162 USHORT platform_id;
1163 USHORT encoding_id;
1164 USHORT language_id;
1165 USHORT name_id;
1166 USHORT length;
1167 USHORT offset; /* from start of storage area */
1168 } tt_name_record;
1170 /* Copied from gdi32/freetype.c */
1172 static const LANGID mac_langid_table[] =
1174 MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ENGLISH */
1175 MAKELANGID(LANG_FRENCH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FRENCH */
1176 MAKELANGID(LANG_GERMAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GERMAN */
1177 MAKELANGID(LANG_ITALIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ITALIAN */
1178 MAKELANGID(LANG_DUTCH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_DUTCH */
1179 MAKELANGID(LANG_SWEDISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SWEDISH */
1180 MAKELANGID(LANG_SPANISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SPANISH */
1181 MAKELANGID(LANG_DANISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_DANISH */
1182 MAKELANGID(LANG_PORTUGUESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_PORTUGUESE */
1183 MAKELANGID(LANG_NORWEGIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_NORWEGIAN */
1184 MAKELANGID(LANG_HEBREW,SUBLANG_DEFAULT), /* TT_MAC_LANGID_HEBREW */
1185 MAKELANGID(LANG_JAPANESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_JAPANESE */
1186 MAKELANGID(LANG_ARABIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ARABIC */
1187 MAKELANGID(LANG_FINNISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FINNISH */
1188 MAKELANGID(LANG_GREEK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GREEK */
1189 MAKELANGID(LANG_ICELANDIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ICELANDIC */
1190 MAKELANGID(LANG_MALTESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALTESE */
1191 MAKELANGID(LANG_TURKISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TURKISH */
1192 MAKELANGID(LANG_CROATIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CROATIAN */
1193 MAKELANGID(LANG_CHINESE_TRADITIONAL,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CHINESE_TRADITIONAL */
1194 MAKELANGID(LANG_URDU,SUBLANG_DEFAULT), /* TT_MAC_LANGID_URDU */
1195 MAKELANGID(LANG_HINDI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_HINDI */
1196 MAKELANGID(LANG_THAI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_THAI */
1197 MAKELANGID(LANG_KOREAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KOREAN */
1198 MAKELANGID(LANG_LITHUANIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_LITHUANIAN */
1199 MAKELANGID(LANG_POLISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_POLISH */
1200 MAKELANGID(LANG_HUNGARIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_HUNGARIAN */
1201 MAKELANGID(LANG_ESTONIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ESTONIAN */
1202 MAKELANGID(LANG_LATVIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_LETTISH */
1203 MAKELANGID(LANG_SAMI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SAAMISK */
1204 MAKELANGID(LANG_FAEROESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FAEROESE */
1205 MAKELANGID(LANG_FARSI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FARSI */
1206 MAKELANGID(LANG_RUSSIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_RUSSIAN */
1207 MAKELANGID(LANG_CHINESE_SIMPLIFIED,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CHINESE_SIMPLIFIED */
1208 MAKELANGID(LANG_DUTCH,SUBLANG_DUTCH_BELGIAN), /* TT_MAC_LANGID_FLEMISH */
1209 MAKELANGID(LANG_IRISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_IRISH */
1210 MAKELANGID(LANG_ALBANIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ALBANIAN */
1211 MAKELANGID(LANG_ROMANIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ROMANIAN */
1212 MAKELANGID(LANG_CZECH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CZECH */
1213 MAKELANGID(LANG_SLOVAK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SLOVAK */
1214 MAKELANGID(LANG_SLOVENIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SLOVENIAN */
1215 0, /* TT_MAC_LANGID_YIDDISH */
1216 MAKELANGID(LANG_SERBIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SERBIAN */
1217 MAKELANGID(LANG_MACEDONIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MACEDONIAN */
1218 MAKELANGID(LANG_BULGARIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BULGARIAN */
1219 MAKELANGID(LANG_UKRAINIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_UKRAINIAN */
1220 MAKELANGID(LANG_BELARUSIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BYELORUSSIAN */
1221 MAKELANGID(LANG_UZBEK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_UZBEK */
1222 MAKELANGID(LANG_KAZAK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KAZAKH */
1223 MAKELANGID(LANG_AZERI,SUBLANG_AZERI_CYRILLIC), /* TT_MAC_LANGID_AZERBAIJANI */
1224 0, /* TT_MAC_LANGID_AZERBAIJANI_ARABIC_SCRIPT */
1225 MAKELANGID(LANG_ARMENIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ARMENIAN */
1226 MAKELANGID(LANG_GEORGIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GEORGIAN */
1227 0, /* TT_MAC_LANGID_MOLDAVIAN */
1228 MAKELANGID(LANG_KYRGYZ,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KIRGHIZ */
1229 MAKELANGID(LANG_TAJIK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TAJIKI */
1230 MAKELANGID(LANG_TURKMEN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TURKMEN */
1231 MAKELANGID(LANG_MONGOLIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MONGOLIAN */
1232 MAKELANGID(LANG_MONGOLIAN,SUBLANG_MONGOLIAN_CYRILLIC_MONGOLIA), /* TT_MAC_LANGID_MONGOLIAN_CYRILLIC_SCRIPT */
1233 MAKELANGID(LANG_PASHTO,SUBLANG_DEFAULT), /* TT_MAC_LANGID_PASHTO */
1234 0, /* TT_MAC_LANGID_KURDISH */
1235 MAKELANGID(LANG_KASHMIRI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KASHMIRI */
1236 MAKELANGID(LANG_SINDHI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SINDHI */
1237 MAKELANGID(LANG_TIBETAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TIBETAN */
1238 MAKELANGID(LANG_NEPALI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_NEPALI */
1239 MAKELANGID(LANG_SANSKRIT,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SANSKRIT */
1240 MAKELANGID(LANG_MARATHI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MARATHI */
1241 MAKELANGID(LANG_BENGALI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BENGALI */
1242 MAKELANGID(LANG_ASSAMESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ASSAMESE */
1243 MAKELANGID(LANG_GUJARATI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GUJARATI */
1244 MAKELANGID(LANG_PUNJABI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_PUNJABI */
1245 MAKELANGID(LANG_ORIYA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ORIYA */
1246 MAKELANGID(LANG_MALAYALAM,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALAYALAM */
1247 MAKELANGID(LANG_KANNADA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KANNADA */
1248 MAKELANGID(LANG_TAMIL,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TAMIL */
1249 MAKELANGID(LANG_TELUGU,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TELUGU */
1250 MAKELANGID(LANG_SINHALESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SINHALESE */
1251 0, /* TT_MAC_LANGID_BURMESE */
1252 MAKELANGID(LANG_KHMER,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KHMER */
1253 MAKELANGID(LANG_LAO,SUBLANG_DEFAULT), /* TT_MAC_LANGID_LAO */
1254 MAKELANGID(LANG_VIETNAMESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_VIETNAMESE */
1255 MAKELANGID(LANG_INDONESIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_INDONESIAN */
1256 0, /* TT_MAC_LANGID_TAGALOG */
1257 MAKELANGID(LANG_MALAY,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALAY_ROMAN_SCRIPT */
1258 0, /* TT_MAC_LANGID_MALAY_ARABIC_SCRIPT */
1259 MAKELANGID(LANG_AMHARIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_AMHARIC */
1260 MAKELANGID(LANG_TIGRIGNA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TIGRINYA */
1261 0, /* TT_MAC_LANGID_GALLA */
1262 0, /* TT_MAC_LANGID_SOMALI */
1263 MAKELANGID(LANG_SWAHILI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SWAHILI */
1264 0, /* TT_MAC_LANGID_RUANDA */
1265 0, /* TT_MAC_LANGID_RUNDI */
1266 0, /* TT_MAC_LANGID_CHEWA */
1267 MAKELANGID(LANG_MALAGASY,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALAGASY */
1268 MAKELANGID(LANG_ESPERANTO,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ESPERANTO */
1269 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 95-111 */
1270 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 112-127 */
1271 MAKELANGID(LANG_WELSH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_WELSH */
1272 MAKELANGID(LANG_BASQUE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BASQUE */
1273 MAKELANGID(LANG_CATALAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CATALAN */
1274 0, /* TT_MAC_LANGID_LATIN */
1275 MAKELANGID(LANG_QUECHUA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_QUECHUA */
1276 0, /* TT_MAC_LANGID_GUARANI */
1277 0, /* TT_MAC_LANGID_AYMARA */
1278 MAKELANGID(LANG_TATAR,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TATAR */
1279 MAKELANGID(LANG_UIGHUR,SUBLANG_DEFAULT), /* TT_MAC_LANGID_UIGHUR */
1280 0, /* TT_MAC_LANGID_DZONGKHA */
1281 0, /* TT_MAC_LANGID_JAVANESE */
1282 0, /* TT_MAC_LANGID_SUNDANESE */
1283 MAKELANGID(LANG_GALICIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GALICIAN */
1284 MAKELANGID(LANG_AFRIKAANS,SUBLANG_DEFAULT), /* TT_MAC_LANGID_AFRIKAANS */
1285 MAKELANGID(LANG_BRETON,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BRETON */
1286 MAKELANGID(LANG_INUKTITUT,SUBLANG_DEFAULT), /* TT_MAC_LANGID_INUKTITUT */
1287 MAKELANGID(LANG_SCOTTISH_GAELIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SCOTTISH_GAELIC */
1288 MAKELANGID(LANG_MANX_GAELIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MANX_GAELIC */
1289 MAKELANGID(LANG_IRISH,SUBLANG_IRISH_IRELAND), /* TT_MAC_LANGID_IRISH_GAELIC */
1290 0, /* TT_MAC_LANGID_TONGAN */
1291 0, /* TT_MAC_LANGID_GREEK_POLYTONIC */
1292 MAKELANGID(LANG_GREENLANDIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GREELANDIC */
1293 MAKELANGID(LANG_AZERI,SUBLANG_AZERI_LATIN), /* TT_MAC_LANGID_AZERBAIJANI_ROMAN_SCRIPT */
1296 static inline WORD get_mac_code_page( const tt_name_record *name )
1298 WORD encoding_id = GET_BE_WORD(name->encoding_id);
1299 if (encoding_id == TT_MAC_ID_SIMPLIFIED_CHINESE) return 10008; /* special case */
1300 return 10000 + encoding_id;
1303 static int match_name_table_language( const tt_name_record *name, LANGID lang )
1305 LANGID name_lang;
1307 switch (GET_BE_WORD(name->platform_id))
1309 case TT_PLATFORM_MICROSOFT:
1310 switch (GET_BE_WORD(name->encoding_id))
1312 case TT_MS_ID_UNICODE_CS:
1313 case TT_MS_ID_SYMBOL_CS:
1314 name_lang = GET_BE_WORD(name->language_id);
1315 break;
1316 default:
1317 return 0;
1319 break;
1320 case TT_PLATFORM_MACINTOSH:
1321 if (!IsValidCodePage( get_mac_code_page( name ))) return 0;
1322 name_lang = GET_BE_WORD(name->language_id);
1323 if (name_lang >= ARRAY_SIZE(mac_langid_table)) return 0;
1324 name_lang = mac_langid_table[name_lang];
1325 break;
1326 case TT_PLATFORM_APPLE_UNICODE:
1327 switch (GET_BE_WORD(name->encoding_id))
1329 case TT_APPLE_ID_DEFAULT:
1330 case TT_APPLE_ID_ISO_10646:
1331 case TT_APPLE_ID_UNICODE_2_0:
1332 name_lang = GET_BE_WORD(name->language_id);
1333 if (name_lang >= ARRAY_SIZE(mac_langid_table)) return 0;
1334 name_lang = mac_langid_table[name_lang];
1335 break;
1336 default:
1337 return 0;
1339 break;
1340 default:
1341 return 0;
1343 if (name_lang == lang) return 3;
1344 if (PRIMARYLANGID( name_lang ) == PRIMARYLANGID( lang )) return 2;
1345 if (name_lang == MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT )) return 1;
1346 return 0;
1349 static WCHAR *copy_name_table_string( const tt_name_record *name, const BYTE *data )
1351 WORD name_len = GET_BE_WORD(name->length);
1352 WORD codepage;
1353 WCHAR *ret;
1354 int len;
1356 switch (GET_BE_WORD(name->platform_id))
1358 case TT_PLATFORM_APPLE_UNICODE:
1359 case TT_PLATFORM_MICROSOFT:
1360 ret = heap_alloc((name_len / 2 + 1) * sizeof(WCHAR));
1361 for (len = 0; len < name_len / 2; len++)
1362 ret[len] = (data[len * 2] << 8) | data[len * 2 + 1];
1363 ret[len] = 0;
1364 return ret;
1365 case TT_PLATFORM_MACINTOSH:
1366 codepage = get_mac_code_page( name );
1367 len = MultiByteToWideChar( codepage, 0, (char *)data, name_len, NULL, 0 ) + 1;
1368 if (!len)
1369 return NULL;
1370 ret = heap_alloc(len * sizeof(WCHAR));
1371 len = MultiByteToWideChar( codepage, 0, (char *)data, name_len, ret, len - 1 );
1372 ret[len] = 0;
1373 return ret;
1375 return NULL;
1378 static WCHAR *load_ttf_name_id( const BYTE *mem, DWORD_PTR size, DWORD id )
1380 LANGID lang = GetSystemDefaultLangID();
1381 const tt_header *header;
1382 const tt_name_table *name_table;
1383 const tt_name_record *name_record;
1384 DWORD pos, ofs, count;
1385 int i, res, best_lang = 0, best_index = -1;
1387 if (sizeof(tt_header) > size)
1388 return NULL;
1389 header = (const tt_header*)mem;
1390 count = GET_BE_WORD(header->tables_no);
1392 if (GET_BE_WORD(header->major_version) != 1 || GET_BE_WORD(header->minor_version) != 0)
1393 return NULL;
1395 pos = sizeof(*header);
1396 for (i = 0; i < count; i++)
1398 const tt_table_directory *table_directory = (const tt_table_directory*)&mem[pos];
1399 pos += sizeof(*table_directory);
1400 if (memcmp(table_directory->tag, "name", 4) == 0)
1402 ofs = GET_BE_DWORD(table_directory->offset);
1403 break;
1406 if (i >= count)
1407 return NULL;
1409 if (ofs >= size)
1410 return NULL;
1411 pos = ofs + sizeof(*name_table);
1412 if (pos > size)
1413 return NULL;
1414 name_table = (const tt_name_table*)&mem[ofs];
1415 count = GET_BE_WORD(name_table->count);
1416 if (GET_BE_WORD(name_table->string_offset) >= size - ofs) return NULL;
1417 ofs += GET_BE_WORD(name_table->string_offset);
1418 for (i=0; i<count; i++)
1420 name_record = (const tt_name_record*)&mem[pos];
1421 pos += sizeof(*name_record);
1422 if (pos > size)
1423 return NULL;
1425 if (GET_BE_WORD(name_record->name_id) != id) continue;
1426 if (GET_BE_WORD(name_record->offset) >= size - ofs) return NULL;
1427 if (GET_BE_WORD(name_record->length) > size - ofs - GET_BE_WORD(name_record->offset)) return NULL;
1429 res = match_name_table_language( name_record, lang );
1430 if (res > best_lang)
1432 best_lang = res;
1433 best_index = i;
1437 if (best_lang)
1439 WCHAR *ret;
1440 name_record = (const tt_name_record*)(name_table + 1) + best_index;
1441 ret = copy_name_table_string( name_record, mem+ofs+GET_BE_WORD(name_record->offset) );
1442 TRACE( "name %u found platform %u lang %04x %s\n", GET_BE_WORD(name_record->name_id),
1443 GET_BE_WORD(name_record->platform_id), GET_BE_WORD(name_record->language_id), debugstr_w( ret ));
1444 return ret;
1446 return NULL;
1449 struct add_font_param
1451 GpFontCollection *collection;
1452 BOOL is_system;
1453 GpStatus stat;
1454 HDC hdc;
1457 static INT CALLBACK add_font_proc(const LOGFONTW *lfw, const TEXTMETRICW *ntm, DWORD type, LPARAM lParam);
1459 /*****************************************************************************
1460 * GdipPrivateAddMemoryFont [GDIPLUS.@]
1462 GpStatus WINGDIPAPI GdipPrivateAddMemoryFont(GpFontCollection* fontCollection,
1463 GDIPCONST void* memory, INT length)
1465 WCHAR *name;
1466 DWORD count = 0;
1467 HANDLE font;
1468 GpStatus ret = Ok;
1469 TRACE("%p, %p, %d\n", fontCollection, memory, length);
1471 if (!fontCollection || !memory || !length)
1472 return InvalidParameter;
1474 name = load_ttf_name_id(memory, length, NAME_ID_FULL_FONT_NAME);
1475 if (!name)
1476 return OutOfMemory;
1478 font = AddFontMemResourceEx((void*)memory, length, NULL, &count);
1479 TRACE("%s: %p/%u\n", debugstr_w(name), font, count);
1480 if (!font || !count)
1481 ret = InvalidParameter;
1482 else
1484 struct add_font_param param;
1485 LOGFONTW lfw;
1487 param.hdc = CreateCompatibleDC(0);
1489 /* Truncate name if necessary, GDI32 can't deal with long names */
1490 if(lstrlenW(name) > LF_FACESIZE - 1)
1491 name[LF_FACESIZE - 1] = 0;
1493 lfw.lfCharSet = DEFAULT_CHARSET;
1494 lstrcpyW(lfw.lfFaceName, name);
1495 lfw.lfPitchAndFamily = 0;
1497 param.collection = fontCollection;
1498 param.is_system = FALSE;
1499 if (!EnumFontFamiliesExW(param.hdc, &lfw, add_font_proc, (LPARAM)&param, 0))
1500 ret = param.stat;
1502 DeleteDC(param.hdc);
1504 heap_free(name);
1505 return ret;
1508 /*****************************************************************************
1509 * GdipGetFontCollectionFamilyCount [GDIPLUS.@]
1511 GpStatus WINGDIPAPI GdipGetFontCollectionFamilyCount(
1512 GpFontCollection* fontCollection, INT* numFound)
1514 TRACE("%p, %p\n", fontCollection, numFound);
1516 if (!(fontCollection && numFound))
1517 return InvalidParameter;
1519 *numFound = fontCollection->count;
1520 return Ok;
1523 /*****************************************************************************
1524 * GdipGetFontCollectionFamilyList [GDIPLUS.@]
1526 GpStatus WINGDIPAPI GdipGetFontCollectionFamilyList(
1527 GpFontCollection* fontCollection, INT numSought,
1528 GpFontFamily* gpfamilies[], INT* numFound)
1530 INT i;
1532 TRACE("%p, %d, %p, %p\n", fontCollection, numSought, gpfamilies, numFound);
1534 if (!(fontCollection && gpfamilies && numFound))
1535 return InvalidParameter;
1537 memset(gpfamilies, 0, sizeof(*gpfamilies) * numSought);
1539 for (i = 0; i < numSought && i < fontCollection->count; i++)
1541 gpfamilies[i] = fontCollection->FontFamilies[i];
1544 *numFound = i;
1546 return Ok;
1549 void free_installed_fonts(void)
1551 INT i;
1553 for (i = 0; i < installedFontCollection.count; i++)
1554 heap_free(installedFontCollection.FontFamilies[i]);
1555 heap_free(installedFontCollection.FontFamilies);
1557 installedFontCollection.FontFamilies = NULL;
1558 installedFontCollection.allocated = 0;
1561 static INT CALLBACK add_font_proc(const LOGFONTW *lfw, const TEXTMETRICW *ntm,
1562 DWORD type, LPARAM lParam)
1564 struct add_font_param *param = (struct add_font_param *)lParam;
1565 GpFontCollection *fonts = param->collection;
1566 GpFontFamily *family;
1567 HFONT hfont, old_hfont;
1568 struct font_metrics fm;
1569 int i;
1571 param->stat = Ok;
1573 if (type == RASTER_FONTTYPE)
1574 return 1;
1576 /* skip rotated fonts */
1577 if (lfw->lfFaceName[0] == '@')
1578 return 1;
1580 if (fonts->count && wcsicmp(lfw->lfFaceName, fonts->FontFamilies[fonts->count-1]->FamilyName) == 0)
1581 return 1;
1583 if (fonts->allocated == fonts->count)
1585 INT new_alloc_count = fonts->allocated+50;
1586 GpFontFamily** new_family_list = heap_alloc(new_alloc_count*sizeof(void*));
1588 if (!new_family_list)
1590 param->stat = OutOfMemory;
1591 return 0;
1594 memcpy(new_family_list, fonts->FontFamilies, fonts->count*sizeof(void*));
1595 heap_free(fonts->FontFamilies);
1596 fonts->FontFamilies = new_family_list;
1597 fonts->allocated = new_alloc_count;
1600 family = heap_alloc(sizeof(*family));
1601 if (!family)
1603 if (param->is_system)
1604 return 1;
1606 param->stat = OutOfMemory;
1607 return 0;
1610 /* skip duplicates */
1611 for (i=0; i<fonts->count; i++)
1613 if (wcsicmp(lfw->lfFaceName, fonts->FontFamilies[i]->FamilyName) == 0)
1615 heap_free(family);
1616 return 1;
1620 hfont = CreateFontIndirectW(lfw);
1621 old_hfont = SelectObject(param->hdc, hfont);
1623 if (!get_font_metrics(param->hdc, &fm))
1625 SelectObject(param->hdc, old_hfont);
1626 DeleteObject(hfont);
1628 heap_free(family);
1629 param->stat = OutOfMemory;
1630 return 0;
1633 SelectObject(param->hdc, old_hfont);
1634 DeleteObject(hfont);
1636 family->em_height = fm.em_height;
1637 family->ascent = fm.ascent;
1638 family->descent = fm.descent;
1639 family->line_spacing = fm.line_spacing;
1640 family->dpi = fm.dpi;
1642 lstrcpyW(family->FamilyName, lfw->lfFaceName);
1644 fonts->FontFamilies[fonts->count++] = family;
1646 return 1;
1649 GpStatus WINGDIPAPI GdipNewInstalledFontCollection(
1650 GpFontCollection** fontCollection)
1652 TRACE("(%p)\n",fontCollection);
1654 if (!fontCollection)
1655 return InvalidParameter;
1657 if (installedFontCollection.count == 0)
1659 struct add_font_param param;
1660 LOGFONTW lfw;
1662 param.hdc = CreateCompatibleDC(0);
1664 lfw.lfCharSet = DEFAULT_CHARSET;
1665 lfw.lfFaceName[0] = 0;
1666 lfw.lfPitchAndFamily = 0;
1668 param.collection = &installedFontCollection;
1669 param.is_system = TRUE;
1670 if (!EnumFontFamiliesExW(param.hdc, &lfw, add_font_proc, (LPARAM)&param, 0))
1672 free_installed_fonts();
1673 DeleteDC(param.hdc);
1674 return param.stat;
1677 DeleteDC(param.hdc);
1680 *fontCollection = &installedFontCollection;
1682 return Ok;