Commit | Line | Data |
---|---|---|
92bda632 | 1 | #include "imager.h" |
fa16b6c6 | 2 | #include "imrender.h" |
02d1d628 AMH |
3 | |
4 | #include <sys/types.h> | |
5 | #include <sys/stat.h> | |
6 | #include <fcntl.h> | |
7 | ||
8 | #include <stdio.h> | |
9 | #include <stdlib.h> | |
10 | ||
f75c1aeb TC |
11 | #ifdef HAVE_LIBT1 |
12 | #include <t1lib.h> | |
13 | #endif | |
14 | ||
15 | ||
02d1d628 AMH |
16 | /* |
17 | =head1 NAME | |
18 | ||
19 | font.c - implements font handling functions for t1 and truetype fonts | |
20 | ||
21 | =head1 SYNOPSIS | |
22 | ||
23 | i_init_fonts(); | |
24 | ||
25 | #ifdef HAVE_LIBT1 | |
26 | fontnum = i_t1_new(path_to_pfb, path_to_afm); | |
27 | i_t1_bbox(fontnum, points, "foo", 3, int cords[6]); | |
28 | rc = i_t1_destroy(fontnum); | |
29 | #endif | |
30 | ||
31 | #ifdef HAVE_LIBTT | |
32 | handle = i_tt_new(path_to_ttf); | |
eeaa33fd | 33 | rc = i_tt_bbox(handle, points, "foo", 3, int cords[6], utf8); |
02d1d628 AMH |
34 | i_tt_destroy(handle); |
35 | ||
36 | // and much more | |
37 | ||
38 | =head1 DESCRIPTION | |
39 | ||
40 | font.c implements font creation, rendering, bounding box functions and | |
41 | more for Imager. | |
42 | ||
43 | =head1 FUNCTION REFERENCE | |
44 | ||
45 | Some of these functions are internal. | |
46 | ||
b8c2033e | 47 | =over |
02d1d628 AMH |
48 | |
49 | =cut | |
50 | ||
51 | */ | |
52 | ||
02d1d628 AMH |
53 | /* |
54 | =item i_init_fonts() | |
55 | ||
56 | Initialize font rendering libraries if they are avaliable. | |
57 | ||
58 | =cut | |
59 | */ | |
60 | ||
61 | undef_int | |
4cb58f1b | 62 | i_init_fonts(int t1log) { |
02d1d628 AMH |
63 | mm_log((1,"Initializing fonts\n")); |
64 | ||
65 | #ifdef HAVE_LIBT1 | |
d1555273 | 66 | if (i_init_t1(t1log)) |
faa9b3e7 TC |
67 | return 0; |
68 | #endif | |
d1555273 | 69 | |
02d1d628 AMH |
70 | return(1); /* FIXME: Always true - check the return values of the init_t1 and init_tt functions */ |
71 | } | |
72 | ||
73 | ||
74 | ||
75 | ||
76 | #ifdef HAVE_LIBT1 | |
77 | ||
1bd75e4c TC |
78 | static int t1_get_flags(char const *flags); |
79 | static char *t1_from_utf8(char const *in, int len, int *outlen); | |
02d1d628 | 80 | |
3799c4d1 TC |
81 | static void t1_push_error(void); |
82 | ||
3a12a980 TC |
83 | static int t1_active_fonts = 0; |
84 | static int t1_initialized = 0; | |
85 | ||
02d1d628 | 86 | /* |
4cb58f1b | 87 | =item i_init_t1(t1log) |
02d1d628 AMH |
88 | |
89 | Initializes the t1lib font rendering engine. | |
90 | ||
91 | =cut | |
92 | */ | |
93 | ||
94 | undef_int | |
b33c08f8 | 95 | i_init_t1(int t1log) { |
4cb58f1b | 96 | int init_flags = IGNORE_CONFIGFILE|IGNORE_FONTDATABASE; |
02d1d628 | 97 | mm_log((1,"init_t1()\n")); |
3a12a980 | 98 | |
d1555273 TC |
99 | i_clear_error(); |
100 | ||
3a12a980 TC |
101 | if (t1_active_fonts) { |
102 | mm_log((1, "Cannot re-initialize T1 - active fonts\n")); | |
d1555273 | 103 | i_push_error(0, "Cannot re-initialize T1 - active fonts"); |
3a12a980 TC |
104 | return 1; |
105 | } | |
106 | ||
107 | if (t1_initialized) { | |
108 | T1_CloseLib(); | |
109 | } | |
4cb58f1b TC |
110 | |
111 | if (t1log) | |
112 | init_flags |= LOGFILE; | |
113 | if ((T1_InitLib(init_flags) == NULL)){ | |
02d1d628 | 114 | mm_log((1,"Initialization of t1lib failed\n")); |
d1555273 | 115 | i_push_error(0, "T1_InitLib failed"); |
02d1d628 AMH |
116 | return(1); |
117 | } | |
118 | T1_SetLogLevel(T1LOG_DEBUG); | |
119 | i_t1_set_aa(1); /* Default Antialias value */ | |
3a12a980 TC |
120 | |
121 | ++t1_initialized; | |
122 | ||
02d1d628 AMH |
123 | return(0); |
124 | } | |
125 | ||
126 | ||
127 | /* | |
128 | =item i_close_t1() | |
129 | ||
130 | Shuts the t1lib font rendering engine down. | |
131 | ||
132 | This it seems that this function is never used. | |
133 | ||
134 | =cut | |
135 | */ | |
136 | ||
137 | void | |
faa9b3e7 | 138 | i_close_t1(void) { |
02d1d628 | 139 | T1_CloseLib(); |
3a12a980 | 140 | t1_initialized = 0; |
02d1d628 AMH |
141 | } |
142 | ||
143 | ||
144 | /* | |
145 | =item i_t1_new(pfb, afm) | |
146 | ||
147 | Loads the fonts with the given filenames, returns its font id | |
148 | ||
149 | pfb - path to pfb file for font | |
150 | afm - path to afm file for font | |
151 | ||
152 | =cut | |
153 | */ | |
154 | ||
155 | int | |
156 | i_t1_new(char *pfb,char *afm) { | |
157 | int font_id; | |
1bd75e4c | 158 | |
d1555273 TC |
159 | i_clear_error(); |
160 | ||
161 | if (!t1_initialized && i_init_t1(0)) | |
162 | return -1; | |
163 | ||
02d1d628 AMH |
164 | mm_log((1,"i_t1_new(pfb %s,afm %s)\n",pfb,(afm?afm:"NULL"))); |
165 | font_id = T1_AddFont(pfb); | |
166 | if (font_id<0) { | |
167 | mm_log((1,"i_t1_new: Failed to load pfb file '%s' - return code %d.\n",pfb,font_id)); | |
168 | return font_id; | |
169 | } | |
170 | ||
171 | if (afm != NULL) { | |
172 | mm_log((1,"i_t1_new: requesting afm file '%s'.\n",afm)); | |
173 | if (T1_SetAfmFileName(font_id,afm)<0) mm_log((1,"i_t1_new: afm loading of '%s' failed.\n",afm)); | |
174 | } | |
1bd75e4c | 175 | |
3a12a980 TC |
176 | ++t1_active_fonts; |
177 | ||
02d1d628 AMH |
178 | return font_id; |
179 | } | |
180 | ||
181 | /* | |
182 | =item i_t1_destroy(font_id) | |
183 | ||
184 | Frees resources for a t1 font with given font id. | |
185 | ||
186 | font_id - number of the font to free | |
187 | ||
188 | =cut | |
189 | */ | |
190 | ||
191 | int | |
192 | i_t1_destroy(int font_id) { | |
193 | mm_log((1,"i_t1_destroy(font_id %d)\n",font_id)); | |
3a12a980 TC |
194 | |
195 | --t1_active_fonts; | |
196 | ||
02d1d628 AMH |
197 | return T1_DeleteFont(font_id); |
198 | } | |
199 | ||
200 | ||
201 | /* | |
202 | =item i_t1_set_aa(st) | |
203 | ||
204 | Sets the antialiasing level of the t1 library. | |
205 | ||
206 | st - 0 = NONE, 1 = LOW, 2 = HIGH. | |
207 | ||
208 | =cut | |
209 | */ | |
210 | ||
211 | void | |
212 | i_t1_set_aa(int st) { | |
213 | int i; | |
214 | unsigned long cst[17]; | |
215 | switch(st) { | |
216 | case 0: | |
217 | T1_AASetBitsPerPixel( 8 ); | |
218 | T1_AASetLevel( T1_AA_NONE ); | |
219 | T1_AANSetGrayValues( 0, 255 ); | |
220 | mm_log((1,"setting T1 antialias to none\n")); | |
221 | break; | |
222 | case 1: | |
223 | T1_AASetBitsPerPixel( 8 ); | |
224 | T1_AASetLevel( T1_AA_LOW ); | |
225 | T1_AASetGrayValues( 0,65,127,191,255 ); | |
226 | mm_log((1,"setting T1 antialias to low\n")); | |
227 | break; | |
228 | case 2: | |
229 | T1_AASetBitsPerPixel(8); | |
230 | T1_AASetLevel(T1_AA_HIGH); | |
231 | for(i=0;i<17;i++) cst[i]=(i*255)/16; | |
232 | T1_AAHSetGrayValues( cst ); | |
233 | mm_log((1,"setting T1 antialias to high\n")); | |
234 | } | |
235 | } | |
236 | ||
237 | ||
238 | /* | |
239 | =item i_t1_cp(im, xb, yb, channel, fontnum, points, str, len, align) | |
240 | ||
241 | Interface to text rendering into a single channel in an image | |
242 | ||
243 | im pointer to image structure | |
244 | xb x coordinate of start of string | |
245 | yb y coordinate of start of string ( see align ) | |
246 | channel - destination channel | |
247 | fontnum - t1 library font id | |
248 | points - number of points in fontheight | |
249 | str - string to render | |
250 | len - string length | |
251 | align - (0 - top of font glyph | 1 - baseline ) | |
252 | ||
253 | =cut | |
254 | */ | |
255 | ||
256 | undef_int | |
1bd75e4c | 257 | i_t1_cp(i_img *im,int xb,int yb,int channel,int fontnum,float points,char* str,int len,int align, int utf8, char const *flags) { |
02d1d628 AMH |
258 | GLYPH *glyph; |
259 | int xsize,ysize,x,y; | |
260 | i_color val; | |
1bd75e4c | 261 | int mod_flags = t1_get_flags(flags); |
02d1d628 AMH |
262 | |
263 | unsigned int ch_mask_store; | |
264 | ||
265 | if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); } | |
266 | ||
1bd75e4c TC |
267 | if (utf8) { |
268 | int worklen; | |
269 | char *work = t1_from_utf8(str, len, &worklen); | |
270 | glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL); | |
271 | myfree(work); | |
272 | } | |
273 | else { | |
274 | glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL); | |
275 | } | |
faa9b3e7 TC |
276 | if (glyph == NULL) |
277 | return 0; | |
02d1d628 AMH |
278 | |
279 | mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent)); | |
280 | mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing)); | |
281 | mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY)); | |
282 | mm_log((1,"bpp: %d\n",glyph->bpp)); | |
283 | ||
284 | xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing; | |
285 | ysize=glyph->metrics.ascent-glyph->metrics.descent; | |
286 | ||
287 | mm_log((1,"width: %d height: %d\n",xsize,ysize)); | |
288 | ||
289 | ch_mask_store=im->ch_mask; | |
290 | im->ch_mask=1<<channel; | |
291 | ||
292 | if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; } | |
293 | ||
294 | for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) { | |
295 | val.channel[channel]=glyph->bits[y*xsize+x]; | |
296 | i_ppix(im,x+xb,y+yb,&val); | |
297 | } | |
298 | ||
299 | im->ch_mask=ch_mask_store; | |
300 | return 1; | |
301 | } | |
302 | ||
5eebac42 TC |
303 | static void |
304 | t1_fix_bbox(BBox *bbox, const char *str, int len, int advance, | |
305 | int space_position) { | |
306 | /* never called with len == 0 */ | |
307 | if (str[0] == space_position && bbox->llx > 0) | |
308 | bbox->llx = 0; | |
309 | if (str[len-1] == space_position && bbox->urx < advance) | |
310 | bbox->urx = advance; | |
311 | if (bbox->lly > bbox->ury) | |
312 | bbox->lly = bbox->ury = 0; | |
313 | } | |
02d1d628 AMH |
314 | |
315 | /* | |
316 | =item i_t1_bbox(handle, fontnum, points, str, len, cords) | |
317 | ||
318 | function to get a strings bounding box given the font id and sizes | |
319 | ||
320 | handle - pointer to font handle | |
321 | fontnum - t1 library font id | |
322 | points - number of points in fontheight | |
323 | str - string to measure | |
324 | len - string length | |
325 | cords - the bounding box (modified in place) | |
326 | ||
327 | =cut | |
328 | */ | |
329 | ||
3799c4d1 | 330 | int |
97ac0a96 | 331 | i_t1_bbox(int fontnum,float points,const char *str,int len,int cords[6], int utf8,char const *flags) { |
02d1d628 AMH |
332 | BBox bbox; |
333 | BBox gbbox; | |
1bd75e4c | 334 | int mod_flags = t1_get_flags(flags); |
3799c4d1 | 335 | int advance; |
5eebac42 | 336 | int space_position = T1_GetEncodingIndex(fontnum, "space"); |
02d1d628 AMH |
337 | |
338 | mm_log((1,"i_t1_bbox(fontnum %d,points %.2f,str '%.*s', len %d)\n",fontnum,points,len,str,len)); | |
339 | T1_LoadFont(fontnum); /* FIXME: Here a return code is ignored - haw haw haw */ | |
5eebac42 TC |
340 | |
341 | if (len == 0) { | |
342 | /* len == 0 has special meaning to T1lib, but it means there's | |
343 | nothing to draw, so return that */ | |
344 | bbox.llx = bbox.lly = bbox.urx = bbox.ury = 0; | |
345 | advance = 0; | |
1bd75e4c TC |
346 | } |
347 | else { | |
5eebac42 TC |
348 | if (utf8) { |
349 | int worklen; | |
350 | char *work = t1_from_utf8(str, len, &worklen); | |
c1b3b04b | 351 | advance = T1_GetStringWidth(fontnum, work, worklen, 0, mod_flags); |
5eebac42 TC |
352 | bbox = T1_GetStringBBox(fontnum,work,worklen,0,mod_flags); |
353 | t1_fix_bbox(&bbox, work, worklen, advance, space_position); | |
354 | myfree(work); | |
355 | } | |
356 | else { | |
c1b3b04b | 357 | advance = T1_GetStringWidth(fontnum, (char *)str, len, 0, mod_flags); |
5eebac42 TC |
358 | bbox = T1_GetStringBBox(fontnum,(char *)str,len,0,mod_flags); |
359 | t1_fix_bbox(&bbox, str, len, advance, space_position); | |
360 | } | |
1bd75e4c | 361 | } |
02d1d628 AMH |
362 | gbbox = T1_GetFontBBox(fontnum); |
363 | ||
364 | mm_log((1,"bbox: (%d,%d,%d,%d)\n", | |
365 | (int)(bbox.llx*points/1000), | |
366 | (int)(gbbox.lly*points/1000), | |
367 | (int)(bbox.urx*points/1000), | |
368 | (int)(gbbox.ury*points/1000), | |
369 | (int)(bbox.lly*points/1000), | |
370 | (int)(bbox.ury*points/1000) )); | |
371 | ||
372 | ||
3799c4d1 TC |
373 | cords[BBOX_NEG_WIDTH]=((float)bbox.llx*points)/1000; |
374 | cords[BBOX_POS_WIDTH]=((float)bbox.urx*points)/1000; | |
375 | ||
376 | cords[BBOX_GLOBAL_DESCENT]=((float)gbbox.lly*points)/1000; | |
377 | cords[BBOX_GLOBAL_ASCENT]=((float)gbbox.ury*points)/1000; | |
378 | ||
379 | cords[BBOX_DESCENT]=((float)bbox.lly*points)/1000; | |
380 | cords[BBOX_ASCENT]=((float)bbox.ury*points)/1000; | |
02d1d628 | 381 | |
3799c4d1 | 382 | cords[BBOX_ADVANCE_WIDTH] = ((float)advance * points)/1000; |
7fdbfba8 TC |
383 | cords[BBOX_RIGHT_BEARING] = |
384 | cords[BBOX_ADVANCE_WIDTH] - cords[BBOX_POS_WIDTH]; | |
02d1d628 | 385 | |
7fdbfba8 | 386 | return BBOX_RIGHT_BEARING+1; |
02d1d628 AMH |
387 | } |
388 | ||
389 | ||
390 | /* | |
391 | =item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align) | |
392 | ||
393 | Interface to text rendering in a single color onto an image | |
394 | ||
395 | im - pointer to image structure | |
396 | xb - x coordinate of start of string | |
397 | yb - y coordinate of start of string ( see align ) | |
398 | cl - color to draw the text in | |
399 | fontnum - t1 library font id | |
400 | points - number of points in fontheight | |
401 | str - char pointer to string to render | |
402 | len - string length | |
403 | align - (0 - top of font glyph | 1 - baseline ) | |
404 | ||
405 | =cut | |
406 | */ | |
407 | ||
408 | undef_int | |
97ac0a96 | 409 | i_t1_text(i_img *im,int xb,int yb,const i_color *cl,int fontnum,float points,const char* str,int len,int align, int utf8, char const *flags) { |
02d1d628 | 410 | GLYPH *glyph; |
919e0000 | 411 | int xsize,ysize,y; |
1bd75e4c | 412 | int mod_flags = t1_get_flags(flags); |
4c84ccfb | 413 | i_render r; |
02d1d628 AMH |
414 | |
415 | if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); } | |
416 | ||
1bd75e4c TC |
417 | if (utf8) { |
418 | int worklen; | |
419 | char *work = t1_from_utf8(str, len, &worklen); | |
420 | glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL); | |
421 | myfree(work); | |
422 | } | |
423 | else { | |
97ac0a96 TC |
424 | /* T1_AASetString() accepts a char * not a const char */ |
425 | glyph=T1_AASetString( fontnum, (char *)str, len, 0, mod_flags, points, NULL); | |
1bd75e4c | 426 | } |
faa9b3e7 TC |
427 | if (glyph == NULL) |
428 | return 0; | |
02d1d628 AMH |
429 | |
430 | mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent)); | |
431 | mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing)); | |
432 | mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY)); | |
433 | mm_log((1,"bpp: %d\n",glyph->bpp)); | |
434 | ||
435 | xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing; | |
436 | ysize=glyph->metrics.ascent-glyph->metrics.descent; | |
437 | ||
438 | mm_log((1,"width: %d height: %d\n",xsize,ysize)); | |
439 | ||
440 | if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; } | |
4c84ccfb TC |
441 | |
442 | i_render_init(&r, im, xsize); | |
443 | for(y=0;y<ysize;y++) { | |
919e0000 | 444 | i_render_color(&r, xb, yb+y, xsize, (unsigned char *)glyph->bits+y*xsize, cl); |
02d1d628 | 445 | } |
4c84ccfb TC |
446 | i_render_done(&r); |
447 | ||
02d1d628 AMH |
448 | return 1; |
449 | } | |
450 | ||
1bd75e4c TC |
451 | /* |
452 | =item t1_get_flags(flags) | |
453 | ||
454 | Processes the characters in I<flags> to create a mod_flags value used | |
455 | by some T1Lib functions. | |
456 | ||
457 | =cut | |
458 | */ | |
459 | static int | |
460 | t1_get_flags(char const *flags) { | |
461 | int mod_flags = T1_KERNING; | |
462 | ||
463 | while (*flags) { | |
464 | switch (*flags++) { | |
465 | case 'u': case 'U': mod_flags |= T1_UNDERLINE; break; | |
466 | case 'o': case 'O': mod_flags |= T1_OVERLINE; break; | |
467 | case 's': case 'S': mod_flags |= T1_OVERSTRIKE; break; | |
468 | /* ignore anything we don't recognize */ | |
469 | } | |
470 | } | |
471 | ||
472 | return mod_flags; | |
473 | } | |
474 | ||
475 | /* | |
476 | =item t1_from_utf8(char const *in, int len, int *outlen) | |
477 | ||
478 | Produces an unencoded version of I<in> by dropping any Unicode | |
479 | character over 255. | |
480 | ||
481 | Returns a newly allocated buffer which should be freed with myfree(). | |
482 | Sets *outlen to the number of bytes used in the output string. | |
483 | ||
484 | =cut | |
485 | */ | |
486 | ||
487 | static char * | |
488 | t1_from_utf8(char const *in, int len, int *outlen) { | |
f0960b14 TC |
489 | /* at this point len is from a perl SV, so can't approach MAXINT */ |
490 | char *out = mymalloc(len+1); /* checked 5Nov05 tonyc */ | |
1bd75e4c TC |
491 | char *p = out; |
492 | unsigned long c; | |
493 | ||
494 | while (len) { | |
495 | c = i_utf8_advance(&in, &len); | |
496 | if (c == ~0UL) { | |
497 | myfree(out); | |
498 | i_push_error(0, "invalid UTF8 character"); | |
499 | return 0; | |
500 | } | |
501 | /* yeah, just drop them */ | |
502 | if (c < 0x100) { | |
503 | *p++ = (char)c; | |
504 | } | |
505 | } | |
506 | *p = '\0'; | |
507 | *outlen = p - out; | |
508 | ||
509 | return out; | |
510 | } | |
02d1d628 | 511 | |
3799c4d1 TC |
512 | /* |
513 | =item i_t1_has_chars(font_num, text, len, utf8, out) | |
514 | ||
515 | Check if the given characters are defined by the font. Note that len | |
516 | is the number of bytes, not the number of characters (when utf8 is | |
517 | non-zero). | |
518 | ||
519 | out[char index] will be true if the character exists. | |
520 | ||
521 | Accepts UTF-8, but since T1 can only have 256 characters, any chars | |
522 | with values over 255 will simply be returned as false. | |
523 | ||
524 | Returns the number of characters that were checked. | |
525 | ||
526 | =cut | |
527 | */ | |
528 | ||
529 | int | |
530 | i_t1_has_chars(int font_num, const char *text, int len, int utf8, | |
531 | char *out) { | |
532 | int count = 0; | |
533 | ||
534 | mm_log((1, "i_t1_has_chars(font_num %d, text %p, len %d, utf8 %d)\n", | |
535 | font_num, text, len, utf8)); | |
536 | ||
537 | i_clear_error(); | |
538 | if (T1_LoadFont(font_num)) { | |
539 | t1_push_error(); | |
540 | return 0; | |
541 | } | |
542 | ||
543 | while (len) { | |
544 | unsigned long c; | |
3799c4d1 TC |
545 | if (utf8) { |
546 | c = i_utf8_advance(&text, &len); | |
547 | if (c == ~0UL) { | |
548 | i_push_error(0, "invalid UTF8 character"); | |
549 | return 0; | |
550 | } | |
551 | } | |
552 | else { | |
553 | c = (unsigned char)*text++; | |
554 | --len; | |
555 | } | |
556 | ||
557 | if (c >= 0x100) { | |
558 | /* limit of 256 characters for T1 */ | |
559 | *out++ = 0; | |
560 | } | |
561 | else { | |
562 | char const * name = T1_GetCharName(font_num, (unsigned char)c); | |
563 | ||
564 | if (name) { | |
565 | *out++ = strcmp(name, ".notdef") != 0; | |
566 | } | |
567 | else { | |
568 | mm_log((2, " No name found for character %lx\n", c)); | |
569 | *out++ = 0; | |
570 | } | |
571 | } | |
572 | ++count; | |
573 | } | |
574 | ||
575 | return count; | |
576 | } | |
577 | ||
578 | /* | |
579 | =item i_t1_face_name(font_num, name_buf, name_buf_size) | |
580 | ||
581 | Copies the face name of the given C<font_num> to C<name_buf>. Returns | |
582 | the number of characters required to store the name (which can be | |
583 | larger than C<name_buf_size>, including the space required to store | |
584 | the terminating NUL). | |
585 | ||
586 | If name_buf is too small (as specified by name_buf_size) then the name | |
587 | will be truncated. name_buf will always be NUL termintaed. | |
588 | ||
589 | =cut | |
590 | */ | |
591 | ||
592 | int | |
593 | i_t1_face_name(int font_num, char *name_buf, size_t name_buf_size) { | |
594 | char *name; | |
595 | ||
596 | T1_errno = 0; | |
597 | if (T1_LoadFont(font_num)) { | |
598 | t1_push_error(); | |
599 | return 0; | |
600 | } | |
601 | name = T1_GetFontName(font_num); | |
602 | ||
603 | if (name) { | |
604 | strncpy(name_buf, name, name_buf_size); | |
605 | name_buf[name_buf_size-1] = '\0'; | |
606 | return strlen(name) + 1; | |
607 | } | |
608 | else { | |
609 | t1_push_error(); | |
610 | return 0; | |
611 | } | |
612 | } | |
613 | ||
614 | int | |
615 | i_t1_glyph_name(int font_num, unsigned long ch, char *name_buf, | |
616 | size_t name_buf_size) { | |
617 | char *name; | |
618 | ||
619 | i_clear_error(); | |
620 | if (ch > 0xFF) { | |
621 | return 0; | |
622 | } | |
623 | if (T1_LoadFont(font_num)) { | |
624 | t1_push_error(); | |
625 | return 0; | |
626 | } | |
627 | name = T1_GetCharName(font_num, (unsigned char)ch); | |
628 | if (name) { | |
629 | if (strcmp(name, ".notdef")) { | |
630 | strncpy(name_buf, name, name_buf_size); | |
631 | name_buf[name_buf_size-1] = '\0'; | |
632 | return strlen(name) + 1; | |
633 | } | |
634 | else { | |
635 | return 0; | |
636 | } | |
637 | } | |
638 | else { | |
639 | t1_push_error(); | |
640 | return 0; | |
641 | } | |
642 | } | |
643 | ||
644 | static void | |
645 | t1_push_error(void) { | |
646 | switch (T1_errno) { | |
647 | case 0: | |
648 | i_push_error(0, "No error"); | |
649 | break; | |
650 | ||
d5a3f460 | 651 | #ifdef T1ERR_SCAN_FONT_FORMAT |
3799c4d1 TC |
652 | case T1ERR_SCAN_FONT_FORMAT: |
653 | i_push_error(T1ERR_SCAN_FONT_FORMAT, "SCAN_FONT_FORMAT"); | |
654 | break; | |
d5a3f460 | 655 | #endif |
3799c4d1 | 656 | |
d5a3f460 | 657 | #ifdef T1ERR_SCAN_FILE_OPEN_ERR |
3799c4d1 TC |
658 | case T1ERR_SCAN_FILE_OPEN_ERR: |
659 | i_push_error(T1ERR_SCAN_FILE_OPEN_ERR, "SCAN_FILE_OPEN_ERR"); | |
660 | break; | |
d5a3f460 | 661 | #endif |
3799c4d1 | 662 | |
d5a3f460 | 663 | #ifdef T1ERR_SCAN_OUT_OF_MEMORY |
3799c4d1 TC |
664 | case T1ERR_SCAN_OUT_OF_MEMORY: |
665 | i_push_error(T1ERR_SCAN_OUT_OF_MEMORY, "SCAN_OUT_OF_MEMORY"); | |
666 | break; | |
d5a3f460 | 667 | #endif |
3799c4d1 | 668 | |
d5a3f460 | 669 | #ifdef T1ERR_SCAN_ERROR |
3799c4d1 TC |
670 | case T1ERR_SCAN_ERROR: |
671 | i_push_error(T1ERR_SCAN_ERROR, "SCAN_ERROR"); | |
672 | break; | |
d5a3f460 | 673 | #endif |
3799c4d1 | 674 | |
d5a3f460 | 675 | #ifdef T1ERR_SCAN_FILE_EOF |
3799c4d1 TC |
676 | case T1ERR_SCAN_FILE_EOF: |
677 | i_push_error(T1ERR_SCAN_FILE_EOF, "SCAN_FILE_EOF"); | |
678 | break; | |
d5a3f460 | 679 | #endif |
3799c4d1 | 680 | |
d5a3f460 | 681 | #ifdef T1ERR_PATH_ERROR |
3799c4d1 TC |
682 | case T1ERR_PATH_ERROR: |
683 | i_push_error(T1ERR_PATH_ERROR, "PATH_ERROR"); | |
684 | break; | |
d5a3f460 | 685 | #endif |
3799c4d1 | 686 | |
d5a3f460 | 687 | #ifdef T1ERR_PARSE_ERROR |
3799c4d1 TC |
688 | case T1ERR_PARSE_ERROR: |
689 | i_push_error(T1ERR_PARSE_ERROR, "PARSE_ERROR"); | |
690 | break; | |
d5a3f460 | 691 | #endif |
3799c4d1 | 692 | |
d5a3f460 | 693 | #ifdef T1ERR_TYPE1_ABORT |
3799c4d1 TC |
694 | case T1ERR_TYPE1_ABORT: |
695 | i_push_error(T1ERR_TYPE1_ABORT, "TYPE1_ABORT"); | |
696 | break; | |
d5a3f460 | 697 | #endif |
3799c4d1 | 698 | |
d5a3f460 | 699 | #ifdef T1ERR_INVALID_FONTID |
3799c4d1 TC |
700 | case T1ERR_INVALID_FONTID: |
701 | i_push_error(T1ERR_INVALID_FONTID, "INVALID_FONTID"); | |
702 | break; | |
d5a3f460 | 703 | #endif |
3799c4d1 | 704 | |
d5a3f460 | 705 | #ifdef T1ERR_INVALID_PARAMETER |
3799c4d1 TC |
706 | case T1ERR_INVALID_PARAMETER: |
707 | i_push_error(T1ERR_INVALID_PARAMETER, "INVALID_PARAMETER"); | |
708 | break; | |
d5a3f460 | 709 | #endif |
3799c4d1 | 710 | |
d5a3f460 | 711 | #ifdef T1ERR_OP_NOT_PERMITTED |
3799c4d1 TC |
712 | case T1ERR_OP_NOT_PERMITTED: |
713 | i_push_error(T1ERR_OP_NOT_PERMITTED, "OP_NOT_PERMITTED"); | |
714 | break; | |
d5a3f460 | 715 | #endif |
3799c4d1 | 716 | |
d5a3f460 | 717 | #ifdef T1ERR_ALLOC_MEM |
3799c4d1 TC |
718 | case T1ERR_ALLOC_MEM: |
719 | i_push_error(T1ERR_ALLOC_MEM, "ALLOC_MEM"); | |
720 | break; | |
d5a3f460 | 721 | #endif |
3799c4d1 | 722 | |
d5a3f460 | 723 | #ifdef T1ERR_FILE_OPEN_ERR |
3799c4d1 TC |
724 | case T1ERR_FILE_OPEN_ERR: |
725 | i_push_error(T1ERR_FILE_OPEN_ERR, "FILE_OPEN_ERR"); | |
726 | break; | |
d5a3f460 | 727 | #endif |
3799c4d1 | 728 | |
d5a3f460 | 729 | #ifdef T1ERR_UNSPECIFIED |
3799c4d1 TC |
730 | case T1ERR_UNSPECIFIED: |
731 | i_push_error(T1ERR_UNSPECIFIED, "UNSPECIFIED"); | |
732 | break; | |
d5a3f460 | 733 | #endif |
3799c4d1 | 734 | |
d5a3f460 | 735 | #ifdef T1ERR_NO_AFM_DATA |
3799c4d1 TC |
736 | case T1ERR_NO_AFM_DATA: |
737 | i_push_error(T1ERR_NO_AFM_DATA, "NO_AFM_DATA"); | |
738 | break; | |
d5a3f460 | 739 | #endif |
3799c4d1 | 740 | |
d5a3f460 | 741 | #ifdef T1ERR_X11 |
3799c4d1 TC |
742 | case T1ERR_X11: |
743 | i_push_error(T1ERR_X11, "X11"); | |
744 | break; | |
d5a3f460 | 745 | #endif |
3799c4d1 | 746 | |
d5a3f460 | 747 | #ifdef T1ERR_COMPOSITE_CHAR |
3799c4d1 TC |
748 | case T1ERR_COMPOSITE_CHAR: |
749 | i_push_error(T1ERR_COMPOSITE_CHAR, "COMPOSITE_CHAR"); | |
750 | break; | |
d5a3f460 | 751 | #endif |
3799c4d1 TC |
752 | |
753 | default: | |
754 | i_push_errorf(T1_errno, "unknown error %d", (int)T1_errno); | |
755 | } | |
756 | } | |
757 | ||
02d1d628 AMH |
758 | #endif /* HAVE_LIBT1 */ |
759 | ||
760 | ||
4f68b48f | 761 | /* Truetype font support */ |
4f68b48f | 762 | #ifdef HAVE_LIBTT |
02d1d628 | 763 | |
d93d5c10 | 764 | /* These are enabled by default when configuring Freetype 1.x |
3799c4d1 TC |
765 | I haven't a clue how to reliably detect it at compile time. |
766 | ||
767 | We need a compilation probe in Makefile.PL | |
768 | */ | |
769 | #define FTXPOST 1 | |
d93d5c10 | 770 | #define FTXERR18 1 |
3799c4d1 | 771 | |
4f68b48f TC |
772 | #include <freetype.h> |
773 | #define TT_CHC 5 | |
02d1d628 | 774 | |
3799c4d1 TC |
775 | #ifdef FTXPOST |
776 | #include <ftxpost.h> | |
777 | #endif | |
778 | ||
d93d5c10 TC |
779 | #ifdef FTXERR18 |
780 | #include <ftxerr18.h> | |
781 | #endif | |
782 | ||
a90b253d TC |
783 | /* some versions of FT1.x don't seem to define this - it's font defined |
784 | so it won't change */ | |
785 | #ifndef TT_MS_LANGID_ENGLISH_GENERAL | |
786 | #define TT_MS_LANGID_ENGLISH_GENERAL 0x0409 | |
787 | #endif | |
788 | ||
4f68b48f TC |
789 | /* convert a code point into an index in the glyph cache */ |
790 | #define TT_HASH(x) ((x) & 0xFF) | |
02d1d628 | 791 | |
4f68b48f TC |
792 | typedef struct i_glyph_entry_ { |
793 | TT_Glyph glyph; | |
794 | unsigned long ch; | |
795 | } i_tt_glyph_entry; | |
02d1d628 | 796 | |
4f68b48f | 797 | #define TT_NOCHAR (~0UL) |
02d1d628 | 798 | |
4f68b48f TC |
799 | struct TT_Instancehandle_ { |
800 | TT_Instance instance; | |
801 | TT_Instance_Metrics imetrics; | |
802 | TT_Glyph_Metrics gmetrics[256]; | |
803 | i_tt_glyph_entry glyphs[256]; | |
804 | int smooth; | |
805 | int ptsize; | |
806 | int order; | |
807 | }; | |
02d1d628 | 808 | |
4f68b48f | 809 | typedef struct TT_Instancehandle_ TT_Instancehandle; |
02d1d628 | 810 | |
4f68b48f TC |
811 | struct TT_Fonthandle_ { |
812 | TT_Face face; | |
813 | TT_Face_Properties properties; | |
814 | TT_Instancehandle instanceh[TT_CHC]; | |
815 | TT_CharMap char_map; | |
3799c4d1 TC |
816 | #ifdef FTXPOST |
817 | int loaded_names; | |
818 | TT_Error load_cond; | |
819 | #endif | |
4f68b48f | 820 | }; |
02d1d628 AMH |
821 | |
822 | /* Defines */ | |
823 | ||
824 | #define USTRCT(x) ((x).z) | |
825 | #define TT_VALID( handle ) ( ( handle ).z != NULL ) | |
826 | ||
d93d5c10 | 827 | static void i_tt_push_error(TT_Error rc); |
02d1d628 AMH |
828 | |
829 | /* Prototypes */ | |
830 | ||
831 | static int i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ); | |
832 | static void i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth ); | |
833 | static void i_tt_done_raster_map( TT_Raster_Map *bit ); | |
834 | static void i_tt_clear_raster_map( TT_Raster_Map* bit ); | |
835 | static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off ); | |
4f68b48f TC |
836 | static int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j ); |
837 | static void | |
838 | i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics, | |
839 | TT_Raster_Map *bit, TT_Raster_Map *small_bit, | |
840 | int x_off, int y_off, int smooth ); | |
841 | static int | |
842 | i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit, | |
843 | TT_Raster_Map *small_bit, int cords[6], | |
844 | char const* txt, int len, int smooth, int utf8 ); | |
97ac0a96 | 845 | static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, const i_color *cl, int smooth ); |
02d1d628 | 846 | static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth ); |
4f68b48f TC |
847 | static int |
848 | i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], | |
849 | float points, char const* txt, int len, int smooth, int utf8 ); | |
850 | static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6], int utf8 ); | |
02d1d628 AMH |
851 | |
852 | ||
853 | /* static globals needed */ | |
854 | ||
d1555273 | 855 | static int TT_initialized = 0; |
02d1d628 AMH |
856 | static TT_Engine engine; |
857 | static int LTT_dpi = 72; /* FIXME: this ought to be a part of the call interface */ | |
858 | static int LTT_hinted = 1; /* FIXME: this too */ | |
859 | ||
860 | ||
861 | /* | |
862 | * FreeType interface | |
863 | */ | |
864 | ||
865 | ||
866 | /* | |
867 | =item init_tt() | |
868 | ||
869 | Initializes the freetype font rendering engine | |
870 | ||
871 | =cut | |
872 | */ | |
873 | ||
874 | undef_int | |
d1555273 | 875 | i_init_tt(void) { |
02d1d628 | 876 | TT_Error error; |
95b2bff4 TC |
877 | TT_Byte palette[] = { 0, 64, 127, 191, 255 }; |
878 | ||
d1555273 TC |
879 | i_clear_error(); |
880 | ||
02d1d628 AMH |
881 | mm_log((1,"init_tt()\n")); |
882 | error = TT_Init_FreeType( &engine ); | |
883 | if ( error ){ | |
884 | mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error)); | |
d1555273 TC |
885 | i_tt_push_error(error); |
886 | i_push_error(0, "Could not initialize freetype 1.x"); | |
02d1d628 AMH |
887 | return(1); |
888 | } | |
3799c4d1 TC |
889 | |
890 | #ifdef FTXPOST | |
891 | error = TT_Init_Post_Extension( engine ); | |
892 | if (error) { | |
893 | mm_log((1, "Initialization of Post extension failed = 0x%x\n", error)); | |
d1555273 TC |
894 | |
895 | i_tt_push_error(error); | |
896 | i_push_error(0, "Could not initialize FT 1.x POST extension"); | |
3799c4d1 TC |
897 | return 1; |
898 | } | |
899 | #endif | |
900 | ||
95b2bff4 TC |
901 | error = TT_Set_Raster_Gray_Palette(engine, palette); |
902 | if (error) { | |
903 | mm_log((1, "Initialization of gray levels failed = 0x%x\n", error)); | |
d1555273 TC |
904 | i_tt_push_error(error); |
905 | i_push_error(0, "Could not initialize FT 1.x POST extension"); | |
95b2bff4 TC |
906 | return 1; |
907 | } | |
908 | ||
d1555273 TC |
909 | TT_initialized = 1; |
910 | ||
02d1d628 AMH |
911 | return(0); |
912 | } | |
913 | ||
914 | ||
915 | /* | |
916 | =item i_tt_get_instance(handle, points, smooth) | |
917 | ||
918 | Finds a points+smooth instance or if one doesn't exist in the cache | |
919 | allocates room and returns its cache entry | |
920 | ||
921 | fontname - path to the font to load | |
922 | handle - handle to the font. | |
923 | points - points of the requested font | |
924 | smooth - boolean (True: antialias on, False: antialias is off) | |
925 | ||
926 | =cut | |
927 | */ | |
928 | ||
929 | static | |
930 | int | |
931 | i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) { | |
932 | int i,idx; | |
933 | TT_Error error; | |
934 | ||
4f68b48f TC |
935 | mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n", |
936 | handle,points,smooth)); | |
02d1d628 AMH |
937 | |
938 | if (smooth == -1) { /* Smooth doesn't matter for this search */ | |
4f68b48f TC |
939 | for(i=0;i<TT_CHC;i++) { |
940 | if (handle->instanceh[i].ptsize==points) { | |
941 | mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i)); | |
942 | return i; | |
943 | } | |
02d1d628 AMH |
944 | } |
945 | smooth=1; /* We will be adding a font - add it as smooth then */ | |
946 | } else { /* Smooth doesn't matter for this search */ | |
4f68b48f TC |
947 | for(i=0;i<TT_CHC;i++) { |
948 | if (handle->instanceh[i].ptsize == points | |
949 | && handle->instanceh[i].smooth == smooth) { | |
950 | mm_log((1,"i_tt_get_instance: in cache returning %d\n",i)); | |
951 | return i; | |
952 | } | |
02d1d628 AMH |
953 | } |
954 | } | |
955 | ||
956 | /* Found the instance in the cache - return the cache index */ | |
957 | ||
4f68b48f TC |
958 | for(idx=0;idx<TT_CHC;idx++) { |
959 | if (!(handle->instanceh[idx].order)) break; /* find the lru item */ | |
960 | } | |
02d1d628 AMH |
961 | |
962 | mm_log((1,"i_tt_get_instance: lru item is %d\n",idx)); | |
4f68b48f TC |
963 | mm_log((1,"i_tt_get_instance: lru pointer 0x%X\n", |
964 | USTRCT(handle->instanceh[idx].instance) )); | |
02d1d628 AMH |
965 | |
966 | if ( USTRCT(handle->instanceh[idx].instance) ) { | |
967 | mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx)); | |
93d0372f | 968 | |
4f68b48f | 969 | /* Free cached glyphs */ |
93d0372f | 970 | for(i=0;i<256;i++) |
4f68b48f TC |
971 | if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) ) |
972 | TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph ); | |
93d0372f | 973 | |
4f68b48f TC |
974 | for(i=0;i<256;i++) { |
975 | handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR; | |
976 | USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL; | |
977 | } | |
978 | ||
979 | /* Free instance if needed */ | |
980 | TT_Done_Instance( handle->instanceh[idx].instance ); | |
02d1d628 AMH |
981 | } |
982 | ||
983 | /* create and initialize instance */ | |
984 | /* FIXME: probably a memory leak on fail */ | |
985 | ||
986 | (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) || | |
987 | ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) || | |
988 | ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) ); | |
989 | ||
990 | if ( error ) { | |
991 | mm_log((1, "Could not create and initialize instance: error 0x%x.\n",error )); | |
992 | return -1; | |
993 | } | |
994 | ||
995 | /* Now that the instance should the inplace we need to lower all of the | |
996 | ru counts and put `this' one with the highest entry */ | |
997 | ||
998 | for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--; | |
999 | ||
1000 | handle->instanceh[idx].order=TT_CHC-1; | |
1001 | handle->instanceh[idx].ptsize=points; | |
1002 | handle->instanceh[idx].smooth=smooth; | |
1003 | TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) ); | |
1004 | ||
4f68b48f TC |
1005 | /* Zero the memory for the glyph storage so they are not thought as |
1006 | cached if they haven't been cached since this new font was loaded */ | |
02d1d628 | 1007 | |
4f68b48f TC |
1008 | for(i=0;i<256;i++) { |
1009 | handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR; | |
1010 | USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL; | |
1011 | } | |
02d1d628 AMH |
1012 | |
1013 | return idx; | |
1014 | } | |
1015 | ||
1016 | ||
1017 | /* | |
1018 | =item i_tt_new(fontname) | |
1019 | ||
1020 | Creates a new font handle object, finds a character map and initialise the | |
1021 | the font handle's cache | |
1022 | ||
1023 | fontname - path to the font to load | |
1024 | ||
1025 | =cut | |
1026 | */ | |
1027 | ||
1028 | TT_Fonthandle* | |
97ac0a96 | 1029 | i_tt_new(const char *fontname) { |
02d1d628 AMH |
1030 | TT_Error error; |
1031 | TT_Fonthandle *handle; | |
1032 | unsigned short i,n; | |
1033 | unsigned short platform,encoding; | |
d93d5c10 | 1034 | |
d1555273 TC |
1035 | if (!TT_initialized && i_init_tt()) { |
1036 | i_push_error(0, "Could not initialize FT1 engine"); | |
1037 | return NULL; | |
1038 | } | |
1039 | ||
d93d5c10 | 1040 | i_clear_error(); |
02d1d628 AMH |
1041 | |
1042 | mm_log((1,"i_tt_new(fontname '%s')\n",fontname)); | |
1043 | ||
1044 | /* allocate memory for the structure */ | |
1045 | ||
f0960b14 | 1046 | handle = mymalloc( sizeof(TT_Fonthandle) ); /* checked 5Nov05 tonyc */ |
02d1d628 AMH |
1047 | |
1048 | /* load the typeface */ | |
1049 | error = TT_Open_Face( engine, fontname, &handle->face ); | |
1050 | if ( error ) { | |
4f68b48f TC |
1051 | if ( error == TT_Err_Could_Not_Open_File ) { |
1052 | mm_log((1, "Could not find/open %s.\n", fontname )); | |
1053 | } | |
1054 | else { | |
1055 | mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname, | |
1056 | error )); | |
1057 | } | |
d93d5c10 | 1058 | i_tt_push_error(error); |
02d1d628 AMH |
1059 | return NULL; |
1060 | } | |
1061 | ||
1062 | TT_Get_Face_Properties( handle->face, &(handle->properties) ); | |
4f68b48f | 1063 | |
02d1d628 | 1064 | /* First, look for a Unicode charmap */ |
02d1d628 AMH |
1065 | n = handle->properties.num_CharMaps; |
1066 | USTRCT( handle->char_map )=NULL; /* Invalidate character map */ | |
1067 | ||
1068 | for ( i = 0; i < n; i++ ) { | |
1069 | TT_Get_CharMap_ID( handle->face, i, &platform, &encoding ); | |
4f68b48f TC |
1070 | if ( (platform == 3 && encoding == 1 ) |
1071 | || (platform == 0 && encoding == 0 ) ) { | |
1072 | mm_log((2,"i_tt_new - found char map platform %u encoding %u\n", | |
1073 | platform, encoding)); | |
02d1d628 AMH |
1074 | TT_Get_CharMap( handle->face, i, &(handle->char_map) ); |
1075 | break; | |
1076 | } | |
1077 | } | |
27e79497 TC |
1078 | if (!USTRCT(handle->char_map) && n != 0) { |
1079 | /* just use the first one */ | |
1080 | TT_Get_CharMap( handle->face, 0, &(handle->char_map)); | |
1081 | } | |
02d1d628 AMH |
1082 | |
1083 | /* Zero the pointsizes - and ordering */ | |
1084 | ||
1085 | for(i=0;i<TT_CHC;i++) { | |
1086 | USTRCT(handle->instanceh[i].instance)=NULL; | |
1087 | handle->instanceh[i].order=i; | |
1088 | handle->instanceh[i].ptsize=0; | |
1089 | handle->instanceh[i].smooth=-1; | |
1090 | } | |
1091 | ||
3799c4d1 TC |
1092 | #ifdef FTXPOST |
1093 | handle->loaded_names = 0; | |
1094 | #endif | |
1095 | ||
02d1d628 AMH |
1096 | mm_log((1,"i_tt_new <- 0x%X\n",handle)); |
1097 | return handle; | |
1098 | } | |
1099 | ||
1100 | ||
1101 | ||
1102 | /* | |
1103 |