57f90e7caad5514c41ad41631447286ccbbf4fa5
[reactos.git] / dll / win32 / gdiplus / font.c
1 /*
2 * Copyright (C) 2007 Google (Evan Stade)
3 * Copyright (C) 2012 Dmitry Timoshkov
4 *
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.
9 *
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.
14 *
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
18 */
19
20 #include "gdiplus_private.h"
21
22 /* PANOSE is 10 bytes in size, need to pack the structure properly */
23 #include <pshpack2.h>
24 typedef struct
25 {
26 USHORT version;
27 SHORT xAvgCharWidth;
28 USHORT usWeightClass;
29 USHORT usWidthClass;
30 SHORT fsType;
31 SHORT ySubscriptXSize;
32 SHORT ySubscriptYSize;
33 SHORT ySubscriptXOffset;
34 SHORT ySubscriptYOffset;
35 SHORT ySuperscriptXSize;
36 SHORT ySuperscriptYSize;
37 SHORT ySuperscriptXOffset;
38 SHORT ySuperscriptYOffset;
39 SHORT yStrikeoutSize;
40 SHORT yStrikeoutPosition;
41 SHORT sFamilyClass;
42 PANOSE panose;
43 ULONG ulUnicodeRange1;
44 ULONG ulUnicodeRange2;
45 ULONG ulUnicodeRange3;
46 ULONG ulUnicodeRange4;
47 CHAR achVendID[4];
48 USHORT fsSelection;
49 USHORT usFirstCharIndex;
50 USHORT usLastCharIndex;
51 /* According to the Apple spec, original version didn't have the below fields,
52 * version numbers were taken from the OpenType spec.
53 */
54 /* version 0 (TrueType 1.5) */
55 USHORT sTypoAscender;
56 USHORT sTypoDescender;
57 USHORT sTypoLineGap;
58 USHORT usWinAscent;
59 USHORT usWinDescent;
60 /* version 1 (TrueType 1.66) */
61 ULONG ulCodePageRange1;
62 ULONG ulCodePageRange2;
63 /* version 2 (OpenType 1.2) */
64 SHORT sxHeight;
65 SHORT sCapHeight;
66 USHORT usDefaultChar;
67 USHORT usBreakChar;
68 USHORT usMaxContext;
69 } TT_OS2_V2;
70
71 typedef struct
72 {
73 ULONG Version;
74 SHORT Ascender;
75 SHORT Descender;
76 SHORT LineGap;
77 USHORT advanceWidthMax;
78 SHORT minLeftSideBearing;
79 SHORT minRightSideBearing;
80 SHORT xMaxExtent;
81 SHORT caretSlopeRise;
82 SHORT caretSlopeRun;
83 SHORT caretOffset;
84 SHORT reserved[4];
85 SHORT metricDataFormat;
86 USHORT numberOfHMetrics;
87 } TT_HHEA;
88 #include <poppack.h>
89
90 #ifdef WORDS_BIGENDIAN
91 #define GET_BE_WORD(x) (x)
92 #define GET_BE_DWORD(x) (x)
93 #else
94 #define GET_BE_WORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
95 #define GET_BE_DWORD(x) MAKELONG(GET_BE_WORD(HIWORD(x)), GET_BE_WORD(LOWORD(x)));
96 #endif
97
98 #define MS_MAKE_TAG(ch0, ch1, ch2, ch3) \
99 ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \
100 ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24))
101 #define MS_OS2_TAG MS_MAKE_TAG('O','S','/','2')
102 #define MS_HHEA_TAG MS_MAKE_TAG('h','h','e','a')
103
104 static GpStatus clone_font_family(const GpFontFamily *, GpFontFamily **);
105
106 static GpFontCollection installedFontCollection = {0};
107
108 /*******************************************************************************
109 * GdipCreateFont [GDIPLUS.@]
110 *
111 * Create a new font based off of a FontFamily
112 *
113 * PARAMS
114 * *fontFamily [I] Family to base the font off of
115 * emSize [I] Size of the font
116 * style [I] Bitwise OR of FontStyle enumeration
117 * unit [I] Unit emSize is measured in
118 * **font [I] the resulting Font object
119 *
120 * RETURNS
121 * SUCCESS: Ok
122 * FAILURE: InvalidParameter if fontfamily or font is NULL.
123 * FAILURE: FontFamilyNotFound if an invalid FontFamily is given
124 *
125 * NOTES
126 * UnitDisplay is unsupported.
127 * emSize is stored separately from lfHeight, to hold the fraction.
128 */
129 GpStatus WINGDIPAPI GdipCreateFont(GDIPCONST GpFontFamily *fontFamily,
130 REAL emSize, INT style, Unit unit, GpFont **font)
131 {
132 HFONT hfont;
133 OUTLINETEXTMETRICW otm;
134 LOGFONTW lfw;
135 HDC hdc;
136 GpStatus stat;
137 int ret;
138
139 if (!fontFamily || !font || emSize < 0.0)
140 return InvalidParameter;
141
142 TRACE("%p (%s), %f, %d, %d, %p\n", fontFamily,
143 debugstr_w(fontFamily->FamilyName), emSize, style, unit, font);
144
145 memset(&lfw, 0, sizeof(lfw));
146
147 stat = GdipGetFamilyName(fontFamily, lfw.lfFaceName, LANG_NEUTRAL);
148 if (stat != Ok) return stat;
149
150 lfw.lfHeight = -units_to_pixels(emSize, unit, fontFamily->dpi);
151 lfw.lfWeight = style & FontStyleBold ? FW_BOLD : FW_REGULAR;
152 lfw.lfItalic = style & FontStyleItalic;
153 lfw.lfUnderline = style & FontStyleUnderline;
154 lfw.lfStrikeOut = style & FontStyleStrikeout;
155
156 hfont = CreateFontIndirectW(&lfw);
157 hdc = CreateCompatibleDC(0);
158 SelectObject(hdc, hfont);
159 otm.otmSize = sizeof(otm);
160 ret = GetOutlineTextMetricsW(hdc, otm.otmSize, &otm);
161 DeleteDC(hdc);
162 DeleteObject(hfont);
163
164 if (!ret) return NotTrueTypeFont;
165
166 *font = heap_alloc_zero(sizeof(GpFont));
167 if (!*font) return OutOfMemory;
168
169 (*font)->unit = unit;
170 (*font)->emSize = emSize;
171 (*font)->otm = otm;
172
173 stat = clone_font_family(fontFamily, &(*font)->family);
174 if (stat != Ok)
175 {
176 heap_free(*font);
177 return stat;
178 }
179
180 TRACE("<-- %p\n", *font);
181
182 return Ok;
183 }
184
185 /*******************************************************************************
186 * GdipCreateFontFromLogfontW [GDIPLUS.@]
187 */
188 GpStatus WINGDIPAPI GdipCreateFontFromLogfontW(HDC hdc,
189 GDIPCONST LOGFONTW *logfont, GpFont **font)
190 {
191 HFONT hfont, oldfont;
192 OUTLINETEXTMETRICW otm;
193 WCHAR facename[LF_FACESIZE];
194 GpStatus stat;
195 int ret;
196
197 TRACE("(%p, %p, %p)\n", hdc, logfont, font);
198
199 if (!hdc || !logfont || !font)
200 return InvalidParameter;
201
202 hfont = CreateFontIndirectW(logfont);
203 oldfont = SelectObject(hdc, hfont);
204 otm.otmSize = sizeof(otm);
205 ret = GetOutlineTextMetricsW(hdc, otm.otmSize, &otm);
206 GetTextFaceW(hdc, LF_FACESIZE, facename);
207 SelectObject(hdc, oldfont);
208 DeleteObject(hfont);
209
210 if (!ret) return NotTrueTypeFont;
211
212 *font = heap_alloc_zero(sizeof(GpFont));
213 if (!*font) return OutOfMemory;
214
215 (*font)->unit = UnitWorld;
216 (*font)->emSize = otm.otmTextMetrics.tmAscent;
217 (*font)->otm = otm;
218
219 stat = GdipCreateFontFamilyFromName(facename, NULL, &(*font)->family);
220 if (stat != Ok)
221 {
222 heap_free(*font);
223 return NotTrueTypeFont;
224 }
225
226 TRACE("<-- %p\n", *font);
227
228 return Ok;
229 }
230
231 /*******************************************************************************
232 * GdipCreateFontFromLogfontA [GDIPLUS.@]
233 */
234 GpStatus WINGDIPAPI GdipCreateFontFromLogfontA(HDC hdc,
235 GDIPCONST LOGFONTA *lfa, GpFont **font)
236 {
237 LOGFONTW lfw;
238
239 TRACE("(%p, %p, %p)\n", hdc, lfa, font);
240
241 if(!lfa || !font)
242 return InvalidParameter;
243
244 memcpy(&lfw, lfa, FIELD_OFFSET(LOGFONTA,lfFaceName) );
245
246 if(!MultiByteToWideChar(CP_ACP, 0, lfa->lfFaceName, -1, lfw.lfFaceName, LF_FACESIZE))
247 return GenericError;
248
249 return GdipCreateFontFromLogfontW(hdc, &lfw, font);
250 }
251
252 /*******************************************************************************
253 * GdipDeleteFont [GDIPLUS.@]
254 */
255 GpStatus WINGDIPAPI GdipDeleteFont(GpFont* font)
256 {
257 TRACE("(%p)\n", font);
258
259 if(!font)
260 return InvalidParameter;
261
262 GdipDeleteFontFamily(font->family);
263 heap_free(font);
264
265 return Ok;
266 }
267
268 /*******************************************************************************
269 * GdipCreateFontFromDC [GDIPLUS.@]
270 */
271 GpStatus WINGDIPAPI GdipCreateFontFromDC(HDC hdc, GpFont **font)
272 {
273 HFONT hfont;
274 LOGFONTW lfw;
275
276 TRACE("(%p, %p)\n", hdc, font);
277
278 if(!font)
279 return InvalidParameter;
280
281 hfont = GetCurrentObject(hdc, OBJ_FONT);
282 if(!hfont)
283 return GenericError;
284
285 if(!GetObjectW(hfont, sizeof(LOGFONTW), &lfw))
286 return GenericError;
287
288 return GdipCreateFontFromLogfontW(hdc, &lfw, font);
289 }
290
291 /*******************************************************************************
292 * GdipGetFamily [GDIPLUS.@]
293 *
294 * Returns the FontFamily for the specified Font
295 *
296 * PARAMS
297 * font [I] Font to request from
298 * family [O] Resulting FontFamily object
299 *
300 * RETURNS
301 * SUCCESS: Ok
302 * FAILURE: An element of GpStatus
303 */
304 GpStatus WINGDIPAPI GdipGetFamily(GpFont *font, GpFontFamily **family)
305 {
306 TRACE("%p %p\n", font, family);
307
308 if (!(font && family))
309 return InvalidParameter;
310
311 return GdipCloneFontFamily(font->family, family);
312 }
313
314 static REAL get_font_size(const GpFont *font)
315 {
316 return font->emSize;
317 }
318
319 /******************************************************************************
320 * GdipGetFontSize [GDIPLUS.@]
321 *
322 * Returns the size of the font in Units
323 *
324 * PARAMS
325 * *font [I] The font to retrieve size from
326 * *size [O] Pointer to hold retrieved value
327 *
328 * RETURNS
329 * SUCCESS: Ok
330 * FAILURE: InvalidParameter (font or size was NULL)
331 *
332 * NOTES
333 * Size returned is actually emSize -- not internal size used for drawing.
334 */
335 GpStatus WINGDIPAPI GdipGetFontSize(GpFont *font, REAL *size)
336 {
337 TRACE("(%p, %p)\n", font, size);
338
339 if (!(font && size)) return InvalidParameter;
340
341 *size = get_font_size(font);
342 TRACE("%s,%d => %f\n", debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *size);
343
344 return Ok;
345 }
346
347 static INT get_font_style(const GpFont *font)
348 {
349 INT style;
350
351 if (font->otm.otmTextMetrics.tmWeight > FW_REGULAR)
352 style = FontStyleBold;
353 else
354 style = FontStyleRegular;
355 if (font->otm.otmTextMetrics.tmItalic)
356 style |= FontStyleItalic;
357 if (font->otm.otmTextMetrics.tmUnderlined)
358 style |= FontStyleUnderline;
359 if (font->otm.otmTextMetrics.tmStruckOut)
360 style |= FontStyleStrikeout;
361
362 return style;
363 }
364
365 /*******************************************************************************
366 * GdipGetFontStyle [GDIPLUS.@]
367 *
368 * Gets the font's style, returned in bitwise OR of FontStyle enumeration
369 *
370 * PARAMS
371 * font [I] font to request from
372 * style [O] resulting pointer to a FontStyle enumeration
373 *
374 * RETURNS
375 * SUCCESS: Ok
376 * FAILURE: InvalidParameter
377 */
378 GpStatus WINGDIPAPI GdipGetFontStyle(GpFont *font, INT *style)
379 {
380 TRACE("%p %p\n", font, style);
381
382 if (!(font && style))
383 return InvalidParameter;
384
385 *style = get_font_style(font);
386 TRACE("%s,%d => %d\n", debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *style);
387
388 return Ok;
389 }
390
391 /*******************************************************************************
392 * GdipGetFontUnit [GDIPLUS.@]
393 *
394 * PARAMS
395 * font [I] Font to retrieve from
396 * unit [O] Return value
397 *
398 * RETURNS
399 * FAILURE: font or unit was NULL
400 * OK: otherwise
401 */
402 GpStatus WINGDIPAPI GdipGetFontUnit(GpFont *font, Unit *unit)
403 {
404 TRACE("(%p, %p)\n", font, unit);
405
406 if (!(font && unit)) return InvalidParameter;
407
408 *unit = font->unit;
409 TRACE("%s,%d => %d\n", debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *unit);
410
411 return Ok;
412 }
413
414 /*******************************************************************************
415 * GdipGetLogFontA [GDIPLUS.@]
416 */
417 GpStatus WINGDIPAPI GdipGetLogFontA(GpFont *font, GpGraphics *graphics,
418 LOGFONTA *lfa)
419 {
420 GpStatus status;
421 LOGFONTW lfw;
422
423 TRACE("(%p, %p, %p)\n", font, graphics, lfa);
424
425 status = GdipGetLogFontW(font, graphics, &lfw);
426 if(status != Ok)
427 return status;
428
429 memcpy(lfa, &lfw, FIELD_OFFSET(LOGFONTA,lfFaceName) );
430
431 if(!WideCharToMultiByte(CP_ACP, 0, lfw.lfFaceName, -1, lfa->lfFaceName, LF_FACESIZE, NULL, NULL))
432 return GenericError;
433
434 return Ok;
435 }
436
437 /*******************************************************************************
438 * GdipGetLogFontW [GDIPLUS.@]
439 */
440 GpStatus WINGDIPAPI GdipGetLogFontW(GpFont *font, GpGraphics *graphics, LOGFONTW *lf)
441 {
442 REAL angle, rel_height, height;
443 GpMatrix matrix;
444 GpPointF pt[3];
445
446 TRACE("(%p, %p, %p)\n", font, graphics, lf);
447
448 if (!font || !graphics || !lf)
449 return InvalidParameter;
450
451 matrix = graphics->worldtrans;
452
453 if (font->unit == UnitPixel || font->unit == UnitWorld)
454 {
455 height = units_to_pixels(font->emSize, graphics->unit, graphics->yres);
456 if (graphics->unit != UnitDisplay)
457 GdipScaleMatrix(&matrix, graphics->scale, graphics->scale, MatrixOrderAppend);
458 }
459 else
460 {
461 if (graphics->unit == UnitDisplay || graphics->unit == UnitPixel)
462 height = units_to_pixels(font->emSize, font->unit, graphics->xres);
463 else
464 height = units_to_pixels(font->emSize, font->unit, graphics->yres);
465 }
466
467 pt[0].X = 0.0;
468 pt[0].Y = 0.0;
469 pt[1].X = 1.0;
470 pt[1].Y = 0.0;
471 pt[2].X = 0.0;
472 pt[2].Y = 1.0;
473 GdipTransformMatrixPoints(&matrix, pt, 3);
474 angle = -gdiplus_atan2((pt[1].Y - pt[0].Y), (pt[1].X - pt[0].X));
475 rel_height = sqrt((pt[2].Y - pt[0].Y) * (pt[2].Y - pt[0].Y)+
476 (pt[2].X - pt[0].X) * (pt[2].X - pt[0].X));
477
478 lf->lfHeight = -gdip_round(height * rel_height);
479 lf->lfWidth = 0;
480 lf->lfEscapement = lf->lfOrientation = gdip_round((angle / M_PI) * 1800.0);
481 if (lf->lfEscapement < 0)
482 {
483 lf->lfEscapement += 3600;
484 lf->lfOrientation += 3600;
485 }
486 lf->lfWeight = font->otm.otmTextMetrics.tmWeight;
487 lf->lfItalic = font->otm.otmTextMetrics.tmItalic ? 1 : 0;
488 lf->lfUnderline = font->otm.otmTextMetrics.tmUnderlined ? 1 : 0;
489 lf->lfStrikeOut = font->otm.otmTextMetrics.tmStruckOut ? 1 : 0;
490 lf->lfCharSet = font->otm.otmTextMetrics.tmCharSet;
491 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
492 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
493 lf->lfQuality = DEFAULT_QUALITY;
494 lf->lfPitchAndFamily = 0;
495 strcpyW(lf->lfFaceName, font->family->FamilyName);
496
497 TRACE("=> %s,%d\n", debugstr_w(lf->lfFaceName), lf->lfHeight);
498
499 return Ok;
500 }
501
502 /*******************************************************************************
503 * GdipCloneFont [GDIPLUS.@]
504 */
505 GpStatus WINGDIPAPI GdipCloneFont(GpFont *font, GpFont **cloneFont)
506 {
507 GpStatus stat;
508
509 TRACE("(%p, %p)\n", font, cloneFont);
510
511 if(!font || !cloneFont)
512 return InvalidParameter;
513
514 *cloneFont = heap_alloc_zero(sizeof(GpFont));
515 if(!*cloneFont) return OutOfMemory;
516
517 **cloneFont = *font;
518 stat = GdipCloneFontFamily(font->family, &(*cloneFont)->family);
519 if (stat != Ok) heap_free(*cloneFont);
520
521 return stat;
522 }
523
524 /*******************************************************************************
525 * GdipGetFontHeight [GDIPLUS.@]
526 * PARAMS
527 * font [I] Font to retrieve height from
528 * graphics [I] The current graphics context
529 * height [O] Resulting height
530 * RETURNS
531 * SUCCESS: Ok
532 * FAILURE: Another element of GpStatus
533 *
534 * NOTES
535 * Forwards to GdipGetFontHeightGivenDPI
536 */
537 GpStatus WINGDIPAPI GdipGetFontHeight(GDIPCONST GpFont *font,
538 GDIPCONST GpGraphics *graphics, REAL *height)
539 {
540 REAL dpi;
541 GpStatus stat;
542 REAL font_height;
543
544 TRACE("%p %p %p\n", font, graphics, height);
545
546 if (!font || !height) return InvalidParameter;
547
548 stat = GdipGetFontHeightGivenDPI(font, font->family->dpi, &font_height);
549 if (stat != Ok) return stat;
550
551 if (!graphics)
552 {
553 *height = font_height;
554 TRACE("%s,%d => %f\n",
555 debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *height);
556 return Ok;
557 }
558
559 stat = GdipGetDpiY((GpGraphics *)graphics, &dpi);
560 if (stat != Ok) return stat;
561
562 *height = pixels_to_units(font_height, graphics->unit, dpi);
563
564 TRACE("%s,%d(unit %d) => %f\n",
565 debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, graphics->unit, *height);
566 return Ok;
567 }
568
569 /*******************************************************************************
570 * GdipGetFontHeightGivenDPI [GDIPLUS.@]
571 * PARAMS
572 * font [I] Font to retrieve DPI from
573 * dpi [I] DPI to assume
574 * height [O] Return value
575 *
576 * RETURNS
577 * SUCCESS: Ok
578 * FAILURE: InvalidParameter if font or height is NULL
579 *
580 * NOTES
581 * According to MSDN, the result is (lineSpacing)*(fontSize / emHeight)*dpi
582 * (for anything other than unit Pixel)
583 */
584 GpStatus WINGDIPAPI GdipGetFontHeightGivenDPI(GDIPCONST GpFont *font, REAL dpi, REAL *height)
585 {
586 GpStatus stat;
587 INT style;
588 UINT16 line_spacing, em_height;
589 REAL font_size;
590
591 if (!font || !height) return InvalidParameter;
592
593 TRACE("%p (%s), %f, %p\n", font,
594 debugstr_w(font->family->FamilyName), dpi, height);
595
596 font_size = units_to_pixels(get_font_size(font), font->unit, dpi);
597 style = get_font_style(font);
598 stat = GdipGetLineSpacing(font->family, style, &line_spacing);
599 if (stat != Ok) return stat;
600 stat = GdipGetEmHeight(font->family, style, &em_height);
601 if (stat != Ok) return stat;
602
603 *height = (REAL)line_spacing * font_size / (REAL)em_height;
604
605 TRACE("%s,%d => %f\n",
606 debugstr_w(font->family->FamilyName), font->otm.otmTextMetrics.tmHeight, *height);
607
608 return Ok;
609 }
610
611 /***********************************************************************
612 * Borrowed from GDI32:
613 *
614 * Elf is really an ENUMLOGFONTEXW, and ntm is a NEWTEXTMETRICEXW.
615 * We have to use other types because of the FONTENUMPROCW definition.
616 */
617 static INT CALLBACK is_font_installed_proc(const LOGFONTW *elf,
618 const TEXTMETRICW *ntm, DWORD type, LPARAM lParam)
619 {
620 const ENUMLOGFONTW *elfW = (const ENUMLOGFONTW *)elf;
621 LOGFONTW *lf = (LOGFONTW *)lParam;
622
623 if (type & RASTER_FONTTYPE)
624 return 1;
625
626 *lf = *elf;
627 /* replace substituted font name by a real one */
628 lstrcpynW(lf->lfFaceName, elfW->elfFullName, LF_FACESIZE);
629 return 0;
630 }
631
632 struct font_metrics
633 {
634 WCHAR facename[LF_FACESIZE];
635 UINT16 em_height, ascent, descent, line_spacing; /* in font units */
636 int dpi;
637 };
638
639 static BOOL get_font_metrics(HDC hdc, struct font_metrics *fm)
640 {
641 OUTLINETEXTMETRICW otm;
642 TT_OS2_V2 tt_os2;
643 TT_HHEA tt_hori;
644 LONG size;
645 UINT16 line_gap;
646
647 otm.otmSize = sizeof(otm);
648 if (!GetOutlineTextMetricsW(hdc, otm.otmSize, &otm)) return FALSE;
649
650 fm->em_height = otm.otmEMSquare;
651 fm->dpi = GetDeviceCaps(hdc, LOGPIXELSY);
652
653 memset(&tt_hori, 0, sizeof(tt_hori));
654 if (GetFontData(hdc, MS_HHEA_TAG, 0, &tt_hori, sizeof(tt_hori)) != GDI_ERROR)
655 {
656 fm->ascent = GET_BE_WORD(tt_hori.Ascender);
657 fm->descent = -GET_BE_WORD(tt_hori.Descender);
658 TRACE("hhea: ascent %d, descent %d\n", fm->ascent, fm->descent);
659 line_gap = GET_BE_WORD(tt_hori.LineGap);
660 fm->line_spacing = fm->ascent + fm->descent + line_gap;
661 TRACE("line_gap %u, line_spacing %u\n", line_gap, fm->line_spacing);
662 if (fm->ascent + fm->descent != 0) return TRUE;
663 }
664
665 size = GetFontData(hdc, MS_OS2_TAG, 0, NULL, 0);
666 if (size == GDI_ERROR) return FALSE;
667
668 if (size > sizeof(tt_os2)) size = sizeof(tt_os2);
669
670 memset(&tt_os2, 0, sizeof(tt_os2));
671 if (GetFontData(hdc, MS_OS2_TAG, 0, &tt_os2, size) != size) return FALSE;
672
673 fm->ascent = GET_BE_WORD(tt_os2.usWinAscent);
674 fm->descent = GET_BE_WORD(tt_os2.usWinDescent);
675 TRACE("usWinAscent %u, usWinDescent %u\n", fm->ascent, fm->descent);
676 if (fm->ascent + fm->descent == 0)
677 {
678 fm->ascent = GET_BE_WORD(tt_os2.sTypoAscender);
679 fm->descent = GET_BE_WORD(tt_os2.sTypoDescender);
680 TRACE("sTypoAscender %u, sTypoDescender %u\n", fm->ascent, fm->descent);
681 }
682 line_gap = GET_BE_WORD(tt_os2.sTypoLineGap);
683 fm->line_spacing = fm->ascent + fm->descent + line_gap;
684 TRACE("line_gap %u, line_spacing %u\n", line_gap, fm->line_spacing);
685 return TRUE;
686 }
687
688 static GpStatus find_installed_font(const WCHAR *name, struct font_metrics *fm)
689 {
690 LOGFONTW lf;
691 HDC hdc = CreateCompatibleDC(0);
692 GpStatus ret = FontFamilyNotFound;
693
694 if(!EnumFontFamiliesW(hdc, name, is_font_installed_proc, (LPARAM)&lf))
695 {
696 HFONT hfont, old_font;
697
698 strcpyW(fm->facename, lf.lfFaceName);
699
700 hfont = CreateFontIndirectW(&lf);
701 old_font = SelectObject(hdc, hfont);
702 ret = get_font_metrics(hdc, fm) ? Ok : NotTrueTypeFont;
703 SelectObject(hdc, old_font);
704 DeleteObject(hfont);
705 }
706
707 DeleteDC(hdc);
708 return ret;
709 }
710
711 /*******************************************************************************
712 * GdipCreateFontFamilyFromName [GDIPLUS.@]
713 *
714 * Creates a font family object based on a supplied name
715 *
716 * PARAMS
717 * name [I] Name of the font
718 * fontCollection [I] What font collection (if any) the font belongs to (may be NULL)
719 * FontFamily [O] Pointer to the resulting FontFamily object
720 *
721 * RETURNS
722 * SUCCESS: Ok
723 * FAILURE: FamilyNotFound if the requested FontFamily does not exist on the system
724 * FAILURE: Invalid parameter if FontFamily or name is NULL
725 *
726 * NOTES
727 * If fontCollection is NULL then the object is not part of any collection
728 *
729 */
730
731 GpStatus WINGDIPAPI GdipCreateFontFamilyFromName(GDIPCONST WCHAR *name,
732 GpFontCollection *fontCollection,
733 GpFontFamily **FontFamily)
734 {
735 GpStatus stat;
736 GpFontFamily* ffamily;
737 struct font_metrics fm;
738
739 TRACE("%s, %p %p\n", debugstr_w(name), fontCollection, FontFamily);
740
741 if (!(name && FontFamily))
742 return InvalidParameter;
743 if (fontCollection)
744 FIXME("No support for FontCollections yet!\n");
745
746 stat = find_installed_font(name, &fm);
747 if (stat != Ok) return stat;
748
749 ffamily = heap_alloc_zero(sizeof (GpFontFamily));
750 if (!ffamily) return OutOfMemory;
751
752 lstrcpyW(ffamily->FamilyName, fm.facename);
753 ffamily->em_height = fm.em_height;
754 ffamily->ascent = fm.ascent;
755 ffamily->descent = fm.descent;
756 ffamily->line_spacing = fm.line_spacing;
757 ffamily->dpi = fm.dpi;
758
759 *FontFamily = ffamily;
760
761 TRACE("<-- %p\n", ffamily);
762
763 return Ok;
764 }
765
766 static GpStatus clone_font_family(const GpFontFamily *family, GpFontFamily **clone)
767 {
768 *clone = heap_alloc_zero(sizeof(GpFontFamily));
769 if (!*clone) return OutOfMemory;
770
771 **clone = *family;
772
773 return Ok;
774 }
775
776 /*******************************************************************************
777 * GdipCloneFontFamily [GDIPLUS.@]
778 *
779 * Creates a deep copy of a Font Family object
780 *
781 * PARAMS
782 * FontFamily [I] Font to clone
783 * clonedFontFamily [O] The resulting cloned font
784 *
785 * RETURNS
786 * SUCCESS: Ok
787 */
788 GpStatus WINGDIPAPI GdipCloneFontFamily(GpFontFamily* FontFamily, GpFontFamily** clonedFontFamily)
789 {
790 GpStatus status;
791
792 if (!(FontFamily && clonedFontFamily)) return InvalidParameter;
793
794 TRACE("%p (%s), %p\n", FontFamily,
795 debugstr_w(FontFamily->FamilyName), clonedFontFamily);
796
797 status = clone_font_family(FontFamily, clonedFontFamily);
798 if (status != Ok) return status;
799
800 TRACE("<-- %p\n", *clonedFontFamily);
801
802 return Ok;
803 }
804
805 /*******************************************************************************
806 * GdipGetFamilyName [GDIPLUS.@]
807 *
808 * Returns the family name into name
809 *
810 * PARAMS
811 * *family [I] Family to retrieve from
812 * *name [O] WCHARS of the family name
813 * LANGID [I] charset
814 *
815 * RETURNS
816 * SUCCESS: Ok
817 * FAILURE: InvalidParameter if family is NULL
818 *
819 * NOTES
820 * If name is a NULL ptr, then both XP and Vista will crash (so we do as well)
821 */
822 GpStatus WINGDIPAPI GdipGetFamilyName (GDIPCONST GpFontFamily *family,
823 WCHAR *name, LANGID language)
824 {
825 static int lang_fixme;
826
827 if (family == NULL)
828 return InvalidParameter;
829
830 TRACE("%p, %p, %d\n", family, name, language);
831
832 if (language != LANG_NEUTRAL && !lang_fixme++)
833 FIXME("No support for handling of multiple languages!\n");
834
835 lstrcpynW (name, family->FamilyName, LF_FACESIZE);
836
837 return Ok;
838 }
839
840
841 /*****************************************************************************
842 * GdipDeleteFontFamily [GDIPLUS.@]
843 *
844 * Removes the specified FontFamily
845 *
846 * PARAMS
847 * *FontFamily [I] The family to delete
848 *
849 * RETURNS
850 * SUCCESS: Ok
851 * FAILURE: InvalidParameter if FontFamily is NULL.
852 *
853 */
854 GpStatus WINGDIPAPI GdipDeleteFontFamily(GpFontFamily *FontFamily)
855 {
856 if (!FontFamily)
857 return InvalidParameter;
858 TRACE("Deleting %p (%s)\n", FontFamily, debugstr_w(FontFamily->FamilyName));
859
860 heap_free (FontFamily);
861
862 return Ok;
863 }
864
865 GpStatus WINGDIPAPI GdipGetCellAscent(GDIPCONST GpFontFamily *family,
866 INT style, UINT16* CellAscent)
867 {
868 if (!(family && CellAscent)) return InvalidParameter;
869
870 *CellAscent = family->ascent;
871 TRACE("%s => %u\n", debugstr_w(family->FamilyName), *CellAscent);
872
873 return Ok;
874 }
875
876 GpStatus WINGDIPAPI GdipGetCellDescent(GDIPCONST GpFontFamily *family,
877 INT style, UINT16* CellDescent)
878 {
879 TRACE("(%p, %d, %p)\n", family, style, CellDescent);
880
881 if (!(family && CellDescent)) return InvalidParameter;
882
883 *CellDescent = family->descent;
884 TRACE("%s => %u\n", debugstr_w(family->FamilyName), *CellDescent);
885
886 return Ok;
887 }
888
889 /*******************************************************************************
890 * GdipGetEmHeight [GDIPLUS.@]
891 *
892 * Gets the height of the specified family in EmHeights
893 *
894 * PARAMS
895 * family [I] Family to retrieve from
896 * style [I] (optional) style
897 * EmHeight [O] return value
898 *
899 * RETURNS
900 * SUCCESS: Ok
901 * FAILURE: InvalidParameter
902 */
903 GpStatus WINGDIPAPI GdipGetEmHeight(GDIPCONST GpFontFamily *family, INT style, UINT16* EmHeight)
904 {
905 if (!(family && EmHeight)) return InvalidParameter;
906
907 TRACE("%p (%s), %d, %p\n", family, debugstr_w(family->FamilyName), style, EmHeight);
908
909 *EmHeight = family->em_height;
910 TRACE("%s => %u\n", debugstr_w(family->FamilyName), *EmHeight);
911
912 return Ok;
913 }
914
915
916 /*******************************************************************************
917 * GdipGetLineSpacing [GDIPLUS.@]
918 *
919 * Returns the line spacing in design units
920 *
921 * PARAMS
922 * family [I] Family to retrieve from
923 * style [I] (Optional) font style
924 * LineSpacing [O] Return value
925 *
926 * RETURNS
927 * SUCCESS: Ok
928 * FAILURE: InvalidParameter (family or LineSpacing was NULL)
929 */
930 GpStatus WINGDIPAPI GdipGetLineSpacing(GDIPCONST GpFontFamily *family,
931 INT style, UINT16* LineSpacing)
932 {
933 TRACE("%p, %d, %p\n", family, style, LineSpacing);
934
935 if (!(family && LineSpacing))
936 return InvalidParameter;
937
938 if (style) FIXME("ignoring style\n");
939
940 *LineSpacing = family->line_spacing;
941 TRACE("%s => %u\n", debugstr_w(family->FamilyName), *LineSpacing);
942
943 return Ok;
944 }
945
946 static INT CALLBACK font_has_style_proc(const LOGFONTW *elf,
947 const TEXTMETRICW *ntm, DWORD type, LPARAM lParam)
948 {
949 INT fontstyle = FontStyleRegular;
950
951 if (!ntm) return 1;
952
953 if (ntm->tmWeight >= FW_BOLD) fontstyle |= FontStyleBold;
954 if (ntm->tmItalic) fontstyle |= FontStyleItalic;
955 if (ntm->tmUnderlined) fontstyle |= FontStyleUnderline;
956 if (ntm->tmStruckOut) fontstyle |= FontStyleStrikeout;
957
958 return (INT)lParam != fontstyle;
959 }
960
961 GpStatus WINGDIPAPI GdipIsStyleAvailable(GDIPCONST GpFontFamily* family,
962 INT style, BOOL* IsStyleAvailable)
963 {
964 HDC hdc;
965
966 TRACE("%p %d %p\n", family, style, IsStyleAvailable);
967
968 if (!(family && IsStyleAvailable))
969 return InvalidParameter;
970
971 *IsStyleAvailable = FALSE;
972
973 hdc = CreateCompatibleDC(0);
974
975 if(!EnumFontFamiliesW(hdc, family->FamilyName, font_has_style_proc, (LPARAM)style))
976 *IsStyleAvailable = TRUE;
977
978 DeleteDC(hdc);
979
980 return Ok;
981 }
982
983 /*****************************************************************************
984 * GdipGetGenericFontFamilyMonospace [GDIPLUS.@]
985 *
986 * Obtains a serif family (Courier New on Windows)
987 *
988 * PARAMS
989 * **nativeFamily [I] Where the font will be stored
990 *
991 * RETURNS
992 * InvalidParameter if nativeFamily is NULL.
993 * Ok otherwise.
994 */
995 GpStatus WINGDIPAPI GdipGetGenericFontFamilyMonospace(GpFontFamily **nativeFamily)
996 {
997 static const WCHAR CourierNew[] = {'C','o','u','r','i','e','r',' ','N','e','w','\0'};
998 static const WCHAR LiberationMono[] = {'L','i','b','e','r','a','t','i','o','n',' ','M','o','n','o','\0'};
999 GpStatus stat;
1000
1001 if (nativeFamily == NULL) return InvalidParameter;
1002
1003 stat = GdipCreateFontFamilyFromName(CourierNew, NULL, nativeFamily);
1004
1005 if (stat == FontFamilyNotFound)
1006 stat = GdipCreateFontFamilyFromName(LiberationMono, NULL, nativeFamily);
1007
1008 if (stat == FontFamilyNotFound)
1009 ERR("Missing 'Courier New' font\n");
1010
1011 return stat;
1012 }
1013
1014 /*****************************************************************************
1015 * GdipGetGenericFontFamilySerif [GDIPLUS.@]
1016 *
1017 * Obtains a serif family (Times New Roman on Windows)
1018 *
1019 * PARAMS
1020 * **nativeFamily [I] Where the font will be stored
1021 *
1022 * RETURNS
1023 * InvalidParameter if nativeFamily is NULL.
1024 * Ok otherwise.
1025 */
1026 GpStatus WINGDIPAPI GdipGetGenericFontFamilySerif(GpFontFamily **nativeFamily)
1027 {
1028 static const WCHAR TimesNewRoman[] = {'T','i','m','e','s',' ','N','e','w',' ','R','o','m','a','n','\0'};
1029 static const WCHAR LiberationSerif[] = {'L','i','b','e','r','a','t','i','o','n',' ','S','e','r','i','f','\0'};
1030 GpStatus stat;
1031
1032 TRACE("(%p)\n", nativeFamily);
1033
1034 if (nativeFamily == NULL) return InvalidParameter;
1035
1036 stat = GdipCreateFontFamilyFromName(TimesNewRoman, NULL, nativeFamily);
1037
1038 if (stat == FontFamilyNotFound)
1039 stat = GdipCreateFontFamilyFromName(LiberationSerif, NULL, nativeFamily);
1040
1041 if (stat == FontFamilyNotFound)
1042 ERR("Missing 'Times New Roman' font\n");
1043
1044 return stat;
1045 }
1046
1047 /*****************************************************************************
1048 * GdipGetGenericFontFamilySansSerif [GDIPLUS.@]
1049 *
1050 * Obtains a serif family (Microsoft Sans Serif on Windows)
1051 *
1052 * PARAMS
1053 * **nativeFamily [I] Where the font will be stored
1054 *
1055 * RETURNS
1056 * InvalidParameter if nativeFamily is NULL.
1057 * Ok otherwise.
1058 */
1059 GpStatus WINGDIPAPI GdipGetGenericFontFamilySansSerif(GpFontFamily **nativeFamily)
1060 {
1061 GpStatus stat;
1062 static const WCHAR MicrosoftSansSerif[] = {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f','\0'};
1063 static const WCHAR Tahoma[] = {'T','a','h','o','m','a','\0'};
1064
1065 TRACE("(%p)\n", nativeFamily);
1066
1067 if (nativeFamily == NULL) return InvalidParameter;
1068
1069 stat = GdipCreateFontFamilyFromName(MicrosoftSansSerif, NULL, nativeFamily);
1070
1071 if (stat == FontFamilyNotFound)
1072 /* FIXME: Microsoft Sans Serif is not installed on Wine. */
1073 stat = GdipCreateFontFamilyFromName(Tahoma, NULL, nativeFamily);
1074
1075 return stat;
1076 }
1077
1078 /*****************************************************************************
1079 * GdipGetGenericFontFamilySansSerif [GDIPLUS.@]
1080 */
1081 GpStatus WINGDIPAPI GdipNewPrivateFontCollection(GpFontCollection** fontCollection)
1082 {
1083 TRACE("%p\n", fontCollection);
1084
1085 if (!fontCollection)
1086 return InvalidParameter;
1087
1088 *fontCollection = heap_alloc_zero(sizeof(GpFontCollection));
1089 if (!*fontCollection) return OutOfMemory;
1090
1091 (*fontCollection)->FontFamilies = NULL;
1092 (*fontCollection)->count = 0;
1093 (*fontCollection)->allocated = 0;
1094
1095 TRACE("<-- %p\n", *fontCollection);
1096
1097 return Ok;
1098 }
1099
1100 /*****************************************************************************
1101 * GdipDeletePrivateFontCollection [GDIPLUS.@]
1102 */
1103 GpStatus WINGDIPAPI GdipDeletePrivateFontCollection(GpFontCollection **fontCollection)
1104 {
1105 INT i;
1106
1107 TRACE("%p\n", fontCollection);
1108
1109 if (!fontCollection)
1110 return InvalidParameter;
1111
1112 for (i = 0; i < (*fontCollection)->count; i++) heap_free((*fontCollection)->FontFamilies[i]);
1113 heap_free(*fontCollection);
1114
1115 return Ok;
1116 }
1117
1118 /*****************************************************************************
1119 * GdipPrivateAddFontFile [GDIPLUS.@]
1120 */
1121 GpStatus WINGDIPAPI GdipPrivateAddFontFile(GpFontCollection *collection, GDIPCONST WCHAR *name)
1122 {
1123 HANDLE file, mapping;
1124 LARGE_INTEGER size;
1125 void *mem;
1126 GpStatus status;
1127
1128 TRACE("%p, %s\n", collection, debugstr_w(name));
1129
1130 if (!collection || !name) return InvalidParameter;
1131
1132 file = CreateFileW(name, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
1133 if (file == INVALID_HANDLE_VALUE) return InvalidParameter;
1134
1135 if (!GetFileSizeEx(file, &size) || size.u.HighPart)
1136 {
1137 CloseHandle(file);
1138 return InvalidParameter;
1139 }
1140
1141 mapping = CreateFileMappingW(file, NULL, PAGE_READONLY, 0, 0, NULL);
1142 CloseHandle(file);
1143 if (!mapping) return InvalidParameter;
1144
1145 mem = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
1146 CloseHandle(mapping);
1147 if (!mem) return InvalidParameter;
1148
1149 /* GdipPrivateAddMemoryFont creates a copy of the memory block */
1150 status = GdipPrivateAddMemoryFont(collection, mem, size.u.LowPart);
1151 UnmapViewOfFile(mem);
1152
1153 return status;
1154 }
1155
1156 #define TT_PLATFORM_APPLE_UNICODE 0
1157 #define TT_PLATFORM_MACINTOSH 1
1158 #define TT_PLATFORM_MICROSOFT 3
1159
1160 #define TT_APPLE_ID_DEFAULT 0
1161 #define TT_APPLE_ID_ISO_10646 2
1162 #define TT_APPLE_ID_UNICODE_2_0 3
1163
1164 #define TT_MS_ID_SYMBOL_CS 0
1165 #define TT_MS_ID_UNICODE_CS 1
1166
1167 #define TT_MAC_ID_SIMPLIFIED_CHINESE 25
1168
1169 #define NAME_ID_FULL_FONT_NAME 4
1170
1171 typedef struct {
1172 USHORT major_version;
1173 USHORT minor_version;
1174 USHORT tables_no;
1175 USHORT search_range;
1176 USHORT entry_selector;
1177 USHORT range_shift;
1178 } tt_header;
1179
1180 typedef struct {
1181 char tag[4]; /* table name */
1182 ULONG check_sum; /* Check sum */
1183 ULONG offset; /* Offset from beginning of file */
1184 ULONG length; /* length of the table in bytes */
1185 } tt_table_directory;
1186
1187 typedef struct {
1188 USHORT format; /* format selector. Always 0 */
1189 USHORT count; /* Name Records count */
1190 USHORT string_offset; /* Offset for strings storage, * from start of the table */
1191 } tt_name_table;
1192
1193 typedef struct {
1194 USHORT platform_id;
1195 USHORT encoding_id;
1196 USHORT language_id;
1197 USHORT name_id;
1198 USHORT length;
1199 USHORT offset; /* from start of storage area */
1200 } tt_name_record;
1201
1202 /* Copied from gdi32/freetype.c */
1203
1204 static const LANGID mac_langid_table[] =
1205 {
1206 MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ENGLISH */
1207 MAKELANGID(LANG_FRENCH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FRENCH */
1208 MAKELANGID(LANG_GERMAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GERMAN */
1209 MAKELANGID(LANG_ITALIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ITALIAN */
1210 MAKELANGID(LANG_DUTCH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_DUTCH */
1211 MAKELANGID(LANG_SWEDISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SWEDISH */
1212 MAKELANGID(LANG_SPANISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SPANISH */
1213 MAKELANGID(LANG_DANISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_DANISH */
1214 MAKELANGID(LANG_PORTUGUESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_PORTUGUESE */
1215 MAKELANGID(LANG_NORWEGIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_NORWEGIAN */
1216 MAKELANGID(LANG_HEBREW,SUBLANG_DEFAULT), /* TT_MAC_LANGID_HEBREW */
1217 MAKELANGID(LANG_JAPANESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_JAPANESE */
1218 MAKELANGID(LANG_ARABIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ARABIC */
1219 MAKELANGID(LANG_FINNISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FINNISH */
1220 MAKELANGID(LANG_GREEK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GREEK */
1221 MAKELANGID(LANG_ICELANDIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ICELANDIC */
1222 MAKELANGID(LANG_MALTESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALTESE */
1223 MAKELANGID(LANG_TURKISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TURKISH */
1224 MAKELANGID(LANG_CROATIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CROATIAN */
1225 MAKELANGID(LANG_CHINESE_TRADITIONAL,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CHINESE_TRADITIONAL */
1226 MAKELANGID(LANG_URDU,SUBLANG_DEFAULT), /* TT_MAC_LANGID_URDU */
1227 MAKELANGID(LANG_HINDI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_HINDI */
1228 MAKELANGID(LANG_THAI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_THAI */
1229 MAKELANGID(LANG_KOREAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KOREAN */
1230 MAKELANGID(LANG_LITHUANIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_LITHUANIAN */
1231 MAKELANGID(LANG_POLISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_POLISH */
1232 MAKELANGID(LANG_HUNGARIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_HUNGARIAN */
1233 MAKELANGID(LANG_ESTONIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ESTONIAN */
1234 MAKELANGID(LANG_LATVIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_LETTISH */
1235 MAKELANGID(LANG_SAMI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SAAMISK */
1236 MAKELANGID(LANG_FAEROESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FAEROESE */
1237 MAKELANGID(LANG_FARSI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FARSI */
1238 MAKELANGID(LANG_RUSSIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_RUSSIAN */
1239 MAKELANGID(LANG_CHINESE_SIMPLIFIED,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CHINESE_SIMPLIFIED */
1240 MAKELANGID(LANG_DUTCH,SUBLANG_DUTCH_BELGIAN), /* TT_MAC_LANGID_FLEMISH */
1241 MAKELANGID(LANG_IRISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_IRISH */
1242 MAKELANGID(LANG_ALBANIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ALBANIAN */
1243 MAKELANGID(LANG_ROMANIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ROMANIAN */
1244 MAKELANGID(LANG_CZECH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CZECH */
1245 MAKELANGID(LANG_SLOVAK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SLOVAK */
1246 MAKELANGID(LANG_SLOVENIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SLOVENIAN */
1247 0, /* TT_MAC_LANGID_YIDDISH */
1248 MAKELANGID(LANG_SERBIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SERBIAN */
1249 MAKELANGID(LANG_MACEDONIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MACEDONIAN */
1250 MAKELANGID(LANG_BULGARIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BULGARIAN */
1251 MAKELANGID(LANG_UKRAINIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_UKRAINIAN */
1252 MAKELANGID(LANG_BELARUSIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BYELORUSSIAN */
1253 MAKELANGID(LANG_UZBEK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_UZBEK */
1254 MAKELANGID(LANG_KAZAK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KAZAKH */
1255 MAKELANGID(LANG_AZERI,SUBLANG_AZERI_CYRILLIC), /* TT_MAC_LANGID_AZERBAIJANI */
1256 0, /* TT_MAC_LANGID_AZERBAIJANI_ARABIC_SCRIPT */
1257 MAKELANGID(LANG_ARMENIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ARMENIAN */
1258 MAKELANGID(LANG_GEORGIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GEORGIAN */
1259 0, /* TT_MAC_LANGID_MOLDAVIAN */
1260 MAKELANGID(LANG_KYRGYZ,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KIRGHIZ */
1261 MAKELANGID(LANG_TAJIK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TAJIKI */
1262 MAKELANGID(LANG_TURKMEN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TURKMEN */
1263 MAKELANGID(LANG_MONGOLIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MONGOLIAN */
1264 MAKELANGID(LANG_MONGOLIAN,SUBLANG_MONGOLIAN_CYRILLIC_MONGOLIA), /* TT_MAC_LANGID_MONGOLIAN_CYRILLIC_SCRIPT */
1265 MAKELANGID(LANG_PASHTO,SUBLANG_DEFAULT), /* TT_MAC_LANGID_PASHTO */
1266 0, /* TT_MAC_LANGID_KURDISH */
1267 MAKELANGID(LANG_KASHMIRI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KASHMIRI */
1268 MAKELANGID(LANG_SINDHI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SINDHI */
1269 MAKELANGID(LANG_TIBETAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TIBETAN */
1270 MAKELANGID(LANG_NEPALI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_NEPALI */
1271 MAKELANGID(LANG_SANSKRIT,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SANSKRIT */
1272 MAKELANGID(LANG_MARATHI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MARATHI */
1273 MAKELANGID(LANG_BENGALI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BENGALI */
1274 MAKELANGID(LANG_ASSAMESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ASSAMESE */
1275 MAKELANGID(LANG_GUJARATI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GUJARATI */
1276 MAKELANGID(LANG_PUNJABI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_PUNJABI */
1277 MAKELANGID(LANG_ORIYA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ORIYA */
1278 MAKELANGID(LANG_MALAYALAM,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALAYALAM */
1279 MAKELANGID(LANG_KANNADA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KANNADA */
1280 MAKELANGID(LANG_TAMIL,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TAMIL */
1281 MAKELANGID(LANG_TELUGU,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TELUGU */
1282 MAKELANGID(LANG_SINHALESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SINHALESE */
1283 0, /* TT_MAC_LANGID_BURMESE */
1284 MAKELANGID(LANG_KHMER,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KHMER */
1285 MAKELANGID(LANG_LAO,SUBLANG_DEFAULT), /* TT_MAC_LANGID_LAO */
1286 MAKELANGID(LANG_VIETNAMESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_VIETNAMESE */
1287 MAKELANGID(LANG_INDONESIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_INDONESIAN */
1288 0, /* TT_MAC_LANGID_TAGALOG */
1289 MAKELANGID(LANG_MALAY,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALAY_ROMAN_SCRIPT */
1290 0, /* TT_MAC_LANGID_MALAY_ARABIC_SCRIPT */
1291 MAKELANGID(LANG_AMHARIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_AMHARIC */
1292 MAKELANGID(LANG_TIGRIGNA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TIGRINYA */
1293 0, /* TT_MAC_LANGID_GALLA */
1294 0, /* TT_MAC_LANGID_SOMALI */
1295 MAKELANGID(LANG_SWAHILI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SWAHILI */
1296 0, /* TT_MAC_LANGID_RUANDA */
1297 0, /* TT_MAC_LANGID_RUNDI */
1298 0, /* TT_MAC_LANGID_CHEWA */
1299 MAKELANGID(LANG_MALAGASY,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALAGASY */
1300 MAKELANGID(LANG_ESPERANTO,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ESPERANTO */
1301 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 95-111 */
1302 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 112-127 */
1303 MAKELANGID(LANG_WELSH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_WELSH */
1304 MAKELANGID(LANG_BASQUE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BASQUE */
1305 MAKELANGID(LANG_CATALAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CATALAN */
1306 0, /* TT_MAC_LANGID_LATIN */
1307 MAKELANGID(LANG_QUECHUA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_QUECHUA */
1308 0, /* TT_MAC_LANGID_GUARANI */
1309 0, /* TT_MAC_LANGID_AYMARA */
1310 MAKELANGID(LANG_TATAR,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TATAR */
1311 MAKELANGID(LANG_UIGHUR,SUBLANG_DEFAULT), /* TT_MAC_LANGID_UIGHUR */
1312 0, /* TT_MAC_LANGID_DZONGKHA */
1313 0, /* TT_MAC_LANGID_JAVANESE */
1314 0, /* TT_MAC_LANGID_SUNDANESE */
1315 MAKELANGID(LANG_GALICIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GALICIAN */
1316 MAKELANGID(LANG_AFRIKAANS,SUBLANG_DEFAULT), /* TT_MAC_LANGID_AFRIKAANS */
1317 MAKELANGID(LANG_BRETON,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BRETON */
1318 MAKELANGID(LANG_INUKTITUT,SUBLANG_DEFAULT), /* TT_MAC_LANGID_INUKTITUT */
1319 MAKELANGID(LANG_SCOTTISH_GAELIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SCOTTISH_GAELIC */
1320 MAKELANGID(LANG_MANX_GAELIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MANX_GAELIC */
1321 MAKELANGID(LANG_IRISH,SUBLANG_IRISH_IRELAND), /* TT_MAC_LANGID_IRISH_GAELIC */
1322 0, /* TT_MAC_LANGID_TONGAN */
1323 0, /* TT_MAC_LANGID_GREEK_POLYTONIC */
1324 MAKELANGID(LANG_GREENLANDIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GREELANDIC */
1325 MAKELANGID(LANG_AZERI,SUBLANG_AZERI_LATIN), /* TT_MAC_LANGID_AZERBAIJANI_ROMAN_SCRIPT */
1326 };
1327
1328 static inline WORD get_mac_code_page( const tt_name_record *name )
1329 {
1330 WORD encoding_id = GET_BE_WORD(name->encoding_id);
1331 if (encoding_id == TT_MAC_ID_SIMPLIFIED_CHINESE) return 10008; /* special case */
1332 return 10000 + encoding_id;
1333 }
1334
1335 static int match_name_table_language( const tt_name_record *name, LANGID lang )
1336 {
1337 LANGID name_lang;
1338
1339 switch (GET_BE_WORD(name->platform_id))
1340 {
1341 case TT_PLATFORM_MICROSOFT:
1342 switch (GET_BE_WORD(name->encoding_id))
1343 {
1344 case TT_MS_ID_UNICODE_CS:
1345 case TT_MS_ID_SYMBOL_CS:
1346 name_lang = GET_BE_WORD(name->language_id);
1347 break;
1348 default:
1349 return 0;
1350 }
1351 break;
1352 case TT_PLATFORM_MACINTOSH:
1353 if (!IsValidCodePage( get_mac_code_page( name ))) return 0;
1354 name_lang = GET_BE_WORD(name->language_id);
1355 if (name_lang >= sizeof(mac_langid_table)/sizeof(mac_langid_table[0])) return 0;
1356 name_lang = mac_langid_table[name_lang];
1357 break;
1358 case TT_PLATFORM_APPLE_UNICODE:
1359 switch (GET_BE_WORD(name->encoding_id))
1360 {
1361 case TT_APPLE_ID_DEFAULT:
1362 case TT_APPLE_ID_ISO_10646:
1363 case TT_APPLE_ID_UNICODE_2_0:
1364 name_lang = GET_BE_WORD(name->language_id);
1365 if (name_lang >= sizeof(mac_langid_table)/sizeof(mac_langid_table[0])) return 0;
1366 name_lang = mac_langid_table[name_lang];
1367 break;
1368 default:
1369 return 0;
1370 }
1371 break;
1372 default:
1373 return 0;
1374 }
1375 if (name_lang == lang) return 3;
1376 if (PRIMARYLANGID( name_lang ) == PRIMARYLANGID( lang )) return 2;
1377 if (name_lang == MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT )) return 1;
1378 return 0;
1379 }
1380
1381 static WCHAR *copy_name_table_string( const tt_name_record *name, const BYTE *data, WCHAR *ret, DWORD len )
1382 {
1383 WORD name_len = GET_BE_WORD(name->length);
1384 WORD codepage;
1385
1386 switch (GET_BE_WORD(name->platform_id))
1387 {
1388 case TT_PLATFORM_APPLE_UNICODE:
1389 case TT_PLATFORM_MICROSOFT:
1390 if (name_len >= len*sizeof(WCHAR))
1391 return NULL;
1392 for (len = 0; len < name_len / 2; len++)
1393 ret[len] = (data[len * 2] << 8) | data[len * 2 + 1];
1394 ret[len] = 0;
1395 return ret;
1396 case TT_PLATFORM_MACINTOSH:
1397 codepage = get_mac_code_page( name );
1398 len = MultiByteToWideChar( codepage, 0, (char *)data, name_len, ret, len-1 );
1399 if (!len)
1400 return NULL;
1401 ret[len] = 0;
1402 return ret;
1403 }
1404 return NULL;
1405 }
1406
1407 static WCHAR *load_ttf_name_id( const BYTE *mem, DWORD_PTR size, DWORD id, WCHAR *ret, DWORD len )
1408 {
1409 LANGID lang = GetSystemDefaultLangID();
1410 const tt_header *header;
1411 const tt_name_table *name_table;
1412 const tt_name_record *name_record;
1413 DWORD pos, ofs, count;
1414 int i, res, best_lang = 0, best_index = -1;
1415
1416 if (sizeof(tt_header) > size)
1417 return NULL;
1418 header = (const tt_header*)mem;
1419 count = GET_BE_WORD(header->tables_no);
1420
1421 if (GET_BE_WORD(header->major_version) != 1 || GET_BE_WORD(header->minor_version) != 0)
1422 return NULL;
1423
1424 pos = sizeof(*header);
1425 for (i = 0; i < count; i++)
1426 {
1427 const tt_table_directory *table_directory = (const tt_table_directory*)&mem[pos];
1428 pos += sizeof(*table_directory);
1429 if (memcmp(table_directory->tag, "name", 4) == 0)
1430 {
1431 ofs = GET_BE_DWORD(table_directory->offset);
1432 break;
1433 }
1434 }
1435 if (i >= count)
1436 return NULL;
1437
1438 if (ofs >= size)
1439 return NULL;
1440 pos = ofs + sizeof(*name_table);
1441 if (pos > size)
1442 return NULL;
1443 name_table = (const tt_name_table*)&mem[ofs];
1444 count = GET_BE_WORD(name_table->count);
1445 if (GET_BE_WORD(name_table->string_offset) >= size - ofs) return NULL;
1446 ofs += GET_BE_WORD(name_table->string_offset);
1447 for (i=0; i<count; i++)
1448 {
1449 name_record = (const tt_name_record*)&mem[pos];
1450 pos += sizeof(*name_record);
1451 if (pos > size)
1452 return NULL;
1453
1454 if (GET_BE_WORD(name_record->name_id) != id) continue;
1455 if (GET_BE_WORD(name_record->offset) >= size - ofs) return NULL;
1456 if (GET_BE_WORD(name_record->length) > size - ofs - GET_BE_WORD(name_record->offset)) return NULL;
1457
1458 res = match_name_table_language( name_record, lang );
1459 if (res > best_lang)
1460 {
1461 best_lang = res;
1462 best_index = i;
1463 }
1464 }
1465
1466 if (best_lang)
1467 {
1468 name_record = (const tt_name_record*)(name_table + 1) + best_index;
1469 ret = copy_name_table_string( name_record, mem+ofs+GET_BE_WORD(name_record->offset), ret, len );
1470 TRACE( "name %u found platform %u lang %04x %s\n", GET_BE_WORD(name_record->name_id),
1471 GET_BE_WORD(name_record->platform_id), GET_BE_WORD(name_record->language_id), debugstr_w( ret ));
1472 return ret;
1473 }
1474 return NULL;
1475 }
1476
1477 static INT CALLBACK add_font_proc(const LOGFONTW *lfw, const TEXTMETRICW *ntm, DWORD type, LPARAM lParam);
1478
1479 /*****************************************************************************
1480 * GdipPrivateAddMemoryFont [GDIPLUS.@]
1481 */
1482 GpStatus WINGDIPAPI GdipPrivateAddMemoryFont(GpFontCollection* fontCollection,
1483 GDIPCONST void* memory, INT length)
1484 {
1485 WCHAR buf[32], *name;
1486 DWORD count = 0;
1487 HANDLE font;
1488 TRACE("%p, %p, %d\n", fontCollection, memory, length);
1489
1490 if (!fontCollection || !memory || !length)
1491 return InvalidParameter;
1492
1493 name = load_ttf_name_id(memory, length, NAME_ID_FULL_FONT_NAME, buf, sizeof(buf)/sizeof(*buf));
1494 if (!name)
1495 return OutOfMemory;
1496
1497 font = AddFontMemResourceEx((void*)memory, length, NULL, &count);
1498 TRACE("%s: %p/%u\n", debugstr_w(name), font, count);
1499 if (!font || !count)
1500 return InvalidParameter;
1501
1502 if (count)
1503 {
1504 HDC hdc;
1505 LOGFONTW lfw;
1506
1507 hdc = CreateCompatibleDC(0);
1508
1509 lfw.lfCharSet = DEFAULT_CHARSET;
1510 lstrcpyW(lfw.lfFaceName, name);
1511 lfw.lfPitchAndFamily = 0;
1512
1513 if (!EnumFontFamiliesExW(hdc, &lfw, add_font_proc, (LPARAM)fontCollection, 0))
1514 {
1515 DeleteDC(hdc);
1516 return OutOfMemory;
1517 }
1518
1519 DeleteDC(hdc);
1520 }
1521 return Ok;
1522 }
1523
1524 /*****************************************************************************
1525 * GdipGetFontCollectionFamilyCount [GDIPLUS.@]
1526 */
1527 GpStatus WINGDIPAPI GdipGetFontCollectionFamilyCount(
1528 GpFontCollection* fontCollection, INT* numFound)
1529 {
1530 TRACE("%p, %p\n", fontCollection, numFound);
1531
1532 if (!(fontCollection && numFound))
1533 return InvalidParameter;
1534
1535 *numFound = fontCollection->count;
1536 return Ok;
1537 }
1538
1539 /*****************************************************************************
1540 * GdipGetFontCollectionFamilyList [GDIPLUS.@]
1541 */
1542 GpStatus WINGDIPAPI GdipGetFontCollectionFamilyList(
1543 GpFontCollection* fontCollection, INT numSought,
1544 GpFontFamily* gpfamilies[], INT* numFound)
1545 {
1546 INT i;
1547 GpStatus stat=Ok;
1548
1549 TRACE("%p, %d, %p, %p\n", fontCollection, numSought, gpfamilies, numFound);
1550
1551 if (!(fontCollection && gpfamilies && numFound))
1552 return InvalidParameter;
1553
1554 memset(gpfamilies, 0, sizeof(*gpfamilies) * numSought);
1555
1556 for (i = 0; i < numSought && i < fontCollection->count && stat == Ok; i++)
1557 {
1558 stat = GdipCloneFontFamily(fontCollection->FontFamilies[i], &gpfamilies[i]);
1559 }
1560
1561 if (stat == Ok)
1562 *numFound = i;
1563 else
1564 {
1565 int numToFree=i;
1566 for (i=0; i<numToFree; i++)
1567 {
1568 GdipDeleteFontFamily(gpfamilies[i]);
1569 gpfamilies[i] = NULL;
1570 }
1571 }
1572
1573 return stat;
1574 }
1575
1576 void free_installed_fonts(void)
1577 {
1578 while (installedFontCollection.count)
1579 GdipDeleteFontFamily(installedFontCollection.FontFamilies[--installedFontCollection.count]);
1580 heap_free(installedFontCollection.FontFamilies);
1581 installedFontCollection.FontFamilies = NULL;
1582 installedFontCollection.allocated = 0;
1583 }
1584
1585 static INT CALLBACK add_font_proc(const LOGFONTW *lfw, const TEXTMETRICW *ntm,
1586 DWORD type, LPARAM lParam)
1587 {
1588 GpFontCollection* fonts = (GpFontCollection*)lParam;
1589 GpFontFamily* family;
1590 int i;
1591
1592 if (type == RASTER_FONTTYPE)
1593 return 1;
1594
1595 /* skip rotated fonts */
1596 if (lfw->lfFaceName[0] == '@')
1597 return 1;
1598
1599 if (fonts->count && strcmpiW(lfw->lfFaceName, fonts->FontFamilies[fonts->count-1]->FamilyName) == 0)
1600 return 1;
1601
1602 if (fonts->allocated == fonts->count)
1603 {
1604 INT new_alloc_count = fonts->allocated+50;
1605 GpFontFamily** new_family_list = heap_alloc(new_alloc_count*sizeof(void*));
1606
1607 if (!new_family_list)
1608 return 0;
1609
1610 memcpy(new_family_list, fonts->FontFamilies, fonts->count*sizeof(void*));
1611 heap_free(fonts->FontFamilies);
1612 fonts->FontFamilies = new_family_list;
1613 fonts->allocated = new_alloc_count;
1614 }
1615
1616 if (GdipCreateFontFamilyFromName(lfw->lfFaceName, NULL, &family) != Ok)
1617 return 0;
1618
1619 /* skip duplicates */
1620 for (i=0; i<fonts->count; i++)
1621 {
1622 if (strcmpiW(family->FamilyName, fonts->FontFamilies[i]->FamilyName) == 0)
1623 {
1624 GdipDeleteFontFamily(family);
1625 return 1;
1626 }
1627 }
1628
1629 fonts->FontFamilies[fonts->count++] = family;
1630
1631 return 1;
1632 }
1633
1634 GpStatus WINGDIPAPI GdipNewInstalledFontCollection(
1635 GpFontCollection** fontCollection)
1636 {
1637 TRACE("(%p)\n",fontCollection);
1638
1639 if (!fontCollection)
1640 return InvalidParameter;
1641
1642 if (installedFontCollection.count == 0)
1643 {
1644 HDC hdc;
1645 LOGFONTW lfw;
1646
1647 hdc = CreateCompatibleDC(0);
1648
1649 lfw.lfCharSet = DEFAULT_CHARSET;
1650 lfw.lfFaceName[0] = 0;
1651 lfw.lfPitchAndFamily = 0;
1652
1653 if (!EnumFontFamiliesExW(hdc, &lfw, add_font_proc, (LPARAM)&installedFontCollection, 0))
1654 {
1655 free_installed_fonts();
1656 DeleteDC(hdc);
1657 return OutOfMemory;
1658 }
1659
1660 DeleteDC(hdc);
1661 }
1662
1663 *fontCollection = &installedFontCollection;
1664
1665 return Ok;
1666 }