include: Add STORAGE_HOTPLUG_INFO structure.
[wine.git] / dlls / gdiplus / font.c
blobda37279fbeffe5af983a13a3d98437c488f58ec4
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 static CRITICAL_SECTION font_cs;
121 static CRITICAL_SECTION_DEBUG critsect_debug =
123 0, 0, &font_cs,
124 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
125 0, 0, { (DWORD_PTR)(__FILE__ ": font_cs") }
127 static CRITICAL_SECTION font_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
129 /*******************************************************************************
130 * GdipCreateFont [GDIPLUS.@]
132 * Create a new font based off of a FontFamily
134 * PARAMS
135 * *fontFamily [I] Family to base the font off of
136 * emSize [I] Size of the font
137 * style [I] Bitwise OR of FontStyle enumeration
138 * unit [I] Unit emSize is measured in
139 * **font [I] the resulting Font object
141 * RETURNS
142 * SUCCESS: Ok
143 * FAILURE: InvalidParameter if fontfamily or font is NULL.
144 * FAILURE: FontFamilyNotFound if an invalid FontFamily is given
146 * NOTES
147 * UnitDisplay is unsupported.
148 * emSize is stored separately from lfHeight, to hold the fraction.
150 GpStatus WINGDIPAPI GdipCreateFont(GDIPCONST GpFontFamily *fontFamily,
151 REAL emSize, INT style, Unit unit, GpFont **font)
153 HFONT hfont;
154 OUTLINETEXTMETRICW otm;
155 LOGFONTW lfw;
156 HDC hdc;
157 GpStatus stat;
158 int ret;
160 if (!fontFamily || !font || emSize < 0.0)
161 return InvalidParameter;
163 TRACE("%p (%s), %f, %d, %d, %p\n", fontFamily,
164 debugstr_w(fontFamily->FamilyName), emSize, style, unit, font);
166 memset(&lfw, 0, sizeof(lfw));
168 stat = GdipGetFamilyName(fontFamily, lfw.lfFaceName, LANG_NEUTRAL);
169 if (stat != Ok) return stat;
171 lfw.lfHeight = -units_to_pixels(emSize, unit, fontFamily->dpi, FALSE);
172 lfw.lfWeight = style & FontStyleBold ? FW_BOLD : FW_REGULAR;
173 lfw.lfItalic = style & FontStyleItalic;
174 lfw.lfUnderline = style & FontStyleUnderline;
175 lfw.lfStrikeOut = style & FontStyleStrikeout;
176 lfw.lfCharSet = DEFAULT_CHARSET;
178 hfont = CreateFontIndirectW(&lfw);
179 hdc = CreateCompatibleDC(0);
180 SelectObject(hdc, hfont);
181 otm.otmSize = sizeof(otm);
182 ret = GetOutlineTextMetricsW(hdc, otm.otmSize, &otm);
183 DeleteDC(hdc);
184 DeleteObject(hfont);
186 if (!ret) return NotTrueTypeFont;
188 *font = calloc(1, sizeof(GpFont));
189 if (!*font) return OutOfMemory;
191 (*font)->unit = unit;
192 (*font)->emSize = emSize;
193 (*font)->otm = otm;
194 GdipCloneFontFamily((GpFontFamily*)fontFamily, &(*font)->family);
196 TRACE("<-- %p\n", *font);
198 return Ok;
201 /*******************************************************************************
202 * GdipCreateFontFromLogfontW [GDIPLUS.@]
204 GpStatus WINGDIPAPI GdipCreateFontFromLogfontW(HDC hdc,
205 GDIPCONST LOGFONTW *logfont, GpFont **font)
207 HFONT hfont, oldfont;
208 OUTLINETEXTMETRICW otm;
209 WCHAR facename[LF_FACESIZE];
210 GpStatus stat;
211 int ret;
213 TRACE("(%p, %p, %p)\n", hdc, logfont, font);
215 if (!hdc || !logfont || !font)
216 return InvalidParameter;
218 hfont = CreateFontIndirectW(logfont);
219 oldfont = SelectObject(hdc, hfont);
220 otm.otmSize = sizeof(otm);
221 ret = GetOutlineTextMetricsW(hdc, otm.otmSize, &otm);
222 GetTextFaceW(hdc, LF_FACESIZE, facename);
223 SelectObject(hdc, oldfont);
224 DeleteObject(hfont);
226 if (!ret) return NotTrueTypeFont;
228 *font = calloc(1, sizeof(GpFont));
229 if (!*font) return OutOfMemory;
231 (*font)->unit = UnitWorld;
232 (*font)->emSize = otm.otmTextMetrics.tmHeight - otm.otmTextMetrics.tmInternalLeading;
233 (*font)->otm = otm;
235 stat = GdipCreateFontFamilyFromName(facename, NULL, &(*font)->family);
236 if (stat != Ok)
238 free(*font);
239 *font = NULL;
240 return NotTrueTypeFont;
243 TRACE("<-- %p\n", *font);
245 return Ok;
248 /*******************************************************************************
249 * GdipCreateFontFromLogfontA [GDIPLUS.@]
251 GpStatus WINGDIPAPI GdipCreateFontFromLogfontA(HDC hdc,
252 GDIPCONST LOGFONTA *lfa, GpFont **font)
254 LOGFONTW lfw;
256 TRACE("(%p, %p, %p)\n", hdc, lfa, font);
258 if(!lfa || !font)
259 return InvalidParameter;
261 memcpy(&lfw, lfa, FIELD_OFFSET(LOGFONTA,lfFaceName) );
263 if(!MultiByteToWideChar(CP_ACP, 0, lfa->lfFaceName, -1, lfw.lfFaceName, LF_FACESIZE))
264 return GenericError;
266 return GdipCreateFontFromLogfontW(hdc, &lfw, font);
269 /*******************************************************************************
270 * GdipDeleteFont [GDIPLUS.@]
272 GpStatus WINGDIPAPI GdipDeleteFont(GpFont* font)
274 TRACE("(%p)\n", font);
276 if(!font)
277 return InvalidParameter;
279 GdipDeleteFontFamily(font->family);
280 free(font);
282 return Ok;
285 /*******************************************************************************
286 * GdipCreateFontFromDC [GDIPLUS.@]
288 GpStatus WINGDIPAPI GdipCreateFontFromDC(HDC hdc, GpFont **font)
290 HFONT hfont;
291 LOGFONTW lfw;
293 TRACE("(%p, %p)\n", hdc, font);
295 if(!font)
296 return InvalidParameter;
298 hfont = GetCurrentObject(hdc, OBJ_FONT);
299 if(!hfont)
300 return GenericError;
302 if(!GetObjectW(hfont, sizeof(LOGFONTW), &lfw))
303 return GenericError;
305 return GdipCreateFontFromLogfontW(hdc, &lfw, font);
308 /*******************************************************************************
309 * GdipGetFamily [GDIPLUS.@]
311 * Returns the FontFamily for the specified Font
313 * PARAMS
314 * font [I] Font to request from
315 * family [O] Resulting FontFamily object
317 * RETURNS
318 * SUCCESS: Ok
319 * FAILURE: An element of GpStatus
321 GpStatus WINGDIPAPI GdipGetFamily(GpFont *font, GpFontFamily **family)
323 TRACE("%p %p\n", font, family);
325 if (!(font && family))
326 return InvalidParameter;
328 return GdipCloneFontFamily(font->family, family);
331 static REAL get_font_size(const GpFont *font)
333 return font->emSize;
336 /******************************************************************************
337 * GdipGetFontSize [GDIPLUS.@]
339 * Returns the size of the font in Units
341 * PARAMS
342 * *font [I] The font to retrieve size from
343 * *size [O] Pointer to hold retrieved value
345 * RETURNS
346 * SUCCESS: Ok
347 * FAILURE: InvalidParameter (font or size was NULL)
349 * NOTES
350 * Size returned is actually emSize -- not internal size used for drawing.
352 GpStatus WINGDIPAPI GdipGetFontSize(GpFont *font, REAL *size)
354 TRACE("(%p, %p)\n", font, size);
356 if (!(font && size)) return InvalidParameter;
358 *size = get_font_size(font);
359 TRACE("%s,%ld => %f\n", debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *size);
361 return Ok;
364 static INT get_font_style(const GpFont *font)
366 INT style;
368 if (font->otm.otmTextMetrics.tmWeight > FW_REGULAR)
369 style = FontStyleBold;
370 else
371 style = FontStyleRegular;
372 if (font->otm.otmTextMetrics.tmItalic)
373 style |= FontStyleItalic;
374 if (font->otm.otmTextMetrics.tmUnderlined)
375 style |= FontStyleUnderline;
376 if (font->otm.otmTextMetrics.tmStruckOut)
377 style |= FontStyleStrikeout;
379 return style;
382 /*******************************************************************************
383 * GdipGetFontStyle [GDIPLUS.@]
385 * Gets the font's style, returned in bitwise OR of FontStyle enumeration
387 * PARAMS
388 * font [I] font to request from
389 * style [O] resulting pointer to a FontStyle enumeration
391 * RETURNS
392 * SUCCESS: Ok
393 * FAILURE: InvalidParameter
395 GpStatus WINGDIPAPI GdipGetFontStyle(GpFont *font, INT *style)
397 TRACE("%p %p\n", font, style);
399 if (!(font && style))
400 return InvalidParameter;
402 *style = get_font_style(font);
403 TRACE("%s,%ld => %d\n", debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *style);
405 return Ok;
408 /*******************************************************************************
409 * GdipGetFontUnit [GDIPLUS.@]
411 * PARAMS
412 * font [I] Font to retrieve from
413 * unit [O] Return value
415 * RETURNS
416 * FAILURE: font or unit was NULL
417 * OK: otherwise
419 GpStatus WINGDIPAPI GdipGetFontUnit(GpFont *font, Unit *unit)
421 TRACE("(%p, %p)\n", font, unit);
423 if (!(font && unit)) return InvalidParameter;
425 *unit = font->unit;
426 TRACE("%s,%ld => %d\n", debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *unit);
428 return Ok;
431 /*******************************************************************************
432 * GdipGetLogFontA [GDIPLUS.@]
434 GpStatus WINGDIPAPI GdipGetLogFontA(GpFont *font, GpGraphics *graphics,
435 LOGFONTA *lfa)
437 GpStatus status;
438 LOGFONTW lfw;
440 TRACE("(%p, %p, %p)\n", font, graphics, lfa);
442 status = GdipGetLogFontW(font, graphics, &lfw);
443 if(status != Ok)
444 return status;
446 memcpy(lfa, &lfw, FIELD_OFFSET(LOGFONTA,lfFaceName) );
448 if(!WideCharToMultiByte(CP_ACP, 0, lfw.lfFaceName, -1, lfa->lfFaceName, LF_FACESIZE, NULL, NULL))
449 return GenericError;
451 return Ok;
454 /*******************************************************************************
455 * GdipGetLogFontW [GDIPLUS.@]
457 GpStatus WINGDIPAPI GdipGetLogFontW(GpFont *font, GpGraphics *graphics, LOGFONTW *lf)
459 REAL angle, rel_height, height;
460 GpMatrix matrix;
462 TRACE("(%p, %p, %p)\n", font, graphics, lf);
464 if (!font || !graphics || !lf)
465 return InvalidParameter;
467 matrix = graphics->worldtrans;
469 if (font->unit == UnitPixel || font->unit == UnitWorld)
471 height = units_to_pixels(font->emSize, graphics->unit, graphics->yres, graphics->printer_display);
472 if (graphics->unit != UnitDisplay)
473 GdipScaleMatrix(&matrix, graphics->scale, graphics->scale, MatrixOrderAppend);
475 else
477 if (graphics->unit == UnitDisplay || graphics->unit == UnitPixel)
478 height = units_to_pixels(font->emSize, font->unit, graphics->xres, graphics->printer_display);
479 else
480 height = units_to_pixels(font->emSize, font->unit, graphics->yres, graphics->printer_display);
483 GdipMultiplyMatrix(&matrix, &graphics->gdi_transform, MatrixOrderAppend);
484 transform_properties(graphics, &matrix, FALSE, NULL, &rel_height, &angle);
485 get_log_fontW(font, graphics, lf);
487 lf->lfHeight = -gdip_round(height * rel_height);
488 lf->lfEscapement = lf->lfOrientation = gdip_round((angle / M_PI) * 1800.0);
489 if (lf->lfEscapement < 0)
491 lf->lfEscapement += 3600;
492 lf->lfOrientation += 3600;
495 TRACE("=> %s,%ld\n", debugstr_w(lf->lfFaceName), lf->lfHeight);
497 return Ok;
500 /*******************************************************************************
501 * GdipCloneFont [GDIPLUS.@]
503 GpStatus WINGDIPAPI GdipCloneFont(GpFont *font, GpFont **cloneFont)
505 TRACE("(%p, %p)\n", font, cloneFont);
507 if(!font || !cloneFont)
508 return InvalidParameter;
510 *cloneFont = calloc(1, sizeof(GpFont));
511 if(!*cloneFont) return OutOfMemory;
513 **cloneFont = *font;
514 return Ok;
517 /*******************************************************************************
518 * GdipGetFontHeight [GDIPLUS.@]
519 * PARAMS
520 * font [I] Font to retrieve height from
521 * graphics [I] The current graphics context
522 * height [O] Resulting height
523 * RETURNS
524 * SUCCESS: Ok
525 * FAILURE: Another element of GpStatus
527 * NOTES
528 * Forwards to GdipGetFontHeightGivenDPI
530 GpStatus WINGDIPAPI GdipGetFontHeight(GDIPCONST GpFont *font,
531 GDIPCONST GpGraphics *graphics, REAL *height)
533 REAL dpi;
534 GpStatus stat;
535 REAL font_height;
537 TRACE("%p %p %p\n", font, graphics, height);
539 if (!font || !height) return InvalidParameter;
541 stat = GdipGetFontHeightGivenDPI(font, font->family->dpi, &font_height);
542 if (stat != Ok) return stat;
544 if (!graphics)
546 *height = font_height;
547 TRACE("%s,%ld => %f\n",
548 debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *height);
549 return Ok;
552 stat = GdipGetDpiY((GpGraphics *)graphics, &dpi);
553 if (stat != Ok) return stat;
555 *height = pixels_to_units(font_height, graphics->unit, dpi, graphics->printer_display);
557 TRACE("%s,%ld(unit %d) => %f\n",
558 debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, graphics->unit, *height);
559 return Ok;
562 /*******************************************************************************
563 * GdipGetFontHeightGivenDPI [GDIPLUS.@]
564 * PARAMS
565 * font [I] Font to retrieve DPI from
566 * dpi [I] DPI to assume
567 * height [O] Return value
569 * RETURNS
570 * SUCCESS: Ok
571 * FAILURE: InvalidParameter if font or height is NULL
573 * NOTES
574 * According to MSDN, the result is (lineSpacing)*(fontSize / emHeight)*dpi
575 * (for anything other than unit Pixel)
577 GpStatus WINGDIPAPI GdipGetFontHeightGivenDPI(GDIPCONST GpFont *font, REAL dpi, REAL *height)
579 GpStatus stat;
580 INT style;
581 UINT16 line_spacing, em_height;
582 REAL font_size;
584 if (!font || !height) return InvalidParameter;
586 TRACE("%p (%s), %f, %p\n", font,
587 debugstr_w(font->family->FamilyName), dpi, height);
589 font_size = units_to_pixels(get_font_size(font), font->unit, dpi, FALSE);
590 style = get_font_style(font);
591 stat = GdipGetLineSpacing(font->family, style, &line_spacing);
592 if (stat != Ok) return stat;
593 stat = GdipGetEmHeight(font->family, style, &em_height);
594 if (stat != Ok) return stat;
596 *height = (REAL)line_spacing * font_size / (REAL)em_height;
598 TRACE("%s,%ld => %f\n",
599 debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *height);
601 return Ok;
604 /***********************************************************************
605 * Borrowed from GDI32:
607 * Elf is really an ENUMLOGFONTEXW, and ntm is a NEWTEXTMETRICEXW.
608 * We have to use other types because of the FONTENUMPROCW definition.
610 static INT CALLBACK is_font_installed_proc(const LOGFONTW *elf,
611 const TEXTMETRICW *ntm, DWORD type, LPARAM lParam)
613 const ENUMLOGFONTW *elfW = (const ENUMLOGFONTW *)elf;
614 LOGFONTW *lf = (LOGFONTW *)lParam;
616 if (type & RASTER_FONTTYPE)
617 return 1;
619 *lf = *elf;
620 /* replace substituted font name by a real one */
621 lstrcpynW(lf->lfFaceName, elfW->elfFullName, LF_FACESIZE);
622 return 0;
625 struct font_metrics
627 WCHAR facename[LF_FACESIZE];
628 UINT16 em_height, ascent, descent, line_spacing; /* in font units */
629 int dpi;
632 static BOOL get_font_metrics(HDC hdc, struct font_metrics *fm)
634 OUTLINETEXTMETRICW otm;
635 TT_OS2_V2 tt_os2;
636 TT_HHEA tt_hori;
637 LONG size;
638 UINT16 line_gap;
640 otm.otmSize = sizeof(otm);
641 if (!GetOutlineTextMetricsW(hdc, otm.otmSize, &otm)) return FALSE;
643 fm->em_height = otm.otmEMSquare;
644 fm->dpi = GetDeviceCaps(hdc, LOGPIXELSY);
646 memset(&tt_hori, 0, sizeof(tt_hori));
647 if (GetFontData(hdc, MS_HHEA_TAG, 0, &tt_hori, sizeof(tt_hori)) != GDI_ERROR)
649 fm->ascent = GET_BE_WORD(tt_hori.Ascender);
650 fm->descent = -GET_BE_WORD(tt_hori.Descender);
651 TRACE("hhea: ascent %d, descent %d\n", fm->ascent, fm->descent);
652 line_gap = GET_BE_WORD(tt_hori.LineGap);
653 fm->line_spacing = fm->ascent + fm->descent + line_gap;
654 TRACE("line_gap %u, line_spacing %u\n", line_gap, fm->line_spacing);
655 if (fm->ascent + fm->descent != 0) return TRUE;
658 size = GetFontData(hdc, MS_OS2_TAG, 0, NULL, 0);
659 if (size == GDI_ERROR) return FALSE;
661 if (size > sizeof(tt_os2)) size = sizeof(tt_os2);
663 memset(&tt_os2, 0, sizeof(tt_os2));
664 if (GetFontData(hdc, MS_OS2_TAG, 0, &tt_os2, size) != size) return FALSE;
666 fm->ascent = GET_BE_WORD(tt_os2.usWinAscent);
667 fm->descent = GET_BE_WORD(tt_os2.usWinDescent);
668 TRACE("usWinAscent %u, usWinDescent %u\n", fm->ascent, fm->descent);
669 if (fm->ascent + fm->descent == 0)
671 fm->ascent = GET_BE_WORD(tt_os2.sTypoAscender);
672 fm->descent = GET_BE_WORD(tt_os2.sTypoDescender);
673 TRACE("sTypoAscender %u, sTypoDescender %u\n", fm->ascent, fm->descent);
675 line_gap = GET_BE_WORD(tt_os2.sTypoLineGap);
676 fm->line_spacing = fm->ascent + fm->descent + line_gap;
677 TRACE("line_gap %u, line_spacing %u\n", line_gap, fm->line_spacing);
678 return TRUE;
681 /*******************************************************************************
682 * GdipCreateFontFamilyFromName [GDIPLUS.@]
684 * Creates a font family object based on a supplied name
686 * PARAMS
687 * name [I] Name of the font
688 * fontCollection [I] What font collection (if any) the font belongs to (may be NULL)
689 * FontFamily [O] Pointer to the resulting FontFamily object
691 * RETURNS
692 * SUCCESS: Ok
693 * FAILURE: FamilyNotFound if the requested FontFamily does not exist on the system
694 * FAILURE: Invalid parameter if FontFamily or name is NULL
696 * NOTES
697 * If fontCollection is NULL then the object is not part of any collection
701 GpStatus WINGDIPAPI GdipCreateFontFamilyFromName(GDIPCONST WCHAR *name,
702 GpFontCollection *collection,
703 GpFontFamily **family)
705 HDC hdc;
706 LOGFONTW lf;
707 GpStatus status;
708 int i;
710 TRACE("%s, %p %p\n", debugstr_w(name), collection, family);
712 if (!name || !family)
713 return InvalidParameter;
715 if (!collection)
717 status = GdipNewInstalledFontCollection(&collection);
718 if (status != Ok) return status;
721 status = FontFamilyNotFound;
723 hdc = CreateCompatibleDC(0);
725 if (!EnumFontFamiliesW(hdc, name, is_font_installed_proc, (LPARAM)&lf))
727 for (i = 0; i < collection->count; i++)
729 if (!wcsicmp(lf.lfFaceName, collection->FontFamilies[i]->FamilyName))
731 status = GdipCloneFontFamily(collection->FontFamilies[i], family);
732 TRACE("<-- %p\n", *family);
733 break;
738 DeleteDC(hdc);
739 return status;
742 /*******************************************************************************
743 * GdipCloneFontFamily [GDIPLUS.@]
745 * Creates a deep copy of a Font Family object
747 * PARAMS
748 * FontFamily [I] Font to clone
749 * clonedFontFamily [O] The resulting cloned font
751 * RETURNS
752 * SUCCESS: Ok
754 GpStatus WINGDIPAPI GdipCloneFontFamily(GpFontFamily *family, GpFontFamily **clone)
756 if (!family || !clone)
757 return InvalidParameter;
759 TRACE("%p (%s), %p\n", family, debugstr_w(family->FamilyName), clone);
761 *clone = family;
763 if (!family->installed)
764 InterlockedIncrement(&family->ref);
766 return Ok;
769 /*******************************************************************************
770 * GdipGetFamilyName [GDIPLUS.@]
772 * Returns the family name into name
774 * PARAMS
775 * *family [I] Family to retrieve from
776 * *name [O] WCHARS of the family name
777 * LANGID [I] charset
779 * RETURNS
780 * SUCCESS: Ok
781 * FAILURE: InvalidParameter if family is NULL
783 * NOTES
784 * If name is NULL, XP and Vista crash but not Windows 7+
786 GpStatus WINGDIPAPI GdipGetFamilyName (GDIPCONST GpFontFamily *family,
787 WCHAR *name, LANGID language)
789 static int lang_fixme;
791 TRACE("%p, %p, %d\n", family, name, language);
793 if (family == NULL)
794 return InvalidParameter;
796 if (name == NULL)
797 return Ok;
799 if (language != LANG_NEUTRAL && !lang_fixme++)
800 FIXME("No support for handling of multiple languages!\n");
802 lstrcpynW (name, family->FamilyName, LF_FACESIZE);
804 return Ok;
808 /*****************************************************************************
809 * GdipDeleteFontFamily [GDIPLUS.@]
811 * Removes the specified FontFamily
813 * PARAMS
814 * *FontFamily [I] The family to delete
816 * RETURNS
817 * SUCCESS: Ok
818 * FAILURE: InvalidParameter if FontFamily is NULL.
821 GpStatus WINGDIPAPI GdipDeleteFontFamily(GpFontFamily *FontFamily)
823 if (!FontFamily)
824 return InvalidParameter;
826 if (!FontFamily->installed && !InterlockedDecrement(&FontFamily->ref))
828 free(FontFamily);
831 return Ok;
834 GpStatus WINGDIPAPI GdipGetCellAscent(GDIPCONST GpFontFamily *family,
835 INT style, UINT16* CellAscent)
837 if (!(family && CellAscent)) return InvalidParameter;
839 *CellAscent = family->ascent;
840 TRACE("%s => %u\n", debugstr_w(family->FamilyName), *CellAscent);
842 return Ok;
845 GpStatus WINGDIPAPI GdipGetCellDescent(GDIPCONST GpFontFamily *family,
846 INT style, UINT16* CellDescent)
848 TRACE("(%p, %d, %p)\n", family, style, CellDescent);
850 if (!(family && CellDescent)) return InvalidParameter;
852 *CellDescent = family->descent;
853 TRACE("%s => %u\n", debugstr_w(family->FamilyName), *CellDescent);
855 return Ok;
858 /*******************************************************************************
859 * GdipGetEmHeight [GDIPLUS.@]
861 * Gets the height of the specified family in EmHeights
863 * PARAMS
864 * family [I] Family to retrieve from
865 * style [I] (optional) style
866 * EmHeight [O] return value
868 * RETURNS
869 * SUCCESS: Ok
870 * FAILURE: InvalidParameter
872 GpStatus WINGDIPAPI GdipGetEmHeight(GDIPCONST GpFontFamily *family, INT style, UINT16* EmHeight)
874 if (!(family && EmHeight)) return InvalidParameter;
876 TRACE("%p (%s), %d, %p\n", family, debugstr_w(family->FamilyName), style, EmHeight);
878 *EmHeight = family->em_height;
879 TRACE("%s => %u\n", debugstr_w(family->FamilyName), *EmHeight);
881 return Ok;
885 /*******************************************************************************
886 * GdipGetLineSpacing [GDIPLUS.@]
888 * Returns the line spacing in design units
890 * PARAMS
891 * family [I] Family to retrieve from
892 * style [I] (Optional) font style
893 * LineSpacing [O] Return value
895 * RETURNS
896 * SUCCESS: Ok
897 * FAILURE: InvalidParameter (family or LineSpacing was NULL)
899 GpStatus WINGDIPAPI GdipGetLineSpacing(GDIPCONST GpFontFamily *family,
900 INT style, UINT16* LineSpacing)
902 TRACE("%p, %d, %p\n", family, style, LineSpacing);
904 if (!(family && LineSpacing))
905 return InvalidParameter;
907 if (style) FIXME("ignoring style\n");
909 *LineSpacing = family->line_spacing;
910 TRACE("%s => %u\n", debugstr_w(family->FamilyName), *LineSpacing);
912 return Ok;
915 static INT CALLBACK font_has_style_proc(const LOGFONTW *elf,
916 const TEXTMETRICW *ntm, DWORD type, LPARAM lParam)
918 INT fontstyle = FontStyleRegular;
920 if (!ntm) return 1;
922 if (ntm->tmWeight >= FW_BOLD) fontstyle |= FontStyleBold;
923 if (ntm->tmItalic) fontstyle |= FontStyleItalic;
924 if (ntm->tmUnderlined) fontstyle |= FontStyleUnderline;
925 if (ntm->tmStruckOut) fontstyle |= FontStyleStrikeout;
927 return (INT)lParam != fontstyle;
930 GpStatus WINGDIPAPI GdipIsStyleAvailable(GDIPCONST GpFontFamily* family,
931 INT style, BOOL* IsStyleAvailable)
933 HDC hdc;
935 TRACE("%p %d %p\n", family, style, IsStyleAvailable);
937 if (!(family && IsStyleAvailable))
938 return InvalidParameter;
940 *IsStyleAvailable = FALSE;
942 hdc = CreateCompatibleDC(0);
944 if(!EnumFontFamiliesW(hdc, family->FamilyName, font_has_style_proc, (LPARAM)style))
945 *IsStyleAvailable = TRUE;
947 DeleteDC(hdc);
949 return Ok;
952 /*****************************************************************************
953 * GdipGetGenericFontFamilyMonospace [GDIPLUS.@]
955 * Obtains a monospace family (Courier New on Windows)
957 * PARAMS
958 * **nativeFamily [O] Where the font will be stored
960 * RETURNS
961 * InvalidParameter if nativeFamily is NULL.
962 * FontFamilyNotFound if unable to get font.
963 * Ok otherwise.
965 GpStatus WINGDIPAPI GdipGetGenericFontFamilyMonospace(GpFontFamily **nativeFamily)
967 GpStatus stat;
969 TRACE("(%p)\n", nativeFamily);
971 if (nativeFamily == NULL) return InvalidParameter;
973 stat = GdipCreateFontFamilyFromName(L"Courier New", NULL, nativeFamily);
975 if (stat == FontFamilyNotFound)
976 stat = GdipCreateFontFamilyFromName(L"Liberation Mono", NULL, nativeFamily);
978 if (stat == FontFamilyNotFound)
979 stat = GdipCreateFontFamilyFromName(L"Courier", NULL, nativeFamily);
981 return stat;
984 /*****************************************************************************
985 * GdipGetGenericFontFamilySerif [GDIPLUS.@]
987 * Obtains a serif family (Times New Roman on Windows)
989 * PARAMS
990 * **nativeFamily [O] Where the font will be stored
992 * RETURNS
993 * InvalidParameter if nativeFamily is NULL.
994 * FontFamilyNotFound if unable to get font.
995 * Ok otherwise.
997 GpStatus WINGDIPAPI GdipGetGenericFontFamilySerif(GpFontFamily **nativeFamily)
999 GpStatus stat;
1001 TRACE("(%p)\n", nativeFamily);
1003 if (nativeFamily == NULL) return InvalidParameter;
1005 stat = GdipCreateFontFamilyFromName(L"Times New Roman", NULL, nativeFamily);
1007 if (stat == FontFamilyNotFound)
1008 stat = GdipCreateFontFamilyFromName(L"Liberation Serif", NULL, nativeFamily);
1010 if (stat == FontFamilyNotFound)
1011 stat = GdipGetGenericFontFamilySansSerif(nativeFamily);
1013 return stat;
1016 /*****************************************************************************
1017 * GdipGetGenericFontFamilySansSerif [GDIPLUS.@]
1019 * Obtains a sans serif family (Microsoft Sans Serif or Arial on Windows)
1021 * PARAMS
1022 * **nativeFamily [O] Where the font will be stored
1024 * RETURNS
1025 * InvalidParameter if nativeFamily is NULL.
1026 * FontFamilyNotFound if unable to get font.
1027 * Ok otherwise.
1029 GpStatus WINGDIPAPI GdipGetGenericFontFamilySansSerif(GpFontFamily **nativeFamily)
1031 GpStatus stat;
1033 TRACE("(%p)\n", nativeFamily);
1035 if (nativeFamily == NULL) return InvalidParameter;
1037 stat = GdipCreateFontFamilyFromName(L"Microsoft Sans Serif", NULL, nativeFamily);
1039 if (stat == FontFamilyNotFound)
1040 stat = GdipCreateFontFamilyFromName(L"Tahoma", NULL, nativeFamily);
1042 if (stat == FontFamilyNotFound)
1043 stat = GdipCreateFontFamilyFromName(L"Arial", NULL, nativeFamily);
1045 if (stat == FontFamilyNotFound)
1046 stat = GdipCreateFontFamilyFromName(L"Liberation Sans", NULL, nativeFamily);
1048 return stat;
1051 /*****************************************************************************
1052 * GdipNewPrivateFontCollection [GDIPLUS.@]
1054 GpStatus WINGDIPAPI GdipNewPrivateFontCollection(GpFontCollection** fontCollection)
1056 TRACE("%p\n", fontCollection);
1058 if (!fontCollection)
1059 return InvalidParameter;
1061 *fontCollection = calloc(1, sizeof(GpFontCollection));
1062 if (!*fontCollection) return OutOfMemory;
1064 (*fontCollection)->FontFamilies = NULL;
1065 (*fontCollection)->count = 0;
1066 (*fontCollection)->allocated = 0;
1068 TRACE("<-- %p\n", *fontCollection);
1070 return Ok;
1073 /*****************************************************************************
1074 * GdipDeletePrivateFontCollection [GDIPLUS.@]
1076 GpStatus WINGDIPAPI GdipDeletePrivateFontCollection(GpFontCollection **fontCollection)
1078 INT i;
1080 TRACE("%p\n", fontCollection);
1082 if (!fontCollection)
1083 return InvalidParameter;
1085 for (i = 0; i < (*fontCollection)->count; i++) GdipDeleteFontFamily((*fontCollection)->FontFamilies[i]);
1086 free((*fontCollection)->FontFamilies);
1087 free(*fontCollection);
1089 return Ok;
1092 /*****************************************************************************
1093 * GdipPrivateAddFontFile [GDIPLUS.@]
1095 GpStatus WINGDIPAPI GdipPrivateAddFontFile(GpFontCollection *collection, GDIPCONST WCHAR *name)
1097 HANDLE file, mapping;
1098 LARGE_INTEGER size;
1099 void *mem;
1100 GpStatus status;
1102 TRACE("%p, %s\n", collection, debugstr_w(name));
1104 if (!collection || !name) return InvalidParameter;
1106 file = CreateFileW(name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
1107 if (file == INVALID_HANDLE_VALUE) return InvalidParameter;
1109 if (!GetFileSizeEx(file, &size) || size.u.HighPart)
1111 CloseHandle(file);
1112 return InvalidParameter;
1115 mapping = CreateFileMappingW(file, NULL, PAGE_READONLY, 0, 0, NULL);
1116 CloseHandle(file);
1117 if (!mapping) return InvalidParameter;
1119 mem = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
1120 CloseHandle(mapping);
1121 if (!mem) return InvalidParameter;
1123 /* GdipPrivateAddMemoryFont creates a copy of the memory block */
1124 status = GdipPrivateAddMemoryFont(collection, mem, size.u.LowPart);
1125 UnmapViewOfFile(mem);
1127 return status;
1130 #define TT_PLATFORM_APPLE_UNICODE 0
1131 #define TT_PLATFORM_MACINTOSH 1
1132 #define TT_PLATFORM_MICROSOFT 3
1134 #define TT_APPLE_ID_DEFAULT 0
1135 #define TT_APPLE_ID_ISO_10646 2
1136 #define TT_APPLE_ID_UNICODE_2_0 3
1138 #define TT_MS_ID_SYMBOL_CS 0
1139 #define TT_MS_ID_UNICODE_CS 1
1141 #define TT_MAC_ID_SIMPLIFIED_CHINESE 25
1143 #define NAME_ID_FULL_FONT_NAME 4
1145 typedef struct {
1146 ULONG version;
1147 USHORT tables_no;
1148 USHORT search_range;
1149 USHORT entry_selector;
1150 USHORT range_shift;
1151 } tt_header;
1153 #define TT_HEADER_VERSION_1 0x00010000
1154 #define TT_HEADER_VERSION_CFF 0x4f54544f
1156 typedef struct {
1157 char tag[4]; /* table name */
1158 ULONG check_sum; /* Check sum */
1159 ULONG offset; /* Offset from beginning of file */
1160 ULONG length; /* length of the table in bytes */
1161 } tt_table_directory;
1163 typedef struct {
1164 USHORT format; /* format selector. Always 0 */
1165 USHORT count; /* Name Records count */
1166 USHORT string_offset; /* Offset for strings storage, * from start of the table */
1167 } tt_name_table;
1169 typedef struct {
1170 USHORT platform_id;
1171 USHORT encoding_id;
1172 USHORT language_id;
1173 USHORT name_id;
1174 USHORT length;
1175 USHORT offset; /* from start of storage area */
1176 } tt_name_record;
1178 /* Copied from gdi32/freetype.c */
1180 static const LANGID mac_langid_table[] =
1182 MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ENGLISH */
1183 MAKELANGID(LANG_FRENCH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FRENCH */
1184 MAKELANGID(LANG_GERMAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GERMAN */
1185 MAKELANGID(LANG_ITALIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ITALIAN */
1186 MAKELANGID(LANG_DUTCH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_DUTCH */
1187 MAKELANGID(LANG_SWEDISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SWEDISH */
1188 MAKELANGID(LANG_SPANISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SPANISH */
1189 MAKELANGID(LANG_DANISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_DANISH */
1190 MAKELANGID(LANG_PORTUGUESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_PORTUGUESE */
1191 MAKELANGID(LANG_NORWEGIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_NORWEGIAN */
1192 MAKELANGID(LANG_HEBREW,SUBLANG_DEFAULT), /* TT_MAC_LANGID_HEBREW */
1193 MAKELANGID(LANG_JAPANESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_JAPANESE */
1194 MAKELANGID(LANG_ARABIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ARABIC */
1195 MAKELANGID(LANG_FINNISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FINNISH */
1196 MAKELANGID(LANG_GREEK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GREEK */
1197 MAKELANGID(LANG_ICELANDIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ICELANDIC */
1198 MAKELANGID(LANG_MALTESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALTESE */
1199 MAKELANGID(LANG_TURKISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TURKISH */
1200 MAKELANGID(LANG_CROATIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CROATIAN */
1201 MAKELANGID(LANG_CHINESE_TRADITIONAL,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CHINESE_TRADITIONAL */
1202 MAKELANGID(LANG_URDU,SUBLANG_DEFAULT), /* TT_MAC_LANGID_URDU */
1203 MAKELANGID(LANG_HINDI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_HINDI */
1204 MAKELANGID(LANG_THAI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_THAI */
1205 MAKELANGID(LANG_KOREAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KOREAN */
1206 MAKELANGID(LANG_LITHUANIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_LITHUANIAN */
1207 MAKELANGID(LANG_POLISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_POLISH */
1208 MAKELANGID(LANG_HUNGARIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_HUNGARIAN */
1209 MAKELANGID(LANG_ESTONIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ESTONIAN */
1210 MAKELANGID(LANG_LATVIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_LETTISH */
1211 MAKELANGID(LANG_SAMI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SAAMISK */
1212 MAKELANGID(LANG_FAEROESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FAEROESE */
1213 MAKELANGID(LANG_FARSI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FARSI */
1214 MAKELANGID(LANG_RUSSIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_RUSSIAN */
1215 MAKELANGID(LANG_CHINESE_SIMPLIFIED,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CHINESE_SIMPLIFIED */
1216 MAKELANGID(LANG_DUTCH,SUBLANG_DUTCH_BELGIAN), /* TT_MAC_LANGID_FLEMISH */
1217 MAKELANGID(LANG_IRISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_IRISH */
1218 MAKELANGID(LANG_ALBANIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ALBANIAN */
1219 MAKELANGID(LANG_ROMANIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ROMANIAN */
1220 MAKELANGID(LANG_CZECH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CZECH */
1221 MAKELANGID(LANG_SLOVAK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SLOVAK */
1222 MAKELANGID(LANG_SLOVENIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SLOVENIAN */
1223 0, /* TT_MAC_LANGID_YIDDISH */
1224 MAKELANGID(LANG_SERBIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SERBIAN */
1225 MAKELANGID(LANG_MACEDONIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MACEDONIAN */
1226 MAKELANGID(LANG_BULGARIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BULGARIAN */
1227 MAKELANGID(LANG_UKRAINIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_UKRAINIAN */
1228 MAKELANGID(LANG_BELARUSIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BYELORUSSIAN */
1229 MAKELANGID(LANG_UZBEK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_UZBEK */
1230 MAKELANGID(LANG_KAZAK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KAZAKH */
1231 MAKELANGID(LANG_AZERI,SUBLANG_AZERI_CYRILLIC), /* TT_MAC_LANGID_AZERBAIJANI */
1232 0, /* TT_MAC_LANGID_AZERBAIJANI_ARABIC_SCRIPT */
1233 MAKELANGID(LANG_ARMENIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ARMENIAN */
1234 MAKELANGID(LANG_GEORGIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GEORGIAN */
1235 0, /* TT_MAC_LANGID_MOLDAVIAN */
1236 MAKELANGID(LANG_KYRGYZ,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KIRGHIZ */
1237 MAKELANGID(LANG_TAJIK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TAJIKI */
1238 MAKELANGID(LANG_TURKMEN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TURKMEN */
1239 MAKELANGID(LANG_MONGOLIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MONGOLIAN */
1240 MAKELANGID(LANG_MONGOLIAN,SUBLANG_MONGOLIAN_CYRILLIC_MONGOLIA), /* TT_MAC_LANGID_MONGOLIAN_CYRILLIC_SCRIPT */
1241 MAKELANGID(LANG_PASHTO,SUBLANG_DEFAULT), /* TT_MAC_LANGID_PASHTO */
1242 0, /* TT_MAC_LANGID_KURDISH */
1243 MAKELANGID(LANG_KASHMIRI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KASHMIRI */
1244 MAKELANGID(LANG_SINDHI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SINDHI */
1245 MAKELANGID(LANG_TIBETAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TIBETAN */
1246 MAKELANGID(LANG_NEPALI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_NEPALI */
1247 MAKELANGID(LANG_SANSKRIT,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SANSKRIT */
1248 MAKELANGID(LANG_MARATHI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MARATHI */
1249 MAKELANGID(LANG_BENGALI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BENGALI */
1250 MAKELANGID(LANG_ASSAMESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ASSAMESE */
1251 MAKELANGID(LANG_GUJARATI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GUJARATI */
1252 MAKELANGID(LANG_PUNJABI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_PUNJABI */
1253 MAKELANGID(LANG_ORIYA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ORIYA */
1254 MAKELANGID(LANG_MALAYALAM,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALAYALAM */
1255 MAKELANGID(LANG_KANNADA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KANNADA */
1256 MAKELANGID(LANG_TAMIL,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TAMIL */
1257 MAKELANGID(LANG_TELUGU,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TELUGU */
1258 MAKELANGID(LANG_SINHALESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SINHALESE */
1259 0, /* TT_MAC_LANGID_BURMESE */
1260 MAKELANGID(LANG_KHMER,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KHMER */
1261 MAKELANGID(LANG_LAO,SUBLANG_DEFAULT), /* TT_MAC_LANGID_LAO */
1262 MAKELANGID(LANG_VIETNAMESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_VIETNAMESE */
1263 MAKELANGID(LANG_INDONESIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_INDONESIAN */
1264 0, /* TT_MAC_LANGID_TAGALOG */
1265 MAKELANGID(LANG_MALAY,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALAY_ROMAN_SCRIPT */
1266 0, /* TT_MAC_LANGID_MALAY_ARABIC_SCRIPT */
1267 MAKELANGID(LANG_AMHARIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_AMHARIC */
1268 MAKELANGID(LANG_TIGRIGNA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TIGRINYA */
1269 0, /* TT_MAC_LANGID_GALLA */
1270 0, /* TT_MAC_LANGID_SOMALI */
1271 MAKELANGID(LANG_SWAHILI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SWAHILI */
1272 0, /* TT_MAC_LANGID_RUANDA */
1273 0, /* TT_MAC_LANGID_RUNDI */
1274 0, /* TT_MAC_LANGID_CHEWA */
1275 0, /* TT_MAC_LANGID_MALAGASY */
1276 0, /* TT_MAC_LANGID_ESPERANTO */
1277 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 95-111 */
1278 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 112-127 */
1279 MAKELANGID(LANG_WELSH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_WELSH */
1280 MAKELANGID(LANG_BASQUE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BASQUE */
1281 MAKELANGID(LANG_CATALAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CATALAN */
1282 0, /* TT_MAC_LANGID_LATIN */
1283 MAKELANGID(LANG_QUECHUA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_QUECHUA */
1284 0, /* TT_MAC_LANGID_GUARANI */
1285 0, /* TT_MAC_LANGID_AYMARA */
1286 MAKELANGID(LANG_TATAR,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TATAR */
1287 MAKELANGID(LANG_UIGHUR,SUBLANG_DEFAULT), /* TT_MAC_LANGID_UIGHUR */
1288 0, /* TT_MAC_LANGID_DZONGKHA */
1289 0, /* TT_MAC_LANGID_JAVANESE */
1290 0, /* TT_MAC_LANGID_SUNDANESE */
1291 MAKELANGID(LANG_GALICIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GALICIAN */
1292 MAKELANGID(LANG_AFRIKAANS,SUBLANG_DEFAULT), /* TT_MAC_LANGID_AFRIKAANS */
1293 MAKELANGID(LANG_BRETON,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BRETON */
1294 MAKELANGID(LANG_INUKTITUT,SUBLANG_DEFAULT), /* TT_MAC_LANGID_INUKTITUT */
1295 MAKELANGID(LANG_SCOTTISH_GAELIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SCOTTISH_GAELIC */
1296 0, /* TT_MAC_LANGID_MANX_GAELIC */
1297 MAKELANGID(LANG_IRISH,SUBLANG_IRISH_IRELAND), /* TT_MAC_LANGID_IRISH_GAELIC */
1298 0, /* TT_MAC_LANGID_TONGAN */
1299 0, /* TT_MAC_LANGID_GREEK_POLYTONIC */
1300 MAKELANGID(LANG_GREENLANDIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GREELANDIC */
1301 MAKELANGID(LANG_AZERI,SUBLANG_AZERI_LATIN), /* TT_MAC_LANGID_AZERBAIJANI_ROMAN_SCRIPT */
1304 static inline WORD get_mac_code_page( const tt_name_record *name )
1306 WORD encoding_id = GET_BE_WORD(name->encoding_id);
1307 if (encoding_id == TT_MAC_ID_SIMPLIFIED_CHINESE) return 10008; /* special case */
1308 return 10000 + encoding_id;
1311 static int match_name_table_language( const tt_name_record *name, LANGID lang )
1313 LANGID name_lang;
1315 switch (GET_BE_WORD(name->platform_id))
1317 case TT_PLATFORM_MICROSOFT:
1318 switch (GET_BE_WORD(name->encoding_id))
1320 case TT_MS_ID_UNICODE_CS:
1321 case TT_MS_ID_SYMBOL_CS:
1322 name_lang = GET_BE_WORD(name->language_id);
1323 break;
1324 default:
1325 return 0;
1327 break;
1328 case TT_PLATFORM_MACINTOSH:
1329 if (!IsValidCodePage( get_mac_code_page( name ))) return 0;
1330 name_lang = GET_BE_WORD(name->language_id);
1331 if (name_lang >= ARRAY_SIZE(mac_langid_table)) return 0;
1332 name_lang = mac_langid_table[name_lang];
1333 break;
1334 case TT_PLATFORM_APPLE_UNICODE:
1335 switch (GET_BE_WORD(name->encoding_id))
1337 case TT_APPLE_ID_DEFAULT:
1338 case TT_APPLE_ID_ISO_10646:
1339 case TT_APPLE_ID_UNICODE_2_0:
1340 name_lang = GET_BE_WORD(name->language_id);
1341 if (name_lang >= ARRAY_SIZE(mac_langid_table)) return 0;
1342 name_lang = mac_langid_table[name_lang];
1343 break;
1344 default:
1345 return 0;
1347 break;
1348 default:
1349 return 0;
1351 if (name_lang == lang) return 3;
1352 if (PRIMARYLANGID( name_lang ) == PRIMARYLANGID( lang )) return 2;
1353 if (name_lang == MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT )) return 1;
1354 return 0;
1357 static WCHAR *copy_name_table_string( const tt_name_record *name, const BYTE *data )
1359 WORD name_len = GET_BE_WORD(name->length);
1360 WORD codepage;
1361 WCHAR *ret;
1362 int len;
1364 switch (GET_BE_WORD(name->platform_id))
1366 case TT_PLATFORM_APPLE_UNICODE:
1367 case TT_PLATFORM_MICROSOFT:
1368 ret = malloc((name_len / 2 + 1) * sizeof(WCHAR));
1369 for (len = 0; len < name_len / 2; len++)
1370 ret[len] = (data[len * 2] << 8) | data[len * 2 + 1];
1371 ret[len] = 0;
1372 return ret;
1373 case TT_PLATFORM_MACINTOSH:
1374 codepage = get_mac_code_page( name );
1375 len = MultiByteToWideChar( codepage, 0, (char *)data, name_len, NULL, 0 ) + 1;
1376 if (!len)
1377 return NULL;
1378 ret = malloc(len * sizeof(WCHAR));
1379 len = MultiByteToWideChar( codepage, 0, (char *)data, name_len, ret, len - 1 );
1380 ret[len] = 0;
1381 return ret;
1383 return NULL;
1386 static WCHAR *load_ttf_name_id( const BYTE *mem, DWORD_PTR size, DWORD id )
1388 LANGID lang = GetSystemDefaultLangID();
1389 const tt_header *header;
1390 const tt_name_table *name_table;
1391 const tt_name_record *name_record;
1392 DWORD pos, ofs = 0, count;
1393 int i, res, best_lang = 0, best_index = -1;
1395 if (sizeof(tt_header) > size)
1396 return NULL;
1397 header = (const tt_header*)mem;
1398 count = GET_BE_WORD(header->tables_no);
1400 if (GET_BE_DWORD(header->version) != TT_HEADER_VERSION_1 &&
1401 GET_BE_DWORD(header->version) != TT_HEADER_VERSION_CFF)
1402 return NULL;
1404 pos = sizeof(*header);
1405 for (i = 0; i < count; i++)
1407 const tt_table_directory *table_directory = (const tt_table_directory*)&mem[pos];
1408 pos += sizeof(*table_directory);
1409 if (memcmp(table_directory->tag, "name", 4) == 0)
1411 ofs = GET_BE_DWORD(table_directory->offset);
1412 break;
1415 if (i >= count)
1416 return NULL;
1418 if (ofs >= size)
1419 return NULL;
1420 pos = ofs + sizeof(*name_table);
1421 if (pos > size)
1422 return NULL;
1423 name_table = (const tt_name_table*)&mem[ofs];
1424 count = GET_BE_WORD(name_table->count);
1425 if (GET_BE_WORD(name_table->string_offset) >= size - ofs) return NULL;
1426 ofs += GET_BE_WORD(name_table->string_offset);
1427 for (i=0; i<count; i++)
1429 name_record = (const tt_name_record*)&mem[pos];
1430 pos += sizeof(*name_record);
1431 if (pos > size)
1432 return NULL;
1434 if (GET_BE_WORD(name_record->name_id) != id) continue;
1435 if (GET_BE_WORD(name_record->offset) >= size - ofs) return NULL;
1436 if (GET_BE_WORD(name_record->length) > size - ofs - GET_BE_WORD(name_record->offset)) return NULL;
1438 res = match_name_table_language( name_record, lang );
1439 if (res > best_lang)
1441 best_lang = res;
1442 best_index = i;
1446 if (best_lang)
1448 WCHAR *ret;
1449 name_record = (const tt_name_record*)(name_table + 1) + best_index;
1450 ret = copy_name_table_string( name_record, mem+ofs+GET_BE_WORD(name_record->offset) );
1451 TRACE( "name %u found platform %u lang %04x %s\n", GET_BE_WORD(name_record->name_id),
1452 GET_BE_WORD(name_record->platform_id), GET_BE_WORD(name_record->language_id), debugstr_w( ret ));
1453 return ret;
1455 return NULL;
1458 struct add_font_param
1460 GpFontCollection *collection;
1461 BOOL is_system;
1462 GpStatus stat;
1463 HDC hdc;
1466 static INT CALLBACK add_font_proc(const LOGFONTW *lfw, const TEXTMETRICW *ntm, DWORD type, LPARAM lParam);
1468 /*****************************************************************************
1469 * GdipPrivateAddMemoryFont [GDIPLUS.@]
1471 GpStatus WINGDIPAPI GdipPrivateAddMemoryFont(GpFontCollection* fontCollection,
1472 GDIPCONST void* memory, INT length)
1474 WCHAR *name;
1475 DWORD count = 0;
1476 HANDLE font;
1477 GpStatus ret = Ok;
1478 TRACE("%p, %p, %d\n", fontCollection, memory, length);
1480 if (!fontCollection || !memory || !length)
1481 return InvalidParameter;
1483 name = load_ttf_name_id(memory, length, NAME_ID_FULL_FONT_NAME);
1484 if (!name)
1485 return OutOfMemory;
1487 font = AddFontMemResourceEx((void*)memory, length, NULL, &count);
1488 TRACE("%s: %p/%lu\n", debugstr_w(name), font, count);
1489 if (!font || !count)
1490 ret = InvalidParameter;
1491 else
1493 struct add_font_param param;
1494 LOGFONTW lfw;
1496 param.hdc = CreateCompatibleDC(0);
1498 /* Truncate name if necessary, GDI32 can't deal with long names */
1499 if(lstrlenW(name) > LF_FACESIZE - 1)
1500 name[LF_FACESIZE - 1] = 0;
1502 lfw.lfCharSet = DEFAULT_CHARSET;
1503 lstrcpyW(lfw.lfFaceName, name);
1504 lfw.lfPitchAndFamily = 0;
1506 param.collection = fontCollection;
1507 param.is_system = FALSE;
1508 if (!EnumFontFamiliesExW(param.hdc, &lfw, add_font_proc, (LPARAM)&param, 0))
1509 ret = param.stat;
1511 DeleteDC(param.hdc);
1513 free(name);
1514 return ret;
1517 /*****************************************************************************
1518 * GdipGetFontCollectionFamilyCount [GDIPLUS.@]
1520 GpStatus WINGDIPAPI GdipGetFontCollectionFamilyCount(
1521 GpFontCollection* fontCollection, INT* numFound)
1523 TRACE("%p, %p\n", fontCollection, numFound);
1525 if (!(fontCollection && numFound))
1526 return InvalidParameter;
1528 *numFound = fontCollection->count;
1529 return Ok;
1532 /*****************************************************************************
1533 * GdipGetFontCollectionFamilyList [GDIPLUS.@]
1535 GpStatus WINGDIPAPI GdipGetFontCollectionFamilyList(
1536 GpFontCollection* fontCollection, INT numSought,
1537 GpFontFamily* gpfamilies[], INT* numFound)
1539 INT i;
1541 TRACE("%p, %d, %p, %p\n", fontCollection, numSought, gpfamilies, numFound);
1543 if (!(fontCollection && gpfamilies && numFound))
1544 return InvalidParameter;
1546 memset(gpfamilies, 0, sizeof(*gpfamilies) * numSought);
1548 for (i = 0; i < numSought && i < fontCollection->count; i++)
1550 /* caller is responsible for cloning these if it keeps references */
1551 gpfamilies[i] = fontCollection->FontFamilies[i];
1554 *numFound = i;
1556 return Ok;
1559 void free_installed_fonts(void)
1561 INT i;
1563 for (i = 0; i < installedFontCollection.count; i++)
1564 free(installedFontCollection.FontFamilies[i]);
1565 free(installedFontCollection.FontFamilies);
1567 installedFontCollection.FontFamilies = NULL;
1568 installedFontCollection.allocated = 0;
1571 static INT CALLBACK add_font_proc(const LOGFONTW *lfw, const TEXTMETRICW *ntm,
1572 DWORD type, LPARAM lParam)
1574 struct add_font_param *param = (struct add_font_param *)lParam;
1575 GpFontCollection *fonts = param->collection;
1576 GpFontFamily *family;
1577 HFONT hfont, old_hfont;
1578 struct font_metrics fm;
1579 int i;
1581 param->stat = Ok;
1583 if (type == RASTER_FONTTYPE)
1584 return 1;
1586 /* skip rotated fonts */
1587 if (lfw->lfFaceName[0] == '@')
1588 return 1;
1590 if (fonts->count && wcsicmp(lfw->lfFaceName, fonts->FontFamilies[fonts->count-1]->FamilyName) == 0)
1591 return 1;
1593 if (fonts->allocated == fonts->count)
1595 INT new_alloc_count = fonts->allocated+50;
1596 GpFontFamily** new_family_list = malloc(new_alloc_count * sizeof(void*));
1598 if (!new_family_list)
1600 param->stat = OutOfMemory;
1601 return 0;
1604 memcpy(new_family_list, fonts->FontFamilies, fonts->count*sizeof(void*));
1605 free(fonts->FontFamilies);
1606 fonts->FontFamilies = new_family_list;
1607 fonts->allocated = new_alloc_count;
1610 family = malloc(sizeof(*family));
1611 if (!family)
1613 if (param->is_system)
1614 return 1;
1616 param->stat = OutOfMemory;
1617 return 0;
1620 /* skip duplicates */
1621 for (i=0; i<fonts->count; i++)
1623 if (wcsicmp(lfw->lfFaceName, fonts->FontFamilies[i]->FamilyName) == 0)
1625 free(family);
1626 return 1;
1630 hfont = CreateFontIndirectW(lfw);
1631 old_hfont = SelectObject(param->hdc, hfont);
1633 if (!get_font_metrics(param->hdc, &fm))
1635 SelectObject(param->hdc, old_hfont);
1636 DeleteObject(hfont);
1638 free(family);
1639 param->stat = OutOfMemory;
1640 return 0;
1643 SelectObject(param->hdc, old_hfont);
1644 DeleteObject(hfont);
1646 family->em_height = fm.em_height;
1647 family->ascent = fm.ascent;
1648 family->descent = fm.descent;
1649 family->line_spacing = fm.line_spacing;
1650 family->dpi = fm.dpi;
1651 family->installed = param->is_system;
1652 family->ref = 1;
1654 lstrcpyW(family->FamilyName, lfw->lfFaceName);
1656 fonts->FontFamilies[fonts->count++] = family;
1658 return 1;
1661 GpStatus WINGDIPAPI GdipNewInstalledFontCollection(
1662 GpFontCollection** fontCollection)
1664 TRACE("(%p)\n",fontCollection);
1666 if (!fontCollection)
1667 return InvalidParameter;
1669 EnterCriticalSection( &font_cs );
1670 if (installedFontCollection.count == 0)
1672 struct add_font_param param;
1673 LOGFONTW lfw;
1675 param.hdc = CreateCompatibleDC(0);
1677 lfw.lfCharSet = DEFAULT_CHARSET;
1678 lfw.lfFaceName[0] = 0;
1679 lfw.lfPitchAndFamily = 0;
1681 param.collection = &installedFontCollection;
1682 param.is_system = TRUE;
1683 if (!EnumFontFamiliesExW(param.hdc, &lfw, add_font_proc, (LPARAM)&param, 0))
1685 free_installed_fonts();
1686 DeleteDC(param.hdc);
1687 LeaveCriticalSection( &font_cs );
1688 return param.stat;
1691 DeleteDC(param.hdc);
1693 LeaveCriticalSection( &font_cs );
1695 *fontCollection = &installedFontCollection;
1697 return Ok;