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