Load libfreetype.so on the fly with dlopen and friends.
[wine] / dlls / wineps / 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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_FREETYPE_FREETYPE_H
39 #include <freetype/freetype.h>
40 #endif
41 #ifdef HAVE_FREETYPE_FTGLYPH_H
42 #include <freetype/ftglyph.h>
43 #endif
44 #ifdef HAVE_FREETYPE_TTTABLES_H
45 #include <freetype/tttables.h>
46 #endif
47 #ifdef HAVE_FREETYPE_FTSNAMES_H
48 #include <freetype/ftsnames.h>
49 #else
50 # ifdef HAVE_FREETYPE_FTNAMES_H
51 # include <freetype/ftnames.h>
52 # endif
53 #endif
54 #ifdef HAVE_FREETYPE_TTNAMEID_H
55 #include <freetype/ttnameid.h>
56 #endif
57
58 #include <sys/types.h>
59 #include <dirent.h>
60 #include <string.h>
61 #include <stdio.h>
62 #include <errno.h>
63
64 #include "winnt.h"
65 #include "winerror.h"
66 #include "winreg.h"
67 #include "psdrv.h"
68 #include "wine/debug.h"
69
70 WINE_DEFAULT_DEBUG_CHANNEL(psdrv);
71
72 #define REQUIRED_FACE_FLAGS     (   FT_FACE_FLAG_SCALABLE   |   \
73                                     FT_FACE_FLAG_HORIZONTAL |   \
74                                     FT_FACE_FLAG_SFNT       |   \
75                                     FT_FACE_FLAG_GLYPH_NAMES    )
76
77 #define GLYPH_LOAD_FLAGS        (   FT_LOAD_NO_SCALE            |   \
78                                     FT_LOAD_IGNORE_TRANSFORM    |   \
79                                     FT_LOAD_LINEAR_DESIGN           )
80
81 static void *ft_handle = NULL;
82
83 #define MAKE_FUNCPTR(f) static typeof(f) * p##f = NULL;
84 MAKE_FUNCPTR(FT_Done_Face)
85 MAKE_FUNCPTR(FT_Done_FreeType)
86 MAKE_FUNCPTR(FT_Get_Char_Index)
87 MAKE_FUNCPTR(FT_Get_Glyph_Name)
88 MAKE_FUNCPTR(FT_Get_Sfnt_Name)
89 MAKE_FUNCPTR(FT_Get_Sfnt_Name_Count)
90 MAKE_FUNCPTR(FT_Get_Sfnt_Table)
91 MAKE_FUNCPTR(FT_Init_FreeType)
92 MAKE_FUNCPTR(FT_Load_Glyph)
93 MAKE_FUNCPTR(FT_New_Face)
94 MAKE_FUNCPTR(FT_Set_Charmap)
95 #undef MAKE_FUNCPTR
96                                     
97 /*******************************************************************************
98  *  FindCharMap
99  *
100  *  Finds Windows character map and creates "EncodingScheme" string.  Returns
101  *  FALSE to indicate memory allocation or FreeType error; sets *p_charmap to
102  *  NULL if no Windows encoding is present.
103  *
104  *  Returns Unicode character map if present; otherwise uses the first Windows
105  *  character map found.
106  *
107  */
108 static const LPCSTR encoding_names[7] =
109 {
110     "WindowsSymbol",        /* TT_MS_ID_SYMBOL_CS */
111     "WindowsUnicode",       /* TT_MS_ID_UNICODE_CS */
112     "WindowsShiftJIS",      /* TT_MS_ID_SJIS */
113     "WindowsPRC",           /* TT_MS_ID_GB2312 */
114     "WindowsBig5",          /* TT_MS_ID_BIG_5 */
115     "WindowsWansung",       /* TT_MS_ID_WANSUNG */
116     "WindowsJohab"          /* TT_MS_ID_JOHAB */
117 /*  "WindowsUnknown65535"   is the longest possible (encoding_id is a UShort) */
118 };
119
120 static BOOL FindCharMap(FT_Face face, FT_CharMap *p_charmap, LPSTR *p_sz)
121 {
122     FT_Int      i;
123     FT_Error    error;
124     FT_CharMap  charmap = NULL;
125     
126     for (i = 0; i < face->num_charmaps; ++i)
127     {
128         if (face->charmaps[i]->platform_id != TT_PLATFORM_MICROSOFT)
129             continue;
130             
131         if (face->charmaps[i]->encoding_id == TT_MS_ID_UNICODE_CS)
132         {
133             charmap = face->charmaps[i];
134             break;
135         }
136         
137         if (charmap == NULL)
138             charmap = face->charmaps[i];
139     }
140     
141     *p_charmap = charmap;
142     
143     if (charmap == NULL)
144     {
145         WARN("No Windows character map found\n");
146         return TRUE;
147     }
148     
149     error = pFT_Set_Charmap(face, charmap);
150     if (error != FT_Err_Ok)
151     {
152         ERR("%s returned %i\n", "FT_Set_Charmap", error);
153         return FALSE;
154     }
155     
156     *p_sz = HeapAlloc(PSDRV_Heap, 0, sizeof("WindowsUnknown65535"));
157     if (*p_sz == NULL)
158         return FALSE;
159         
160     if (charmap->encoding_id < 7)
161         strcpy(*p_sz, encoding_names[charmap->encoding_id]);
162     else
163         sprintf(*p_sz, "%s%u", "WindowsUnknown", charmap->encoding_id);
164         
165     return TRUE;
166 }
167
168 /*******************************************************************************
169  *  MSTTStrToSz
170  *
171  *  Converts a string in the TrueType NAME table to a null-terminated ASCII
172  *  character string.  Space for the string is allocated from the driver heap.
173  *  Only handles platform_id = 3 (TT_PLATFORM_MICROSOFT) strings (16-bit, big
174  *  endian).  It also only handles ASCII character codes (< 128).
175  *
176  *  Sets *p_sz to NULL if string cannot be converted; only returns FALSE for
177  *  memory allocation failure.
178  *
179  */
180 static BOOL MSTTStrToSz(const FT_SfntName *name, LPSTR *p_sz)
181 {
182     FT_UShort   i;
183     INT         len;
184     USHORT      *wsz;
185     LPSTR       sz;
186     
187     len = name->string_len / 2;                     /* # of 16-bit chars */
188     
189     *p_sz = sz = HeapAlloc(PSDRV_Heap, 0, len + 1);
190     if (sz == NULL)
191         return FALSE;
192         
193     wsz = (USHORT *)(name->string);
194     
195     for (i = 0; i < len; ++i, ++sz, ++wsz)
196     {
197         USHORT  wc = *wsz;
198         
199 #ifndef WORDS_BIGENDIAN
200         wc = (wc >> 8) | (wc << 8);
201 #endif
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 inline static 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;
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     CHAR        name_buf[256], value_buf[256];
581     INT         i = 0;
582     FT_Error    error;
583     FT_Library  library;
584     HKEY        hkey;
585     DWORD       type, name_len, value_len;
586
587     if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
588             "Software\\Wine\\Wine\\Config\\TrueType Font Directories",
589             0, KEY_READ, &hkey) != ERROR_SUCCESS)
590         return TRUE;
591
592
593     ft_handle = wine_dlopen("libfreetype.so", RTLD_NOW, NULL, 0);
594     if(!ft_handle) {
595         WINE_MESSAGE(
596       "Wine cannot find the FreeType font library.  To enable Wine to\n"
597       "use TrueType fonts please install a version of FreeType greater than\n"
598       "or equal to 2.0.5.\n"
599       "http://www.freetype.org\n");
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     name_len = sizeof(name_buf);
627     value_len = sizeof(value_buf);
628     
629     while (RegEnumValueA(hkey, i++, name_buf, &name_len, NULL, &type, value_buf,
630             &value_len) == ERROR_SUCCESS)
631     {
632         value_buf[sizeof(value_buf) - 1] = '\0';
633         
634         if (ReadTrueTypeDir(library, value_buf) == FALSE)
635         {
636             RegCloseKey(hkey);
637             pFT_Done_FreeType(library);
638             return FALSE;
639         }
640         
641         /* initialize lengths for new iteration */
642         
643         name_len = sizeof(name_buf);
644         value_len = sizeof(value_buf);
645     }
646     
647     RegCloseKey(hkey);
648     pFT_Done_FreeType(library);
649     wine_dlclose(ft_handle, NULL, 0);
650     ft_handle = NULL;
651     return TRUE;
652
653 sym_not_found:
654     WINE_MESSAGE(
655       "Wine cannot find certain functions that it needs inside the FreeType\n"
656       "font library.  To enable Wine to use TrueType fonts please upgrade\n"
657       "FreeType to at least version 2.0.5.\n"
658       "http://www.freetype.org\n");
659     wine_dlclose(ft_handle, NULL, 0);
660     ft_handle = NULL;
661     return TRUE;
662 }
663
664 #endif  /* HAVE_FREETYPE */