]> git.wh0rd.org Git - fontconfig.git/blob - src/fcfreetype.c
Reimplement FC_LANG as FcTypeLang, freeze patterns, other cleanup
[fontconfig.git] / src / fcfreetype.c
1 /*
2  * $XFree86: xc/lib/fontconfig/src/fcfreetype.c,v 1.9 2002/07/13 05:43:25 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
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
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             utf8 = malloc (src_len * 3 + 1);
352             if (!utf8)
353                 continue;
354
355             u8 = utf8;
356             while (src_len > 0)
357             {
358                 ucs4 = FcFreeTypePrivateToUcs4 (*src++, map);
359                 src_len--;
360                 olen = FcUcs4ToUtf8 (ucs4, u8);
361                 u8 += olen;
362             }
363             *u8 = '\0';
364             break;
365         default:
366             continue;
367         }
368         if ((prio & FC_NAME_PRIO_LANG) == FC_NAME_PRIO_LANG_NONE)
369             if (FcUtf8IsLatin (utf8, strlen ((char *) utf8)))
370                 prio |= FC_NAME_PRIO_LANG_LATIN;
371                                
372         if (FcDebug () & FC_DBG_SCANV)
373             printf ("\nfound name (name %d platform %d encoding %d language 0x%x prio 0x%x) %s\n",
374                     sname.name_id, sname.platform_id,
375                     sname.encoding_id, sname.language_id,
376                     prio, utf8);
377     
378         switch (sname.name_id) {
379         case TT_NAME_ID_FONT_FAMILY:
380         case TT_NAME_ID_PS_NAME:
381             if (!family || prio > family_prio)
382             {
383                 if (family)
384                     free (family);
385                 family = utf8;
386                 utf8 = 0;
387                 family_allocated = FcTrue;
388                 family_prio = prio;
389             }
390             break;
391         case TT_NAME_ID_FONT_SUBFAMILY:
392             if (!style || prio > style_prio)
393             {
394                 if (style)
395                     free (style);
396                 style = utf8;
397                 utf8 = 0;
398                 style_allocated = FcTrue;
399                 style_prio = prio;
400             }
401             break;
402         }
403         if (utf8)
404             free (utf8);
405     }
406     
407     if (!family)
408         family = (FcChar8 *) face->family_name;
409     
410     if (!style)
411         style = (FcChar8 *) face->style_name;
412     
413     if (!family)
414     {
415         FcChar8 *start, *end;
416         
417         start = (FcChar8 *) strrchr ((char *) file, '/');
418         if (start)
419             start++;
420         else
421             start = (FcChar8 *) file;
422         end = (FcChar8 *) strrchr ((char *) start, '.');
423         if (!end)
424             end = start + strlen ((char *) start);
425         family = malloc (end - start + 1);
426         strncpy ((char *) family, (char *) start, end - start);
427         family[end - start] = '\0';
428         family_allocated = FcTrue;
429     }
430
431     if (FcDebug() & FC_DBG_SCAN)
432         printf ("\"%s\" \"%s\" ", family, style ? style : (FcChar8 *) "<none>");
433
434     if (!FcPatternAddString (pat, FC_FAMILY, family))
435     {
436         if (family_allocated)
437             free (family);
438         if (style_allocated)
439             free (style);
440         goto bail1;
441     }
442
443     if (family_allocated)
444         free (family);
445
446     if (style)
447     {
448         if (!FcPatternAddString (pat, FC_STYLE, style))
449         {
450             if (style_allocated)
451                 free (style);
452             goto bail1;
453         }
454         if (style_allocated)
455             free (style);
456     }
457
458     if (!FcPatternAddString (pat, FC_FILE, file))
459         goto bail1;
460
461     if (!FcPatternAddInteger (pat, FC_INDEX, id))
462         goto bail1;
463
464     if (!FcPatternAddString (pat, FC_SOURCE, (FcChar8 *) "FreeType"))
465         goto bail1;
466
467 #if 1
468     if ((face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) != 0)
469         if (!FcPatternAddInteger (pat, FC_SPACING, FC_MONO))
470             goto bail1;
471 #endif
472
473     /*
474      * Get the OS/2 table and poke about
475      */
476     os2 = (TT_OS2 *) FT_Get_Sfnt_Table (face, ft_sfnt_os2);
477     if (os2 && os2->version >= 0x0001 && os2->version != 0xffff)
478     {
479         for (i = 0; i < NUM_CODE_PAGE_RANGE; i++)
480         {
481             FT_ULong    bits;
482             int         bit;
483             if (FcCodePageRange[i].bit < 32)
484             {
485                 bits = os2->ulCodePageRange1;
486                 bit = FcCodePageRange[i].bit;
487             }
488             else
489             {
490                 bits = os2->ulCodePageRange2;
491                 bit = FcCodePageRange[i].bit - 32;
492             }
493             if (bits & (1 << bit))
494             {
495                 /* 
496                  * If the font advertises support for multiple
497                  * "exclusive" languages, then include support
498                  * for any language found to have coverage
499                  */
500                 if (exclusiveLang)
501                 {
502                     exclusiveLang = 0;
503                     break;
504                 }
505                 exclusiveLang = FcCodePageRange[i].lang;
506             }
507         }
508     }
509
510     /*
511      * Compute the unicode coverage for the font
512      */
513     cs = FcFreeTypeCharSet (face, blanks);
514     if (!cs)
515         goto bail1;
516
517     /*
518      * Skip over PCF fonts that have no encoded characters; they're
519      * usually just Unicode fonts transcoded to some legacy encoding
520      */
521     if (FcCharSetCount (cs) == 0)
522     {
523         if (!strcmp(FT_MODULE_CLASS(&face->driver->root)->module_name, "pcf"))
524             goto bail2;
525     }
526
527     if (!FcPatternAddCharSet (pat, FC_CHARSET, cs))
528         goto bail2;
529
530     ls = FcFreeTypeLangSet (cs, exclusiveLang);
531     if (!ls)
532         goto bail2;
533
534     if (!FcPatternAddLangSet (pat, FC_LANG, ls))
535         goto bail2;
536
537     /*
538      * Drop our reference to the charset
539      */
540     FcCharSetDestroy (cs);
541     
542     if (!(face->face_flags & FT_FACE_FLAG_SCALABLE))
543     {
544         for (i = 0; i < face->num_fixed_sizes; i++)
545             if (!FcPatternAddDouble (pat, FC_PIXEL_SIZE,
546                                      (double) face->available_sizes[i].height))
547                 goto bail1;
548         if (!FcPatternAddBool (pat, FC_ANTIALIAS, FcFalse))
549             goto bail1;
550     }
551
552     FT_Done_Face (face);
553     FT_Done_FreeType (ftLibrary);
554     return pat;
555
556 bail2:
557     FcCharSetDestroy (cs);
558 bail1:
559     FcPatternDestroy (pat);
560 bail0:
561     FT_Done_Face (face);
562 bail:
563     FT_Done_FreeType (ftLibrary);
564     return 0;
565 }