long delayed renaming of m_fatal() to i_fatal() to match Imager's
[imager.git] / font.c
CommitLineData
92bda632 1#include "imager.h"
02d1d628
AMH
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
97ac0a96 318i_t1_bbox(int fontnum,float points,const 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 {
97ac0a96 333 bbox = T1_GetStringBBox(fontnum,(char *)str,len,0,mod_flags);
1bd75e4c 334 }
02d1d628 335 gbbox = T1_GetFontBBox(fontnum);
97ac0a96 336 advance = T1_GetStringWidth(fontnum, (char *)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
97ac0a96 383i_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
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 {
97ac0a96
TC
399 /* T1_AASetString() accepts a char * not a const char */
400 glyph=T1_AASetString( fontnum, (char *)str, len, 0, mod_flags, points, NULL);
1bd75e4c 401 }
faa9b3e7
TC
402 if (glyph == NULL)
403 return 0;
02d1d628
AMH
404
405 mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
406 mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
407 mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
408 mm_log((1,"bpp: %d\n",glyph->bpp));
409
410 xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
411 ysize=glyph->metrics.ascent-glyph->metrics.descent;
412
413 mm_log((1,"width: %d height: %d\n",xsize,ysize));
414
415 if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
416
417 for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
418 c=glyph->bits[y*xsize+x];
419 i=255-c;
420 i_gpix(im,x+xb,y+yb,&val);
421 for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
422 i_ppix(im,x+xb,y+yb,&val);
423 }
424 return 1;
425}
426
1bd75e4c
TC
427/*
428=item t1_get_flags(flags)
429
430Processes the characters in I<flags> to create a mod_flags value used
431by some T1Lib functions.
432
433=cut
434 */
435static int
436t1_get_flags(char const *flags) {
437 int mod_flags = T1_KERNING;
438
439 while (*flags) {
440 switch (*flags++) {
441 case 'u': case 'U': mod_flags |= T1_UNDERLINE; break;
442 case 'o': case 'O': mod_flags |= T1_OVERLINE; break;
443 case 's': case 'S': mod_flags |= T1_OVERSTRIKE; break;
444 /* ignore anything we don't recognize */
445 }
446 }
447
448 return mod_flags;
449}
450
451/*
452=item t1_from_utf8(char const *in, int len, int *outlen)
453
454Produces an unencoded version of I<in> by dropping any Unicode
455character over 255.
456
457Returns a newly allocated buffer which should be freed with myfree().
458Sets *outlen to the number of bytes used in the output string.
459
460=cut
461*/
462
463static char *
464t1_from_utf8(char const *in, int len, int *outlen) {
f0960b14
TC
465 /* at this point len is from a perl SV, so can't approach MAXINT */
466 char *out = mymalloc(len+1); /* checked 5Nov05 tonyc */
1bd75e4c
TC
467 char *p = out;
468 unsigned long c;
469
470 while (len) {
471 c = i_utf8_advance(&in, &len);
472 if (c == ~0UL) {
473 myfree(out);
474 i_push_error(0, "invalid UTF8 character");
475 return 0;
476 }
477 /* yeah, just drop them */
478 if (c < 0x100) {
479 *p++ = (char)c;
480 }
481 }
482 *p = '\0';
483 *outlen = p - out;
484
485 return out;
486}
02d1d628 487
3799c4d1
TC
488/*
489=item i_t1_has_chars(font_num, text, len, utf8, out)
490
491Check if the given characters are defined by the font. Note that len
492is the number of bytes, not the number of characters (when utf8 is
493non-zero).
494
495out[char index] will be true if the character exists.
496
497Accepts UTF-8, but since T1 can only have 256 characters, any chars
498with values over 255 will simply be returned as false.
499
500Returns the number of characters that were checked.
501
502=cut
503*/
504
505int
506i_t1_has_chars(int font_num, const char *text, int len, int utf8,
507 char *out) {
508 int count = 0;
509
510 mm_log((1, "i_t1_has_chars(font_num %d, text %p, len %d, utf8 %d)\n",
511 font_num, text, len, utf8));
512
513 i_clear_error();
514 if (T1_LoadFont(font_num)) {
515 t1_push_error();
516 return 0;
517 }
518
519 while (len) {
520 unsigned long c;
3799c4d1
TC
521 if (utf8) {
522 c = i_utf8_advance(&text, &len);
523 if (c == ~0UL) {
524 i_push_error(0, "invalid UTF8 character");
525 return 0;
526 }
527 }
528 else {
529 c = (unsigned char)*text++;
530 --len;
531 }
532
533 if (c >= 0x100) {
534 /* limit of 256 characters for T1 */
535 *out++ = 0;
536 }
537 else {
538 char const * name = T1_GetCharName(font_num, (unsigned char)c);
539
540 if (name) {
541 *out++ = strcmp(name, ".notdef") != 0;
542 }
543 else {
544 mm_log((2, " No name found for character %lx\n", c));
545 *out++ = 0;
546 }
547 }
548 ++count;
549 }
550
551 return count;
552}
553
554/*
555=item i_t1_face_name(font_num, name_buf, name_buf_size)
556
557Copies the face name of the given C<font_num> to C<name_buf>. Returns
558the number of characters required to store the name (which can be
559larger than C<name_buf_size>, including the space required to store
560the terminating NUL).
561
562If name_buf is too small (as specified by name_buf_size) then the name
563will be truncated. name_buf will always be NUL termintaed.
564
565=cut
566*/
567
568int
569i_t1_face_name(int font_num, char *name_buf, size_t name_buf_size) {
570 char *name;
571
572 T1_errno = 0;
573 if (T1_LoadFont(font_num)) {
574 t1_push_error();
575 return 0;
576 }
577 name = T1_GetFontName(font_num);
578
579 if (name) {
580 strncpy(name_buf, name, name_buf_size);
581 name_buf[name_buf_size-1] = '\0';
582 return strlen(name) + 1;
583 }
584 else {
585 t1_push_error();
586 return 0;
587 }
588}
589
590int
591i_t1_glyph_name(int font_num, unsigned long ch, char *name_buf,
592 size_t name_buf_size) {
593 char *name;
594
595 i_clear_error();
596 if (ch > 0xFF) {
597 return 0;
598 }
599 if (T1_LoadFont(font_num)) {
600 t1_push_error();
601 return 0;
602 }
603 name = T1_GetCharName(font_num, (unsigned char)ch);
604 if (name) {
605 if (strcmp(name, ".notdef")) {
606 strncpy(name_buf, name, name_buf_size);
607 name_buf[name_buf_size-1] = '\0';
608 return strlen(name) + 1;
609 }
610 else {
611 return 0;
612 }
613 }
614 else {
615 t1_push_error();
616 return 0;
617 }
618}
619
620static void
621t1_push_error(void) {
622 switch (T1_errno) {
623 case 0:
624 i_push_error(0, "No error");
625 break;
626
d5a3f460 627#ifdef T1ERR_SCAN_FONT_FORMAT
3799c4d1
TC
628 case T1ERR_SCAN_FONT_FORMAT:
629 i_push_error(T1ERR_SCAN_FONT_FORMAT, "SCAN_FONT_FORMAT");
630 break;
d5a3f460 631#endif
3799c4d1 632
d5a3f460 633#ifdef T1ERR_SCAN_FILE_OPEN_ERR
3799c4d1
TC
634 case T1ERR_SCAN_FILE_OPEN_ERR:
635 i_push_error(T1ERR_SCAN_FILE_OPEN_ERR, "SCAN_FILE_OPEN_ERR");
636 break;
d5a3f460 637#endif
3799c4d1 638
d5a3f460 639#ifdef T1ERR_SCAN_OUT_OF_MEMORY
3799c4d1
TC
640 case T1ERR_SCAN_OUT_OF_MEMORY:
641 i_push_error(T1ERR_SCAN_OUT_OF_MEMORY, "SCAN_OUT_OF_MEMORY");
642 break;
d5a3f460 643#endif
3799c4d1 644
d5a3f460 645#ifdef T1ERR_SCAN_ERROR
3799c4d1
TC
646 case T1ERR_SCAN_ERROR:
647 i_push_error(T1ERR_SCAN_ERROR, "SCAN_ERROR");
648 break;
d5a3f460 649#endif
3799c4d1 650
d5a3f460 651#ifdef T1ERR_SCAN_FILE_EOF
3799c4d1
TC
652 case T1ERR_SCAN_FILE_EOF:
653 i_push_error(T1ERR_SCAN_FILE_EOF, "SCAN_FILE_EOF");
654 break;
d5a3f460 655#endif
3799c4d1 656
d5a3f460 657#ifdef T1ERR_PATH_ERROR
3799c4d1
TC
658 case T1ERR_PATH_ERROR:
659 i_push_error(T1ERR_PATH_ERROR, "PATH_ERROR");
660 break;
d5a3f460 661#endif
3799c4d1 662
d5a3f460 663#ifdef T1ERR_PARSE_ERROR
3799c4d1
TC
664 case T1ERR_PARSE_ERROR:
665 i_push_error(T1ERR_PARSE_ERROR, "PARSE_ERROR");
666 break;
d5a3f460 667#endif
3799c4d1 668
d5a3f460 669#ifdef T1ERR_TYPE1_ABORT
3799c4d1
TC
670 case T1ERR_TYPE1_ABORT:
671 i_push_error(T1ERR_TYPE1_ABORT, "TYPE1_ABORT");
672 break;
d5a3f460 673#endif
3799c4d1 674
d5a3f460 675#ifdef T1ERR_INVALID_FONTID
3799c4d1
TC
676 case T1ERR_INVALID_FONTID:
677 i_push_error(T1ERR_INVALID_FONTID, "INVALID_FONTID");
678 break;
d5a3f460 679#endif
3799c4d1 680
d5a3f460 681#ifdef T1ERR_INVALID_PARAMETER
3799c4d1
TC
682 case T1ERR_INVALID_PARAMETER:
683 i_push_error(T1ERR_INVALID_PARAMETER, "INVALID_PARAMETER");
684 break;
d5a3f460 685#endif
3799c4d1 686
d5a3f460 687#ifdef T1ERR_OP_NOT_PERMITTED
3799c4d1
TC
688 case T1ERR_OP_NOT_PERMITTED:
689 i_push_error(T1ERR_OP_NOT_PERMITTED, "OP_NOT_PERMITTED");
690 break;
d5a3f460 691#endif
3799c4d1 692
d5a3f460 693#ifdef T1ERR_ALLOC_MEM
3799c4d1
TC
694 case T1ERR_ALLOC_MEM:
695 i_push_error(T1ERR_ALLOC_MEM, "ALLOC_MEM");
696 break;
d5a3f460 697#endif
3799c4d1 698
d5a3f460 699#ifdef T1ERR_FILE_OPEN_ERR
3799c4d1
TC
700 case T1ERR_FILE_OPEN_ERR:
701 i_push_error(T1ERR_FILE_OPEN_ERR, "FILE_OPEN_ERR");
702 break;
d5a3f460 703#endif
3799c4d1 704
d5a3f460 705#ifdef T1ERR_UNSPECIFIED
3799c4d1
TC
706 case T1ERR_UNSPECIFIED:
707 i_push_error(T1ERR_UNSPECIFIED, "UNSPECIFIED");
708 break;
d5a3f460 709#endif
3799c4d1 710
d5a3f460 711#ifdef T1ERR_NO_AFM_DATA
3799c4d1
TC
712 case T1ERR_NO_AFM_DATA:
713 i_push_error(T1ERR_NO_AFM_DATA, "NO_AFM_DATA");
714 break;
d5a3f460 715#endif
3799c4d1 716
d5a3f460 717#ifdef T1ERR_X11
3799c4d1
TC
718 case T1ERR_X11:
719 i_push_error(T1ERR_X11, "X11");
720 break;
d5a3f460 721#endif
3799c4d1 722
d5a3f460 723#ifdef T1ERR_COMPOSITE_CHAR
3799c4d1
TC
724 case T1ERR_COMPOSITE_CHAR:
725 i_push_error(T1ERR_COMPOSITE_CHAR, "COMPOSITE_CHAR");
726 break;
d5a3f460 727#endif
3799c4d1
TC
728
729 default:
730 i_push_errorf(T1_errno, "unknown error %d", (int)T1_errno);
731 }
732}
733
02d1d628
AMH
734#endif /* HAVE_LIBT1 */
735
736
4f68b48f 737/* Truetype font support */
4f68b48f 738#ifdef HAVE_LIBTT
02d1d628 739
d93d5c10 740/* These are enabled by default when configuring Freetype 1.x
3799c4d1
TC
741 I haven't a clue how to reliably detect it at compile time.
742
743 We need a compilation probe in Makefile.PL
744*/
745#define FTXPOST 1
d93d5c10 746#define FTXERR18 1
3799c4d1 747
4f68b48f
TC
748#include <freetype.h>
749#define TT_CHC 5
02d1d628 750
3799c4d1
TC
751#ifdef FTXPOST
752#include <ftxpost.h>
753#endif
754
d93d5c10
TC
755#ifdef FTXERR18
756#include <ftxerr18.h>
757#endif
758
a90b253d
TC
759/* some versions of FT1.x don't seem to define this - it's font defined
760 so it won't change */
761#ifndef TT_MS_LANGID_ENGLISH_GENERAL
762#define TT_MS_LANGID_ENGLISH_GENERAL 0x0409
763#endif
764
4f68b48f
TC
765/* convert a code point into an index in the glyph cache */
766#define TT_HASH(x) ((x) & 0xFF)
02d1d628 767
4f68b48f
TC
768typedef struct i_glyph_entry_ {
769 TT_Glyph glyph;
770 unsigned long ch;
771} i_tt_glyph_entry;
02d1d628 772
4f68b48f 773#define TT_NOCHAR (~0UL)
02d1d628 774
4f68b48f
TC
775struct TT_Instancehandle_ {
776 TT_Instance instance;
777 TT_Instance_Metrics imetrics;
778 TT_Glyph_Metrics gmetrics[256];
779 i_tt_glyph_entry glyphs[256];
780 int smooth;
781 int ptsize;
782 int order;
783};
02d1d628 784
4f68b48f 785typedef struct TT_Instancehandle_ TT_Instancehandle;
02d1d628 786
4f68b48f
TC
787struct TT_Fonthandle_ {
788 TT_Face face;
789 TT_Face_Properties properties;
790 TT_Instancehandle instanceh[TT_CHC];
791 TT_CharMap char_map;
3799c4d1
TC
792#ifdef FTXPOST
793 int loaded_names;
794 TT_Error load_cond;
795#endif
4f68b48f 796};
02d1d628
AMH
797
798/* Defines */
799
800#define USTRCT(x) ((x).z)
801#define TT_VALID( handle ) ( ( handle ).z != NULL )
802
d93d5c10 803static void i_tt_push_error(TT_Error rc);
02d1d628
AMH
804
805/* Prototypes */
806
807static int i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth );
808static void i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth );
809static void i_tt_done_raster_map( TT_Raster_Map *bit );
810static void i_tt_clear_raster_map( TT_Raster_Map* bit );
811static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off );
4f68b48f
TC
812static int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j );
813static void
814i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics,
815 TT_Raster_Map *bit, TT_Raster_Map *small_bit,
816 int x_off, int y_off, int smooth );
817static int
818i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
819 TT_Raster_Map *small_bit, int cords[6],
820 char const* txt, int len, int smooth, int utf8 );
97ac0a96 821static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, const i_color *cl, int smooth );
02d1d628 822static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth );
4f68b48f
TC
823static int
824i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6],
825 float points, char const* txt, int len, int smooth, int utf8 );
826static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6], int utf8 );
02d1d628
AMH
827
828
829/* static globals needed */
830
831static TT_Engine engine;
832static int LTT_dpi = 72; /* FIXME: this ought to be a part of the call interface */
833static int LTT_hinted = 1; /* FIXME: this too */
834
835
836/*
837 * FreeType interface
838 */
839
840
841/*
842=item init_tt()
843
844Initializes the freetype font rendering engine
845
846=cut
847*/
848
849undef_int
b33c08f8 850i_init_tt() {
02d1d628
AMH
851 TT_Error error;
852 mm_log((1,"init_tt()\n"));
853 error = TT_Init_FreeType( &engine );
854 if ( error ){
855 mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error));
856 return(1);
857 }
3799c4d1
TC
858
859#ifdef FTXPOST
860 error = TT_Init_Post_Extension( engine );
861 if (error) {
862 mm_log((1, "Initialization of Post extension failed = 0x%x\n", error));
863 return 1;
864 }
865#endif
866
02d1d628
AMH
867 return(0);
868}
869
870
871/*
872=item i_tt_get_instance(handle, points, smooth)
873
874Finds a points+smooth instance or if one doesn't exist in the cache
875allocates room and returns its cache entry
876
877 fontname - path to the font to load
878 handle - handle to the font.
879 points - points of the requested font
880 smooth - boolean (True: antialias on, False: antialias is off)
881
882=cut
883*/
884
885static
886int
887i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) {
888 int i,idx;
889 TT_Error error;
890
4f68b48f
TC
891 mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n",
892 handle,points,smooth));
02d1d628
AMH
893
894 if (smooth == -1) { /* Smooth doesn't matter for this search */
4f68b48f
TC
895 for(i=0;i<TT_CHC;i++) {
896 if (handle->instanceh[i].ptsize==points) {
897 mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
898 return i;
899 }
02d1d628
AMH
900 }
901 smooth=1; /* We will be adding a font - add it as smooth then */
902 } else { /* Smooth doesn't matter for this search */
4f68b48f
TC
903 for(i=0;i<TT_CHC;i++) {
904 if (handle->instanceh[i].ptsize == points
905 && handle->instanceh[i].smooth == smooth) {
906 mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
907 return i;
908 }
02d1d628
AMH
909 }
910 }
911
912 /* Found the instance in the cache - return the cache index */
913
4f68b48f
TC
914 for(idx=0;idx<TT_CHC;idx++) {
915 if (!(handle->instanceh[idx].order)) break; /* find the lru item */
916 }
02d1d628
AMH
917
918 mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
4f68b48f
TC
919 mm_log((1,"i_tt_get_instance: lru pointer 0x%X\n",
920 USTRCT(handle->instanceh[idx].instance) ));
02d1d628
AMH
921
922 if ( USTRCT(handle->instanceh[idx].instance) ) {
923 mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
93d0372f 924
4f68b48f 925 /* Free cached glyphs */
93d0372f 926 for(i=0;i<256;i++)
4f68b48f
TC
927 if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
928 TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
93d0372f 929
4f68b48f
TC
930 for(i=0;i<256;i++) {
931 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
932 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
933 }
934
935 /* Free instance if needed */
936 TT_Done_Instance( handle->instanceh[idx].instance );
02d1d628
AMH
937 }
938
939 /* create and initialize instance */
940 /* FIXME: probably a memory leak on fail */
941
942 (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) ||
943 ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
944 ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
945
946 if ( error ) {
947 mm_log((1, "Could not create and initialize instance: error 0x%x.\n",error ));
948 return -1;
949 }
950
951 /* Now that the instance should the inplace we need to lower all of the
952 ru counts and put `this' one with the highest entry */
953
954 for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
955
956 handle->instanceh[idx].order=TT_CHC-1;
957 handle->instanceh[idx].ptsize=points;
958 handle->instanceh[idx].smooth=smooth;
959 TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
960
4f68b48f
TC
961 /* Zero the memory for the glyph storage so they are not thought as
962 cached if they haven't been cached since this new font was loaded */
02d1d628 963
4f68b48f
TC
964 for(i=0;i<256;i++) {
965 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
966 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
967 }
02d1d628
AMH
968
969 return idx;
970}
971
972
973/*
974=item i_tt_new(fontname)
975
976Creates a new font handle object, finds a character map and initialise the
977the font handle's cache
978
979 fontname - path to the font to load
980
981=cut
982*/
983
984TT_Fonthandle*
97ac0a96 985i_tt_new(const char *fontname) {
02d1d628
AMH
986 TT_Error error;
987 TT_Fonthandle *handle;
988 unsigned short i,n;
989 unsigned short platform,encoding;
d93d5c10
TC
990
991 i_clear_error();
02d1d628
AMH
992
993 mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
994
995 /* allocate memory for the structure */
996
f0960b14 997 handle = mymalloc( sizeof(TT_Fonthandle) ); /* checked 5Nov05 tonyc */
02d1d628
AMH
998
999 /* load the typeface */
1000 error = TT_Open_Face( engine, fontname, &handle->face );
1001 if ( error ) {
4f68b48f
TC
1002 if ( error == TT_Err_Could_Not_Open_File ) {
1003 mm_log((1, "Could not find/open %s.\n", fontname ));
1004 }
1005 else {
1006 mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname,
1007 error ));
1008 }
d93d5c10 1009 i_tt_push_error(error);
02d1d628
AMH
1010 return NULL;
1011 }
1012
1013 TT_Get_Face_Properties( handle->face, &(handle->properties) );
4f68b48f 1014
02d1d628 1015 /* First, look for a Unicode charmap */
02d1d628
AMH
1016 n = handle->properties.num_CharMaps;
1017 USTRCT( handle->char_map )=NULL; /* Invalidate character map */
1018
1019 for ( i = 0; i < n; i++ ) {
1020 TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
4f68b48f
TC
1021 if ( (platform == 3 && encoding == 1 )
1022 || (platform == 0 && encoding == 0 ) ) {
1023 mm_log((2,"i_tt_new - found char map platform %u encoding %u\n",
1024 platform, encoding));
02d1d628
AMH
1025 TT_Get_CharMap( handle->face, i, &(handle->char_map) );
1026 break;
1027 }
1028 }
27e79497
TC
1029 if (!USTRCT(handle->char_map) && n != 0) {
1030 /* just use the first one */
1031 TT_Get_CharMap( handle->face, 0, &(handle->char_map));
1032 }
02d1d628
AMH
1033
1034 /* Zero the pointsizes - and ordering */
1035
1036 for(i=0;i<TT_CHC;i++) {
1037 USTRCT(handle->instanceh[i].instance)=NULL;
1038 handle->instanceh[i].order=i;
1039 handle->instanceh[i].ptsize=0;
1040 handle->instanceh[i].smooth=-1;
1041 }
1042
3799c4d1
TC
1043#ifdef FTXPOST
1044 handle->loaded_names = 0;
1045#endif
1046
02d1d628
AMH
1047 mm_log((1,"i_tt_new <- 0x%X\n",handle));
1048 return handle;
1049}
1050
1051
1052
1053/*
1054