1 /* 2 * PROJECT: ReactOS win32 subsystem 3 * LICENSE: GPL - See COPYING in the top level directory 4 * PURPOSE: GDI font driver for bitmap fonts 5 * PROGRAMMER: Timo Kreuzer (timo.kreuzer@reactos.org) 6 */ 7 8 #include "bmfd.h" 9 10 static 11 BOOLEAN 12 IsValidPtr( 13 PVOID p, 14 ULONG cjSize, 15 PVOID pStart, 16 PVOID pEnd, 17 ULONG cjAlign) 18 { 19 if ((ULONG_PTR)p < (ULONG_PTR)pStart || 20 (ULONG_PTR)p + cjSize >= (ULONG_PTR)pEnd || 21 (ULONG_PTR)p & (cjAlign -1)) 22 { 23 return FALSE; 24 } 25 return TRUE; 26 } 27 28 static 29 BOOL 30 FillFaceInfo( 31 PBMFD_FACE pface, 32 PFONTINFO16 pFontInfo) 33 { 34 CHAR ansi[4]; 35 WCHAR unicode[4]; 36 ULONG written; 37 DWORD dfFlags; 38 39 pface->pFontInfo = pFontInfo; 40 pface->ulVersion = GETVAL(pFontInfo->dfVersion); 41 pface->cGlyphs = pFontInfo->dfLastChar - pFontInfo->dfFirstChar + 1; 42 43 /* Convert chars to unicode */ 44 ansi[0] = pFontInfo->dfFirstChar; 45 ansi[1] = pFontInfo->dfLastChar; 46 ansi[2] = pFontInfo->dfFirstChar + pFontInfo->dfDefaultChar; 47 ansi[3] = pFontInfo->dfFirstChar + pFontInfo->dfBreakChar; 48 EngMultiByteToUnicodeN(unicode, 4 * sizeof(WCHAR), &written, ansi, 4); 49 pface->wcFirstChar = unicode[0]; 50 pface->wcLastChar = unicode[1]; 51 pface->wcDefaultChar = unicode[2]; 52 pface->wcBreakChar = unicode[3]; 53 54 /* Copy some values */ 55 pface->wPixHeight = GETVAL(pFontInfo->dfPixHeight); 56 pface->wPixWidth = GETVAL(pFontInfo->dfPixWidth); 57 pface->wWidthBytes = GETVAL(pFontInfo->dfWidthBytes); 58 pface->wAscent = GETVAL(pFontInfo->dfAscent); 59 pface->wDescent = pface->wPixHeight - pface->wAscent; 60 61 /* Some version specific members */ 62 if (pface->ulVersion >= 0x300) 63 { 64 dfFlags = GETVAL(pFontInfo->dfFlags); 65 pface->wA = GETVAL(pFontInfo->dfAspace); 66 pface->wB = GETVAL(pFontInfo->dfBspace); 67 pface->wC = GETVAL(pFontInfo->dfCspace); 68 pface->pCharTable = pFontInfo->dfCharTable; 69 pface->cjEntrySize = sizeof(GLYPHENTRY30); 70 } 71 else 72 { 73 dfFlags = DFF_1COLOR; 74 pface->wA = 0; 75 pface->wB = 0; 76 pface->wC = 0; 77 pface->pCharTable = &pFontInfo->dfReserved + 1; 78 pface->cjEntrySize = sizeof(GLYPHENTRY20); 79 } 80 81 pface->flInfo = FM_INFO_MASK; 82 83 /* If dfWidth is non-null, we have a fixed width font */ 84 if (dfFlags & DFF_FIXED || pface->wPixWidth) 85 pface->flInfo |= FM_INFO_CONSTANT_WIDTH; 86 87 /* Initialize color depth flags */ 88 if (dfFlags & DFF_1COLOR) 89 pface->flInfo |= FM_INFO_1BPP; 90 else if (dfFlags & DFF_16COLOR) 91 pface->flInfo |= FM_INFO_4BPP; 92 else if (dfFlags & DFF_256COLOR) 93 pface->flInfo |= FM_INFO_8BPP; 94 else if (dfFlags & DFF_RGBCOLOR) 95 pface->flInfo |= FM_INFO_24BPP; 96 97 // TODO: walk through all glyphs and veryfy them and calculate max values 98 99 // FIXME: After this point, the whole font data should be verified! 100 101 return TRUE; 102 } 103 104 static 105 PVOID 106 ParseFntFile( 107 PVOID pvView, 108 ULONG cjView) 109 { 110 /* unimplemented */ 111 return NULL; 112 } 113 114 115 static 116 PVOID 117 ParseFonFile( 118 PVOID pvView, 119 ULONG cjView) 120 { 121 PIMAGE_DOS_HEADER pDosHeader = pvView; 122 PIMAGE_OS2_HEADER pOs2Header; 123 PNE_RESTABLE pResTable; 124 PNE_TYPEINFO pTInfo; 125 PFONTINFO16 pFontInfo; 126 PCHAR pStart, pEnd; 127 PBMFD_FILE pfile = NULL; 128 WORD wShift; 129 ULONG i, cjOffset, cjLength; 130 ULONG type_id, count; 131 132 /* Initial margins for valid pointers */ 133 pStart = pvView; 134 pEnd = pStart + cjView; 135 136 /* Check for image dos header */ 137 if (GETVAL(pDosHeader->e_magic) != IMAGE_DOS_MAGIC) 138 { 139 return NULL; 140 } 141 142 /* Get pointer to OS2 header and veryfy it is valid */ 143 pOs2Header = (PVOID)((PCHAR)pDosHeader + GETVAL(pDosHeader->e_lfanew)); 144 pStart += sizeof(IMAGE_DOS_HEADER); 145 if (!IsValidPtr(pOs2Header, sizeof(IMAGE_OS2_HEADER), pStart, pEnd, 4)) 146 { 147 DbgPrint("e_lfanew is invalid: 0x%lx\n", pDosHeader->e_lfanew); 148 return NULL; 149 } 150 151 /* Get pointer to resource table and verify it is valid */ 152 pResTable = (PVOID)((PCHAR)pOs2Header + GETVAL(pOs2Header->ne_rsrctab)); 153 pStart = (PCHAR)pOs2Header; 154 if (!IsValidPtr(pResTable, sizeof(NE_RESTABLE), pStart, pEnd, 1)) 155 { 156 DbgPrint("pTInfo is invalid: 0x%p\n", pResTable); 157 return NULL; 158 } 159 160 wShift = GETVAL(pResTable->size_shift); 161 pTInfo = pResTable->typeinfo; 162 type_id = GETVAL(pTInfo->type_id); 163 164 /* Loop the resource table to find a font resource */ 165 while (type_id) 166 { 167 /* Get number of nameinfo entries */ 168 count = GETVAL(pTInfo->count); 169 170 /* Look for a font resource */ 171 if (type_id == NE_RSCTYPE_FONT && count > 0) 172 { 173 DbgPrint("Found NE_RSCTYPE_FONT\n"); 174 175 /* Allocate an info structure for this font and all faces */ 176 cjLength = sizeof(BMFD_FILE) + (count-1) * sizeof(BMFD_FACE); 177 pfile = EngAllocMem(0, cjLength, TAG_FONTINFO); 178 if (!pfile) 179 { 180 DbgPrint("Not enough memory: %ld\n", cjLength); 181 return NULL; 182 } 183 184 pfile->cNumFaces = count; 185 186 /* Fill all face info structures */ 187 for (i = 0; i < count; i++) 188 { 189 cjOffset = GETVAL(pTInfo->nameinfo[i].offset) << wShift; 190 cjLength = GETVAL(pTInfo->nameinfo[i].length) << wShift; 191 pFontInfo = (PVOID)((PCHAR)pDosHeader + cjOffset); 192 193 if (!IsValidPtr(pFontInfo, cjLength, pStart, pEnd, 1)) 194 { 195 DbgPrint("pFontInfo is invalid: 0x%p\n", pFontInfo); 196 EngFreeMem(pfile); 197 return NULL; 198 } 199 200 /* Validate FONTINFO and fill face info */ 201 if (!FillFaceInfo(&pfile->aface[i], pFontInfo)) 202 { 203 DbgPrint("pFontInfo is invalid: 0x%p\n", pFontInfo); 204 EngFreeMem(pfile); 205 return NULL; 206 } 207 } 208 209 /* Break out of the loop */ 210 break; 211 } 212 213 /* Following pointers must be bigger than this */ 214 pStart = (PCHAR)pTInfo; 215 216 /* Goto next entry in resource table */ 217 pTInfo = (PVOID)&pTInfo->nameinfo[count]; 218 219 /* Verify that the new pTInfo pointer is valid */ 220 if (!IsValidPtr(pTInfo, sizeof(NE_TYPEINFO), pStart, pEnd, 1)) 221 { 222 DbgPrint("pTInfo is invalid: 0x%p\n", pTInfo); 223 return NULL; 224 } 225 226 type_id = GETVAL(pTInfo->type_id); 227 } 228 229 return pfile; 230 } 231 232 /** Public Interface **********************************************************/ 233 234 ULONG_PTR 235 APIENTRY 236 BmfdLoadFontFile( 237 ULONG cFiles, 238 ULONG_PTR *piFile, 239 PVOID *ppvView, 240 ULONG *pcjView, 241 DESIGNVECTOR *pdv, 242 ULONG ulLangID, 243 ULONG ulFastCheckSum) 244 { 245 PBMFD_FILE pfile = NULL; 246 PVOID pvView; 247 ULONG cjView; 248 249 DbgPrint("BmfdLoadFontFile()\n"); 250 __debugbreak(); 251 252 /* Check parameters */ 253 if (cFiles != 1) 254 { 255 DbgPrint("Only 1 File is allowed, got %ld!\n", cFiles); 256 return HFF_INVALID; 257 } 258 259 /* Map the font file */ 260 if (!EngMapFontFileFD(*piFile, (PULONG*)&pvView, &cjView)) 261 { 262 DbgPrint("Could not map font file!\n", cFiles); 263 return HFF_INVALID; 264 } 265 266 DbgPrint("mapped font file to %p, site if %ld\n", pvView, cjView); 267 268 /* Try to parse a .fon file */ 269 pfile = ParseFonFile(pvView, cjView); 270 271 if (!pfile) 272 { 273 /* Could be a .fnt file */ 274 pfile = ParseFntFile(pvView, cjView); 275 } 276 277 /* Check whether we succeeded finding a font */ 278 if (!pfile) 279 { 280 DbgPrint("No font data found\n"); 281 282 /* Unmap the file */ 283 EngUnmapFontFileFD(*piFile); 284 285 /* Failure! */ 286 return HFF_INVALID; 287 } 288 289 pfile->iFile = *piFile; 290 pfile->pvView = pvView; 291 292 /* Success, return the pointer to font info structure */ 293 return (ULONG_PTR)pfile; 294 } 295 296 BOOL 297 APIENTRY 298 BmfdUnloadFontFile( 299 IN ULONG_PTR iFile) 300 { 301 PBMFD_FILE pfile = (PBMFD_FILE)iFile; 302 303 DbgPrint("BmfdUnloadFontFile()\n"); 304 305 /* Unmap the font file */ 306 EngUnmapFontFileFD(pfile->iFile); 307 308 /* Free the memory that was allocated for the font */ 309 EngFreeMem(pfile); 310 311 return TRUE; 312 } 313 314 315 LONG 316 APIENTRY 317 BmfdQueryFontFile( 318 ULONG_PTR iFile, 319 ULONG ulMode, 320 ULONG cjBuf, 321 ULONG *pulBuf) 322 { 323 PBMFD_FILE pfile = (PBMFD_FILE)iFile; 324 325 DbgPrint("BmfdQueryFontFile()\n"); 326 // __debugbreak(); 327 328 switch (ulMode) 329 { 330 case QFF_DESCRIPTION: 331 { 332 /* We copy the face name of the 1st face */ 333 PCHAR pDesc = pfile->aface[0].pszFaceName; 334 ULONG cOutSize; 335 if (pulBuf) 336 { 337 EngMultiByteToUnicodeN((LPWSTR)pulBuf, 338 cjBuf, 339 &cOutSize, 340 pDesc, 341 strnlen(pDesc, LF_FACESIZE)); 342 } 343 else 344 { 345 cOutSize = (strnlen(pDesc, LF_FACESIZE) + 1) * sizeof(WCHAR); 346 } 347 return cOutSize; 348 } 349 350 case QFF_NUMFACES: 351 /* return the number of faces in the file */ 352 return pfile->cNumFaces; 353 354 default: 355 return FD_ERROR; 356 } 357 } 358 359 LONG 360 APIENTRY 361 BmfdQueryFontCaps( 362 ULONG culCaps, 363 ULONG *pulCaps) 364 { 365 DbgPrint("BmfdQueryFontCaps()\n"); 366 367 /* We need room for 2 ULONGs */ 368 if (culCaps < 2) 369 { 370 return FD_ERROR; 371 } 372 373 /* We only support 1 bpp */ 374 pulCaps[0] = 2; 375 pulCaps[1] = QC_1BIT; 376 377 return 2; 378 } 379 380 381 PVOID 382 APIENTRY 383 BmfdQueryFontTree( 384 DHPDEV dhpdev, 385 ULONG_PTR iFile, 386 ULONG iFace, 387 ULONG iMode, 388 ULONG_PTR *pid) 389 { 390 PBMFD_FILE pfile = (PBMFD_FILE)iFile; 391 PBMFD_FACE pface; 392 ULONG i, j, cjSize, cGlyphs, cRuns; 393 CHAR ch, chFirst, ach[256]; 394 WCHAR wc, awc[256]; 395 PFD_GLYPHSET pGlyphSet; 396 WCRUN *pwcrun; 397 HGLYPH * phglyphs; 398 399 DbgPrint("DrvQueryFontTree(iMode=%ld)\n", iMode); 400 // __debugbreak(); 401 402 /* Check parameters, we only support QFT_GLYPHSET */ 403 if (!iFace || iFace > pfile->cNumFaces || iMode != QFT_GLYPHSET) 404 { 405 DbgPrint("iFace = %ld, cNumFaces = %ld\n", iFace, pfile->cNumFaces); 406 return NULL; 407 } 408 409 /* Get a pointer to the face data */ 410 pface = &pfile->aface[iFace - 1]; 411 412 /* Get the number of characters in the face */ 413 cGlyphs = pface->cGlyphs; 414 415 chFirst = pface->pFontInfo->dfFirstChar; 416 417 /* Build array of supported chars */ 418 for (i = 0; i < cGlyphs; i++) 419 { 420 ach[i] = chFirst + i; 421 } 422 423 /* Convert the chars to unicode */ 424 EngMultiByteToUnicodeN(awc, sizeof(awc), NULL, ach, cGlyphs); 425 426 /* Sort both arrays in wchar order */ 427 for (i = 0; i < cGlyphs - 1; i++) 428 { 429 wc = awc[i]; 430 for (j = i + 1; j < cGlyphs; j++) 431 { 432 if (awc[j] < wc) 433 { 434 awc[i] = awc[j]; 435 awc[j] = wc; 436 wc = awc[i]; 437 ch = ach[i]; 438 ach[i] = ach[j]; 439 ach[j] = ch; 440 } 441 } 442 } 443 444 /* Find number of WCRUNs */ 445 cRuns = 1; 446 for (i = 1; i < cGlyphs; i++) 447 { 448 if (awc[i] != awc[i - 1] + 1) 449 { 450 cRuns++; 451 } 452 } 453 454 /* Calculate FD_GLYPHSET size */ 455 cjSize = sizeof(FD_GLYPHSET) 456 + (cRuns - 1) * sizeof(WCRUN) 457 + cGlyphs * sizeof(HGLYPH); 458 459 /* Allocate the FD_GLYPHSET structure */ 460 pGlyphSet = EngAllocMem(0, cjSize, TAG_GLYPHSET); 461 if (!pGlyphSet) 462 { 463 return NULL; 464 } 465 466 /* Initialize FD_GLYPHSET */ 467 pGlyphSet->cjThis = cjSize; 468 pGlyphSet->flAccel = 0; 469 pGlyphSet->cGlyphsSupported = cGlyphs; 470 pGlyphSet->cRuns = cRuns; 471 472 /* Initialize 1st WCRUN */ 473 pwcrun = pGlyphSet->awcrun; 474 phglyphs = (PHGLYPH)&pGlyphSet->awcrun[cRuns]; 475 pwcrun[0].wcLow = awc[0]; 476 pwcrun[0].cGlyphs = 1; 477 pwcrun[0].phg = phglyphs; 478 phglyphs[0] = 0; 479 480 /* Walk through all supported chars */ 481 for (i = 1, j = 0; i < cGlyphs; i++) 482 { 483 /* Use offset to glyph entry as hglyph */ 484 phglyphs[i] = (ach[i] - chFirst) * pface->cjEntrySize; 485 486 /* Check whether we can append the wchar to a run */ 487 if (awc[i] == awc[i - 1] + 1) 488 { 489 /* Append to current WCRUN */ 490 pwcrun[j].cGlyphs++; 491 } 492 else 493 { 494 /* Add a new WCRUN */ 495 j++; 496 pwcrun[j].wcLow = awc[i]; 497 pwcrun[j].cGlyphs = 1; 498 pwcrun[j].phg = &phglyphs[i]; 499 } 500 } 501 502 /* Set *pid to the allocated structure for use in BmfdFree */ 503 *pid = (ULONG_PTR)pGlyphSet; 504 505 return pGlyphSet; 506 } 507 508 PIFIMETRICS 509 APIENTRY 510 BmfdQueryFont( 511 IN DHPDEV dhpdev, 512 IN ULONG_PTR iFile, 513 IN ULONG iFace, 514 IN ULONG_PTR *pid) 515 { 516 PBMFD_FILE pfile = (PBMFD_FILE)iFile; 517 PBMFD_FACE pface; 518 PFONTINFO16 pFontInfo; 519 PIFIMETRICS pifi; 520 PBMFD_IFIMETRICS pifiX; 521 PANOSE panose = {0}; 522 523 DbgPrint("BmfdQueryFont()\n"); 524 // __debugbreak(); 525 526 /* Validate parameters */ 527 if (iFace > pfile->cNumFaces || !pid) 528 { 529 return NULL; 530 } 531 532 pface = &pfile->aface[iFace - 1]; 533 pFontInfo = pface->pFontInfo; 534 535 /* Allocate the structure */ 536 pifiX = EngAllocMem(FL_ZERO_MEMORY, sizeof(BMFD_IFIMETRICS), TAG_IFIMETRICS); 537 if (!pifiX) 538 { 539 return NULL; 540 } 541 542 /* Return a pointer to free it later */ 543 *pid = (ULONG_PTR)pifiX; 544 545 /* Fill IFIMETRICS */ 546 pifi = &pifiX->ifim; 547 pifi->cjThis = sizeof(BMFD_IFIMETRICS); 548 pifi->cjIfiExtra = 0; 549 pifi->dpwszFamilyName = FIELD_OFFSET(BMFD_IFIMETRICS, wszFamilyName); 550 pifi->dpwszStyleName = FIELD_OFFSET(BMFD_IFIMETRICS, wszFamilyName); 551 pifi->dpwszFaceName = FIELD_OFFSET(BMFD_IFIMETRICS, wszFaceName); 552 pifi->dpwszUniqueName = FIELD_OFFSET(BMFD_IFIMETRICS, wszFaceName); 553 pifi->dpFontSim = 0; 554 pifi->lEmbedId = 0; 555 pifi->lItalicAngle = 0; 556 pifi->lCharBias = 0; 557 pifi->dpCharSets = 0; 558 pifi->jWinCharSet = pFontInfo->dfCharSet; 559 pifi->jWinPitchAndFamily = pFontInfo->dfPitchAndFamily; 560 pifi->usWinWeight = GETVAL(pFontInfo->dfWeight); 561 pifi->flInfo = pface->flInfo; 562 pifi->fsSelection = 0; 563 pifi->fsType = 0; 564 pifi->fwdUnitsPerEm = GETVAL(pFontInfo->dfPixHeight); 565 pifi->fwdLowestPPEm = 0; 566 pifi->fwdWinAscender = GETVAL(pFontInfo->dfAscent); 567 pifi->fwdWinDescender = pifi->fwdUnitsPerEm - pifi->fwdWinAscender; 568 pifi->fwdMacAscender = pifi->fwdWinAscender; 569 pifi->fwdMacDescender = - pifi->fwdWinDescender; 570 pifi->fwdMacLineGap = 0; 571 pifi->fwdTypoAscender = pifi->fwdWinAscender; 572 pifi->fwdTypoDescender = - pifi->fwdWinDescender; 573 pifi->fwdTypoLineGap = 0; 574 pifi->fwdAveCharWidth = GETVAL(pFontInfo->dfAvgWidth); 575 pifi->fwdMaxCharInc = GETVAL(pFontInfo->dfMaxWidth); 576 pifi->fwdCapHeight = pifi->fwdUnitsPerEm / 2; 577 pifi->fwdXHeight = pifi->fwdUnitsPerEm / 4; 578 pifi->fwdSubscriptXSize = 0; 579 pifi->fwdSubscriptYSize = 0; 580 pifi->fwdSubscriptXOffset = 0; 581 pifi->fwdSubscriptYOffset = 0; 582 pifi->fwdSuperscriptXSize = 0; 583 pifi->fwdSuperscriptYSize = 0; 584 pifi->fwdSuperscriptXOffset = 0; 585 pifi->fwdSuperscriptYOffset = 0; 586 pifi->fwdUnderscoreSize = 01; 587 pifi->fwdUnderscorePosition = -1; 588 pifi->fwdStrikeoutSize = 1; 589 pifi->fwdStrikeoutPosition = pifi->fwdXHeight + 1; 590 pifi->chFirstChar = pFontInfo->dfFirstChar; 591 pifi->chLastChar = pFontInfo->dfLastChar; 592 pifi->chDefaultChar = pFontInfo->dfFirstChar + pFontInfo->dfDefaultChar; 593 pifi->chBreakChar = pFontInfo->dfFirstChar + pFontInfo->dfBreakChar; 594 pifi->wcFirstChar = pface->wcFirstChar; 595 pifi->wcLastChar = pface->wcLastChar; 596 pifi->wcDefaultChar = pface->wcDefaultChar; 597 pifi->wcBreakChar = pface->wcBreakChar; 598 pifi->ptlBaseline.x = 1; 599 pifi->ptlBaseline.y = 0; 600 pifi->ptlAspect.x = pFontInfo->dfVertRes; // CHECKME 601 pifi->ptlAspect.y = pFontInfo->dfHorizRes; 602 pifi->ptlCaret.x = 0; 603 pifi->ptlCaret.y = 1; 604 pifi->rclFontBox.left = 0; 605 pifi->rclFontBox.right = pifi->fwdAveCharWidth; 606 pifi->rclFontBox.top = pifi->fwdWinAscender; 607 pifi->rclFontBox.bottom = - pifi->fwdWinDescender; 608 *(DWORD*)&pifi->achVendId = 0x30303030; // FIXME 609 pifi->cKerningPairs = 0; 610 pifi->ulPanoseCulture = FM_PANOSE_CULTURE_LATIN; 611 pifi->panose = panose; 612 613 /* Set char sets */ 614 pifiX->ajCharSet[0] = pifi->jWinCharSet; 615 pifiX->ajCharSet[1] = DEFAULT_CHARSET; 616 617 if (pface->flInfo & FM_INFO_CONSTANT_WIDTH) 618 pifi->jWinPitchAndFamily |= FIXED_PITCH; 619 620 #if 0 621 EngMultiByteToUnicodeN(pifiX->wszFaceName, 622 LF_FACESIZE * sizeof(WCHAR), 623 NULL, 624 pFontInfo->, 625 strnlen(pDesc, LF_FACESIZE)); 626 #endif 627 wcscpy(pifiX->wszFaceName, L"Courier-X"); 628 wcscpy(pifiX->wszFamilyName, L"Courier-X"); 629 630 /* Initialize font weight style flags and string */ 631 if (pifi->usWinWeight == FW_REGULAR) 632 { 633 // pifi->fsSelection |= FM_SEL_REGULAR; 634 } 635 else if (pifi->usWinWeight > FW_SEMIBOLD) 636 { 637 pifi->fsSelection |= FM_SEL_BOLD; 638 wcscat(pifiX->wszStyleName, L"Bold "); 639 } 640 else if (pifi->usWinWeight <= FW_LIGHT) 641 { 642 wcscat(pifiX->wszStyleName, L"Light "); 643 } 644 645 if (pFontInfo->dfItalic) 646 { 647 pifi->fsSelection |= FM_SEL_ITALIC; 648 wcscat(pifiX->wszStyleName, L"Italic "); 649 } 650 651 if (pFontInfo->dfUnderline) 652 { 653 pifi->fsSelection |= FM_SEL_UNDERSCORE; 654 wcscat(pifiX->wszStyleName, L"Underscore "); 655 } 656 657 if (pFontInfo->dfStrikeOut) 658 { 659 pifi->fsSelection |= FM_SEL_STRIKEOUT; 660 wcscat(pifiX->wszStyleName, L"Strikeout "); 661 } 662 663 return pifi; 664 } 665 666 667 VOID 668 APIENTRY 669 BmfdFree( 670 PVOID pv, 671 ULONG_PTR id) 672 { 673 DbgPrint("BmfdFree()\n"); 674 if (id) 675 { 676 EngFreeMem((PVOID)id); 677 } 678 } 679 680 681 VOID 682 APIENTRY 683 BmfdDestroyFont( 684 IN FONTOBJ *pfo) 685 { 686 /* Free the font realization info */ 687 EngFreeMem(pfo->pvProducer); 688 pfo->pvProducer = NULL; 689 } 690