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 ) 1382 { 1383 WORD name_len = GET_BE_WORD(name->length); 1384 WORD codepage; 1385 WCHAR *ret; 1386 int len; 1387 1388 switch (GET_BE_WORD(name->platform_id)) 1389 { 1390 case TT_PLATFORM_APPLE_UNICODE: 1391 case TT_PLATFORM_MICROSOFT: 1392 ret = heap_alloc((name_len / 2 + 1) * sizeof(WCHAR)); 1393 for (len = 0; len < name_len / 2; len++) 1394 ret[len] = (data[len * 2] << 8) | data[len * 2 + 1]; 1395 ret[len] = 0; 1396 return ret; 1397 case TT_PLATFORM_MACINTOSH: 1398 codepage = get_mac_code_page( name ); 1399 len = MultiByteToWideChar( codepage, 0, (char *)data, name_len, NULL, 0 ) + 1; 1400 if (!len) 1401 return NULL; 1402 ret = heap_alloc(len * sizeof(WCHAR)); 1403 len = MultiByteToWideChar( codepage, 0, (char *)data, name_len, ret, len - 1 ); 1404 ret[len] = 0; 1405 return ret; 1406 } 1407 return NULL; 1408 } 1409 1410 static WCHAR *load_ttf_name_id( const BYTE *mem, DWORD_PTR size, DWORD id ) 1411 { 1412 LANGID lang = GetSystemDefaultLangID(); 1413 const tt_header *header; 1414 const tt_name_table *name_table; 1415 const tt_name_record *name_record; 1416 DWORD pos, ofs, count; 1417 int i, res, best_lang = 0, best_index = -1; 1418 1419 if (sizeof(tt_header) > size) 1420 return NULL; 1421 header = (const tt_header*)mem; 1422 count = GET_BE_WORD(header->tables_no); 1423 1424 if (GET_BE_WORD(header->major_version) != 1 || GET_BE_WORD(header->minor_version) != 0) 1425 return NULL; 1426 1427 pos = sizeof(*header); 1428 for (i = 0; i < count; i++) 1429 { 1430 const tt_table_directory *table_directory = (const tt_table_directory*)&mem[pos]; 1431 pos += sizeof(*table_directory); 1432 if (memcmp(table_directory->tag, "name", 4) == 0) 1433 { 1434 ofs = GET_BE_DWORD(table_directory->offset); 1435 break; 1436 } 1437 } 1438 if (i >= count) 1439 return NULL; 1440 1441 if (ofs >= size) 1442 return NULL; 1443 pos = ofs + sizeof(*name_table); 1444 if (pos > size) 1445 return NULL; 1446 name_table = (const tt_name_table*)&mem[ofs]; 1447 count = GET_BE_WORD(name_table->count); 1448 if (GET_BE_WORD(name_table->string_offset) >= size - ofs) return NULL; 1449 ofs += GET_BE_WORD(name_table->string_offset); 1450 for (i=0; i<count; i++) 1451 { 1452 name_record = (const tt_name_record*)&mem[pos]; 1453 pos += sizeof(*name_record); 1454 if (pos > size) 1455 return NULL; 1456 1457 if (GET_BE_WORD(name_record->name_id) != id) continue; 1458 if (GET_BE_WORD(name_record->offset) >= size - ofs) return NULL; 1459 if (GET_BE_WORD(name_record->length) > size - ofs - GET_BE_WORD(name_record->offset)) return NULL; 1460 1461 res = match_name_table_language( name_record, lang ); 1462 if (res > best_lang) 1463 { 1464 best_lang = res; 1465 best_index = i; 1466 } 1467 } 1468 1469 if (best_lang) 1470 { 1471 WCHAR *ret; 1472 name_record = (const tt_name_record*)(name_table + 1) + best_index; 1473 ret = copy_name_table_string( name_record, mem+ofs+GET_BE_WORD(name_record->offset) ); 1474 TRACE( "name %u found platform %u lang %04x %s\n", GET_BE_WORD(name_record->name_id), 1475 GET_BE_WORD(name_record->platform_id), GET_BE_WORD(name_record->language_id), debugstr_w( ret )); 1476 return ret; 1477 } 1478 return NULL; 1479 } 1480 1481 static INT CALLBACK add_font_proc(const LOGFONTW *lfw, const TEXTMETRICW *ntm, DWORD type, LPARAM lParam); 1482 1483 /***************************************************************************** 1484 * GdipPrivateAddMemoryFont [GDIPLUS.@] 1485 */ 1486 GpStatus WINGDIPAPI GdipPrivateAddMemoryFont(GpFontCollection* fontCollection, 1487 GDIPCONST void* memory, INT length) 1488 { 1489 WCHAR *name; 1490 DWORD count = 0; 1491 HANDLE font; 1492 GpStatus ret = Ok; 1493 TRACE("%p, %p, %d\n", fontCollection, memory, length); 1494 1495 if (!fontCollection || !memory || !length) 1496 return InvalidParameter; 1497 1498 name = load_ttf_name_id(memory, length, NAME_ID_FULL_FONT_NAME); 1499 if (!name) 1500 return OutOfMemory; 1501 1502 font = AddFontMemResourceEx((void*)memory, length, NULL, &count); 1503 TRACE("%s: %p/%u\n", debugstr_w(name), font, count); 1504 if (!font || !count) 1505 ret = InvalidParameter; 1506 else 1507 { 1508 HDC hdc; 1509 LOGFONTW lfw; 1510 1511 hdc = CreateCompatibleDC(0); 1512 1513 /* Truncate name if necessary, GDI32 can't deal with long names */ 1514 if(lstrlenW(name) > LF_FACESIZE - 1) 1515 name[LF_FACESIZE - 1] = 0; 1516 1517 lfw.lfCharSet = DEFAULT_CHARSET; 1518 lstrcpyW(lfw.lfFaceName, name); 1519 lfw.lfPitchAndFamily = 0; 1520 1521 if (!EnumFontFamiliesExW(hdc, &lfw, add_font_proc, (LPARAM)fontCollection, 0)) 1522 ret = OutOfMemory; 1523 1524 DeleteDC(hdc); 1525 } 1526 heap_free(name); 1527 return ret; 1528 } 1529 1530 /***************************************************************************** 1531 * GdipGetFontCollectionFamilyCount [GDIPLUS.@] 1532 */ 1533 GpStatus WINGDIPAPI GdipGetFontCollectionFamilyCount( 1534 GpFontCollection* fontCollection, INT* numFound) 1535 { 1536 TRACE("%p, %p\n", fontCollection, numFound); 1537 1538 if (!(fontCollection && numFound)) 1539 return InvalidParameter; 1540 1541 *numFound = fontCollection->count; 1542 return Ok; 1543 } 1544 1545 /***************************************************************************** 1546 * GdipGetFontCollectionFamilyList [GDIPLUS.@] 1547 */ 1548 GpStatus WINGDIPAPI GdipGetFontCollectionFamilyList( 1549 GpFontCollection* fontCollection, INT numSought, 1550 GpFontFamily* gpfamilies[], INT* numFound) 1551 { 1552 INT i; 1553 GpStatus stat=Ok; 1554 1555 TRACE("%p, %d, %p, %p\n", fontCollection, numSought, gpfamilies, numFound); 1556 1557 if (!(fontCollection && gpfamilies && numFound)) 1558 return InvalidParameter; 1559 1560 memset(gpfamilies, 0, sizeof(*gpfamilies) * numSought); 1561 1562 for (i = 0; i < numSought && i < fontCollection->count && stat == Ok; i++) 1563 { 1564 stat = GdipCloneFontFamily(fontCollection->FontFamilies[i], &gpfamilies[i]); 1565 } 1566 1567 if (stat == Ok) 1568 *numFound = i; 1569 else 1570 { 1571 int numToFree=i; 1572 for (i=0; i<numToFree; i++) 1573 { 1574 GdipDeleteFontFamily(gpfamilies[i]); 1575 gpfamilies[i] = NULL; 1576 } 1577 } 1578 1579 return stat; 1580 } 1581 1582 void free_installed_fonts(void) 1583 { 1584 while (installedFontCollection.count) 1585 GdipDeleteFontFamily(installedFontCollection.FontFamilies[--installedFontCollection.count]); 1586 heap_free(installedFontCollection.FontFamilies); 1587 installedFontCollection.FontFamilies = NULL; 1588 installedFontCollection.allocated = 0; 1589 } 1590 1591 static INT CALLBACK add_font_proc(const LOGFONTW *lfw, const TEXTMETRICW *ntm, 1592 DWORD type, LPARAM lParam) 1593 { 1594 GpFontCollection* fonts = (GpFontCollection*)lParam; 1595 GpFontFamily* family; 1596 int i; 1597 1598 if (type == RASTER_FONTTYPE) 1599 return 1; 1600 1601 /* skip rotated fonts */ 1602 if (lfw->lfFaceName[0] == '@') 1603 return 1; 1604 1605 if (fonts->count && strcmpiW(lfw->lfFaceName, fonts->FontFamilies[fonts->count-1]->FamilyName) == 0) 1606 return 1; 1607 1608 if (fonts->allocated == fonts->count) 1609 { 1610 INT new_alloc_count = fonts->allocated+50; 1611 GpFontFamily** new_family_list = heap_alloc(new_alloc_count*sizeof(void*)); 1612 1613 if (!new_family_list) 1614 return 0; 1615 1616 memcpy(new_family_list, fonts->FontFamilies, fonts->count*sizeof(void*)); 1617 heap_free(fonts->FontFamilies); 1618 fonts->FontFamilies = new_family_list; 1619 fonts->allocated = new_alloc_count; 1620 } 1621 1622 if (GdipCreateFontFamilyFromName(lfw->lfFaceName, NULL, &family) != Ok) 1623 return 0; 1624 1625 /* skip duplicates */ 1626 for (i=0; i<fonts->count; i++) 1627 { 1628 if (strcmpiW(family->FamilyName, fonts->FontFamilies[i]->FamilyName) == 0) 1629 { 1630 GdipDeleteFontFamily(family); 1631 return 1; 1632 } 1633 } 1634 1635 fonts->FontFamilies[fonts->count++] = family; 1636 1637 return 1; 1638 } 1639 1640 GpStatus WINGDIPAPI GdipNewInstalledFontCollection( 1641 GpFontCollection** fontCollection) 1642 { 1643 TRACE("(%p)\n",fontCollection); 1644 1645 if (!fontCollection) 1646 return InvalidParameter; 1647 1648 if (installedFontCollection.count == 0) 1649 { 1650 HDC hdc; 1651 LOGFONTW lfw; 1652 1653 hdc = CreateCompatibleDC(0); 1654 1655 lfw.lfCharSet = DEFAULT_CHARSET; 1656 lfw.lfFaceName[0] = 0; 1657 lfw.lfPitchAndFamily = 0; 1658 1659 if (!EnumFontFamiliesExW(hdc, &lfw, add_font_proc, (LPARAM)&installedFontCollection, 0)) 1660 { 1661 free_installed_fonts(); 1662 DeleteDC(hdc); 1663 return OutOfMemory; 1664 } 1665 1666 DeleteDC(hdc); 1667 } 1668 1669 *fontCollection = &installedFontCollection; 1670 1671 return Ok; 1672 } 1673