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