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