[BMFD]
[reactos.git] / reactos / win32ss / drivers / font / bmfd / font.c
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 }