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 static const REAL mm_per_inch = 25.4;
38 static const REAL inch_per_point = 1.0/72.0;
40 static GpFontCollection installedFontCollection = {0};
42 static inline LONG get_dpi(void)
46 dpi = GetDeviceCaps(hdc, LOGPIXELSY);
52 static LONG em_size_to_pixel(REAL em_size, Unit unit, LONG dpi)
57 FIXME("Unhandled unit type: %d\n", unit);
62 /* FIXME: Figure out when World != Pixel */
65 FIXME("Unknown behavior for UnitDisplay! Please report!\n");
66 /* FIXME: Figure out how this works...
67 * MSDN says that if "DISPLAY" is a monitor, then pixel should be
68 * used. That's not what I got. Tests on Windows revealed no output,
69 * and the tests in tests/font crash windows */
72 return em_size * dpi * inch_per_point;
76 return em_size * dpi / 300.0; /* Per MSDN */
78 return em_size * dpi / mm_per_inch;
82 /*******************************************************************************
83 * GdipCreateFont [GDIPLUS.@]
85 * Create a new font based off of a FontFamily
88 * *fontFamily [I] Family to base the font off of
89 * emSize [I] Size of the font
90 * style [I] Bitwise OR of FontStyle enumeration
91 * unit [I] Unit emSize is measured in
92 * **font [I] the resulting Font object
96 * FAILURE: InvalidParameter if fontfamily or font is NULL.
97 * FAILURE: FontFamilyNotFound if an invalid FontFamily is given
100 * UnitDisplay is unsupported.
101 * emSize is stored separately from lfHeight, to hold the fraction.
103 GpStatus WINGDIPAPI GdipCreateFont(GDIPCONST GpFontFamily *fontFamily,
104 REAL emSize, INT style, Unit unit, GpFont **font)
107 OUTLINETEXTMETRICW otm;
113 if (!fontFamily || !font || emSize < 0.0)
114 return InvalidParameter;
116 TRACE("%p (%s), %f, %d, %d, %p\n", fontFamily,
117 debugstr_w(fontFamily->FamilyName), emSize, style, unit, font);
119 memset(&lfw, 0, sizeof(lfw));
121 stat = GdipGetFamilyName(fontFamily, lfw.lfFaceName, LANG_NEUTRAL);
122 if (stat != Ok) return stat;
124 lfw.lfHeight = -em_size_to_pixel(emSize, unit, get_dpi());
125 lfw.lfWeight = style & FontStyleBold ? FW_BOLD : FW_REGULAR;
126 lfw.lfItalic = style & FontStyleItalic;
127 lfw.lfUnderline = style & FontStyleUnderline;
128 lfw.lfStrikeOut = style & FontStyleStrikeout;
130 hfont = CreateFontIndirectW(&lfw);
131 hdc = CreateCompatibleDC(0);
132 SelectObject(hdc, hfont);
133 otm.otmSize = sizeof(otm);
134 ret = GetOutlineTextMetricsW(hdc, otm.otmSize, &otm);
138 if (!ret) return NotTrueTypeFont;
140 *font = GdipAlloc(sizeof(GpFont));
141 if (!*font) return OutOfMemory;
143 (*font)->unit = unit;
144 (*font)->emSize = emSize;
147 stat = GdipCloneFontFamily((GpFontFamily *)fontFamily, &(*font)->family);
154 TRACE("<-- %p\n", *font);
159 /*******************************************************************************
160 * GdipCreateFontFromLogfontW [GDIPLUS.@]
162 GpStatus WINGDIPAPI GdipCreateFontFromLogfontW(HDC hdc,
163 GDIPCONST LOGFONTW *logfont, GpFont **font)
165 HFONT hfont, oldfont;
166 OUTLINETEXTMETRICW otm;
170 TRACE("(%p, %p, %p)\n", hdc, logfont, font);
172 if (!hdc || !logfont || !font)
173 return InvalidParameter;
175 hfont = CreateFontIndirectW(logfont);
176 oldfont = SelectObject(hdc, hfont);
177 otm.otmSize = sizeof(otm);
178 ret = GetOutlineTextMetricsW(hdc, otm.otmSize, &otm);
179 SelectObject(hdc, oldfont);
182 if (!ret) return NotTrueTypeFont;
184 *font = GdipAlloc(sizeof(GpFont));
185 if (!*font) return OutOfMemory;
187 (*font)->unit = UnitWorld;
188 (*font)->emSize = otm.otmTextMetrics.tmAscent;
191 stat = GdipCreateFontFamilyFromName(logfont->lfFaceName, NULL, &(*font)->family);
195 return NotTrueTypeFont;
198 TRACE("<-- %p\n", *font);
203 /*******************************************************************************
204 * GdipCreateFontFromLogfontA [GDIPLUS.@]
206 GpStatus WINGDIPAPI GdipCreateFontFromLogfontA(HDC hdc,
207 GDIPCONST LOGFONTA *lfa, GpFont **font)
211 TRACE("(%p, %p, %p)\n", hdc, lfa, font);
214 return InvalidParameter;
216 memcpy(&lfw, lfa, FIELD_OFFSET(LOGFONTA,lfFaceName) );
218 if(!MultiByteToWideChar(CP_ACP, 0, lfa->lfFaceName, -1, lfw.lfFaceName, LF_FACESIZE))
221 return GdipCreateFontFromLogfontW(hdc, &lfw, font);
224 /*******************************************************************************
225 * GdipDeleteFont [GDIPLUS.@]
227 GpStatus WINGDIPAPI GdipDeleteFont(GpFont* font)
229 TRACE("(%p)\n", font);
232 return InvalidParameter;
234 GdipDeleteFontFamily(font->family);
240 /*******************************************************************************
241 * GdipCreateFontFromDC [GDIPLUS.@]
243 GpStatus WINGDIPAPI GdipCreateFontFromDC(HDC hdc, GpFont **font)
248 TRACE("(%p, %p)\n", hdc, font);
251 return InvalidParameter;
253 hfont = GetCurrentObject(hdc, OBJ_FONT);
257 if(!GetObjectW(hfont, sizeof(LOGFONTW), &lfw))
260 return GdipCreateFontFromLogfontW(hdc, &lfw, font);
263 /*******************************************************************************
264 * GdipGetFamily [GDIPLUS.@]
266 * Returns the FontFamily for the specified Font
269 * font [I] Font to request from
270 * family [O] Resulting FontFamily object
274 * FAILURE: An element of GpStatus
276 GpStatus WINGDIPAPI GdipGetFamily(GpFont *font, GpFontFamily **family)
278 TRACE("%p %p\n", font, family);
280 if (!(font && family))
281 return InvalidParameter;
283 return GdipCloneFontFamily(font->family, family);
286 /******************************************************************************
287 * GdipGetFontSize [GDIPLUS.@]
289 * Returns the size of the font in Units
292 * *font [I] The font to retrieve size from
293 * *size [O] Pointer to hold retrieved value
297 * FAILURE: InvalidParameter (font or size was NULL)
300 * Size returned is actually emSize -- not internal size used for drawing.
302 GpStatus WINGDIPAPI GdipGetFontSize(GpFont *font, REAL *size)
304 TRACE("(%p, %p)\n", font, size);
306 if (!(font && size)) return InvalidParameter;
308 *size = font->emSize;
309 TRACE("%s,%d => %f\n", debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *size);
314 /*******************************************************************************
315 * GdipGetFontStyle [GDIPLUS.@]
317 * Gets the font's style, returned in bitwise OR of FontStyle enumeration
320 * font [I] font to request from
321 * style [O] resulting pointer to a FontStyle enumeration
325 * FAILURE: InvalidParameter
327 GpStatus WINGDIPAPI GdipGetFontStyle(GpFont *font, INT *style)
329 TRACE("%p %p\n", font, style);
331 if (!(font && style))
332 return InvalidParameter;
334 if (font->otm.otmTextMetrics.tmWeight > FW_REGULAR)
335 *style = FontStyleBold;
337 *style = FontStyleRegular;
338 if (font->otm.otmTextMetrics.tmItalic)
339 *style |= FontStyleItalic;
340 if (font->otm.otmTextMetrics.tmUnderlined)
341 *style |= FontStyleUnderline;
342 if (font->otm.otmTextMetrics.tmStruckOut)
343 *style |= FontStyleStrikeout;
348 /*******************************************************************************
349 * GdipGetFontUnit [GDIPLUS.@]
352 * font [I] Font to retrieve from
353 * unit [O] Return value
356 * FAILURE: font or unit was NULL
359 GpStatus WINGDIPAPI GdipGetFontUnit(GpFont *font, Unit *unit)
361 TRACE("(%p, %p)\n", font, unit);
363 if (!(font && unit)) return InvalidParameter;
366 TRACE("%s,%d => %d\n", debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *unit);
371 /*******************************************************************************
372 * GdipGetLogFontA [GDIPLUS.@]
374 GpStatus WINGDIPAPI GdipGetLogFontA(GpFont *font, GpGraphics *graphics,
380 TRACE("(%p, %p, %p)\n", font, graphics, lfa);
382 status = GdipGetLogFontW(font, graphics, &lfw);
386 memcpy(lfa, &lfw, FIELD_OFFSET(LOGFONTA,lfFaceName) );
388 if(!WideCharToMultiByte(CP_ACP, 0, lfw.lfFaceName, -1, lfa->lfFaceName, LF_FACESIZE, NULL, NULL))
394 /*******************************************************************************
395 * GdipGetLogFontW [GDIPLUS.@]
397 GpStatus WINGDIPAPI GdipGetLogFontW(GpFont *font, GpGraphics *graphics,
400 TRACE("(%p, %p, %p)\n", font, graphics, lfw);
402 /* FIXME: use graphics */
403 if(!font || !graphics || !lfw)
404 return InvalidParameter;
406 lfw->lfHeight = -font->otm.otmTextMetrics.tmAscent;
408 lfw->lfEscapement = 0;
409 lfw->lfOrientation = 0;
410 lfw->lfWeight = font->otm.otmTextMetrics.tmWeight;
411 lfw->lfItalic = font->otm.otmTextMetrics.tmItalic ? 1 : 0;
412 lfw->lfUnderline = font->otm.otmTextMetrics.tmUnderlined ? 1 : 0;
413 lfw->lfStrikeOut = font->otm.otmTextMetrics.tmStruckOut ? 1 : 0;
414 lfw->lfCharSet = font->otm.otmTextMetrics.tmCharSet;
415 lfw->lfOutPrecision = OUT_DEFAULT_PRECIS;
416 lfw->lfClipPrecision = CLIP_DEFAULT_PRECIS;
417 lfw->lfQuality = DEFAULT_QUALITY;
418 lfw->lfPitchAndFamily = 0;
419 strcpyW(lfw->lfFaceName, font->family->FamilyName);
421 TRACE("=> %s,%d\n", debugstr_w(lfw->lfFaceName), lfw->lfHeight);
426 /*******************************************************************************
427 * GdipCloneFont [GDIPLUS.@]
429 GpStatus WINGDIPAPI GdipCloneFont(GpFont *font, GpFont **cloneFont)
433 TRACE("(%p, %p)\n", font, cloneFont);
435 if(!font || !cloneFont)
436 return InvalidParameter;
438 *cloneFont = GdipAlloc(sizeof(GpFont));
439 if(!*cloneFont) return OutOfMemory;
442 stat = GdipCloneFontFamily(font->family, &(*cloneFont)->family);
443 if (stat != Ok) GdipFree(*cloneFont);
448 /*******************************************************************************
449 * GdipGetFontHeight [GDIPLUS.@]
451 * font [I] Font to retrieve height from
452 * graphics [I] The current graphics context
453 * height [O] Resulting height
456 * FAILURE: Another element of GpStatus
459 * Forwards to GdipGetFontHeightGivenDPI
461 GpStatus WINGDIPAPI GdipGetFontHeight(GDIPCONST GpFont *font,
462 GDIPCONST GpGraphics *graphics, REAL *height)
467 TRACE("%p %p %p\n", font, graphics, height);
469 stat = GdipGetDpiY((GpGraphics*)graphics, &dpi);
472 stat = GdipGetFontHeightGivenDPI(font, dpi, height);
477 /*******************************************************************************
478 * GdipGetFontHeightGivenDPI [GDIPLUS.@]
480 * font [I] Font to retrieve DPI from
481 * dpi [I] DPI to assume
482 * height [O] Return value
486 * FAILURE: InvalidParameter if font or height is NULL
489 * According to MSDN, the result is (lineSpacing)*(fontSize / emHeight)*dpi
490 * (for anything other than unit Pixel)
492 GpStatus WINGDIPAPI GdipGetFontHeightGivenDPI(GDIPCONST GpFont *font, REAL dpi, REAL *height)
496 UINT16 line_spacing, em_height;
497 REAL font_height, font_size;
499 if (!font || !height) return InvalidParameter;
501 TRACE("%p (%s), %f, %p\n", font,
502 debugstr_w(font->family->FamilyName), dpi, height);
504 stat = GdipGetFontSize((GpFont *)font, &font_size);
505 if (stat != Ok) return stat;
506 stat = GdipGetFontStyle((GpFont *)font, &style);
507 if (stat != Ok) return stat;
508 stat = GdipGetLineSpacing(font->family, style, &line_spacing);
509 if (stat != Ok) return stat;
510 stat = GdipGetEmHeight(font->family, style, &em_height);
511 if (stat != Ok) return stat;
513 font_height = (REAL)line_spacing * font_size / (REAL)em_height;
519 *height = font_height;
522 *height = font_height * dpi * inch_per_point;
525 *height = font_height * dpi;
528 *height = font_height * (dpi / 300.0);
531 *height = font_height * (dpi / mm_per_inch);
534 FIXME("Unhandled unit type: %d\n", font->unit);
535 return NotImplemented;
538 TRACE("%s,%d(unit %d) => %f\n",
539 debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, font->unit, *height);
544 /***********************************************************************
545 * Borrowed from GDI32:
547 * Elf is really an ENUMLOGFONTEXW, and ntm is a NEWTEXTMETRICEXW.
548 * We have to use other types because of the FONTENUMPROCW definition.
550 static INT CALLBACK is_font_installed_proc(const LOGFONTW *elf,
551 const TEXTMETRICW *ntm, DWORD type, LPARAM lParam)
553 if (!ntm || type == RASTER_FONTTYPE)
558 *(LOGFONTW *)lParam = *elf;
563 static BOOL find_installed_font(const WCHAR *name, OUTLINETEXTMETRICW *otm)
566 HDC hdc = CreateCompatibleDC(0);
569 if(!EnumFontFamiliesW(hdc, name, is_font_installed_proc, (LPARAM)&lf))
574 hfont = CreateFontIndirectW(&lf);
575 hfont = SelectObject(hdc, hfont);
577 otm->otmSize = sizeof(*otm);
578 if (GetOutlineTextMetricsW(hdc, otm->otmSize, otm))
581 DeleteObject(SelectObject(hdc, hfont));
588 /*******************************************************************************
589 * GdipCreateFontFamilyFromName [GDIPLUS.@]
591 * Creates a font family object based on a supplied name
594 * name [I] Name of the font
595 * fontCollection [I] What font collection (if any) the font belongs to (may be NULL)
596 * FontFamily [O] Pointer to the resulting FontFamily object
600 * FAILURE: FamilyNotFound if the requested FontFamily does not exist on the system
601 * FAILURE: Invalid parameter if FontFamily or name is NULL
604 * If fontCollection is NULL then the object is not part of any collection
608 GpStatus WINGDIPAPI GdipCreateFontFamilyFromName(GDIPCONST WCHAR *name,
609 GpFontCollection *fontCollection,
610 GpFontFamily **FontFamily)
612 GpFontFamily* ffamily;
613 OUTLINETEXTMETRICW otm;
615 TRACE("%s, %p %p\n", debugstr_w(name), fontCollection, FontFamily);
617 if (!(name && FontFamily))
618 return InvalidParameter;
620 FIXME("No support for FontCollections yet!\n");
622 if (!find_installed_font(name, &otm))
623 return FontFamilyNotFound;
625 ffamily = GdipAlloc(sizeof (GpFontFamily));
626 if (!ffamily) return OutOfMemory;
629 lstrcpynW(ffamily->FamilyName, name, LF_FACESIZE);
631 *FontFamily = ffamily;
633 TRACE("<-- %p\n", ffamily);
638 /*******************************************************************************
639 * GdipCloneFontFamily [GDIPLUS.@]
641 * Creates a deep copy of a Font Family object
644 * FontFamily [I] Font to clone
645 * clonedFontFamily [O] The resulting cloned font
650 GpStatus WINGDIPAPI GdipCloneFontFamily(GpFontFamily* FontFamily, GpFontFamily** clonedFontFamily)
652 if (!(FontFamily && clonedFontFamily)) return InvalidParameter;
654 TRACE("%p (%s), %p\n", FontFamily,
655 debugstr_w(FontFamily->FamilyName), clonedFontFamily);
657 *clonedFontFamily = GdipAlloc(sizeof(GpFontFamily));
658 if (!*clonedFontFamily) return OutOfMemory;
660 **clonedFontFamily = *FontFamily;
661 lstrcpyW((*clonedFontFamily)->FamilyName, FontFamily->FamilyName);
663 TRACE("<-- %p\n", *clonedFontFamily);
668 /*******************************************************************************
669 * GdipGetFamilyName [GDIPLUS.@]
671 * Returns the family name into name
674 * *family [I] Family to retrieve from
675 * *name [O] WCHARS of the family name
680 * FAILURE: InvalidParameter if family is NULL
683 * If name is a NULL ptr, then both XP and Vista will crash (so we do as well)
685 GpStatus WINGDIPAPI GdipGetFamilyName (GDIPCONST GpFontFamily *family,
686 WCHAR *name, LANGID language)
688 static int lang_fixme;
691 return InvalidParameter;
693 TRACE("%p, %p, %d\n", family, name, language);
695 if (language != LANG_NEUTRAL && !lang_fixme++)
696 FIXME("No support for handling of multiple languages!\n");
698 lstrcpynW (name, family->FamilyName, LF_FACESIZE);
704 /*****************************************************************************
705 * GdipDeleteFontFamily [GDIPLUS.@]
707 * Removes the specified FontFamily
710 * *FontFamily [I] The family to delete
714 * FAILURE: InvalidParameter if FontFamily is NULL.
717 GpStatus WINGDIPAPI GdipDeleteFontFamily(GpFontFamily *FontFamily)
720 return InvalidParameter;
721 TRACE("Deleting %p (%s)\n", FontFamily, debugstr_w(FontFamily->FamilyName));
723 GdipFree (FontFamily);
728 GpStatus WINGDIPAPI GdipGetCellAscent(GDIPCONST GpFontFamily *family,
729 INT style, UINT16* CellAscent)
731 if (!(family && CellAscent)) return InvalidParameter;
733 *CellAscent = family->otm.otmTextMetrics.tmAscent;
734 TRACE("%d => %u\n", family->otm.otmTextMetrics.tmHeight, *CellAscent);
739 GpStatus WINGDIPAPI GdipGetCellDescent(GDIPCONST GpFontFamily *family,
740 INT style, UINT16* CellDescent)
742 TRACE("(%p, %d, %p)\n", family, style, CellDescent);
744 if (!(family && CellDescent)) return InvalidParameter;
746 *CellDescent = family->otm.otmTextMetrics.tmDescent;
747 TRACE("%d => %u\n", family->otm.otmTextMetrics.tmHeight, *CellDescent);
752 /*******************************************************************************
753 * GdipGetEmHeight [GDIPLUS.@]
755 * Gets the height of the specified family in EmHeights
758 * family [I] Family to retrieve from
759 * style [I] (optional) style
760 * EmHeight [O] return value
764 * FAILURE: InvalidParameter
766 GpStatus WINGDIPAPI GdipGetEmHeight(GDIPCONST GpFontFamily *family, INT style, UINT16* EmHeight)
768 if (!(family && EmHeight)) return InvalidParameter;
770 TRACE("%p (%s), %d, %p\n", family, debugstr_w(family->FamilyName), style, EmHeight);
772 *EmHeight = family->otm.otmEMSquare;
773 TRACE("%d => %u\n", family->otm.otmTextMetrics.tmHeight, *EmHeight);
779 /*******************************************************************************
780 * GdipGetLineSpacing [GDIPLUS.@]
782 * Returns the line spacing in design units
785 * family [I] Family to retrieve from
786 * style [I] (Optional) font style
787 * LineSpacing [O] Return value
791 * FAILURE: InvalidParameter (family or LineSpacing was NULL)
793 GpStatus WINGDIPAPI GdipGetLineSpacing(GDIPCONST GpFontFamily *family,
794 INT style, UINT16* LineSpacing)
796 TRACE("%p, %d, %p\n", family, style, LineSpacing);
798 if (!(family && LineSpacing))
799 return InvalidParameter;
801 if (style) FIXME("ignoring style\n");
803 *LineSpacing = family->otm.otmTextMetrics.tmAscent + family->otm.otmTextMetrics.tmDescent + family->otm.otmTextMetrics.tmExternalLeading;
804 TRACE("%d => %u\n", family->otm.otmTextMetrics.tmHeight, *LineSpacing);
809 static INT CALLBACK font_has_style_proc(const LOGFONTW *elf,
810 const TEXTMETRICW *ntm, DWORD type, LPARAM lParam)
812 INT fontstyle = FontStyleRegular;
816 if (ntm->tmWeight >= FW_BOLD) fontstyle |= FontStyleBold;
817 if (ntm->tmItalic) fontstyle |= FontStyleItalic;
818 if (ntm->tmUnderlined) fontstyle |= FontStyleUnderline;
819 if (ntm->tmStruckOut) fontstyle |= FontStyleStrikeout;
821 return (INT)lParam != fontstyle;
824 GpStatus WINGDIPAPI GdipIsStyleAvailable(GDIPCONST GpFontFamily* family,
825 INT style, BOOL* IsStyleAvailable)
829 TRACE("%p %d %p\n", family, style, IsStyleAvailable);
831 if (!(family && IsStyleAvailable))
832 return InvalidParameter;
834 *IsStyleAvailable = FALSE;
838 if(!EnumFontFamiliesW(hdc, family->FamilyName, font_has_style_proc, (LPARAM)style))
839 *IsStyleAvailable = TRUE;
846 /*****************************************************************************
847 * GdipGetGenericFontFamilyMonospace [GDIPLUS.@]
849 * Obtains a serif family (Courier New on Windows)
852 * **nativeFamily [I] Where the font will be stored
855 * InvalidParameter if nativeFamily is NULL.
858 GpStatus WINGDIPAPI GdipGetGenericFontFamilyMonospace(GpFontFamily **nativeFamily)
860 static const WCHAR CourierNew[] = {'C','o','u','r','i','e','r',' ','N','e','w','\0'};
861 static const WCHAR LiberationMono[] = {'L','i','b','e','r','a','t','i','o','n',' ','M','o','n','o','\0'};
864 if (nativeFamily == NULL) return InvalidParameter;
866 stat = GdipCreateFontFamilyFromName(CourierNew, NULL, nativeFamily);
868 if (stat == FontFamilyNotFound)
869 stat = GdipCreateFontFamilyFromName(LiberationMono, NULL, nativeFamily);
871 if (stat == FontFamilyNotFound)
872 ERR("Missing 'Courier New' font\n");
877 /*****************************************************************************
878 * GdipGetGenericFontFamilySerif [GDIPLUS.@]
880 * Obtains a serif family (Times New Roman on Windows)
883 * **nativeFamily [I] Where the font will be stored
886 * InvalidParameter if nativeFamily is NULL.
889 GpStatus WINGDIPAPI GdipGetGenericFontFamilySerif(GpFontFamily **nativeFamily)
891 static const WCHAR TimesNewRoman[] = {'T','i','m','e','s',' ','N','e','w',' ','R','o','m','a','n','\0'};
892 static const WCHAR LiberationSerif[] = {'L','i','b','e','r','a','t','i','o','n',' ','S','e','r','i','f','\0'};
895 TRACE("(%p)\n", nativeFamily);
897 if (nativeFamily == NULL) return InvalidParameter;
899 stat = GdipCreateFontFamilyFromName(TimesNewRoman, NULL, nativeFamily);
901 if (stat == FontFamilyNotFound)
902 stat = GdipCreateFontFamilyFromName(LiberationSerif, NULL, nativeFamily);
904 if (stat == FontFamilyNotFound)
905 ERR("Missing 'Times New Roman' font\n");
910 /*****************************************************************************
911 * GdipGetGenericFontFamilySansSerif [GDIPLUS.@]
913 * Obtains a serif family (Microsoft Sans Serif on Windows)
916 * **nativeFamily [I] Where the font will be stored
919 * InvalidParameter if nativeFamily is NULL.
922 GpStatus WINGDIPAPI GdipGetGenericFontFamilySansSerif(GpFontFamily **nativeFamily)
925 static const WCHAR MicrosoftSansSerif[] = {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f','\0'};
926 static const WCHAR Tahoma[] = {'T','a','h','o','m','a','\0'};
928 TRACE("(%p)\n", nativeFamily);
930 if (nativeFamily == NULL) return InvalidParameter;
932 stat = GdipCreateFontFamilyFromName(MicrosoftSansSerif, NULL, nativeFamily);
934 if (stat == FontFamilyNotFound)
935 /* FIXME: Microsoft Sans Serif is not installed on Wine. */
936 stat = GdipCreateFontFamilyFromName(Tahoma, NULL, nativeFamily);
941 /*****************************************************************************
942 * GdipGetGenericFontFamilySansSerif [GDIPLUS.@]
944 GpStatus WINGDIPAPI GdipNewPrivateFontCollection(GpFontCollection** fontCollection)
946 TRACE("%p\n", fontCollection);
949 return InvalidParameter;
951 *fontCollection = GdipAlloc(sizeof(GpFontCollection));
952 if (!*fontCollection) return OutOfMemory;
954 (*fontCollection)->FontFamilies = NULL;
955 (*fontCollection)->count = 0;
956 (*fontCollection)->allocated = 0;
958 TRACE("<-- %p\n", *fontCollection);
963 /*****************************************************************************
964 * GdipDeletePrivateFontCollection [GDIPLUS.@]
966 GpStatus WINGDIPAPI GdipDeletePrivateFontCollection(GpFontCollection **fontCollection)
970 TRACE("%p\n", fontCollection);
973 return InvalidParameter;
975 for (i = 0; i < (*fontCollection)->count; i++) GdipFree((*fontCollection)->FontFamilies[i]);
976 GdipFree(*fontCollection);
981 /*****************************************************************************
982 * GdipPrivateAddFontFile [GDIPLUS.@]
984 GpStatus WINGDIPAPI GdipPrivateAddFontFile(GpFontCollection* fontCollection,
985 GDIPCONST WCHAR* filename)
987 FIXME("stub: %p, %s\n", fontCollection, debugstr_w(filename));
989 if (!(fontCollection && filename))
990 return InvalidParameter;
992 return NotImplemented;
995 /* Copied from msi/font.c */
997 typedef struct _tagTT_OFFSET_TABLE {
998 USHORT uMajorVersion;
999 USHORT uMinorVersion;
1000 USHORT uNumOfTables;
1001 USHORT uSearchRange;
1002 USHORT uEntrySelector;
1006 typedef struct _tagTT_TABLE_DIRECTORY {
1007 char szTag[4]; /* table name */
1008 ULONG uCheckSum; /* Check sum */
1009 ULONG uOffset; /* Offset from beginning of file */
1010 ULONG uLength; /* length of the table in bytes */
1011 } TT_TABLE_DIRECTORY;
1013 typedef struct _tagTT_NAME_TABLE_HEADER {
1014 USHORT uFSelector; /* format selector. Always 0 */
1015 USHORT uNRCount; /* Name Records count */
1016 USHORT uStorageOffset; /* Offset for strings storage,
1017 * from start of the table */
1018 } TT_NAME_TABLE_HEADER;
1020 #define NAME_ID_FULL_FONT_NAME 4
1021 #define NAME_ID_VERSION 5
1023 typedef struct _tagTT_NAME_RECORD {
1028 USHORT uStringLength;
1029 USHORT uStringOffset; /* from start of storage area */
1032 #define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
1033 #define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))
1036 * Code based off of code located here
1037 * http://www.codeproject.com/gdi/fontnamefromfile.asp
1039 static WCHAR *load_ttf_name_id( const char *mem, DWORD_PTR size, DWORD id, WCHAR *ret, DWORD len )
1041 const TT_TABLE_DIRECTORY *tblDir;
1042 TT_OFFSET_TABLE ttOffsetTable;
1043 TT_NAME_TABLE_HEADER ttNTHeader;
1044 TT_NAME_RECORD ttRecord;
1048 if (sizeof(TT_OFFSET_TABLE) > size)
1050 ttOffsetTable = *(TT_OFFSET_TABLE*)mem;
1051 ttOffsetTable.uNumOfTables = SWAPWORD(ttOffsetTable.uNumOfTables);
1052 ttOffsetTable.uMajorVersion = SWAPWORD(ttOffsetTable.uMajorVersion);
1053 ttOffsetTable.uMinorVersion = SWAPWORD(ttOffsetTable.uMinorVersion);
1055 if (ttOffsetTable.uMajorVersion != 1 || ttOffsetTable.uMinorVersion != 0)
1058 pos = sizeof(ttOffsetTable);
1059 for (i = 0; i < ttOffsetTable.uNumOfTables; i++)
1061 tblDir = (const TT_TABLE_DIRECTORY*)&mem[pos];
1062 pos += sizeof(*tblDir);
1063 if (memcmp(tblDir->szTag,"name",4)==0)
1065 ofs = SWAPLONG(tblDir->uOffset);
1069 if (i >= ttOffsetTable.uNumOfTables)
1072 pos = ofs + sizeof(ttNTHeader);
1075 ttNTHeader = *(TT_NAME_TABLE_HEADER*)&mem[ofs];
1076 ttNTHeader.uNRCount = SWAPWORD(ttNTHeader.uNRCount);
1077 ttNTHeader.uStorageOffset = SWAPWORD(ttNTHeader.uStorageOffset);
1078 for(i=0; i<ttNTHeader.uNRCount; i++)
1080 ttRecord = *(TT_NAME_RECORD*)&mem[pos];
1081 pos += sizeof(ttRecord);
1085 ttRecord.uNameID = SWAPWORD(ttRecord.uNameID);
1086 if (ttRecord.uNameID == id)
1090 ttRecord.uStringLength = SWAPWORD(ttRecord.uStringLength);
1091 ttRecord.uStringOffset = SWAPWORD(ttRecord.uStringOffset);
1092 if (ofs + ttRecord.uStringOffset + ttNTHeader.uStorageOffset + ttRecord.uStringLength > size)
1094 buf = mem + ofs + ttRecord.uStringOffset + ttNTHeader.uStorageOffset;
1095 len = MultiByteToWideChar(CP_ACP, 0, buf, ttRecord.uStringLength, ret, len-1);
1103 static INT CALLBACK add_font_proc(const LOGFONTW *lfw, const TEXTMETRICW *ntm, DWORD type, LPARAM lParam);
1105 /*****************************************************************************
1106 * GdipPrivateAddMemoryFont [GDIPLUS.@]
1108 GpStatus WINGDIPAPI GdipPrivateAddMemoryFont(GpFontCollection* fontCollection,
1109 GDIPCONST void* memory, INT length)
1111 WCHAR buf[32], *name;
1114 TRACE("%p, %p, %d\n", fontCollection, memory, length);
1116 if (!fontCollection || !memory || !length)
1117 return InvalidParameter;
1119 name = load_ttf_name_id(memory, length, NAME_ID_FULL_FONT_NAME, buf, sizeof(buf)/sizeof(*buf));
1123 font = AddFontMemResourceEx((void*)memory, length, NULL, &count);
1124 TRACE("%s: %p/%u\n", debugstr_w(name), font, count);
1125 if (!font || !count)
1126 return InvalidParameter;
1135 lfw.lfCharSet = DEFAULT_CHARSET;
1136 lstrcpyW(lfw.lfFaceName, name);
1137 lfw.lfPitchAndFamily = 0;
1139 if (!EnumFontFamiliesExW(hdc, &lfw, add_font_proc, (LPARAM)fontCollection, 0))
1150 /*****************************************************************************
1151 * GdipGetFontCollectionFamilyCount [GDIPLUS.@]
1153 GpStatus WINGDIPAPI GdipGetFontCollectionFamilyCount(
1154 GpFontCollection* fontCollection, INT* numFound)
1156 TRACE("%p, %p\n", fontCollection, numFound);
1158 if (!(fontCollection && numFound))
1159 return InvalidParameter;
1161 *numFound = fontCollection->count;
1165 /*****************************************************************************
1166 * GdipGetFontCollectionFamilyList [GDIPLUS.@]
1168 GpStatus WINGDIPAPI GdipGetFontCollectionFamilyList(
1169 GpFontCollection* fontCollection, INT numSought,
1170 GpFontFamily* gpfamilies[], INT* numFound)
1175 TRACE("%p, %d, %p, %p\n", fontCollection, numSought, gpfamilies, numFound);
1177 if (!(fontCollection && gpfamilies && numFound))
1178 return InvalidParameter;
1180 memset(gpfamilies, 0, sizeof(*gpfamilies) * numSought);
1182 for (i = 0; i < numSought && i < fontCollection->count && stat == Ok; i++)
1184 stat = GdipCloneFontFamily(fontCollection->FontFamilies[i], &gpfamilies[i]);
1192 for (i=0; i<numToFree; i++)
1194 GdipDeleteFontFamily(gpfamilies[i]);
1195 gpfamilies[i] = NULL;
1202 void free_installed_fonts(void)
1204 while (installedFontCollection.count)
1205 GdipDeleteFontFamily(installedFontCollection.FontFamilies[--installedFontCollection.count]);
1206 HeapFree(GetProcessHeap(), 0, installedFontCollection.FontFamilies);
1207 installedFontCollection.FontFamilies = NULL;
1208 installedFontCollection.allocated = 0;
1211 static INT CALLBACK add_font_proc(const LOGFONTW *lfw, const TEXTMETRICW *ntm,
1212 DWORD type, LPARAM lParam)
1214 GpFontCollection* fonts = (GpFontCollection*)lParam;
1217 if (type == RASTER_FONTTYPE)
1220 /* skip duplicates */
1221 for (i=0; i<fonts->count; i++)
1222 if (strcmpiW(lfw->lfFaceName, fonts->FontFamilies[i]->FamilyName) == 0)
1225 if (fonts->allocated == fonts->count)
1227 INT new_alloc_count = fonts->allocated+50;
1228 GpFontFamily** new_family_list = HeapAlloc(GetProcessHeap(), 0, new_alloc_count*sizeof(void*));
1230 if (!new_family_list)
1233 memcpy(new_family_list, fonts->FontFamilies, fonts->count*sizeof(void*));
1234 HeapFree(GetProcessHeap(), 0, fonts->FontFamilies);
1235 fonts->FontFamilies = new_family_list;
1236 fonts->allocated = new_alloc_count;
1239 if (GdipCreateFontFamilyFromName(lfw->lfFaceName, NULL, &fonts->FontFamilies[fonts->count]) == Ok)
1247 GpStatus WINGDIPAPI GdipNewInstalledFontCollection(
1248 GpFontCollection** fontCollection)
1250 TRACE("(%p)\n",fontCollection);
1252 if (!fontCollection)
1253 return InvalidParameter;
1255 if (installedFontCollection.count == 0)
1262 lfw.lfCharSet = DEFAULT_CHARSET;
1263 lfw.lfFaceName[0] = 0;
1264 lfw.lfPitchAndFamily = 0;
1266 if (!EnumFontFamiliesExW(hdc, &lfw, add_font_proc, (LPARAM)&installedFontCollection, 0))
1268 free_installed_fonts();
1276 *fontCollection = &installedFontCollection;