]> git.wh0rd.org - fontconfig.git/blob - src/fcfreetype.c
Add fontversion field
[fontconfig.git] / src / fcfreetype.c
1 /*
2 * $XFree86: xc/lib/fontconfig/src/fcfreetype.c,v 1.11 2002/08/31 22:17:32 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 TT_Header *head;
132 const FcChar8 *exclusiveLang = 0;
133 FT_SfntName sname;
134 FT_UInt snamei, snamec;
135 FcBool family_allocated = FcFalse;
136 FcBool style_allocated = FcFalse;
137 int family_prio = 0;
138 int style_prio = 0;
139
140 if (FT_Init_FreeType (&ftLibrary))
141 return 0;
142
143 if (FT_New_Face (ftLibrary, (char *) file, id, &face))
144 goto bail;
145
146 *count = face->num_faces;
147
148 pat = FcPatternCreate ();
149 if (!pat)
150 goto bail0;
151
152 if (!FcPatternAddBool (pat, FC_OUTLINE,
153 (face->face_flags & FT_FACE_FLAG_SCALABLE) != 0))
154 goto bail1;
155
156 if (!FcPatternAddBool (pat, FC_SCALABLE,
157 (face->face_flags & FT_FACE_FLAG_SCALABLE) != 0))
158 goto bail1;
159
160
161 slant = FC_SLANT_ROMAN;
162 if (face->style_flags & FT_STYLE_FLAG_ITALIC)
163 slant = FC_SLANT_ITALIC;
164
165 if (!FcPatternAddInteger (pat, FC_SLANT, slant))
166 goto bail1;
167
168 weight = FC_WEIGHT_MEDIUM;
169 if (face->style_flags & FT_STYLE_FLAG_BOLD)
170 weight = FC_WEIGHT_BOLD;
171
172 if (!FcPatternAddInteger (pat, FC_WEIGHT, weight))
173 goto bail1;
174
175 /*
176 * Grub through the name table looking for family
177 * and style names. FreeType makes quite a hash
178 * of them
179 */
180 family = 0;
181 style = 0;
182 snamec = FT_Get_Sfnt_Name_Count (face);
183 for (snamei = 0; snamei < snamec; snamei++)
184 {
185 FcChar8 *utf8;
186 int len;
187 int wchar;
188 FcChar8 *src;
189 int src_len;
190 FcChar8 *u8;
191 FcChar32 ucs4;
192 int ilen, olen;
193 int prio = 0;
194
195 const FcCharMap *map;
196 enum {
197 FcNameEncodingUtf16,
198 FcNameEncodingAppleRoman,
199 FcNameEncodingLatin1
200 } encoding;
201
202
203 if (FT_Get_Sfnt_Name (face, snamei, &sname) != 0)
204 break;
205
206 /*
207 * Look for Unicode strings
208 */
209 switch (sname.platform_id) {
210 case TT_PLATFORM_APPLE_UNICODE:
211 /*
212 * All APPLE_UNICODE encodings are Utf16 BE
213 *
214 * Because there's no language id for Unicode,
215 * assume it's English
216 */
217 prio |= FC_NAME_PRIO_LANG_ENGLISH;
218 prio |= FC_NAME_PRIO_ENC_UNICODE;
219 encoding = FcNameEncodingUtf16;
220 break;
221 case TT_PLATFORM_MACINTOSH:
222 switch (sname.encoding_id) {
223 case TT_MAC_ID_ROMAN:
224 encoding = FcNameEncodingAppleRoman;
225 break;
226 default:
227 continue;
228 }
229 switch (sname.language_id) {
230 case TT_MAC_LANGID_ENGLISH:
231 prio |= FC_NAME_PRIO_LANG_ENGLISH;
232 break;
233 default:
234 /*
235 * Sometimes Microsoft language ids
236 * end up in the macintosh table. This
237 * is often accompanied by data in
238 * some mystic encoding. Ignore these names
239 */
240 if (sname.language_id >= 0x100)
241 continue;
242 break;
243 }
244 break;
245 case TT_PLATFORM_MICROSOFT:
246 switch (sname.encoding_id) {
247 case TT_MS_ID_UNICODE_CS:
248 encoding = FcNameEncodingUtf16;
249 prio |= FC_NAME_PRIO_ENC_UNICODE;
250 break;
251 default:
252 continue;
253 }
254 switch (sname.language_id & 0xff) {
255 case 0x09:
256 prio |= FC_NAME_PRIO_LANG_ENGLISH;
257 break;
258 default:
259 break;
260 }
261 break;
262 case TT_PLATFORM_ISO:
263 switch (sname.encoding_id) {
264 case TT_ISO_ID_10646:
265 encoding = FcNameEncodingUtf16;
266 prio |= FC_NAME_PRIO_ENC_UNICODE;
267 break;
268 case TT_ISO_ID_7BIT_ASCII:
269 case TT_ISO_ID_8859_1:
270 encoding = FcNameEncodingLatin1;
271 break;
272 default:
273 continue;
274 }
275 break;
276 default:
277 continue;
278 }
279
280 /*
281 * Look for family and style names
282 */
283 switch (sname.name_id) {
284 case TT_NAME_ID_FONT_FAMILY:
285 prio |= FC_NAME_PRIO_NAME_FAMILY;
286 break;
287 case TT_NAME_ID_PS_NAME:
288 prio |= FC_NAME_PRIO_NAME_PS;
289 break;
290 case TT_NAME_ID_FONT_SUBFAMILY:
291 break;
292 default:
293 continue;
294 }
295
296 src = (FcChar8 *) sname.string;
297 src_len = sname.string_len;
298
299 switch (encoding) {
300 case FcNameEncodingUtf16:
301 /*
302 * Convert Utf16 to Utf8
303 */
304
305 if (!FcUtf16Len (src, FcEndianBig, src_len, &len, &wchar))
306 continue;
307
308 /*
309 * Allocate plenty of space. Freed below
310 */
311 utf8 = malloc (len * FC_UTF8_MAX_LEN + 1);
312 if (!utf8)
313 continue;
314
315 u8 = utf8;
316
317 while ((ilen = FcUtf16ToUcs4 (src, FcEndianBig, &ucs4, src_len)) > 0)
318 {
319 src_len -= ilen;
320 src += ilen;
321 olen = FcUcs4ToUtf8 (ucs4, u8);
322 u8 += olen;
323 }
324 *u8 = '\0';
325 break;
326 case FcNameEncodingLatin1:
327 /*
328 * Convert Latin1 to Utf8. Freed below
329 */
330 utf8 = malloc (src_len * 2 + 1);
331 if (!utf8)
332 continue;
333
334 u8 = utf8;
335 while (src_len > 0)
336 {
337 ucs4 = *src++;
338 src_len--;
339 olen = FcUcs4ToUtf8 (ucs4, u8);
340 u8 += olen;
341 }
342 *u8 = '\0';
343 break;
344 case FcNameEncodingAppleRoman:
345 /*
346 * Convert AppleRoman to Utf8
347 */
348 map = FcFreeTypeGetPrivateMap (ft_encoding_apple_roman);
349 if (!map)
350 continue;
351
352 /* freed below */
353 utf8 = malloc (src_len * 3 + 1);
354 if (!utf8)
355 continue;
356
357 u8 = utf8;
358 while (src_len > 0)
359 {
360 ucs4 = FcFreeTypePrivateToUcs4 (*src++, map);
361 src_len--;
362 olen = FcUcs4ToUtf8 (ucs4, u8);
363 u8 += olen;
364 }
365 *u8 = '\0';
366 break;
367 default:
368 continue;
369 }
370 if ((prio & FC_NAME_PRIO_LANG) == FC_NAME_PRIO_LANG_NONE)
371 if (FcUtf8IsLatin (utf8, strlen ((char *) utf8)))
372 prio |= FC_NAME_PRIO_LANG_LATIN;
373
374 if (FcDebug () & FC_DBG_SCANV)
375 printf ("\nfound name (name %d platform %d encoding %d language 0x%x prio 0x%x) %s\n",
376 sname.name_id, sname.platform_id,
377 sname.encoding_id, sname.language_id,
378 prio, utf8);
379
380 switch (sname.name_id) {
381 case TT_NAME_ID_FONT_FAMILY:
382 case TT_NAME_ID_PS_NAME:
383 if (!family || prio > family_prio)
384 {
385 if (family)
386 free (family);
387 family = utf8;
388 utf8 = 0;
389 family_allocated = FcTrue;
390 family_prio = prio;
391 }
392 break;
393 case TT_NAME_ID_FONT_SUBFAMILY:
394 if (!style || prio > style_prio)
395 {
396 if (style)
397 free (style);
398 style = utf8;
399 utf8 = 0;
400 style_allocated = FcTrue;
401 style_prio = prio;
402 }
403 break;
404 }
405 if (utf8)
406 free (utf8);
407 }
408
409 if (!family)
410 family = (FcChar8 *) face->family_name;
411
412 if (!style)
413 style = (FcChar8 *) face->style_name;
414
415 if (!family)
416 {
417 FcChar8 *start, *end;
418
419 start = (FcChar8 *) strrchr ((char *) file, '/');
420 if (start)
421 start++;
422 else
423 start = (FcChar8 *) file;
424 end = (FcChar8 *) strrchr ((char *) start, '.');
425 if (!end)
426 end = start + strlen ((char *) start);
427 /* freed below */
428 family = malloc (end - start + 1);
429 strncpy ((char *) family, (char *) start, end - start);
430 family[end - start] = '\0';
431 family_allocated = FcTrue;
432 }
433
434 if (FcDebug() & FC_DBG_SCAN)
435 printf ("\"%s\" \"%s\" ", family, style ? style : (FcChar8 *) "<none>");
436
437 if (!FcPatternAddString (pat, FC_FAMILY, family))
438 {
439 if (family_allocated)
440 free (family);
441 if (style_allocated)
442 free (style);
443 goto bail1;
444 }
445
446 if (family_allocated)
447 free (family);
448
449 if (style)
450 {
451 if (!FcPatternAddString (pat, FC_STYLE, style))
452 {
453 if (style_allocated)
454 free (style);
455 goto bail1;
456 }
457 if (style_allocated)
458 free (style);
459 }
460
461 if (!FcPatternAddString (pat, FC_FILE, file))
462 goto bail1;
463
464 if (!FcPatternAddInteger (pat, FC_INDEX, id))
465 goto bail1;
466
467 if (!FcPatternAddString (pat, FC_SOURCE, (FcChar8 *) "FreeType"))
468 goto bail1;
469
470 #if 1
471 if ((face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) != 0)
472 if (!FcPatternAddInteger (pat, FC_SPACING, FC_MONO))
473 goto bail1;
474 #endif
475
476 /*
477 * Find the font revision (if available)
478 */
479 head = (TT_Header *) FT_Get_Sfnt_Table (face, ft_sfnt_head);
480 if (head)
481 {
482 if (!FcPatternAddInteger (pat, FC_FONTVERSION, head->Font_Revision))
483 goto bail1;
484 }
485 else
486 {
487 if (!FcPatternAddInteger (pat, FC_FONTVERSION, 0))
488 goto bail1;
489 }
490
491 /*
492 * Get the OS/2 table and poke about
493 */
494 os2 = (TT_OS2 *) FT_Get_Sfnt_Table (face, ft_sfnt_os2);
495 if (os2 && os2->version >= 0x0001 && os2->version != 0xffff)
496 {
497 for (i = 0; i < NUM_CODE_PAGE_RANGE; i++)
498 {
499 FT_ULong bits;
500 int bit;
501 if (FcCodePageRange[i].bit < 32)
502 {
503 bits = os2->ulCodePageRange1;
504 bit = FcCodePageRange[i].bit;
505 }
506 else
507 {
508 bits = os2->ulCodePageRange2;
509 bit = FcCodePageRange[i].bit - 32;
510 }
511 if (bits & (1 << bit))
512 {
513 /*
514 * If the font advertises support for multiple
515 * "exclusive" languages, then include support
516 * for any language found to have coverage
517 */
518 if (exclusiveLang)
519 {
520 exclusiveLang = 0;
521 break;
522 }
523 exclusiveLang = FcCodePageRange[i].lang;
524 }
525 }
526 }
527
528 /*
529 * Compute the unicode coverage for the font
530 */
531 cs = FcFreeTypeCharSet (face, blanks);
532 if (!cs)
533 goto bail1;
534
535 /*
536 * Skip over PCF fonts that have no encoded characters; they're
537 * usually just Unicode fonts transcoded to some legacy encoding
538 */
539 if (FcCharSetCount (cs) == 0)
540 {
541 if (!strcmp(FT_MODULE_CLASS(&face->driver->root)->module_name, "pcf"))
542 goto bail2;
543 }
544
545 if (!FcPatternAddCharSet (pat, FC_CHARSET, cs))
546 goto bail2;
547
548 ls = FcFreeTypeLangSet (cs, exclusiveLang);
549 if (!ls)
550 goto bail2;
551
552 if (!FcPatternAddLangSet (pat, FC_LANG, ls))
553 goto bail2;
554
555 /*
556 * Drop our reference to the charset
557 */
558 FcCharSetDestroy (cs);
559
560 if (!(face->face_flags & FT_FACE_FLAG_SCALABLE))
561 {
562 for (i = 0; i < face->num_fixed_sizes; i++)
563 if (!FcPatternAddDouble (pat, FC_PIXEL_SIZE,
564 (double) face->available_sizes[i].height))
565 goto bail1;
566 if (!FcPatternAddBool (pat, FC_ANTIALIAS, FcFalse))
567 goto bail1;
568 }
569
570 FT_Done_Face (face);
571 FT_Done_FreeType (ftLibrary);
572 return pat;
573
574 bail2:
575 FcCharSetDestroy (cs);
576 bail1:
577 FcPatternDestroy (pat);
578 bail0:
579 FT_Done_Face (face);
580 bail:
581 FT_Done_FreeType (ftLibrary);
582 return 0;
583 }