]> git.wh0rd.org Git - fontconfig.git/blob - src/fcfreetype.c
More complete memory tracking. Install always overwrites header files
[fontconfig.git] / src / fcfreetype.c
1 /*
2  * $XFree86: xc/lib/fontconfig/src/fcfreetype.c,v 1.10 2002/08/22 07:36:44 keithp Exp $
3  *
4  * Copyright © 2001 Keith Packard, member of The XFree86 Project, Inc.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of Keith Packard not be used in
11  * advertising or publicity pertaining to distribution of the software without
12  * specific, written prior permission.  Keith Packard makes no
13  * representations about the suitability of this software for any purpose.  It
14  * is provided "as is" without express or implied warranty.
15  *
16  * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18  * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22  * PERFORMANCE OF THIS SOFTWARE.
23  */
24
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include "fcint.h"
29 #include <freetype/freetype.h>
30 #include <freetype/internal/ftobjs.h>
31 #include <freetype/tttables.h>
32 #include <freetype/ftsnames.h>
33 #include <freetype/ttnameid.h>
34
35 /*
36  * Keep Han languages separated by eliminating languages
37  * that the codePageRange bits says aren't supported
38  */
39
40 static const struct {
41     int             bit;
42     const FcChar8   *lang;
43 } FcCodePageRange[] = {
44     { 17,       (const FcChar8 *) "ja" },
45     { 18,       (const FcChar8 *) "zh-cn" },
46     { 19,       (const FcChar8 *) "ko" },
47     { 20,       (const FcChar8 *) "zh-tw" },
48 };
49
50 #define NUM_CODE_PAGE_RANGE (sizeof FcCodePageRange / sizeof FcCodePageRange[0])
51
52 FcBool
53 FcFreeTypeIsExclusiveLang (const FcChar8  *lang)
54 {
55     int     i;
56
57     for (i = 0; i < NUM_CODE_PAGE_RANGE; i++)
58     {
59         if (FcLangCompare (lang, FcCodePageRange[i].lang) != FcLangDifferentLang)
60             return FcTrue;
61     }
62     return FcFalse;
63 }
64
65 #define FC_NAME_PRIO_LANG           0x0f00
66 #define FC_NAME_PRIO_LANG_ENGLISH   0x0200
67 #define FC_NAME_PRIO_LANG_LATIN     0x0100
68 #define FC_NAME_PRIO_LANG_NONE      0x0000
69
70 #define FC_NAME_PRIO_ENC            0x00f0
71 #define FC_NAME_PRIO_ENC_UNICODE    0x0010
72 #define FC_NAME_PRIO_ENC_NONE       0x0000
73
74 #define FC_NAME_PRIO_NAME           0x000f
75 #define FC_NAME_PRIO_NAME_FAMILY    0x0002
76 #define FC_NAME_PRIO_NAME_PS        0x0001
77 #define FC_NAME_PRIO_NAME_NONE      0x0000
78
79 static FcBool
80 FcUcs4IsLatin (FcChar32 ucs4)
81 {
82     FcChar32    page = ucs4 >> 8;
83     
84     if (page <= 2)
85         return FcTrue;
86     if (page == 0x1e)
87         return FcTrue;
88     if (0x20 <= page && page <= 0x23)
89         return FcTrue;
90     if (page == 0xfb)
91         return FcTrue;
92     if (page == 0xff)
93         return FcTrue;
94     return FcFalse;
95 }
96
97 static FcBool
98 FcUtf8IsLatin (FcChar8 *str, int len)
99 {
100     while (len)
101     {
102         FcChar32    ucs4;
103         int         clen = FcUtf8ToUcs4 (str, &ucs4, len);
104         if (clen <= 0)
105             return FcFalse;
106         if (!FcUcs4IsLatin (ucs4))
107             return FcFalse;
108         len -= clen;
109         str += clen;
110     }
111     return FcTrue;
112 }
113
114 FcPattern *
115 FcFreeTypeQuery (const FcChar8  *file,
116                  int            id,
117                  FcBlanks       *blanks,
118                  int            *count)
119 {
120     FT_Face         face;
121     FcPattern       *pat;
122     int             slant;
123     int             weight;
124     int             i;
125     FcCharSet       *cs;
126     FcLangSet       *ls;
127     FT_Library      ftLibrary;
128     FcChar8         *family;
129     FcChar8         *style;
130     TT_OS2          *os2;
131     const FcChar8   *exclusiveLang = 0;
132     FT_SfntName     sname;
133     FT_UInt         snamei, snamec;
134     FcBool          family_allocated = FcFalse;
135     FcBool          style_allocated = FcFalse;
136     int             family_prio = 0;
137     int             style_prio = 0;
138
139     if (FT_Init_FreeType (&ftLibrary))
140         return 0;
141     
142     if (FT_New_Face (ftLibrary, (char *) file, id, &face))
143         goto bail;
144
145     *count = face->num_faces;
146
147     pat = FcPatternCreate ();
148     if (!pat)
149         goto bail0;
150
151     if (!FcPatternAddBool (pat, FC_OUTLINE,
152                            (face->face_flags & FT_FACE_FLAG_SCALABLE) != 0))
153         goto bail1;
154
155     if (!FcPatternAddBool (pat, FC_SCALABLE,
156                            (face->face_flags & FT_FACE_FLAG_SCALABLE) != 0))
157         goto bail1;
158
159
160     slant = FC_SLANT_ROMAN;
161     if (face->style_flags & FT_STYLE_FLAG_ITALIC)
162         slant = FC_SLANT_ITALIC;
163
164     if (!FcPatternAddInteger (pat, FC_SLANT, slant))
165         goto bail1;
166
167     weight = FC_WEIGHT_MEDIUM;
168     if (face->style_flags & FT_STYLE_FLAG_BOLD)
169         weight = FC_WEIGHT_BOLD;
170
171     if (!FcPatternAddInteger (pat, FC_WEIGHT, weight))
172         goto bail1;
173
174     /*
175      * Grub through the name table looking for family
176      * and style names.  FreeType makes quite a hash
177      * of them
178      */
179     family = 0;
180     style = 0;
181     snamec = FT_Get_Sfnt_Name_Count (face);
182     for (snamei = 0; snamei < snamec; snamei++)
183     {
184         FcChar8         *utf8;
185         int             len;
186         int             wchar;
187         FcChar8         *src;
188         int             src_len;
189         FcChar8         *u8;
190         FcChar32        ucs4;
191         int             ilen, olen;
192         int             prio = 0;
193         
194         const FcCharMap *map;
195         enum {
196             FcNameEncodingUtf16, 
197             FcNameEncodingAppleRoman,
198             FcNameEncodingLatin1 
199         } encoding;
200         
201         
202         if (FT_Get_Sfnt_Name (face, snamei, &sname) != 0)
203             break;
204         
205         /*
206          * Look for Unicode strings
207          */
208         switch (sname.platform_id) {
209         case TT_PLATFORM_APPLE_UNICODE:
210             /*
211              * All APPLE_UNICODE encodings are Utf16 BE
212              *
213              * Because there's no language id for Unicode,
214              * assume it's English
215              */
216             prio |= FC_NAME_PRIO_LANG_ENGLISH;
217             prio |= FC_NAME_PRIO_ENC_UNICODE;
218             encoding = FcNameEncodingUtf16;
219             break;
220         case TT_PLATFORM_MACINTOSH:
221             switch (sname.encoding_id) {
222             case TT_MAC_ID_ROMAN:
223                 encoding = FcNameEncodingAppleRoman;
224                 break;
225             default:
226                 continue;
227             }
228             switch (sname.language_id) {
229             case TT_MAC_LANGID_ENGLISH:
230                 prio |= FC_NAME_PRIO_LANG_ENGLISH;
231                 break;
232             default:
233                 /*
234                  * Sometimes Microsoft language ids
235                  * end up in the macintosh table.  This
236                  * is often accompanied by data in
237                  * some mystic encoding.  Ignore these names
238                  */
239                 if (sname.language_id >= 0x100)
240                     continue;
241                 break;
242             }
243             break;
244         case TT_PLATFORM_MICROSOFT:
245             switch (sname.encoding_id) {
246             case TT_MS_ID_UNICODE_CS:
247                 encoding = FcNameEncodingUtf16;
248                 prio |= FC_NAME_PRIO_ENC_UNICODE;
249                 break;
250             default:
251                 continue;
252             }
253             switch (sname.language_id & 0xff) {
254             case 0x09:
255                 prio |= FC_NAME_PRIO_LANG_ENGLISH;
256                 break;
257             default:
258                 break;
259             }
260             break;
261         case TT_PLATFORM_ISO:
262             switch (sname.encoding_id) {
263             case TT_ISO_ID_10646:
264                 encoding = FcNameEncodingUtf16;
265                 prio |= FC_NAME_PRIO_ENC_UNICODE;
266                 break;
267             case TT_ISO_ID_7BIT_ASCII:
268             case TT_ISO_ID_8859_1:
269                 encoding = FcNameEncodingLatin1;
270                 break;
271             default:
272                 continue;
273             }
274             break;
275         default:
276             continue;
277         }
278         
279         /*
280          * Look for family and style names 
281          */
282         switch (sname.name_id) {
283         case TT_NAME_ID_FONT_FAMILY:
284             prio |= FC_NAME_PRIO_NAME_FAMILY;
285             break;
286         case TT_NAME_ID_PS_NAME:
287             prio |= FC_NAME_PRIO_NAME_PS;
288             break;
289         case TT_NAME_ID_FONT_SUBFAMILY:
290             break;
291         default:
292             continue;
293         }
294             
295         src = (FcChar8 *) sname.string;
296         src_len = sname.string_len;
297         
298         switch (encoding) {
299         case FcNameEncodingUtf16:
300             /*
301              * Convert Utf16 to Utf8
302              */
303             
304             if (!FcUtf16Len (src, FcEndianBig, src_len, &len, &wchar))
305                 continue;
306     
307             /*
308              * Allocate plenty of space.  Freed below
309              */
310             utf8 = malloc (len * FC_UTF8_MAX_LEN + 1);
311             if (!utf8)
312                 continue;
313                 
314             u8 = utf8;
315             
316             while ((ilen = FcUtf16ToUcs4 (src, FcEndianBig, &ucs4, src_len)) > 0)
317             {
318                 src_len -= ilen;
319                 src += ilen;
320                 olen = FcUcs4ToUtf8 (ucs4, u8);
321                 u8 += olen;
322             }
323             *u8 = '\0';
324             break;
325         case FcNameEncodingLatin1:
326             /*
327              * Convert Latin1 to Utf8. Freed below
328              */
329             utf8 = malloc (src_len * 2 + 1);
330             if (!utf8)
331                 continue;
332
333             u8 = utf8;
334             while (src_len > 0)
335             {
336                 ucs4 = *src++;
337                 src_len--;
338                 olen = FcUcs4ToUtf8 (ucs4, u8);
339                 u8 += olen;
340             }
341             *u8 = '\0';
342             break;
343         case FcNameEncodingAppleRoman:
344             /*
345              * Convert AppleRoman to Utf8
346              */
347             map = FcFreeTypeGetPrivateMap (ft_encoding_apple_roman);
348             if (!map)
349                 continue;
350
351             /* freed below */
352             utf8 = malloc (src_len * 3 + 1);
353             if (!utf8)
354                 continue;
355
356             u8 = utf8;
357             while (src_len > 0)
358             {
359                 ucs4 = FcFreeTypePrivateToUcs4 (*src++, map);
360                 src_len--;
361                 olen = FcUcs4ToUtf8 (ucs4, u8);
362                 u8 += olen;
363             }
364             *u8 = '\0';
365             break;
366         default:
367             continue;
368         }
369         if ((prio & FC_NAME_PRIO_LANG) == FC_NAME_PRIO_LANG_NONE)
370             if (FcUtf8IsLatin (utf8, strlen ((char *) utf8)))
371                 prio |= FC_NAME_PRIO_LANG_LATIN;
372                                
373         if (FcDebug () & FC_DBG_SCANV)
374             printf ("\nfound name (name %d platform %d encoding %d language 0x%x prio 0x%x) %s\n",
375                     sname.name_id, sname.platform_id,
376                     sname.encoding_id, sname.language_id,
377                     prio, utf8);
378     
379         switch (sname.name_id) {
380         case TT_NAME_ID_FONT_FAMILY:
381         case TT_NAME_ID_PS_NAME:
382             if (!family || prio > family_prio)
383             {
384                 if (family)
385                     free (family);
386                 family = utf8;
387                 utf8 = 0;
388                 family_allocated = FcTrue;
389                 family_prio = prio;
390             }
391             break;
392         case TT_NAME_ID_FONT_SUBFAMILY:
393             if (!style || prio > style_prio)
394             {
395                 if (style)
396                     free (style);
397                 style = utf8;
398                 utf8 = 0;
399                 style_allocated = FcTrue;
400                 style_prio = prio;
401             }
402             break;
403         }
404         if (utf8)
405             free (utf8);
406     }
407     
408     if (!family)
409         family = (FcChar8 *) face->family_name;
410     
411     if (!style)
412         style = (FcChar8 *) face->style_name;
413     
414     if (!family)
415     {
416         FcChar8 *start, *end;
417         
418         start = (FcChar8 *) strrchr ((char *) file, '/');
419         if (start)
420             start++;
421         else
422             start = (FcChar8 *) file;
423         end = (FcChar8 *) strrchr ((char *) start, '.');
424         if (!end)
425             end = start + strlen ((char *) start);
426         /* freed below */
427         family = malloc (end - start + 1);
428         strncpy ((char *) family, (char *) start, end - start);
429         family[end - start] = '\0';
430         family_allocated = FcTrue;
431     }
432
433     if (FcDebug() & FC_DBG_SCAN)
434         printf ("\"%s\" \"%s\" ", family, style ? style : (FcChar8 *) "<none>");
435
436     if (!FcPatternAddString (pat, FC_FAMILY, family))
437     {
438         if (family_allocated)
439             free (family);
440         if (style_allocated)
441             free (style);
442         goto bail1;
443     }
444
445     if (family_allocated)
446         free (family);
447
448     if (style)
449     {
450         if (!FcPatternAddString (pat, FC_STYLE, style))
451         {
452             if (style_allocated)
453                 free (style);
454             goto bail1;
455         }
456         if (style_allocated)
457             free (style);
458     }
459
460     if (!FcPatternAddString (pat, FC_FILE, file))
461         goto bail1;
462
463     if (!FcPatternAddInteger (pat, FC_INDEX, id))
464         goto bail1;
465
466     if (!FcPatternAddString (pat, FC_SOURCE, (FcChar8 *) "FreeType"))
467         goto bail1;
468
469 #if 1
470     if ((face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) != 0)
471         if (!FcPatternAddInteger (pat, FC_SPACING, FC_MONO))
472             goto bail1;
473 #endif
474
475     /*
476      * Get the OS/2 table and poke about
477      */
478     os2 = (TT_OS2 *) FT_Get_Sfnt_Table (face, ft_sfnt_os2);
479     if (os2 && os2->version >= 0x0001 && os2->version != 0xffff)
480     {
481         for (i = 0; i < NUM_CODE_PAGE_RANGE; i++)
482         {
483             FT_ULong    bits;
484             int         bit;
485             if (FcCodePageRange[i].bit < 32)
486             {
487                 bits = os2->ulCodePageRange1;
488                 bit = FcCodePageRange[i].bit;
489             }
490             else
491             {
492                 bits = os2->ulCodePageRange2;
493                 bit = FcCodePageRange[i].bit - 32;
494             }
495             if (bits & (1 << bit))
496             {
497                 /* 
498                  * If the font advertises support for multiple
499                  * "exclusive" languages, then include support
500                  * for any language found to have coverage
501                  */
502                 if (exclusiveLang)
503                 {
504                     exclusiveLang = 0;
505                     break;
506                 }
507                 exclusiveLang = FcCodePageRange[i].lang;
508             }
509         }
510     }
511
512     /*
513      * Compute the unicode coverage for the font
514      */
515     cs = FcFreeTypeCharSet (face, blanks);
516     if (!cs)
517         goto bail1;
518
519     /*
520      * Skip over PCF fonts that have no encoded characters; they're
521      * usually just Unicode fonts transcoded to some legacy encoding
522      */
523     if (FcCharSetCount (cs) == 0)
524     {
525         if (!strcmp(FT_MODULE_CLASS(&face->driver->root)->module_name, "pcf"))
526             goto bail2;
527     }
528
529     if (!FcPatternAddCharSet (pat, FC_CHARSET, cs))
530         goto bail2;
531
532     ls = FcFreeTypeLangSet (cs, exclusiveLang);
533     if (!ls)
534         goto bail2;
535
536     if (!FcPatternAddLangSet (pat, FC_LANG, ls))
537         goto bail2;
538
539     /*
540      * Drop our reference to the charset
541      */
542     FcCharSetDestroy (cs);
543     
544     if (!(face->face_flags & FT_FACE_FLAG_SCALABLE))
545     {
546         for (i = 0; i < face->num_fixed_sizes; i++)
547             if (!FcPatternAddDouble (pat, FC_PIXEL_SIZE,
548                                      (double) face->available_sizes[i].height))
549                 goto bail1;
550         if (!FcPatternAddBool (pat, FC_ANTIALIAS, FcFalse))
551             goto bail1;
552     }
553
554     FT_Done_Face (face);
555     FT_Done_FreeType (ftLibrary);
556     return pat;
557
558 bail2:
559     FcCharSetDestroy (cs);
560 bail1:
561     FcPatternDestroy (pat);
562 bail0:
563     FT_Done_Face (face);
564 bail:
565     FT_Done_FreeType (ftLibrary);
566     return 0;
567 }