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