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
27 #include "wine/debug.h"
28 #include "wine/unicode.h"
30 WINE_DEFAULT_DEBUG_CHANNEL (gdiplus);
35 #include "gdiplus_private.h"
37 /* PANOSE is 10 bytes in size, need to pack the structure properly */
46 SHORT ySubscriptXSize;
47 SHORT ySubscriptYSize;
48 SHORT ySubscriptXOffset;
49 SHORT ySubscriptYOffset;
50 SHORT ySuperscriptXSize;
51 SHORT ySuperscriptYSize;
52 SHORT ySuperscriptXOffset;
53 SHORT ySuperscriptYOffset;
55 SHORT yStrikeoutPosition;
58 ULONG ulUnicodeRange1;
59 ULONG ulUnicodeRange2;
60 ULONG ulUnicodeRange3;
61 ULONG ulUnicodeRange4;
64 USHORT usFirstCharIndex;
65 USHORT usLastCharIndex;
66 /* According to the Apple spec, original version didn't have the below fields,
67 * version numbers were taken from the OpenType spec.
69 /* version 0 (TrueType 1.5) */
71 USHORT sTypoDescender;
75 /* version 1 (TrueType 1.66) */
76 ULONG ulCodePageRange1;
77 ULONG ulCodePageRange2;
78 /* version 2 (OpenType 1.2) */
92 USHORT advanceWidthMax;
93 SHORT minLeftSideBearing;
94 SHORT minRightSideBearing;
100 SHORT metricDataFormat;
101 USHORT numberOfHMetrics;
105 #ifdef WORDS_BIGENDIAN
106 #define GET_BE_WORD(x) (x)
107 #define GET_BE_DWORD(x) (x)
109 #define GET_BE_WORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
110 #define GET_BE_DWORD(x) MAKELONG(GET_BE_WORD(HIWORD(x)), GET_BE_WORD(LOWORD(x)));
113 #define MS_MAKE_TAG(ch0, ch1, ch2, ch3) \
114 ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \
115 ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24))
116 #define MS_OS2_TAG MS_MAKE_TAG('O','S','/','2')
117 #define MS_HHEA_TAG MS_MAKE_TAG('h','h','e','a')
119 static const REAL mm_per_inch = 25.4;
120 static const REAL inch_per_point = 1.0/72.0;
122 static GpFontCollection installedFontCollection = {0};
124 static LONG em_size_to_pixel(REAL em_size, Unit unit, LONG dpi)
129 FIXME("Unhandled unit type: %d\n", unit);
134 /* FIXME: Figure out when World != Pixel */
137 FIXME("Unknown behavior for UnitDisplay! Please report!\n");
138 /* FIXME: Figure out how this works...
139 * MSDN says that if "DISPLAY" is a monitor, then pixel should be
140 * used. That's not what I got. Tests on Windows revealed no output,
141 * and the tests in tests/font crash windows */
144 return em_size * dpi * inch_per_point;
146 return em_size * dpi;
148 return em_size * dpi / 300.0; /* Per MSDN */
150 return em_size * dpi / mm_per_inch;
154 /*******************************************************************************
155 * GdipCreateFont [GDIPLUS.@]
157 * Create a new font based off of a FontFamily
160 * *fontFamily [I] Family to base the font off of
161 * emSize [I] Size of the font
162 * style [I] Bitwise OR of FontStyle enumeration
163 * unit [I] Unit emSize is measured in
164 * **font [I] the resulting Font object
168 * FAILURE: InvalidParameter if fontfamily or font is NULL.
169 * FAILURE: FontFamilyNotFound if an invalid FontFamily is given
172 * UnitDisplay is unsupported.
173 * emSize is stored separately from lfHeight, to hold the fraction.
175 GpStatus WINGDIPAPI GdipCreateFont(GDIPCONST GpFontFamily *fontFamily,
176 REAL emSize, INT style, Unit unit, GpFont **font)
179 OUTLINETEXTMETRICW otm;
185 if (!fontFamily || !font || emSize < 0.0)
186 return InvalidParameter;
188 TRACE("%p (%s), %f, %d, %d, %p\n", fontFamily,
189 debugstr_w(fontFamily->FamilyName), emSize, style, unit, font);
191 memset(&lfw, 0, sizeof(lfw));
193 stat = GdipGetFamilyName(fontFamily, lfw.lfFaceName, LANG_NEUTRAL);
194 if (stat != Ok) return stat;
196 lfw.lfHeight = -em_size_to_pixel(emSize, unit, fontFamily->dpi);
197 lfw.lfWeight = style & FontStyleBold ? FW_BOLD : FW_REGULAR;
198 lfw.lfItalic = style & FontStyleItalic;
199 lfw.lfUnderline = style & FontStyleUnderline;
200 lfw.lfStrikeOut = style & FontStyleStrikeout;
202 hfont = CreateFontIndirectW(&lfw);
203 hdc = CreateCompatibleDC(0);
204 SelectObject(hdc, hfont);
205 otm.otmSize = sizeof(otm);
206 ret = GetOutlineTextMetricsW(hdc, otm.otmSize, &otm);
210 if (!ret) return NotTrueTypeFont;
212 *font = GdipAlloc(sizeof(GpFont));
213 if (!*font) return OutOfMemory;
215 (*font)->unit = unit;
216 (*font)->emSize = emSize;
219 stat = GdipCloneFontFamily((GpFontFamily *)fontFamily, &(*font)->family);
226 TRACE("<-- %p\n", *font);
231 /*******************************************************************************
232 * GdipCreateFontFromLogfontW [GDIPLUS.@]
234 GpStatus WINGDIPAPI GdipCreateFontFromLogfontW(HDC hdc,
235 GDIPCONST LOGFONTW *logfont, GpFont **font)
237 HFONT hfont, oldfont;
238 OUTLINETEXTMETRICW otm;
242 TRACE("(%p, %p, %p)\n", hdc, logfont, font);
244 if (!hdc || !logfont || !font)
245 return InvalidParameter;
247 hfont = CreateFontIndirectW(logfont);
248 oldfont = SelectObject(hdc, hfont);
249 otm.otmSize = sizeof(otm);
250 ret = GetOutlineTextMetricsW(hdc, otm.otmSize, &otm);
251 SelectObject(hdc, oldfont);
254 if (!ret) return NotTrueTypeFont;
256 *font = GdipAlloc(sizeof(GpFont));
257 if (!*font) return OutOfMemory;
259 (*font)->unit = UnitWorld;
260 (*font)->emSize = otm.otmTextMetrics.tmAscent;
263 stat = GdipCreateFontFamilyFromName(logfont->lfFaceName, NULL, &(*font)->family);
267 return NotTrueTypeFont;
270 TRACE("<-- %p\n", *font);
275 /*******************************************************************************
276 * GdipCreateFontFromLogfontA [GDIPLUS.@]
278 GpStatus WINGDIPAPI GdipCreateFontFromLogfontA(HDC hdc,
279 GDIPCONST LOGFONTA *lfa, GpFont **font)
283 TRACE("(%p, %p, %p)\n", hdc, lfa, font);
286 return InvalidParameter;
288 memcpy(&lfw, lfa, FIELD_OFFSET(LOGFONTA,lfFaceName) );
290 if(!MultiByteToWideChar(CP_ACP, 0, lfa->lfFaceName, -1, lfw.lfFaceName, LF_FACESIZE))
293 return GdipCreateFontFromLogfontW(hdc, &lfw, font);
296 /*******************************************************************************
297 * GdipDeleteFont [GDIPLUS.@]
299 GpStatus WINGDIPAPI GdipDeleteFont(GpFont* font)
301 TRACE("(%p)\n", font);
304 return InvalidParameter;
306 GdipDeleteFontFamily(font->family);
312 /*******************************************************************************
313 * GdipCreateFontFromDC [GDIPLUS.@]
315 GpStatus WINGDIPAPI GdipCreateFontFromDC(HDC hdc, GpFont **font)
320 TRACE("(%p, %p)\n", hdc, font);
323 return InvalidParameter;
325 hfont = GetCurrentObject(hdc, OBJ_FONT);
329 if(!GetObjectW(hfont, sizeof(LOGFONTW), &lfw))
332 return GdipCreateFontFromLogfontW(hdc, &lfw, font);
335 /*******************************************************************************
336 * GdipGetFamily [GDIPLUS.@]
338 * Returns the FontFamily for the specified Font
341 * font [I] Font to request from
342 * family [O] Resulting FontFamily object
346 * FAILURE: An element of GpStatus
348 GpStatus WINGDIPAPI GdipGetFamily(GpFont *font, GpFontFamily **family)
350 TRACE("%p %p\n", font, family);
352 if (!(font && family))
353 return InvalidParameter;
355 return GdipCloneFontFamily(font->family, family);
358 /******************************************************************************
359 * GdipGetFontSize [GDIPLUS.@]
361 * Returns the size of the font in Units
364 * *font [I] The font to retrieve size from
365 * *size [O] Pointer to hold retrieved value
369 * FAILURE: InvalidParameter (font or size was NULL)
372 * Size returned is actually emSize -- not internal size used for drawing.
374 GpStatus WINGDIPAPI GdipGetFontSize(GpFont *font, REAL *size)
376 TRACE("(%p, %p)\n", font, size);
378 if (!(font && size)) return InvalidParameter;
380 *size = font->emSize;
381 TRACE("%s,%d => %f\n", debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *size);
386 /*******************************************************************************
387 * GdipGetFontStyle [GDIPLUS.@]
389 * Gets the font's style, returned in bitwise OR of FontStyle enumeration
392 * font [I] font to request from
393 * style [O] resulting pointer to a FontStyle enumeration
397 * FAILURE: InvalidParameter
399 GpStatus WINGDIPAPI GdipGetFontStyle(GpFont *font, INT *style)
401 TRACE("%p %p\n", font, style);
403 if (!(font && style))
404 return InvalidParameter;
406 if (font->otm.otmTextMetrics.tmWeight > FW_REGULAR)
407 *style = FontStyleBold;
409 *style = FontStyleRegular;
410 if (font->otm.otmTextMetrics.tmItalic)
411 *style |= FontStyleItalic;
412 if (font->otm.otmTextMetrics.tmUnderlined)
413 *style |= FontStyleUnderline;
414 if (font->otm.otmTextMetrics.tmStruckOut)
415 *style |= FontStyleStrikeout;
420 /*******************************************************************************
421 * GdipGetFontUnit [GDIPLUS.@]
424 * font [I] Font to retrieve from
425 * unit [O] Return value
428 * FAILURE: font or unit was NULL
431 GpStatus WINGDIPAPI GdipGetFontUnit(GpFont *font, Unit *unit)
433 TRACE("(%p, %p)\n", font, unit);
435 if (!(font && unit)) return InvalidParameter;
438 TRACE("%s,%d => %d\n", debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *unit);
443 /*******************************************************************************
444 * GdipGetLogFontA [GDIPLUS.@]
446 GpStatus WINGDIPAPI GdipGetLogFontA(GpFont *font, GpGraphics *graphics,
452 TRACE("(%p, %p, %p)\n", font, graphics, lfa);
454 status = GdipGetLogFontW(font, graphics, &lfw);
458 memcpy(lfa, &lfw, FIELD_OFFSET(LOGFONTA,lfFaceName) );
460 if(!WideCharToMultiByte(CP_ACP, 0, lfw.lfFaceName, -1, lfa->lfFaceName, LF_FACESIZE, NULL, NULL))
466 /*******************************************************************************
467 * GdipGetLogFontW [GDIPLUS.@]
469 GpStatus WINGDIPAPI GdipGetLogFontW(GpFont *font, GpGraphics *graphics,
472 TRACE("(%p, %p, %p)\n", font, graphics, lfw);
474 /* FIXME: use graphics */
475 if(!font || !graphics || !lfw)
476 return InvalidParameter;
478 lfw->lfHeight = -font->otm.otmTextMetrics.tmAscent;
480 lfw->lfEscapement = 0;
481 lfw->lfOrientation = 0;
482 lfw->lfWeight = font->otm.otmTextMetrics.tmWeight;
483 lfw->lfItalic = font->otm.otmTextMetrics.tmItalic ? 1 : 0;
484 lfw->lfUnderline = font->otm.otmTextMetrics.tmUnderlined ? 1 : 0;
485 lfw->lfStrikeOut = font->otm.otmTextMetrics.tmStruckOut ? 1 : 0;
486 lfw->lfCharSet = font->otm.otmTextMetrics.tmCharSet;
487 lfw->lfOutPrecision = OUT_DEFAULT_PRECIS;
488 lfw->lfClipPrecision = CLIP_DEFAULT_PRECIS;
489 lfw->lfQuality = DEFAULT_QUALITY;
490 lfw->lfPitchAndFamily = 0;
491 strcpyW(lfw->lfFaceName, font->family->FamilyName);
493 TRACE("=> %s,%d\n", debugstr_w(lfw->lfFaceName), lfw->lfHeight);
498 /*******************************************************************************
499 * GdipCloneFont [GDIPLUS.@]
501 GpStatus WINGDIPAPI GdipCloneFont(GpFont *font, GpFont **cloneFont)
505 TRACE("(%p, %p)\n", font, cloneFont);
507 if(!font || !cloneFont)
508 return InvalidParameter;
510 *cloneFont = GdipAlloc(sizeof(GpFont));
511 if(!*cloneFont) return OutOfMemory;
514 stat = GdipCloneFontFamily(font->family, &(*cloneFont)->family);
515 if (stat != Ok) GdipFree(*cloneFont);
520 /*******************************************************************************
521 * GdipGetFontHeight [GDIPLUS.@]
523 * font [I] Font to retrieve height from
524 * graphics [I] The current graphics context
525 * height [O] Resulting height
528 * FAILURE: Another element of GpStatus
531 * Forwards to GdipGetFontHeightGivenDPI
533 GpStatus WINGDIPAPI GdipGetFontHeight(GDIPCONST GpFont *font,
534 GDIPCONST GpGraphics *graphics, REAL *height)
539 TRACE("%p %p %p\n", font, graphics, height);
543 stat = GdipGetDpiY((GpGraphics*)graphics, &dpi);
544 if (stat != Ok) return stat;
547 dpi = font->family->dpi;
549 return GdipGetFontHeightGivenDPI(font, dpi, height);
552 /*******************************************************************************
553 * GdipGetFontHeightGivenDPI [GDIPLUS.@]
555 * font [I] Font to retrieve DPI from
556 * dpi [I] DPI to assume
557 * height [O] Return value
561 * FAILURE: InvalidParameter if font or height is NULL
564 * According to MSDN, the result is (lineSpacing)*(fontSize / emHeight)*dpi
565 * (for anything other than unit Pixel)
567 GpStatus WINGDIPAPI GdipGetFontHeightGivenDPI(GDIPCONST GpFont *font, REAL dpi, REAL *height)
571 UINT16 line_spacing, em_height;
572 REAL font_height, font_size;
574 if (!font || !height) return InvalidParameter;
576 TRACE("%p (%s), %f, %p\n", font,
577 debugstr_w(font->family->FamilyName), dpi, height);
579 stat = GdipGetFontSize((GpFont *)font, &font_size);
580 if (stat != Ok) return stat;
581 stat = GdipGetFontStyle((GpFont *)font, &style);
582 if (stat != Ok) return stat;
583 stat = GdipGetLineSpacing(font->family, style, &line_spacing);
584 if (stat != Ok) return stat;
585 stat = GdipGetEmHeight(font->family, style, &em_height);
586 if (stat != Ok) return stat;
588 font_height = (REAL)line_spacing * font_size / (REAL)em_height;
594 *height = font_height;
597 *height = font_height * dpi * inch_per_point;
600 *height = font_height * dpi;
603 *height = font_height * (dpi / 300.0);
606 *height = font_height * (dpi / mm_per_inch);
609 FIXME("Unhandled unit type: %d\n", font->unit);
610 return NotImplemented;
613 TRACE("%s,%d(unit %d) => %f\n",
614 debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, font->unit, *height);
619 /***********************************************************************
620 * Borrowed from GDI32:
622 * Elf is really an ENUMLOGFONTEXW, and ntm is a NEWTEXTMETRICEXW.
623 * We have to use other types because of the FONTENUMPROCW definition.
625 static INT CALLBACK is_font_installed_proc(const LOGFONTW *elf,
626 const TEXTMETRICW *ntm, DWORD type, LPARAM lParam)
628 if (type != TRUETYPE_FONTTYPE)
631 *(LOGFONTW *)lParam = *elf;
638 UINT16 em_height, ascent, descent, line_spacing; /* in font units */
642 static BOOL get_font_metrics(HDC hdc, struct font_metrics *fm)
644 OUTLINETEXTMETRICW otm;
650 otm.otmSize = sizeof(otm);
651 if (!GetOutlineTextMetricsW(hdc, otm.otmSize, &otm)) return FALSE;
653 fm->em_height = otm.otmEMSquare;
654 fm->dpi = GetDeviceCaps(hdc, LOGPIXELSY);
656 memset(&tt_hori, 0, sizeof(tt_hori));
657 if (GetFontData(hdc, MS_HHEA_TAG, 0, &tt_hori, sizeof(tt_hori)) != GDI_ERROR)
659 fm->ascent = GET_BE_WORD(tt_hori.Ascender);
660 fm->descent = -GET_BE_WORD(tt_hori.Descender);
661 TRACE("hhea: ascent %d, descent %d\n", fm->ascent, fm->descent);
662 line_gap = GET_BE_WORD(tt_hori.LineGap);
663 fm->line_spacing = fm->ascent + fm->descent + line_gap;
664 TRACE("line_gap %u, line_spacing %u\n", line_gap, fm->line_spacing);
665 if (fm->ascent + fm->descent != 0) return TRUE;
668 size = GetFontData(hdc, MS_OS2_TAG, 0, NULL, 0);
669 if (size == GDI_ERROR) return FALSE;
671 if (size > sizeof(tt_os2)) size = sizeof(tt_os2);
673 memset(&tt_os2, 0, sizeof(tt_os2));
674 if (GetFontData(hdc, MS_OS2_TAG, 0, &tt_os2, size) != size) return FALSE;
676 fm->ascent = GET_BE_WORD(tt_os2.usWinAscent);
677 fm->descent = GET_BE_WORD(tt_os2.usWinDescent);
678 TRACE("usWinAscent %u, usWinDescent %u\n", fm->ascent, fm->descent);
679 if (fm->ascent + fm->descent == 0)
681 fm->ascent = GET_BE_WORD(tt_os2.sTypoAscender);
682 fm->descent = GET_BE_WORD(tt_os2.sTypoDescender);
683 TRACE("sTypoAscender %u, sTypoDescender %u\n", fm->ascent, fm->descent);
685 line_gap = GET_BE_WORD(tt_os2.sTypoLineGap);
686 fm->line_spacing = fm->ascent + fm->descent + line_gap;
687 TRACE("line_gap %u, line_spacing %u\n", line_gap, fm->line_spacing);
691 static GpStatus find_installed_font(const WCHAR *name, struct font_metrics *fm)
694 HDC hdc = CreateCompatibleDC(0);
695 GpStatus ret = FontFamilyNotFound;
697 if(!EnumFontFamiliesW(hdc, name, is_font_installed_proc, (LPARAM)&lf))
699 HFONT hfont, old_font;
701 hfont = CreateFontIndirectW(&lf);
702 old_font = SelectObject(hdc, hfont);
703 ret = get_font_metrics(hdc, fm) ? Ok : NotTrueTypeFont;
704 SelectObject(hdc, old_font);
712 /*******************************************************************************
713 * GdipCreateFontFamilyFromName [GDIPLUS.@]
715 * Creates a font family object based on a supplied name
718 * name [I] Name of the font
719 * fontCollection [I] What font collection (if any) the font belongs to (may be NULL)
720 * FontFamily [O] Pointer to the resulting FontFamily object
724 * FAILURE: FamilyNotFound if the requested FontFamily does not exist on the system
725 * FAILURE: Invalid parameter if FontFamily or name is NULL
728 * If fontCollection is NULL then the object is not part of any collection
732 GpStatus WINGDIPAPI GdipCreateFontFamilyFromName(GDIPCONST WCHAR *name,
733 GpFontCollection *fontCollection,
734 GpFontFamily **FontFamily)
737 GpFontFamily* ffamily;
738 struct font_metrics fm;
740 TRACE("%s, %p %p\n", debugstr_w(name), fontCollection, FontFamily);
742 if (!(name && FontFamily))
743 return InvalidParameter;
745 FIXME("No support for FontCollections yet!\n");
747 stat = find_installed_font(name, &fm);
748 if (stat != Ok) return stat;
750 ffamily = GdipAlloc(sizeof (GpFontFamily));
751 if (!ffamily) return OutOfMemory;
753 lstrcpynW(ffamily->FamilyName, name, LF_FACESIZE);
754 ffamily->em_height = fm.em_height;
755 ffamily->ascent = fm.ascent;
756 ffamily->descent = fm.descent;
757 ffamily->line_spacing = fm.line_spacing;
758 ffamily->dpi = fm.dpi;
760 *FontFamily = ffamily;
762 TRACE("<-- %p\n", ffamily);
767 /*******************************************************************************
768 * GdipCloneFontFamily [GDIPLUS.@]
770 * Creates a deep copy of a Font Family object
773 * FontFamily [I] Font to clone
774 * clonedFontFamily [O] The resulting cloned font
779 GpStatus WINGDIPAPI GdipCloneFontFamily(GpFontFamily* FontFamily, GpFontFamily** clonedFontFamily)
781 if (!(FontFamily && clonedFontFamily)) return InvalidParameter;
783 TRACE("%p (%s), %p\n", FontFamily,
784 debugstr_w(FontFamily->FamilyName), clonedFontFamily);
786 *clonedFontFamily = GdipAlloc(sizeof(GpFontFamily));
787 if (!*clonedFontFamily) return OutOfMemory;
789 **clonedFontFamily = *FontFamily;
790 lstrcpyW((*clonedFontFamily)->FamilyName, FontFamily->FamilyName);
792 TRACE("<-- %p\n", *clonedFontFamily);
797 /*******************************************************************************
798 * GdipGetFamilyName [GDIPLUS.@]
800 * Returns the family name into name
803 * *family [I] Family to retrieve from
804 * *name [O] WCHARS of the family name
809 * FAILURE: InvalidParameter if family is NULL
812 * If name is a NULL ptr, then both XP and Vista will crash (so we do as well)
814 GpStatus WINGDIPAPI GdipGetFamilyName (GDIPCONST GpFontFamily *family,
815 WCHAR *name, LANGID language)
817 static int lang_fixme;
820 return InvalidParameter;
822 TRACE("%p, %p, %d\n", family, name, language);
824 if (language != LANG_NEUTRAL && !lang_fixme++)
825 FIXME("No support for handling of multiple languages!\n");
827 lstrcpynW (name, family->FamilyName, LF_FACESIZE);
833 /*****************************************************************************
834 * GdipDeleteFontFamily [GDIPLUS.@]
836 * Removes the specified FontFamily
839 * *FontFamily [I] The family to delete
843 * FAILURE: InvalidParameter if FontFamily is NULL.
846 GpStatus WINGDIPAPI GdipDeleteFontFamily(GpFontFamily *FontFamily)
849 return InvalidParameter;
850 TRACE("Deleting %p (%s)\n", FontFamily, debugstr_w(FontFamily->FamilyName));
852 GdipFree (FontFamily);
857 GpStatus WINGDIPAPI GdipGetCellAscent(GDIPCONST GpFontFamily *family,
858 INT style, UINT16* CellAscent)
860 if (!(family && CellAscent)) return InvalidParameter;
862 *CellAscent = family->ascent;
863 TRACE("%s => %u\n", debugstr_w(family->FamilyName), *CellAscent);
868 GpStatus WINGDIPAPI GdipGetCellDescent(GDIPCONST GpFontFamily *family,
869 INT style, UINT16* CellDescent)
871 TRACE("(%p, %d, %p)\n", family, style, CellDescent);
873 if (!(family && CellDescent)) return InvalidParameter;
875 *CellDescent = family->descent;
876 TRACE("%s => %u\n", debugstr_w(family->FamilyName), *CellDescent);
881 /*******************************************************************************
882 * GdipGetEmHeight [GDIPLUS.@]
884 * Gets the height of the specified family in EmHeights
887 * family [I] Family to retrieve from
888 * style [I] (optional) style
889 * EmHeight [O] return value
893 * FAILURE: InvalidParameter
895 GpStatus WINGDIPAPI GdipGetEmHeight(GDIPCONST GpFontFamily *family, INT style, UINT16* EmHeight)
897 if (!(family && EmHeight)) return InvalidParameter;
899 TRACE("%p (%s), %d, %p\n", family, debugstr_w(family->FamilyName), style, EmHeight);
901 *EmHeight = family->em_height;
902 TRACE("%s => %u\n", debugstr_w(family->FamilyName), *EmHeight);
908 /*******************************************************************************
909 * GdipGetLineSpacing [GDIPLUS.@]
911 * Returns the line spacing in design units
914 * family [I] Family to retrieve from
915 * style [I] (Optional) font style
916 * LineSpacing [O] Return value
920 * FAILURE: InvalidParameter (family or LineSpacing was NULL)
922 GpStatus WINGDIPAPI GdipGetLineSpacing(GDIPCONST GpFontFamily *family,
923 INT style, UINT16* LineSpacing)
925 TRACE("%p, %d, %p\n", family, style, LineSpacing);
927 if (!(family && LineSpacing))
928 return InvalidParameter;
930 if (style) FIXME("ignoring style\n");
932 *LineSpacing = family->line_spacing;
933 TRACE("%s => %u\n", debugstr_w(family->FamilyName), *LineSpacing);
938 static INT CALLBACK font_has_style_proc(const LOGFONTW *elf,
939 const TEXTMETRICW *ntm, DWORD type, LPARAM lParam)
941 INT fontstyle = FontStyleRegular;
945 if (ntm->tmWeight >= FW_BOLD) fontstyle |= FontStyleBold;
946 if (ntm->tmItalic) fontstyle |= FontStyleItalic;
947 if (ntm->tmUnderlined) fontstyle |= FontStyleUnderline;
948 if (ntm->tmStruckOut) fontstyle |= FontStyleStrikeout;
950 return (INT)lParam != fontstyle;
953 GpStatus WINGDIPAPI GdipIsStyleAvailable(GDIPCONST GpFontFamily* family,
954 INT style, BOOL* IsStyleAvailable)
958 TRACE("%p %d %p\n", family, style, IsStyleAvailable);
960 if (!(family && IsStyleAvailable))
961 return InvalidParameter;
963 *IsStyleAvailable = FALSE;
967 if(!EnumFontFamiliesW(hdc, family->FamilyName, font_has_style_proc, (LPARAM)style))
968 *IsStyleAvailable = TRUE;
975 /*****************************************************************************
976 * GdipGetGenericFontFamilyMonospace [GDIPLUS.@]
978 * Obtains a serif family (Courier New on Windows)
981 * **nativeFamily [I] Where the font will be stored
984 * InvalidParameter if nativeFamily is NULL.
987 GpStatus WINGDIPAPI GdipGetGenericFontFamilyMonospace(GpFontFamily **nativeFamily)
989 static const WCHAR CourierNew[] = {'C','o','u','r','i','e','r',' ','N','e','w','\0'};
990 static const WCHAR LiberationMono[] = {'L','i','b','e','r','a','t','i','o','n',' ','M','o','n','o','\0'};
993 if (nativeFamily == NULL) return InvalidParameter;
995 stat = GdipCreateFontFamilyFromName(CourierNew, NULL, nativeFamily);
997 if (stat == FontFamilyNotFound)
998 stat = GdipCreateFontFamilyFromName(LiberationMono, NULL, nativeFamily);
1000 if (stat == FontFamilyNotFound)
1001 ERR("Missing 'Courier New' font\n");
1006 /*****************************************************************************
1007 * GdipGetGenericFontFamilySerif [GDIPLUS.@]
1009 * Obtains a serif family (Times New Roman on Windows)
1012 * **nativeFamily [I] Where the font will be stored
1015 * InvalidParameter if nativeFamily is NULL.
1018 GpStatus WINGDIPAPI GdipGetGenericFontFamilySerif(GpFontFamily **nativeFamily)
1020 static const WCHAR TimesNewRoman[] = {'T','i','m','e','s',' ','N','e','w',' ','R','o','m','a','n','\0'};
1021 static const WCHAR LiberationSerif[] = {'L','i','b','e','r','a','t','i','o','n',' ','S','e','r','i','f','\0'};
1024 TRACE("(%p)\n", nativeFamily);
1026 if (nativeFamily == NULL) return InvalidParameter;
1028 stat = GdipCreateFontFamilyFromName(TimesNewRoman, NULL, nativeFamily);
1030 if (stat == FontFamilyNotFound)
1031 stat = GdipCreateFontFamilyFromName(LiberationSerif, NULL, nativeFamily);
1033 if (stat == FontFamilyNotFound)
1034 ERR("Missing 'Times New Roman' font\n");
1039 /*****************************************************************************
1040 * GdipGetGenericFontFamilySansSerif [GDIPLUS.@]
1042 * Obtains a serif family (Microsoft Sans Serif on Windows)
1045 * **nativeFamily [I] Where the font will be stored
1048 * InvalidParameter if nativeFamily is NULL.
1051 GpStatus WINGDIPAPI GdipGetGenericFontFamilySansSerif(GpFontFamily **nativeFamily)
1054 static const WCHAR MicrosoftSansSerif[] = {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f','\0'};
1055 static const WCHAR Tahoma[] = {'T','a','h','o','m','a','\0'};
1057 TRACE("(%p)\n", nativeFamily);
1059 if (nativeFamily == NULL) return InvalidParameter;
1061 stat = GdipCreateFontFamilyFromName(MicrosoftSansSerif, NULL, nativeFamily);
1063 if (stat == FontFamilyNotFound)
1064 /* FIXME: Microsoft Sans Serif is not installed on Wine. */
1065 stat = GdipCreateFontFamilyFromName(Tahoma, NULL, nativeFamily);
1070 /*****************************************************************************
1071 * GdipGetGenericFontFamilySansSerif [GDIPLUS.@]
1073 GpStatus WINGDIPAPI GdipNewPrivateFontCollection(GpFontCollection** fontCollection)
1075 TRACE("%p\n", fontCollection);
1077 if (!fontCollection)
1078 return InvalidParameter;
1080 *fontCollection = GdipAlloc(sizeof(GpFontCollection));
1081 if (!*fontCollection) return OutOfMemory;
1083 (*fontCollection)->FontFamilies = NULL;
1084 (*fontCollection)->count = 0;
1085 (*fontCollection)->allocated = 0;
1087 TRACE("<-- %p\n", *fontCollection);
1092 /*****************************************************************************
1093 * GdipDeletePrivateFontCollection [GDIPLUS.@]
1095 GpStatus WINGDIPAPI GdipDeletePrivateFontCollection(GpFontCollection **fontCollection)
1099 TRACE("%p\n", fontCollection);
1101 if (!fontCollection)
1102 return InvalidParameter;
1104 for (i = 0; i < (*fontCollection)->count; i++) GdipFree((*fontCollection)->FontFamilies[i]);
1105 GdipFree(*fontCollection);
1110 /*****************************************************************************
1111 * GdipPrivateAddFontFile [GDIPLUS.@]
1113 GpStatus WINGDIPAPI GdipPrivateAddFontFile(GpFontCollection* fontCollection,
1114 GDIPCONST WCHAR* filename)
1116 FIXME("stub: %p, %s\n", fontCollection, debugstr_w(filename));
1118 if (!(fontCollection && filename))
1119 return InvalidParameter;
1121 return NotImplemented;
1124 /* Copied from msi/font.c */
1126 typedef struct _tagTT_OFFSET_TABLE {
1127 USHORT uMajorVersion;
1128 USHORT uMinorVersion;
1129 USHORT uNumOfTables;
1130 USHORT uSearchRange;
1131 USHORT uEntrySelector;
1135 typedef struct _tagTT_TABLE_DIRECTORY {
1136 char szTag[4]; /* table name */
1137 ULONG uCheckSum; /* Check sum */
1138 ULONG uOffset; /* Offset from beginning of file */
1139 ULONG uLength; /* length of the table in bytes */
1140 } TT_TABLE_DIRECTORY;
1142 typedef struct _tagTT_NAME_TABLE_HEADER {
1143 USHORT uFSelector; /* format selector. Always 0 */
1144 USHORT uNRCount; /* Name Records count */
1145 USHORT uStorageOffset; /* Offset for strings storage,
1146 * from start of the table */
1147 } TT_NAME_TABLE_HEADER;
1149 #define NAME_ID_FULL_FONT_NAME 4
1150 #define NAME_ID_VERSION 5
1152 typedef struct _tagTT_NAME_RECORD {
1157 USHORT uStringLength;
1158 USHORT uStringOffset; /* from start of storage area */
1161 #define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
1162 #define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))
1165 * Code based off of code located here
1166 * http://www.codeproject.com/gdi/fontnamefromfile.asp
1168 static WCHAR *load_ttf_name_id( const char *mem, DWORD_PTR size, DWORD id, WCHAR *ret, DWORD len )
1170 const TT_TABLE_DIRECTORY *tblDir;
1171 TT_OFFSET_TABLE ttOffsetTable;
1172 TT_NAME_TABLE_HEADER ttNTHeader;
1173 TT_NAME_RECORD ttRecord;
1177 if (sizeof(TT_OFFSET_TABLE) > size)
1179 ttOffsetTable = *(TT_OFFSET_TABLE*)mem;
1180 ttOffsetTable.uNumOfTables = SWAPWORD(ttOffsetTable.uNumOfTables);
1181 ttOffsetTable.uMajorVersion = SWAPWORD(ttOffsetTable.uMajorVersion);
1182 ttOffsetTable.uMinorVersion = SWAPWORD(ttOffsetTable.uMinorVersion);
1184 if (ttOffsetTable.uMajorVersion != 1 || ttOffsetTable.uMinorVersion != 0)
1187 pos = sizeof(ttOffsetTable);
1188 for (i = 0; i < ttOffsetTable.uNumOfTables; i++)
1190 tblDir = (const TT_TABLE_DIRECTORY*)&mem[pos];
1191 pos += sizeof(*tblDir);
1192 if (memcmp(tblDir->szTag,"name",4)==0)
1194 ofs = SWAPLONG(tblDir->uOffset);
1198 if (i >= ttOffsetTable.uNumOfTables)
1201 pos = ofs + sizeof(ttNTHeader);
1204 ttNTHeader = *(TT_NAME_TABLE_HEADER*)&mem[ofs];
1205 ttNTHeader.uNRCount = SWAPWORD(ttNTHeader.uNRCount);
1206 ttNTHeader.uStorageOffset = SWAPWORD(ttNTHeader.uStorageOffset);
1207 for(i=0; i<ttNTHeader.uNRCount; i++)
1209 ttRecord = *(TT_NAME_RECORD*)&mem[pos];
1210 pos += sizeof(ttRecord);
1214 ttRecord.uNameID = SWAPWORD(ttRecord.uNameID);
1215 if (ttRecord.uNameID == id)
1219 ttRecord.uStringLength = SWAPWORD(ttRecord.uStringLength);
1220 ttRecord.uStringOffset = SWAPWORD(ttRecord.uStringOffset);
1221 if (ofs + ttRecord.uStringOffset + ttNTHeader.uStorageOffset + ttRecord.uStringLength > size)
1223 buf = mem + ofs + ttRecord.uStringOffset + ttNTHeader.uStorageOffset;
1224 len = MultiByteToWideChar(CP_ACP, 0, buf, ttRecord.uStringLength, ret, len-1);
1232 static INT CALLBACK add_font_proc(const LOGFONTW *lfw, const TEXTMETRICW *ntm, DWORD type, LPARAM lParam);
1234 /*****************************************************************************
1235 * GdipPrivateAddMemoryFont [GDIPLUS.@]
1237 GpStatus WINGDIPAPI GdipPrivateAddMemoryFont(GpFontCollection* fontCollection,
1238 GDIPCONST void* memory, INT length)
1240 WCHAR buf[32], *name;
1243 TRACE("%p, %p, %d\n", fontCollection, memory, length);
1245 if (!fontCollection || !memory || !length)
1246 return InvalidParameter;
1248 name = load_ttf_name_id(memory, length, NAME_ID_FULL_FONT_NAME, buf, sizeof(buf)/sizeof(*buf));
1252 font = AddFontMemResourceEx((void*)memory, length, NULL, &count);
1253 TRACE("%s: %p/%u\n", debugstr_w(name), font, count);
1254 if (!font || !count)
1255 return InvalidParameter;
1264 lfw.lfCharSet = DEFAULT_CHARSET;
1265 lstrcpyW(lfw.lfFaceName, name);
1266 lfw.lfPitchAndFamily = 0;
1268 if (!EnumFontFamiliesExW(hdc, &lfw, add_font_proc, (LPARAM)fontCollection, 0))
1279 /*****************************************************************************
1280 * GdipGetFontCollectionFamilyCount [GDIPLUS.@]
1282 GpStatus WINGDIPAPI GdipGetFontCollectionFamilyCount(
1283 GpFontCollection* fontCollection, INT* numFound)
1285 TRACE("%p, %p\n", fontCollection, numFound);
1287 if (!(fontCollection && numFound))
1288 return InvalidParameter;
1290 *numFound = fontCollection->count;
1294 /*****************************************************************************
1295 * GdipGetFontCollectionFamilyList [GDIPLUS.@]
1297 GpStatus WINGDIPAPI GdipGetFontCollectionFamilyList(
1298 GpFontCollection* fontCollection, INT numSought,
1299 GpFontFamily* gpfamilies[], INT* numFound)
1304 TRACE("%p, %d, %p, %p\n", fontCollection, numSought, gpfamilies, numFound);
1306 if (!(fontCollection && gpfamilies && numFound))
1307 return InvalidParameter;
1309 memset(gpfamilies, 0, sizeof(*gpfamilies) * numSought);
1311 for (i = 0; i < numSought && i < fontCollection->count && stat == Ok; i++)
1313 stat = GdipCloneFontFamily(fontCollection->FontFamilies[i], &gpfamilies[i]);
1321 for (i=0; i<numToFree; i++)
1323 GdipDeleteFontFamily(gpfamilies[i]);
1324 gpfamilies[i] = NULL;
1331 void free_installed_fonts(void)
1333 while (installedFontCollection.count)
1334 GdipDeleteFontFamily(installedFontCollection.FontFamilies[--installedFontCollection.count]);
1335 HeapFree(GetProcessHeap(), 0, installedFontCollection.FontFamilies);
1336 installedFontCollection.FontFamilies = NULL;
1337 installedFontCollection.allocated = 0;
1340 static INT CALLBACK add_font_proc(const LOGFONTW *lfw, const TEXTMETRICW *ntm,
1341 DWORD type, LPARAM lParam)
1343 GpFontCollection* fonts = (GpFontCollection*)lParam;
1346 if (type == RASTER_FONTTYPE)
1349 /* skip duplicates */
1350 for (i=0; i<fonts->count; i++)
1351 if (strcmpiW(lfw->lfFaceName, fonts->FontFamilies[i]->FamilyName) == 0)
1354 if (fonts->allocated == fonts->count)
1356 INT new_alloc_count = fonts->allocated+50;
1357 GpFontFamily** new_family_list = HeapAlloc(GetProcessHeap(), 0, new_alloc_count*sizeof(void*));
1359 if (!new_family_list)
1362 memcpy(new_family_list, fonts->FontFamilies, fonts->count*sizeof(void*));
1363 HeapFree(GetProcessHeap(), 0, fonts->FontFamilies);
1364 fonts->FontFamilies = new_family_list;
1365 fonts->allocated = new_alloc_count;
1368 if (GdipCreateFontFamilyFromName(lfw->lfFaceName, NULL, &fonts->FontFamilies[fonts->count]) == Ok)
1376 GpStatus WINGDIPAPI GdipNewInstalledFontCollection(
1377 GpFontCollection** fontCollection)
1379 TRACE("(%p)\n",fontCollection);
1381 if (!fontCollection)
1382 return InvalidParameter;
1384 if (installedFontCollection.count == 0)
1391 lfw.lfCharSet = DEFAULT_CHARSET;
1392 lfw.lfFaceName[0] = 0;
1393 lfw.lfPitchAndFamily = 0;
1395 if (!EnumFontFamiliesExW(hdc, &lfw, add_font_proc, (LPARAM)&installedFontCollection, 0))
1397 free_installed_fonts();
1405 *fontCollection = &installedFontCollection;