dsound: Restore old format on SetFormat failure.
[wine] / dlls / wineps.drv / truetype.c
1 /*******************************************************************************
2  *  TrueType font-related functions for Wine PostScript driver.  Currently just
3  *  uses FreeType to read font metrics.
4  *
5  *  Copyright 2001  Ian Pilcher
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  *
21  *  NOTE:  Many of the functions in this file can return either fatal errors
22  *      (memory allocation failure or unexpected FreeType error) or non-fatal
23  *      errors (unusable font file).  Fatal errors are indicated by returning
24  *      FALSE; see individual function descriptions for how they indicate non-
25  *      fatal errors.
26  *
27  */
28 #include "config.h"
29 #include "wine/port.h"
30
31 #ifdef HAVE_FREETYPE
32
33 /*
34  *  These stupid #ifdefs should work for FreeType 2.0.1 and 2.0.2.  Beyond that
35  *  is anybody's guess.
36  */
37
38 #ifdef HAVE_FT2BUILD_H
39 #include <ft2build.h>
40 #endif
41 #ifdef HAVE_FREETYPE_FREETYPE_H
42 #include <freetype/freetype.h>
43 #endif
44 #ifdef HAVE_FREETYPE_FTGLYPH_H
45 #include <freetype/ftglyph.h>
46 #endif
47 #ifdef HAVE_FREETYPE_TTTABLES_H
48 #include <freetype/tttables.h>
49 #endif
50 #ifdef HAVE_FREETYPE_FTSNAMES_H
51 #include <freetype/ftsnames.h>
52 #endif
53 #ifdef HAVE_FREETYPE_TTNAMEID_H
54 #include <freetype/ttnameid.h>
55 #endif
56
57 #include <sys/types.h>
58 #ifdef HAVE_DIRENT_H
59 # include <dirent.h>
60 #endif
61 #include <string.h>
62 #include <stdarg.h>
63 #include <stdio.h>
64 #include <errno.h>
65
66 #include "windef.h"
67 #include "winbase.h"
68 #include "winerror.h"
69 #include "winreg.h"
70 #include "winnls.h"
71 #include "psdrv.h"
72 #include "wine/library.h"
73 #include "wine/debug.h"
74
75 WINE_DEFAULT_DEBUG_CHANNEL(psdrv);
76
77 #define REQUIRED_FACE_FLAGS     (   FT_FACE_FLAG_SCALABLE   |   \
78                                     FT_FACE_FLAG_HORIZONTAL |   \
79                                     FT_FACE_FLAG_SFNT       |   \
80                                     FT_FACE_FLAG_GLYPH_NAMES    )
81
82 #define GLYPH_LOAD_FLAGS        (   FT_LOAD_NO_SCALE            |   \
83                                     FT_LOAD_IGNORE_TRANSFORM    |   \
84                                     FT_LOAD_LINEAR_DESIGN           )
85
86 static void *ft_handle = NULL;
87
88 #define MAKE_FUNCPTR(f) static typeof(f) * p##f = NULL;
89 MAKE_FUNCPTR(FT_Done_Face)
90 MAKE_FUNCPTR(FT_Done_FreeType)
91 MAKE_FUNCPTR(FT_Get_Char_Index)
92 MAKE_FUNCPTR(FT_Get_Glyph_Name)
93 MAKE_FUNCPTR(FT_Get_Sfnt_Name)
94 MAKE_FUNCPTR(FT_Get_Sfnt_Name_Count)
95 MAKE_FUNCPTR(FT_Get_Sfnt_Table)
96 MAKE_FUNCPTR(FT_Init_FreeType)
97 MAKE_FUNCPTR(FT_Load_Glyph)
98 MAKE_FUNCPTR(FT_New_Face)
99 MAKE_FUNCPTR(FT_Set_Charmap)
100 #undef MAKE_FUNCPTR
101
102 /*******************************************************************************
103  *  FindCharMap
104  *
105  *  Finds Windows character map and creates "EncodingScheme" string.  Returns
106  *  FALSE to indicate memory allocation or FreeType error; sets *p_charmap to
107  *  NULL if no Windows encoding is present.
108  *
109  *  Returns Unicode character map if present; otherwise uses the first Windows
110  *  character map found.
111  *
112  */
113 static const LPCSTR encoding_names[7] =
114 {
115     "WindowsSymbol",        /* TT_MS_ID_SYMBOL_CS */
116     "WindowsUnicode",       /* TT_MS_ID_UNICODE_CS */
117     "WindowsShiftJIS",      /* TT_MS_ID_SJIS */
118     "WindowsPRC",           /* TT_MS_ID_GB2312 */
119     "WindowsBig5",          /* TT_MS_ID_BIG_5 */
120     "WindowsWansung",       /* TT_MS_ID_WANSUNG */
121     "WindowsJohab"          /* TT_MS_ID_JOHAB */
122 /*  "WindowsUnknown65535"   is the longest possible (encoding_id is a UShort) */
123 };
124
125 static BOOL FindCharMap(FT_Face face, FT_CharMap *p_charmap, LPSTR *p_sz)
126 {
127     FT_Int      i;
128     FT_Error    error;
129     FT_CharMap  charmap = NULL;
130
131     for (i = 0; i < face->num_charmaps; ++i)
132     {
133         if (face->charmaps[i]->platform_id != TT_PLATFORM_MICROSOFT)
134             continue;
135
136         if (face->charmaps[i]->encoding_id == TT_MS_ID_UNICODE_CS)
137         {
138             charmap = face->charmaps[i];
139             break;
140         }
141
142         if (charmap == NULL)
143             charmap = face->charmaps[i];
144     }
145
146     *p_charmap = charmap;
147
148     if (charmap == NULL)
149     {
150         WARN("No Windows character map found\n");
151         return TRUE;
152     }
153
154     error = pFT_Set_Charmap(face, charmap);
155     if (error != FT_Err_Ok)
156     {
157         ERR("%s returned %i\n", "FT_Set_Charmap", error);
158         return FALSE;
159     }
160
161     *p_sz = HeapAlloc(PSDRV_Heap, 0, sizeof("WindowsUnknown65535"));
162     if (*p_sz == NULL)
163         return FALSE;
164
165     if (charmap->encoding_id < 7)
166         strcpy(*p_sz, encoding_names[charmap->encoding_id]);
167     else
168         sprintf(*p_sz, "%s%u", "WindowsUnknown", charmap->encoding_id);
169
170     return TRUE;
171 }
172
173 /*******************************************************************************
174  *  MSTTStrToSz
175  *
176  *  Converts a string in the TrueType NAME table to a null-terminated ASCII
177  *  character string.  Space for the string is allocated from the driver heap.
178  *  Only handles platform_id = 3 (TT_PLATFORM_MICROSOFT) strings (16-bit, big
179  *  endian).  It also only handles ASCII character codes (< 128).
180  *
181  *  Sets *p_sz to NULL if string cannot be converted; only returns FALSE for
182  *  memory allocation failure.
183  *
184  */
185 static BOOL MSTTStrToSz(const FT_SfntName *name, LPSTR *p_sz)
186 {
187     FT_UShort   i;
188     INT         len;
189     BYTE        *wsz;
190     LPSTR       sz;
191
192     len = name->string_len / 2;                     /* # of 16-bit chars */
193
194     *p_sz = sz = HeapAlloc(PSDRV_Heap, 0, len + 1);
195     if (sz == NULL)
196         return FALSE;
197
198     wsz = (BYTE *)name->string;
199
200     for (i = 0; i < len; ++i, ++sz)
201     {
202         USHORT wc = (wsz[0] << 8) + wsz[1];
203         wsz += 2;
204
205         if (wc > 127)
206         {
207             WARN("Non-ASCII character 0x%.4x\n", wc);
208             HeapFree(PSDRV_Heap, 0, *p_sz);
209             *p_sz = NULL;
210             return TRUE;
211         }
212
213         *sz = (CHAR)wc;
214     }
215
216     *sz = '\0';
217
218     return TRUE;
219 }
220
221 /*******************************************************************************
222  *  FindMSTTString
223  *
224  *  Finds the requested Microsoft platform string in the TrueType NAME table and
225  *  converts it to a null-terminated ASCII string.  Currently looks for U.S.
226  *  English names only.
227  *
228  *  Sets string to NULL if not present or cannot be converted; returns FALSE
229  *  only for memory allocation failure.
230  *
231  */
232 static BOOL FindMSTTString(FT_Face face, FT_CharMap charmap, FT_UShort name_id,
233         LPSTR *p_sz)
234 {
235     FT_UInt         num_strings, string_index;
236     FT_SfntName     name;
237     FT_Error        error;
238
239     num_strings = pFT_Get_Sfnt_Name_Count(face);
240
241     for (string_index = 0; string_index < num_strings; ++string_index)
242     {
243         error = pFT_Get_Sfnt_Name(face, string_index, &name);
244         if (error != FT_Err_Ok)
245         {
246             ERR("%s returned %i\n", "FT_Get_Sfnt_Name", error);
247             return FALSE;
248         }
249
250         /* FIXME - Handle other languages? */
251
252         if (name.platform_id != TT_PLATFORM_MICROSOFT ||
253                 name.language_id != TT_MS_LANGID_ENGLISH_UNITED_STATES)
254             continue;
255
256         if (name.platform_id != charmap->platform_id ||
257                 name.encoding_id != charmap->encoding_id)
258             continue;
259
260         if (name.name_id != name_id)
261             continue;
262
263         return MSTTStrToSz(&name, p_sz);
264     }
265
266     *p_sz = NULL;                   /* didn't find it */
267
268     return TRUE;
269 }
270
271 /*******************************************************************************
272  *  PSUnits
273  *
274  *  Convert TrueType font units (relative to font em square) to PostScript
275  *  units.
276  *
277  */
278 static inline float PSUnits(LONG x, USHORT em_size)
279 {
280     return 1000.0 * (float)x / (float)em_size;
281 }
282
283 /*******************************************************************************
284  *  StartAFM
285  *
286  *  Allocates space for the AFM on the driver heap and reads basic font metrics
287  *  from the HEAD, POST, HHEA, and OS/2 tables.  Returns FALSE for memory
288  *  allocation error; sets *p_afm to NULL if required information is missing.
289  *
290  */
291 static BOOL StartAFM(FT_Face face, AFM **p_afm)
292 {
293     TT_Header       *head;
294     TT_Postscript   *post;
295     TT_OS2          *os2;
296     TT_HoriHeader   *hhea;
297     USHORT          em_size;
298     AFM             *afm;
299
300     head = pFT_Get_Sfnt_Table(face, ft_sfnt_head);
301     post = pFT_Get_Sfnt_Table(face, ft_sfnt_post);
302     os2 = pFT_Get_Sfnt_Table(face, ft_sfnt_os2);
303     hhea = pFT_Get_Sfnt_Table(face, ft_sfnt_hhea);
304
305     if (head == NULL || post == NULL || os2 == NULL || hhea == NULL ||
306             os2->version == 0xffff)                     /* old Macintosh font */
307     {
308         WARN("Required table(s) missing\n");
309         *p_afm = NULL;
310         return TRUE;
311     }
312
313     *p_afm = afm = HeapAlloc(PSDRV_Heap, 0, sizeof(*afm));
314     if (afm == NULL)
315         return FALSE;
316
317     afm->WinMetrics.usUnitsPerEm = em_size = head->Units_Per_EM;
318     afm->WinMetrics.sAscender = hhea->Ascender;
319     afm->WinMetrics.sDescender = hhea->Descender;
320     afm->WinMetrics.sLineGap = hhea->Line_Gap;
321     afm->WinMetrics.sTypoAscender = os2->sTypoAscender;
322     afm->WinMetrics.sTypoDescender = os2->sTypoDescender;
323     afm->WinMetrics.sTypoLineGap = os2->sTypoLineGap;
324     afm->WinMetrics.usWinAscent = os2->usWinAscent;
325     afm->WinMetrics.usWinDescent = os2->usWinDescent;
326     afm->WinMetrics.sAvgCharWidth = os2->xAvgCharWidth;
327
328     afm->Weight = os2->usWeightClass;
329     afm->ItalicAngle = ((float)(post->italicAngle)) / 65536.0;
330     afm->IsFixedPitch = (post-> isFixedPitch == 0) ? FALSE : TRUE;
331     afm->UnderlinePosition = PSUnits(post->underlinePosition, em_size);
332     afm->UnderlineThickness = PSUnits(post->underlineThickness, em_size);
333
334     afm->FontBBox.llx = PSUnits(head->xMin, em_size);
335     afm->FontBBox.lly = PSUnits(head->yMin, em_size);
336     afm->FontBBox.urx = PSUnits(head->xMax, em_size);
337     afm->FontBBox.ury = PSUnits(head->yMax, em_size);
338
339     afm->Ascender = PSUnits(os2->sTypoAscender, em_size);
340     afm->Descender = PSUnits(os2->sTypoDescender, em_size);
341
342     return TRUE;
343 }
344
345 /*******************************************************************************
346  *  ReadCharMetrics
347  *
348  *  Reads metrics for each glyph in a TrueType font.  Returns false for memory
349  *  allocation or FreeType error; sets *p_metrics to NULL for non-fatal error.
350  *
351  */
352 static BOOL ReadCharMetrics(FT_Face face, AFM *afm, AFMMETRICS **p_metrics)
353 {
354     FT_ULong    charcode, index;
355     AFMMETRICS  *metrics;
356     USHORT      em_size = afm->WinMetrics.usUnitsPerEm;
357
358     for (charcode = 0, index = 0; charcode < 65536; ++charcode)
359         if (pFT_Get_Char_Index(face, charcode) != 0)
360             ++index;                                    /* count # of glyphs */
361
362     afm->NumofMetrics = index;
363
364     *p_metrics = metrics = HeapAlloc(PSDRV_Heap, 0, index * sizeof(*metrics));
365     if (metrics == NULL)
366         return FALSE;
367
368     for (charcode = 0, index = 0; charcode < 65536; ++charcode)
369     {
370         FT_UInt     glyph_index = pFT_Get_Char_Index(face, charcode);
371         FT_Error    error;
372         CHAR        buffer[128];                /* for glyph names */
373
374         if (glyph_index == 0)
375             continue;
376
377         error = pFT_Load_Glyph(face, glyph_index, GLYPH_LOAD_FLAGS);
378         if (error != FT_Err_Ok)
379         {
380             ERR("%s returned %i\n", "FT_Load_Glyph", error);
381             goto cleanup;
382         }
383
384         error = pFT_Get_Glyph_Name(face, glyph_index, buffer, sizeof(buffer));
385         if (error != FT_Err_Ok)
386         {
387             ERR("%s returned %i\n", "FT_Get_Glyph_Name", error);
388             goto cleanup;
389         }
390
391         metrics[index].N = PSDRV_GlyphName(buffer);
392         if (metrics[index].N == NULL)
393             goto cleanup;
394
395         metrics[index].C = metrics[index].UV = charcode;
396         metrics[index].WX = PSUnits(face->glyph->metrics.horiAdvance, em_size);
397
398         ++index;
399     }
400
401     if (afm->WinMetrics.sAvgCharWidth == 0)
402         afm->WinMetrics.sAvgCharWidth = PSDRV_CalcAvgCharWidth(afm);
403
404     return TRUE;
405
406     cleanup:
407         HeapFree(PSDRV_Heap, 0, metrics);
408
409     return FALSE;
410 }
411
412 /*******************************************************************************
413  *  BuildTrueTypeAFM
414  *
415  *  Builds the AFM for a TrueType font and adds it to the driver font list.
416  *  Returns FALSE only on an unexpected error (memory allocation failure or
417  *  FreeType error).
418  *
419  */
420 static BOOL BuildTrueTypeAFM(FT_Face face)
421 {
422     AFM         *afm;
423     AFMMETRICS  *metrics;
424     LPSTR       font_name, full_name, family_name, encoding_scheme = NULL;
425     FT_CharMap  charmap;
426     BOOL        retval, added;
427
428     retval = StartAFM(face, &afm);
429     if (retval == FALSE || afm == NULL)
430         return retval;
431
432     retval = FindCharMap(face, &charmap, &encoding_scheme);
433     if (retval == FALSE || charmap == NULL)
434         goto cleanup_afm;
435
436     retval = FindMSTTString(face, charmap, TT_NAME_ID_PS_NAME, &font_name);
437     if (retval == FALSE || font_name == NULL)
438         goto cleanup_encoding_scheme;
439
440     retval = FindMSTTString(face, charmap, TT_NAME_ID_FULL_NAME, &full_name);
441     if (retval == FALSE || full_name == NULL)
442         goto cleanup_font_name;
443
444     retval = FindMSTTString(face, charmap, TT_NAME_ID_FONT_FAMILY,
445             &family_name);
446     if (retval == FALSE || family_name == NULL)
447         goto cleanup_full_name;
448
449     retval = ReadCharMetrics(face, afm, &metrics);
450     if (retval == FALSE || metrics == NULL)
451         goto cleanup_family_name;
452
453     afm->EncodingScheme = encoding_scheme; afm->FontName = font_name;
454     afm->FullName = full_name; afm->FamilyName = family_name;
455     afm->Metrics = metrics;
456
457     retval = PSDRV_AddAFMtoList(&PSDRV_AFMFontList, afm, &added);
458     if (retval == FALSE || added == FALSE)
459         goto cleanup_family_name;
460
461     return TRUE;
462
463     /* clean up after fatal or non-fatal errors */
464
465     cleanup_family_name:
466         HeapFree(PSDRV_Heap, 0, family_name);
467     cleanup_full_name:
468         HeapFree(PSDRV_Heap, 0, full_name);
469     cleanup_font_name:
470         HeapFree(PSDRV_Heap, 0, font_name);
471     cleanup_encoding_scheme:
472         HeapFree(PSDRV_Heap, 0, encoding_scheme);
473     cleanup_afm:
474         HeapFree(PSDRV_Heap, 0, afm);
475
476     return retval;
477 }
478
479 /*******************************************************************************
480  *  ReadTrueTypeFile
481  *
482  *  Reads font metrics from TrueType font file.  Only returns FALSE for
483  *  unexpected errors (memory allocation failure or FreeType error).
484  *
485  */
486 static BOOL ReadTrueTypeFile(FT_Library library, LPCSTR filename)
487 {
488     FT_Error        error;
489     FT_Face         face;
490
491     TRACE("%s\n", filename);
492
493     error = pFT_New_Face(library, filename, 0, &face);
494     if (error != FT_Err_Ok)
495     {
496         WARN("FreeType error %i opening %s\n", error, filename);
497         return TRUE;
498     }
499
500     if ((face->face_flags & REQUIRED_FACE_FLAGS) == REQUIRED_FACE_FLAGS)
501     {
502         if (BuildTrueTypeAFM(face) == FALSE)
503         {
504             pFT_Done_Face(face);
505             return FALSE;
506         }
507     }
508     else
509     {
510         WARN("Required information missing from %s\n", filename);
511     }
512
513     error = pFT_Done_Face(face);
514     if (error != FT_Err_Ok)
515     {
516         ERR("%s returned %i\n", "FT_Done_Face", error);
517         return FALSE;
518     }
519
520     return TRUE;
521 }
522
523 /*******************************************************************************
524  *  ReadTrueTypeDir
525  *
526  *  Reads all TrueType font files in a directory.
527  *
528  */
529 static BOOL ReadTrueTypeDir(FT_Library library, LPCSTR dirname)
530 {
531     struct dirent   *dent;
532     DIR             *dir;
533     CHAR            filename[256];
534
535     dir = opendir(dirname);
536     if (dir == NULL)
537     {
538         WARN("'%s' opening %s\n", strerror(errno), dirname);
539         return TRUE;
540     }
541
542     while ((dent = readdir(dir)) != NULL)
543     {
544         CHAR        *file_extension = strrchr(dent->d_name, '.');
545         int         fn_len;
546
547         if (file_extension == NULL || strcasecmp(file_extension, ".ttf") != 0)
548             continue;
549
550         fn_len = snprintf(filename, 256, "%s/%s", dirname, dent->d_name);
551         if (fn_len < 0 || fn_len > sizeof(filename) - 1)
552         {
553             WARN("Path '%s/%s' is too long\n", dirname, dent->d_name);
554             continue;
555         }
556
557         if (ReadTrueTypeFile(library, filename) ==  FALSE)
558         {
559             closedir(dir);
560             return FALSE;
561         }
562     }
563
564     closedir(dir);
565
566     return TRUE;
567 }
568
569 /*******************************************************************************
570  *  PSDRV_GetTrueTypeMetrics
571  *
572  *  Reads font metrics from TrueType font files in directories listed in the
573  *  HKEY_CURRENT_USER\\Software\\Wine\\Fonts\\Path registry string.
574  *
575  *  If this function fails (returns FALSE), the driver will fail to initialize
576  *  and the driver heap will be destroyed, so it's not necessary to HeapFree
577  *  everything in that event.
578  *
579  */
580 BOOL PSDRV_GetTrueTypeMetrics(void)
581 {
582     static const WCHAR pathW[] = {'P','a','t','h',0};
583     FT_Error    error;
584     FT_Library  library;
585     HKEY hkey;
586     DWORD len;
587     LPWSTR valueW;
588     LPSTR valueA, ptr;
589
590     /* @@ Wine registry key: HKCU\Software\Wine\Fonts */
591     if (RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Fonts", &hkey) != ERROR_SUCCESS)
592         return TRUE;
593
594     ft_handle = wine_dlopen(SONAME_LIBFREETYPE, RTLD_NOW, NULL, 0);
595     if(!ft_handle) {
596         WINE_MESSAGE(
597       "Wine cannot find the FreeType font library.  To enable Wine to\n"
598       "use TrueType fonts please install a version of FreeType greater than\n"
599       "or equal to 2.0.5.\n"
600       "http://www.freetype.org\n");
601         RegCloseKey(hkey);
602         return TRUE;
603     }
604
605 #define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(ft_handle, #f, NULL, 0)) == NULL) goto sym_not_found;
606     LOAD_FUNCPTR(FT_Done_Face)
607     LOAD_FUNCPTR(FT_Done_FreeType)
608     LOAD_FUNCPTR(FT_Get_Char_Index)
609     LOAD_FUNCPTR(FT_Get_Glyph_Name)
610     LOAD_FUNCPTR(FT_Get_Sfnt_Name)
611     LOAD_FUNCPTR(FT_Get_Sfnt_Name_Count)
612     LOAD_FUNCPTR(FT_Get_Sfnt_Table)
613     LOAD_FUNCPTR(FT_Init_FreeType)
614     LOAD_FUNCPTR(FT_Load_Glyph)
615     LOAD_FUNCPTR(FT_New_Face)
616     LOAD_FUNCPTR(FT_Set_Charmap)
617 #undef LOAD_FUNCPTR
618
619     error = pFT_Init_FreeType(&library);
620     if (error != FT_Err_Ok)
621     {
622         ERR("%s returned %i\n", "FT_Init_FreeType", error);
623         wine_dlclose(ft_handle, NULL, 0);
624         RegCloseKey(hkey);
625         return FALSE;
626     }
627
628     if (RegQueryValueExW( hkey, pathW, NULL, NULL, NULL, &len ) == ERROR_SUCCESS)
629     {
630         len += sizeof(WCHAR);
631         valueW = HeapAlloc( GetProcessHeap(), 0, len );
632         if (RegQueryValueExW( hkey, pathW, NULL, NULL, (LPBYTE)valueW, &len ) == ERROR_SUCCESS)
633         {
634             len = WideCharToMultiByte( CP_UNIXCP, 0, valueW, -1, NULL, 0, NULL, NULL );
635             valueA = HeapAlloc( GetProcessHeap(), 0, len );
636             WideCharToMultiByte( CP_UNIXCP, 0, valueW, -1, valueA, len, NULL, NULL );
637             TRACE( "got font path %s\n", debugstr_a(valueA) );
638             ptr = valueA;
639             while (ptr)
640             {
641                 LPSTR next = strchr( ptr, ':' );
642                 if (next) *next++ = 0;
643                 ReadTrueTypeDir( library, ptr );
644                 ptr = next;
645             }
646             HeapFree( GetProcessHeap(), 0, valueA );
647         }
648         HeapFree( GetProcessHeap(), 0, valueW );
649     }
650
651     RegCloseKey(hkey);
652     pFT_Done_FreeType(library);
653     wine_dlclose(ft_handle, NULL, 0);
654     ft_handle = NULL;
655     return TRUE;
656
657 sym_not_found:
658     WINE_MESSAGE(
659       "Wine cannot find certain functions that it needs inside the FreeType\n"
660       "font library.  To enable Wine to use TrueType fonts please upgrade\n"
661       "FreeType to at least version 2.0.5.\n"
662       "http://www.freetype.org\n");
663     RegCloseKey(hkey);
664     wine_dlclose(ft_handle, NULL, 0);
665     ft_handle = NULL;
666     return TRUE;
667 }
668
669 #endif  /* HAVE_FREETYPE */