]> git.imager.perl.org - imager.git/blob - font.c
- added sampled/slant_text.pl, and notes on shearing/rotating text in
[imager.git] / font.c
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
10 #ifdef HAVE_LIBT1
11 #include <t1lib.h>
12 #endif
13
14
15 /*
16 =head1 NAME
17
18 font.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);
32   rc = i_tt_bbox(handle, points, "foo", 3, int cords[6], utf8);
33   i_tt_destroy(handle);
34
35   // and much more
36
37 =head1 DESCRIPTION
38
39 font.c implements font creation, rendering, bounding box functions and
40 more for Imager.
41
42 =head1 FUNCTION REFERENCE
43
44 Some of these functions are internal.
45
46 =over
47
48 =cut
49
50 */
51
52 /* 
53 =item i_init_fonts()
54
55 Initialize font rendering libraries if they are avaliable.
56
57 =cut
58 */
59
60 undef_int 
61 i_init_fonts(int t1log) {
62   mm_log((1,"Initializing fonts\n"));
63
64 #ifdef HAVE_LIBT1
65   i_init_t1(t1log);
66 #endif
67   
68 #ifdef HAVE_LIBTT
69   i_init_tt();
70 #endif
71
72 #ifdef HAVE_FT2
73   if (!i_ft2_init())
74     return 0;
75 #endif
76
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
85 static int t1_get_flags(char const *flags);
86 static char *t1_from_utf8(char const *in, int len, int *outlen);
87
88 static void t1_push_error(void);
89
90 /* 
91 =item i_init_t1(t1log)
92
93 Initializes the t1lib font rendering engine.
94
95 =cut
96 */
97
98 undef_int
99 i_init_t1(int t1log) {
100   int init_flags = IGNORE_CONFIGFILE|IGNORE_FONTDATABASE;
101   mm_log((1,"init_t1()\n"));
102   
103   if (t1log)
104     init_flags |= LOGFILE;
105   if ((T1_InitLib(init_flags) == NULL)){
106     mm_log((1,"Initialization of t1lib failed\n"));
107     return(1);
108   }
109   T1_SetLogLevel(T1LOG_DEBUG);
110   i_t1_set_aa(1); /* Default Antialias value */
111   return(0);
112 }
113
114
115 /* 
116 =item i_close_t1()
117
118 Shuts the t1lib font rendering engine down.
119
120   This it seems that this function is never used.
121
122 =cut
123 */
124
125 void
126 i_close_t1(void) {
127   T1_CloseLib();
128 }
129
130
131 /*
132 =item i_t1_new(pfb, afm)
133
134 Loads the fonts with the given filenames, returns its font id
135
136  pfb -  path to pfb file for font
137  afm -  path to afm file for font
138
139 =cut
140 */
141
142 int
143 i_t1_new(char *pfb,char *afm) {
144   int font_id;
145
146   mm_log((1,"i_t1_new(pfb %s,afm %s)\n",pfb,(afm?afm:"NULL")));
147   font_id = T1_AddFont(pfb);
148   if (font_id<0) {
149     mm_log((1,"i_t1_new: Failed to load pfb file '%s' - return code %d.\n",pfb,font_id));
150     return font_id;
151   }
152   
153   if (afm != NULL) {
154     mm_log((1,"i_t1_new: requesting afm file '%s'.\n",afm));
155     if (T1_SetAfmFileName(font_id,afm)<0) mm_log((1,"i_t1_new: afm loading of '%s' failed.\n",afm));
156   }
157
158   return font_id;
159 }
160
161 /*
162 =item i_t1_destroy(font_id)
163
164 Frees resources for a t1 font with given font id.
165
166    font_id - number of the font to free
167
168 =cut
169 */
170
171 int
172 i_t1_destroy(int font_id) {
173   mm_log((1,"i_t1_destroy(font_id %d)\n",font_id));
174   return T1_DeleteFont(font_id);
175 }
176
177
178 /*
179 =item i_t1_set_aa(st)
180
181 Sets the antialiasing level of the t1 library.
182
183    st - 0 =  NONE, 1 = LOW, 2 =  HIGH.
184
185 =cut
186 */
187
188 void
189 i_t1_set_aa(int st) {
190   int i;
191   unsigned long cst[17];
192   switch(st) {
193   case 0:
194     T1_AASetBitsPerPixel( 8 );
195     T1_AASetLevel( T1_AA_NONE );
196     T1_AANSetGrayValues( 0, 255 );
197     mm_log((1,"setting T1 antialias to none\n"));
198     break;
199   case 1:
200     T1_AASetBitsPerPixel( 8 );
201     T1_AASetLevel( T1_AA_LOW );
202     T1_AASetGrayValues( 0,65,127,191,255 );
203     mm_log((1,"setting T1 antialias to low\n"));
204     break;
205   case 2:
206     T1_AASetBitsPerPixel(8);
207     T1_AASetLevel(T1_AA_HIGH);
208     for(i=0;i<17;i++) cst[i]=(i*255)/16;
209     T1_AAHSetGrayValues( cst );
210     mm_log((1,"setting T1 antialias to high\n"));
211   }
212 }
213
214
215 /* 
216 =item i_t1_cp(im, xb, yb, channel, fontnum, points, str, len, align)
217
218 Interface to text rendering into a single channel in an image
219
220    im        pointer to image structure
221    xb        x coordinate of start of string
222    yb        y coordinate of start of string ( see align )
223    channel - destination channel
224    fontnum - t1 library font id
225    points  - number of points in fontheight
226    str     - string to render
227    len     - string length
228    align   - (0 - top of font glyph | 1 - baseline )
229
230 =cut
231 */
232
233 undef_int
234 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) {
235   GLYPH *glyph;
236   int xsize,ysize,x,y;
237   i_color val;
238   int mod_flags = t1_get_flags(flags);
239
240   unsigned int ch_mask_store;
241   
242   if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
243
244   if (utf8) {
245     int worklen;
246     char *work = t1_from_utf8(str, len, &worklen);
247     glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
248     myfree(work);
249   }
250   else {
251     glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL);
252   }
253   if (glyph == NULL)
254     return 0;
255
256   mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
257   mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
258   mm_log((1," advanceX: %d  advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
259   mm_log((1,"bpp: %d\n",glyph->bpp));
260   
261   xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
262   ysize=glyph->metrics.ascent-glyph->metrics.descent;
263   
264   mm_log((1,"width: %d height: %d\n",xsize,ysize));
265
266   ch_mask_store=im->ch_mask;
267   im->ch_mask=1<<channel;
268
269   if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
270   
271   for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
272     val.channel[channel]=glyph->bits[y*xsize+x];
273     i_ppix(im,x+xb,y+yb,&val);
274   }
275   
276   im->ch_mask=ch_mask_store;
277   return 1;
278 }
279
280
281 /*
282 =item i_t1_bbox(handle, fontnum, points, str, len, cords)
283
284 function to get a strings bounding box given the font id and sizes
285
286    handle  - pointer to font handle   
287    fontnum - t1 library font id
288    points  - number of points in fontheight
289    str     - string to measure
290    len     - string length
291    cords   - the bounding box (modified in place)
292
293 =cut
294 */
295
296 int
297 i_t1_bbox(int fontnum,float points,char *str,int len,int cords[6], int utf8,char const *flags) {
298   BBox bbox;
299   BBox gbbox;
300   int mod_flags = t1_get_flags(flags);
301   int advance;
302   
303   mm_log((1,"i_t1_bbox(fontnum %d,points %.2f,str '%.*s', len %d)\n",fontnum,points,len,str,len));
304   T1_LoadFont(fontnum);  /* FIXME: Here a return code is ignored - haw haw haw */ 
305   if (utf8) {
306     int worklen;
307     char *work = t1_from_utf8(str, len, &worklen);
308     bbox = T1_GetStringBBox(fontnum,work,worklen,0,mod_flags);
309     myfree(work);
310   }
311   else {
312     bbox = T1_GetStringBBox(fontnum,str,len,0,mod_flags);
313   }
314   gbbox = T1_GetFontBBox(fontnum);
315   advance = T1_GetStringWidth(fontnum, str, len, 0, mod_flags);
316   
317   mm_log((1,"bbox: (%d,%d,%d,%d)\n",
318           (int)(bbox.llx*points/1000),
319           (int)(gbbox.lly*points/1000),
320           (int)(bbox.urx*points/1000),
321           (int)(gbbox.ury*points/1000),
322           (int)(bbox.lly*points/1000),
323           (int)(bbox.ury*points/1000) ));
324
325
326   cords[BBOX_NEG_WIDTH]=((float)bbox.llx*points)/1000;
327   cords[BBOX_POS_WIDTH]=((float)bbox.urx*points)/1000;
328
329   cords[BBOX_GLOBAL_DESCENT]=((float)gbbox.lly*points)/1000;
330   cords[BBOX_GLOBAL_ASCENT]=((float)gbbox.ury*points)/1000;
331
332   cords[BBOX_DESCENT]=((float)bbox.lly*points)/1000;
333   cords[BBOX_ASCENT]=((float)bbox.ury*points)/1000;
334
335   cords[BBOX_ADVANCE_WIDTH] = ((float)advance * points)/1000;
336   cords[BBOX_RIGHT_BEARING] = 
337     cords[BBOX_ADVANCE_WIDTH] - cords[BBOX_POS_WIDTH];
338
339   return BBOX_RIGHT_BEARING+1;
340 }
341
342
343 /*
344 =item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align)
345
346 Interface to text rendering in a single color onto an image
347
348    im      - pointer to image structure
349    xb      - x coordinate of start of string
350    yb      - y coordinate of start of string ( see align )
351    cl      - color to draw the text in
352    fontnum - t1 library font id
353    points  - number of points in fontheight
354    str     - char pointer to string to render
355    len     - string length
356    align   - (0 - top of font glyph | 1 - baseline )
357
358 =cut
359 */
360
361 undef_int
362 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) {
363   GLYPH *glyph;
364   int xsize,ysize,x,y,ch;
365   i_color val;
366   unsigned char c,i;
367   int mod_flags = t1_get_flags(flags);
368
369   if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
370
371   if (utf8) {
372     int worklen;
373     char *work = t1_from_utf8(str, len, &worklen);
374     glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
375     myfree(work);
376   }
377   else {
378     glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL);
379   }
380   if (glyph == NULL)
381     return 0;
382
383   mm_log((1,"metrics:  ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
384   mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
385   mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
386   mm_log((1,"bpp: %d\n",glyph->bpp));
387   
388   xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
389   ysize=glyph->metrics.ascent-glyph->metrics.descent;
390   
391   mm_log((1,"width: %d height: %d\n",xsize,ysize));
392
393   if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
394   
395   for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
396     c=glyph->bits[y*xsize+x];
397     i=255-c;
398     i_gpix(im,x+xb,y+yb,&val);
399     for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
400     i_ppix(im,x+xb,y+yb,&val);
401   }
402   return 1;
403 }
404
405 /*
406 =item t1_get_flags(flags)
407
408 Processes the characters in I<flags> to create a mod_flags value used
409 by some T1Lib functions.
410
411 =cut
412  */
413 static int
414 t1_get_flags(char const *flags) {
415   int mod_flags = T1_KERNING;
416
417   while (*flags) {
418     switch (*flags++) {
419     case 'u': case 'U': mod_flags |= T1_UNDERLINE; break;
420     case 'o': case 'O': mod_flags |= T1_OVERLINE; break;
421     case 's': case 'S': mod_flags |= T1_OVERSTRIKE; break;
422       /* ignore anything we don't recognize */
423     }
424   }
425
426   return mod_flags;
427 }
428
429 /*
430 =item t1_from_utf8(char const *in, int len, int *outlen)
431
432 Produces an unencoded version of I<in> by dropping any Unicode
433 character over 255.
434
435 Returns a newly allocated buffer which should be freed with myfree().
436 Sets *outlen to the number of bytes used in the output string.
437
438 =cut
439 */
440
441 static char *
442 t1_from_utf8(char const *in, int len, int *outlen) {
443   char *out = mymalloc(len+1);
444   char *p = out;
445   unsigned long c;
446
447   while (len) {
448     c = i_utf8_advance(&in, &len);
449     if (c == ~0UL) {
450       myfree(out);
451       i_push_error(0, "invalid UTF8 character");
452       return 0;
453     }
454     /* yeah, just drop them */
455     if (c < 0x100) {
456       *p++ = (char)c;
457     }
458   }
459   *p = '\0';
460   *outlen = p - out;
461
462   return out;
463 }
464
465 /*
466 =item i_t1_has_chars(font_num, text, len, utf8, out)
467
468 Check if the given characters are defined by the font.  Note that len
469 is the number of bytes, not the number of characters (when utf8 is
470 non-zero).
471
472 out[char index] will be true if the character exists.
473
474 Accepts UTF-8, but since T1 can only have 256 characters, any chars
475 with values over 255 will simply be returned as false.
476
477 Returns the number of characters that were checked.
478
479 =cut
480 */
481
482 int
483 i_t1_has_chars(int font_num, const char *text, int len, int utf8,
484                char *out) {
485   int count = 0;
486   
487   mm_log((1, "i_t1_has_chars(font_num %d, text %p, len %d, utf8 %d)\n", 
488           font_num, text, len, utf8));
489
490   i_clear_error();
491   if (T1_LoadFont(font_num)) {
492     t1_push_error();
493     return 0;
494   }
495
496   while (len) {
497     unsigned long c;
498     if (utf8) {
499       c = i_utf8_advance(&text, &len);
500       if (c == ~0UL) {
501         i_push_error(0, "invalid UTF8 character");
502         return 0;
503       }
504     }
505     else {
506       c = (unsigned char)*text++;
507       --len;
508     }
509     
510     if (c >= 0x100) {
511       /* limit of 256 characters for T1 */
512       *out++ = 0;
513     }
514     else {
515       char const * name = T1_GetCharName(font_num, (unsigned char)c);
516
517       if (name) {
518         *out++ = strcmp(name, ".notdef") != 0;
519       }
520       else {
521         mm_log((2, "  No name found for character %lx\n", c));
522         *out++ = 0;
523       }
524     }
525     ++count;
526   }
527
528   return count;
529 }
530
531 /*
532 =item i_t1_face_name(font_num, name_buf, name_buf_size)
533
534 Copies the face name of the given C<font_num> to C<name_buf>.  Returns
535 the number of characters required to store the name (which can be
536 larger than C<name_buf_size>, including the space required to store
537 the terminating NUL).
538
539 If name_buf is too small (as specified by name_buf_size) then the name
540 will be truncated.  name_buf will always be NUL termintaed.
541
542 =cut
543 */
544
545 int
546 i_t1_face_name(int font_num, char *name_buf, size_t name_buf_size) {
547   char *name;
548
549   T1_errno = 0;
550   if (T1_LoadFont(font_num)) {
551     t1_push_error();
552     return 0;
553   }
554   name = T1_GetFontName(font_num);
555
556   if (name) {
557     strncpy(name_buf, name, name_buf_size);
558     name_buf[name_buf_size-1] = '\0';
559     return strlen(name) + 1;
560   }
561   else {
562     t1_push_error();
563     return 0;
564   }
565 }
566
567 int
568 i_t1_glyph_name(int font_num, unsigned long ch, char *name_buf, 
569                  size_t name_buf_size) {
570   char *name;
571
572   i_clear_error();
573   if (ch > 0xFF) {
574     return 0;
575   }
576   if (T1_LoadFont(font_num)) {
577     t1_push_error();
578     return 0;
579   }
580   name = T1_GetCharName(font_num, (unsigned char)ch);
581   if (name) {
582     if (strcmp(name, ".notdef")) {
583       strncpy(name_buf, name, name_buf_size);
584       name_buf[name_buf_size-1] = '\0';
585       return strlen(name) + 1;
586     }
587     else {
588       return 0;
589     }
590   }
591   else {
592     t1_push_error();
593     return 0;
594   }
595 }
596
597 static void
598 t1_push_error(void) {
599   switch (T1_errno) {
600   case 0: 
601     i_push_error(0, "No error"); 
602     break;
603
604 #ifdef T1ERR_SCAN_FONT_FORMAT
605   case T1ERR_SCAN_FONT_FORMAT:
606     i_push_error(T1ERR_SCAN_FONT_FORMAT, "SCAN_FONT_FORMAT"); 
607     break;
608 #endif
609
610 #ifdef T1ERR_SCAN_FILE_OPEN_ERR
611   case T1ERR_SCAN_FILE_OPEN_ERR:
612     i_push_error(T1ERR_SCAN_FILE_OPEN_ERR, "SCAN_FILE_OPEN_ERR"); 
613     break;
614 #endif
615
616 #ifdef T1ERR_SCAN_OUT_OF_MEMORY
617   case T1ERR_SCAN_OUT_OF_MEMORY:
618     i_push_error(T1ERR_SCAN_OUT_OF_MEMORY, "SCAN_OUT_OF_MEMORY"); 
619     break;
620 #endif
621
622 #ifdef T1ERR_SCAN_ERROR
623   case T1ERR_SCAN_ERROR:
624     i_push_error(T1ERR_SCAN_ERROR, "SCAN_ERROR"); 
625     break;
626 #endif
627
628 #ifdef T1ERR_SCAN_FILE_EOF
629   case T1ERR_SCAN_FILE_EOF:
630     i_push_error(T1ERR_SCAN_FILE_EOF, "SCAN_FILE_EOF"); 
631     break;
632 #endif
633
634 #ifdef T1ERR_PATH_ERROR
635   case T1ERR_PATH_ERROR:
636     i_push_error(T1ERR_PATH_ERROR, "PATH_ERROR"); 
637     break;
638 #endif
639
640 #ifdef T1ERR_PARSE_ERROR
641   case T1ERR_PARSE_ERROR:
642     i_push_error(T1ERR_PARSE_ERROR, "PARSE_ERROR"); 
643     break;
644 #endif
645
646 #ifdef T1ERR_TYPE1_ABORT
647   case T1ERR_TYPE1_ABORT:
648     i_push_error(T1ERR_TYPE1_ABORT, "TYPE1_ABORT"); 
649     break;
650 #endif
651
652 #ifdef T1ERR_INVALID_FONTID
653   case T1ERR_INVALID_FONTID:
654     i_push_error(T1ERR_INVALID_FONTID, "INVALID_FONTID"); 
655     break;
656 #endif
657
658 #ifdef T1ERR_INVALID_PARAMETER
659   case T1ERR_INVALID_PARAMETER:
660     i_push_error(T1ERR_INVALID_PARAMETER, "INVALID_PARAMETER"); 
661     break;
662 #endif
663
664 #ifdef T1ERR_OP_NOT_PERMITTED
665   case T1ERR_OP_NOT_PERMITTED:
666     i_push_error(T1ERR_OP_NOT_PERMITTED, "OP_NOT_PERMITTED"); 
667     break;
668 #endif
669
670 #ifdef T1ERR_ALLOC_MEM
671   case T1ERR_ALLOC_MEM:
672     i_push_error(T1ERR_ALLOC_MEM, "ALLOC_MEM"); 
673     break;
674 #endif
675
676 #ifdef T1ERR_FILE_OPEN_ERR
677   case T1ERR_FILE_OPEN_ERR:
678     i_push_error(T1ERR_FILE_OPEN_ERR, "FILE_OPEN_ERR"); 
679     break;
680 #endif
681
682 #ifdef T1ERR_UNSPECIFIED
683   case T1ERR_UNSPECIFIED:
684     i_push_error(T1ERR_UNSPECIFIED, "UNSPECIFIED"); 
685     break;
686 #endif
687
688 #ifdef T1ERR_NO_AFM_DATA
689   case T1ERR_NO_AFM_DATA:
690     i_push_error(T1ERR_NO_AFM_DATA, "NO_AFM_DATA"); 
691     break;
692 #endif
693
694 #ifdef T1ERR_X11
695   case T1ERR_X11:
696     i_push_error(T1ERR_X11, "X11"); 
697     break;
698 #endif
699
700 #ifdef T1ERR_COMPOSITE_CHAR
701   case T1ERR_COMPOSITE_CHAR:
702     i_push_error(T1ERR_COMPOSITE_CHAR, "COMPOSITE_CHAR"); 
703     break;
704 #endif
705
706   default:
707     i_push_errorf(T1_errno, "unknown error %d", (int)T1_errno);
708   }
709 }
710
711 #endif /* HAVE_LIBT1 */
712
713
714 /* Truetype font support */
715 #ifdef HAVE_LIBTT
716
717 /* These are enabled by default when configuring Freetype 1.x
718    I haven't a clue how to reliably detect it at compile time.
719
720    We need a compilation probe in Makefile.PL
721 */
722 #define FTXPOST 1
723 #define FTXERR18 1
724
725 #include <freetype.h>
726 #define TT_CHC 5
727
728 #ifdef FTXPOST
729 #include <ftxpost.h>
730 #endif
731
732 #ifdef FTXERR18
733 #include <ftxerr18.h>
734 #endif
735
736 /* some versions of FT1.x don't seem to define this - it's font defined
737    so it won't change */
738 #ifndef TT_MS_LANGID_ENGLISH_GENERAL
739 #define TT_MS_LANGID_ENGLISH_GENERAL 0x0409
740 #endif
741
742 /* convert a code point into an index in the glyph cache */
743 #define TT_HASH(x) ((x) & 0xFF)
744
745 typedef struct i_glyph_entry_ {
746   TT_Glyph glyph;
747   unsigned long ch;
748 } i_tt_glyph_entry;
749
750 #define TT_NOCHAR (~0UL)
751
752 struct TT_Instancehandle_ {
753   TT_Instance instance;
754   TT_Instance_Metrics imetrics;
755   TT_Glyph_Metrics gmetrics[256];
756   i_tt_glyph_entry glyphs[256];
757   int smooth;
758   int ptsize;
759   int order;
760 };
761
762 typedef struct TT_Instancehandle_ TT_Instancehandle;
763
764 struct TT_Fonthandle_ {
765   TT_Face face;
766   TT_Face_Properties properties;
767   TT_Instancehandle instanceh[TT_CHC];
768   TT_CharMap char_map;
769 #ifdef FTXPOST
770   int loaded_names;
771   TT_Error load_cond;
772 #endif
773 };
774
775 /* Defines */
776
777 #define USTRCT(x) ((x).z)
778 #define TT_VALID( handle )  ( ( handle ).z != NULL )
779
780 static void i_tt_push_error(TT_Error rc);
781
782 /* Prototypes */
783
784 static  int i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth );
785 static void i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth );
786 static void i_tt_done_raster_map( TT_Raster_Map *bit );
787 static void i_tt_clear_raster_map( TT_Raster_Map* bit );
788 static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off );
789 static  int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j );
790 static void 
791 i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics, 
792                    TT_Raster_Map *bit, TT_Raster_Map *small_bit, 
793                    int x_off, int y_off, int smooth );
794 static int
795 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit, 
796                         TT_Raster_Map *small_bit, int cords[6], 
797                         char const* txt, int len, int smooth, int utf8 );
798 static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, i_color *cl, int smooth );
799 static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth );
800 static  int
801 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], 
802                 float points, char const* txt, int len, int smooth, int utf8 );
803 static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6], int utf8 );
804
805
806 /* static globals needed */
807
808 static TT_Engine    engine;
809 static int  LTT_dpi    = 72; /* FIXME: this ought to be a part of the call interface */
810 static int  LTT_hinted = 1;  /* FIXME: this too */
811
812
813 /*
814  * FreeType interface
815  */
816
817
818 /*
819 =item init_tt()
820
821 Initializes the freetype font rendering engine
822
823 =cut
824 */
825
826 undef_int
827 i_init_tt() {
828   TT_Error  error;
829   mm_log((1,"init_tt()\n"));
830   error = TT_Init_FreeType( &engine );
831   if ( error ){
832     mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error));
833     return(1);
834   }
835
836 #ifdef FTXPOST
837   error = TT_Init_Post_Extension( engine );
838   if (error) {
839     mm_log((1, "Initialization of Post extension failed = 0x%x\n", error));
840     return 1;
841   }
842 #endif
843
844   return(0);
845 }
846
847
848 /* 
849 =item i_tt_get_instance(handle, points, smooth)
850
851 Finds a points+smooth instance or if one doesn't exist in the cache
852 allocates room and returns its cache entry
853
854    fontname - path to the font to load
855    handle   - handle to the font.
856    points   - points of the requested font
857    smooth   - boolean (True: antialias on, False: antialias is off)
858
859 =cut
860 */
861
862 static
863 int
864 i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) {
865   int i,idx;
866   TT_Error error;
867   
868   mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n",
869           handle,points,smooth));
870   
871   if (smooth == -1) { /* Smooth doesn't matter for this search */
872     for(i=0;i<TT_CHC;i++) {
873       if (handle->instanceh[i].ptsize==points) {
874         mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
875         return i;
876       }
877     }
878     smooth=1; /* We will be adding a font - add it as smooth then */
879   } else { /* Smooth doesn't matter for this search */
880     for(i=0;i<TT_CHC;i++) {
881       if (handle->instanceh[i].ptsize == points 
882           && handle->instanceh[i].smooth == smooth) {
883         mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
884         return i;
885       }
886     }
887   }
888   
889   /* Found the instance in the cache - return the cache index */
890   
891   for(idx=0;idx<TT_CHC;idx++) {
892     if (!(handle->instanceh[idx].order)) break; /* find the lru item */
893   }
894
895   mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
896   mm_log((1,"i_tt_get_instance: lru pointer 0x%X\n",
897           USTRCT(handle->instanceh[idx].instance) ));
898   
899   if ( USTRCT(handle->instanceh[idx].instance) ) {
900     mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
901
902     /* Free cached glyphs */
903     for(i=0;i<256;i++)
904       if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
905         TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
906
907     for(i=0;i<256;i++) {
908       handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
909       USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
910     }
911
912     /* Free instance if needed */
913     TT_Done_Instance( handle->instanceh[idx].instance );
914   }
915   
916   /* create and initialize instance */
917   /* FIXME: probably a memory leak on fail */
918   
919   (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) || 
920           ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
921           ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
922   
923   if ( error ) {
924     mm_log((1, "Could not create and initialize instance: error 0x%x.\n",error ));
925     return -1;
926   }
927   
928   /* Now that the instance should the inplace we need to lower all of the
929      ru counts and put `this' one with the highest entry */
930   
931   for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
932
933   handle->instanceh[idx].order=TT_CHC-1;
934   handle->instanceh[idx].ptsize=points;
935   handle->instanceh[idx].smooth=smooth;
936   TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
937
938   /* Zero the memory for the glyph storage so they are not thought as
939      cached if they haven't been cached since this new font was loaded */
940
941   for(i=0;i<256;i++) {
942     handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
943     USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
944   }
945   
946   return idx;
947 }
948
949
950 /*
951 =item i_tt_new(fontname)
952
953 Creates a new font handle object, finds a character map and initialise the
954 the font handle's cache
955
956    fontname - path to the font to load
957
958 =cut
959 */
960
961 TT_Fonthandle*
962 i_tt_new(char *fontname) {
963   TT_Error error;
964   TT_Fonthandle *handle;
965   unsigned short i,n;
966   unsigned short platform,encoding;
967
968   i_clear_error();
969   
970   mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
971   
972   /* allocate memory for the structure */
973   
974   handle = mymalloc( sizeof(TT_Fonthandle) );
975
976   /* load the typeface */
977   error = TT_Open_Face( engine, fontname, &handle->face );
978   if ( error ) {
979     if ( error == TT_Err_Could_Not_Open_File ) {
980       mm_log((1, "Could not find/open %s.\n", fontname ));
981     }
982     else {
983       mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname, 
984               error )); 
985     }
986     i_tt_push_error(error);
987     return NULL;
988   }
989   
990   TT_Get_Face_Properties( handle->face, &(handle->properties) );
991
992   /* First, look for a Unicode charmap */
993   n = handle->properties.num_CharMaps;
994   USTRCT( handle->char_map )=NULL; /* Invalidate character map */
995   
996   for ( i = 0; i < n; i++ ) {
997     TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
998     if ( (platform == 3 && encoding == 1 ) 
999          || (platform == 0 && encoding == 0 ) ) {
1000       mm_log((2,"i_tt_new - found char map platform %u encoding %u\n", 
1001               platform, encoding));
1002       TT_Get_CharMap( handle->face, i, &(handle->char_map) );
1003       break;
1004     }
1005   }
1006   if (!USTRCT(handle->char_map) && n != 0) {
1007     /* just use the first one */
1008     TT_Get_CharMap( handle->face, 0, &(handle->char_map));
1009   }
1010
1011   /* Zero the pointsizes - and ordering */
1012   
1013   for(i=0;i<TT_CHC;i++) {
1014     USTRCT(handle->instanceh[i].instance)=NULL;
1015     handle->instanceh[i].order=i;
1016     handle->instanceh[i].ptsize=0;
1017     handle->instanceh[i].smooth=-1;
1018   }
1019
1020 #ifdef FTXPOST
1021   handle->loaded_names = 0;
1022 #endif
1023
1024   mm_log((1,"i_tt_new <- 0x%X\n",handle));
1025   return handle;
1026 }
1027
1028
1029
1030 /*
1031  * raster map management
1032  */
1033
1034 /* 
1035 =item i_tt_init_raster_map(bit, width, height, smooth)
1036
1037 Allocates internal memory for the bitmap as needed by the parameters (internal)
1038                  
1039    bit    - bitmap to allocate into
1040    width  - width of the bitmap
1041    height - height of the bitmap
1042    smooth - boolean (True: antialias on, False: antialias is off)
1043
1044 =cut
1045 */
1046
1047 static
1048 void
1049 i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth ) {
1050
1051   mm_log((1,"i_tt_init_raster_map( bit 08x%08X, width %d, height %d, smooth %d)\n", bit, width, height, smooth));
1052   
1053   bit->rows  = height;
1054   bit->width = ( width + 3 ) & -4;
1055   bit->flow  = TT_Flow_Down;
1056   
1057   if ( smooth ) {
1058     bit->cols  = bit->width;
1059     bit->size  = bit->rows * bit->width;
1060   } else {
1061     bit->cols  = ( bit->width + 7 ) / 8;    /* convert to # of bytes     */
1062     bit->size  = bit->rows * bit->cols;     /* number of bytes in buffer */
1063   }
1064   
1065   mm_log((1,"i_tt_init_raster_map: bit->width %d, bit->cols %d, bit->rows %d, bit->size %d)\n", bit->width, bit->cols, bit->rows, bit->size ));
1066
1067   bit->bitmap = (void *) mymalloc( bit->size );
1068   if ( !bit->bitmap ) m_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",bit->size );
1069 }
1070
1071
1072 /*
1073 =item i_tt_clear_raster_map(bit)
1074
1075 Frees the bitmap data and sets pointer to NULL (internal)
1076                  
1077    bit - bitmap to free
1078
1079 =cut
1080 */
1081
1082 static
1083 void
1084 i_tt_done_raster_map( TT_Raster_Map *bit ) {
1085   myfree( bit->bitmap );
1086   bit->bitmap = NULL;
1087 }
1088
1089
1090 /*
1091 =item i_tt_clear_raster_map(bit)
1092
1093 Clears the specified bitmap (internal)
1094                  
1095    bit - bitmap to zero
1096
1097 =cut
1098 */
1099
1100
1101 static
1102 void
1103 i_tt_clear_raster_map( TT_Raster_Map*  bit ) {
1104   memset( bit->bitmap, 0, bit->size );
1105 }
1106
1107
1108 /* 
1109 =item i_tt_blit_or(dst, src, x_off, y_off)
1110
1111 function that blits one raster map into another (internal)
1112                  
1113    dst   - destination bitmap
1114    src   - source bitmap
1115    x_off - x offset into the destination bitmap
1116    y_off - y offset into the destination bitmap
1117
1118 =cut
1119 */
1120
1121 static
1122 void
1123 i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off ) {
1124   int  x,  y;
1125   int  x1, x2, y1, y2;
1126   char *s, *d;
1127   
1128   x1 = x_off < 0 ? -x_off : 0;
1129   y1 = y_off < 0 ? -y_off : 0;
1130   
1131   x2 = (int)dst->cols - x_off;
1132   if ( x2 > src->cols ) x2 = src->cols;
1133   
1134   y2 = (int)dst->rows - y_off;
1135   if ( y2 > src->rows ) y2 = src->rows;
1136
1137   if ( x1 >= x2 ) return;
1138
1139   /* do the real work now */
1140
1141   for ( y = y1; y < y2; ++y ) {
1142     s = ( (char*)src->bitmap ) + y * src->cols + x1;
1143     d = ( (char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
1144     
1145     for ( x = x1; x < x2; ++x ) {
1146       if (*s > *d)
1147         *d = *s;
1148       d++;
1149       s++;
1150     }
1151   }
1152 }
1153
1154 /* useful for debugging */
1155 #if 0
1156
1157 static void dump_raster_map(FILE *out, TT_Raster_Map *bit ) {
1158   int x, y;
1159   fprintf(out, "cols %d rows %d  flow %d\n", bit->cols, bit->rows, bit->flow);
1160   for (y = 0; y < bit->rows; ++y) {
1161     fprintf(out, "%2d:", y);
1162     for (x = 0; x < bit->cols; ++x) {
1163       if ((x & 7) == 0 && x) putc(' ', out);
1164       fprintf(out, "%02x", ((unsigned char *)bit->bitmap)[y*bit->cols+x]);
1165     }
1166     putc('\n', out);
1167   }
1168 }
1169
1170 #endif
1171
1172 /* 
1173 =item i_tt_get_glyph(handle, inst, j) 
1174
1175 Function to see if a glyph exists and if so cache it (internal)
1176                  
1177    handle - pointer to font handle
1178    inst   - font instance
1179    j      - charcode of glyph
1180
1181 =cut
1182 */
1183
1184 static
1185 int
1186 i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j) {
1187   unsigned short load_flags, code;
1188   TT_Error error;
1189
1190   mm_log((1, "i_tt_get_glyph(handle 0x%X, inst %d, j %d (%c))\n",
1191           handle,inst,j, ((j >= ' ' && j <= '~') ? j : '.')));
1192   
1193   /*mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] ));*/
1194
1195   if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)
1196        && handle->instanceh[inst].glyphs[TT_HASH(j)].ch == j) {
1197     mm_log((1,"i_tt_get_glyph: %d in cache\n",j));
1198     return 1;
1199   }
1200
1201   if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) ) {
1202     /* clean up the entry */
1203     TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1204     USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1205     handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
1206   }
1207   
1208   /* Ok - it wasn't cached - try to get it in */
1209   load_flags = TTLOAD_SCALE_GLYPH;
1210   if ( LTT_hinted ) load_flags |= TTLOAD_HINT_GLYPH;
1211   
1212   if ( !TT_VALID(handle->char_map) ) {
1213     code = (j - ' ' + 1) < 0 ? 0 : (j - ' ' + 1);
1214     if ( code >= handle->properties.num_Glyphs ) code = 0;
1215   } else code = TT_Char_Index( handle->char_map, j );
1216   
1217   if ( (error = TT_New_Glyph( handle->face, &handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)) ) {
1218     mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
1219     i_push_error(error, "TT_New_Glyph()");
1220     return 0;
1221   }
1222   if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, code, load_flags)) ) {
1223     mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
1224     /* Don't leak */
1225     TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1226     USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1227     i_push_error(error, "TT_Load_Glyph()");
1228     return 0;
1229   }
1230
1231   /* At this point the glyph should be allocated and loaded */
1232   handle->instanceh[inst].glyphs[TT_HASH(j)].ch = j;
1233
1234   /* Next get the glyph metrics */
1235   error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, 
1236                                 &handle->instanceh[inst].gmetrics[TT_HASH(j)] );
1237   if (error) {
1238     mm_log((1, "TT_Get_Glyph_Metrics: error 0x%x.\n", error ));
1239     TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1240     USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1241     handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
1242     i_push_error(error, "TT_Get_Glyph_Metrics()");
1243     return 0;
1244   }
1245
1246   return 1;
1247 }
1248
1249 /*
1250 =item i_tt_has_chars(handle, text, len, utf8, out)
1251
1252 Check if the given characters are defined by the font.  Note that len
1253 is the number of bytes, not the number of characters (when utf8 is
1254 non-zero).
1255
1256 Returns the number of characters that were checked.
1257
1258 =cut
1259 */
1260
1261 int
1262 i_tt_has_chars(TT_Fonthandle *handle, char const *text, int len, int utf8,
1263                char *out) {
1264   int count = 0;
1265   mm_log((1, "i_tt_has_chars(handle %p, text %p, len %d, utf8 %d)\n", 
1266           handle, text, len, utf8));
1267
1268   while (len) {
1269     unsigned long c;
1270     int index;
1271     if (utf8) {
1272       c = i_utf8_advance(&text, &len);
1273       if (c == ~0UL) {
1274         i_push_error(0, "invalid UTF8 character");
1275         return 0;
1276       }
1277     }
1278     else {
1279       c = (unsigned char)*text++;
1280       --len;
1281     }
1282     
1283     if (TT_VALID(handle->char_map)) {
1284       index = TT_Char_Index(handle->char_map, c);
1285     }
1286     else {
1287       index = (c - ' ' + 1) < 0 ? 0 : (c - ' ' + 1);
1288       if (index >= handle->properties.num_Glyphs)
1289         index = 0;
1290     }
1291     *out++ = index != 0;
1292     ++count;
1293   }
1294
1295   return count;
1296 }
1297
1298 /* 
1299 =item i_tt_destroy(handle)
1300
1301 Clears the data taken by a font including all cached data such as
1302 pixmaps and glyphs
1303                  
1304    handle - pointer to font handle
1305
1306 =cut
1307 */
1308
1309 void
1310 i_tt_destroy( TT_Fonthandle *handle) {
1311   TT_Close_Face( handle->face );
1312   myfree( handle );
1313   
1314   /* FIXME: Should these be freed automatically by the library? 
1315
1316   TT_Done_Instance( instance );
1317   void
1318     i_tt_done_glyphs( void ) {
1319     int  i;
1320
1321     if ( !glyphs ) return;
1322     
1323     for ( i = 0; i < 256; ++i ) TT_Done_Glyph( glyphs[i] );
1324     free( glyphs );
1325     
1326     glyphs = NULL;
1327   }
1328   */
1329 }
1330
1331
1332 /*
1333  * FreeType Rendering functions
1334  */
1335
1336
1337 /* 
1338 =item i_tt_render_glyph(handle, gmetrics, bit, smallbit, x_off, y_off, smooth)
1339
1340 Renders a single glyph into the bit rastermap (internal)
1341
1342    handle   - pointer to font handle
1343    gmetrics - the metrics for the glyph to be rendered
1344    bit      - large bitmap that is the destination for the text
1345    smallbit - small bitmap that is used only if smooth is true
1346    x_off    - x offset of glyph
1347    y_off    - y offset of glyph
1348    smooth   - boolean (True: antialias on, False: antialias is off)
1349
1350 =cut
1351 */
1352
1353 static
1354 void
1355 i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics, TT_Raster_Map *bit, TT_Raster_Map *small_bit, int x_off, int y_off, int smooth ) {
1356   
1357   mm_log((1,"i_tt_render_glyph(glyph 0x0%X, gmetrics 0x0%X, bit 0x%X, small_bit 0x%X, x_off %d, y_off %d, smooth %d)\n",
1358           USTRCT(glyph), gmetrics, bit, small_bit, x_off,y_off,smooth));
1359   
1360   if ( !smooth ) TT_Get_Glyph_Bitmap( glyph, bit, x_off * 64, y_off * 64);
1361   else {
1362     TT_F26Dot6 xmin, ymin, xmax, ymax;
1363
1364     xmin =  gmetrics->bbox.xMin & -64;
1365     ymin =  gmetrics->bbox.yMin & -64;
1366     xmax = (gmetrics->bbox.xMax + 63) & -64;
1367     ymax = (gmetrics->bbox.yMax + 63) & -64;
1368     
1369     i_tt_clear_raster_map( small_bit );
1370     TT_Get_Glyph_Pixmap( glyph, small_bit, -xmin, -ymin );
1371     i_tt_blit_or( bit, small_bit, xmin/64 + x_off, -ymin/64 - y_off );
1372   }
1373 }
1374
1375
1376 /*
1377 =item i_tt_render_all_glyphs(handle, inst, bit, small_bit, cords, txt, len, smooth)
1378
1379 calls i_tt_render_glyph to render each glyph into the bit rastermap (internal)
1380
1381    handle   - pointer to font handle
1382    inst     - font instance
1383    bit      - large bitmap that is the destination for the text
1384    smallbit - small bitmap that is used only if smooth is true
1385    txt      - string to render
1386    len      - length of the string to render
1387    smooth   - boolean (True: antialias on, False: antialias is off)
1388
1389 =cut
1390 */
1391
1392 static
1393 int
1394 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
1395                         TT_Raster_Map *small_bit, int cords[6], 
1396                         char const* txt, int len, int smooth, int utf8 ) {
1397   unsigned long j;
1398   TT_F26Dot6 x,y;
1399   
1400   mm_log((1,"i_tt_render_all_glyphs( handle 0x%X, inst %d, bit 0x%X, small_bit 0x%X, txt '%.*s', len %d, smooth %d, utf8 %d)\n",
1401           handle, inst, bit, small_bit, len, txt, len, smooth, utf8));
1402   
1403   /* 
1404      y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM);
1405   */
1406
1407   x=-cords[0]; /* FIXME: If you font is antialiased this should be expanded by one to allow for aa expansion and the allocation too - do before passing here */
1408   y=-cords[4];
1409   
1410   while (len) {
1411     if (utf8) {
1412       j = i_utf8_advance(&txt, &len);
1413       if (j == ~0UL) {
1414         i_push_error(0, "invalid UTF8 character");
1415         return 0;
1416       }
1417     }
1418     else {
1419       j = (unsigned char)*txt++;
1420       --len;
1421     }
1422     if ( !i_tt_get_glyph(handle,inst,j) ) 
1423       continue;
1424     i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, 
1425                        &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit, 
1426                        small_bit, x, y, smooth );
1427     x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64;
1428   }
1429
1430   return 1;
1431 }
1432
1433
1434 /*
1435  * Functions to render rasters (single channel images) onto images
1436  */
1437
1438 /* 
1439 =item i_tt_dump_raster_map2(im, bit, xb, yb, cl, smooth)
1440
1441 Function to dump a raster onto an image in color used by i_tt_text() (internal).
1442
1443    im     - image to dump raster on
1444    bit    - bitmap that contains the text to be dumped to im
1445    xb, yb - coordinates, left edge and baseline
1446    cl     - color to use for text
1447    smooth - boolean (True: antialias on, False: antialias is off)
1448
1449 =cut
1450 */
1451
1452 static
1453 void
1454 i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, i_color *cl, int smooth ) {
1455   char *bmap;
1456   i_color val;
1457   int c, i, ch, x, y;
1458   mm_log((1,"i_tt_dump_raster_map2(im 0x%x, bit 0x%X, xb %d, yb %d, cl 0x%X)\n",im,bit,xb,yb,cl));
1459   
1460   bmap = (char *)bit->bitmap;
1461
1462   if ( smooth ) {
1463
1464     for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1465       c=(255*bmap[y*(bit->cols)+x])/4;
1466       i=255-c;
1467       i_gpix(im,x+xb,y+yb,&val);
1468       for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
1469       i_ppix(im,x+xb,y+yb,&val);
1470     }
1471
1472   } else {
1473
1474     for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1475       c=( bmap[y*bit->cols+x/8] & (128>>(x%8)) ) ? 255 : 0;
1476       i=255-c;
1477       i_gpix(im,x+xb,y+yb,&val);
1478       for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
1479       i_ppix(im,x+xb,y+yb,&val);
1480     }
1481
1482   }
1483 }
1484
1485
1486 /*
1487 =item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth)
1488
1489 Function to dump a raster onto a single channel image in color (internal)
1490
1491    im      - image to dump raster on
1492    bit     - bitmap that contains the text to be dumped to im
1493    xb, yb  - coordinates, left edge and baseline
1494    channel - channel to copy to
1495    smooth  - boolean (True: antialias on, False: antialias is off)
1496
1497 =cut
1498 */
1499
1500 static
1501 void
1502 i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map*  bit, int xb, int yb, int channel, int smooth ) {
1503   char *bmap;
1504   i_color val;
1505   int c,x,y;
1506
1507   mm_log((1,"i_tt_dump_raster_channel(im 0x%x, bit 0x%X, xb %d, yb %d, channel %d)\n",im,bit,xb,yb,channel));
1508   
1509   bmap = (char *)bit->bitmap;
1510   
1511   if ( smooth ) {
1512     for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1513       c=(255*bmap[y*(bit->cols)+x])/4;
1514       i_gpix(im,x+xb,y+yb,&val);
1515       val.channel[channel]=c;
1516       i_ppix(im,x+xb,y+yb,&val);
1517     }
1518   } else {
1519     for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1520       c=( bmap[y*bit->cols+x/8] & (128>>(x%8)) ) ? 255 : 0;
1521       i_gpix(im,x+xb,y+yb,&val);
1522       val.channel[channel]=c;
1523       i_ppix(im,x+xb,y+yb,&val);
1524     }
1525   }
1526 }
1527
1528
1529 /* 
1530 =item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth) 
1531
1532 interface for generating single channel raster of text (internal)
1533
1534    handle - pointer to font handle
1535    bit    - the bitmap that is allocated, rendered into and NOT freed
1536    cords  - the bounding box (modified in place)
1537    points - font size to use
1538    txt    - string to render
1539    len    - length of the string to render
1540    smooth - boolean (True: antialias on, False: antialias is off)
1541
1542 =cut
1543 */
1544
1545 static
1546 int
1547 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char const* txt, int len, int smooth, int utf8 ) {
1548   int inst;
1549   int width, height;
1550   TT_Raster_Map small_bit;
1551   
1552   /* find or install an instance */
1553   if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) { 
1554     mm_log((1,"i_tt_rasterize: get instance failed\n"));
1555     return 0;
1556   }
1557   
1558   /* calculate bounding box */
1559   if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 ))
1560     return 0;
1561     
1562   
1563   width  = cords[2]-cords[0];
1564   height = cords[5]-cords[4];
1565   
1566   mm_log((1,"i_tt_rasterize: width=%d, height=%d\n",width, height )); 
1567   
1568   i_tt_init_raster_map ( bit, width, height, smooth );
1569   i_tt_clear_raster_map( bit );
1570   if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth );
1571   
1572   if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len, 
1573                                smooth, utf8 )) {
1574     if ( smooth ) 
1575       i_tt_done_raster_map( &small_bit );
1576     return 0;
1577   }
1578
1579   if ( smooth ) i_tt_done_raster_map( &small_bit );
1580   return 1;
1581 }
1582
1583
1584
1585 /* 
1586  * Exported text rendering interfaces
1587  */
1588
1589
1590 /*
1591 =item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth, utf8)
1592
1593 Interface to text rendering into a single channel in an image
1594
1595    handle  - pointer to font handle
1596    im      - image to render text on to
1597    xb, yb  - coordinates, left edge and baseline
1598    channel - channel to render into
1599    points  - font size to use
1600    txt     - string to render
1601    len     - length of the string to render
1602    smooth  - boolean (True: antialias on, False: antialias is off)
1603
1604 =cut
1605 */
1606
1607 undef_int
1608 i_tt_cp( TT_Fonthandle *handle, i_img *im, int xb, int yb, int channel, float points, char const* txt, int len, int smooth, int utf8, int align ) {
1609
1610   int cords[BOUNDING_BOX_COUNT];
1611   int ascent, st_offset, y;
1612   TT_Raster_Map bit;
1613   
1614   i_clear_error();
1615   if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1616   
1617   ascent=cords[BBOX_ASCENT];
1618   st_offset=cords[BBOX_NEG_WIDTH];
1619   y = align ? yb-ascent : yb;
1620
1621   i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , y, channel, smooth );
1622   i_tt_done_raster_map( &bit );
1623
1624   return 1;
1625 }
1626
1627
1628 /* 
1629 =item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8) 
1630
1631 Interface to text rendering in a single color onto an image
1632
1633    handle  - pointer to font handle
1634    im      - image to render text on to
1635    xb, yb  - coordinates, left edge and baseline
1636    cl      - color to use for text
1637    points  - font size to use
1638    txt     - string to render
1639    len     - length of the string to render
1640    smooth  - boolean (True: antialias on, False: antialias is off)
1641
1642 =cut
1643 */
1644
1645 undef_int
1646 i_tt_text( TT_Fonthandle *handle, i_img *im, int xb, int yb, i_color *cl, float points, char const* txt, int len, int smooth, int utf8, int align) {
1647   int cords[BOUNDING_BOX_COUNT];
1648   int ascent, st_offset, y;
1649   TT_Raster_Map bit;
1650
1651   i_clear_error();
1652   
1653   if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1654   
1655   ascent=cords[BBOX_ASCENT];
1656   st_offset=cords[BBOX_NEG_WIDTH];
1657   y = align ? yb-ascent : yb;
1658
1659   i_tt_dump_raster_map2( im, &bit, xb+st_offset, y, cl, smooth ); 
1660   i_tt_done_raster_map( &bit );
1661
1662   return 1;
1663 }
1664
1665
1666 /*
1667 =item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8) 
1668
1669 Function to get texts bounding boxes given the instance of the font (internal)
1670
1671    handle - pointer to font handle
1672    inst   -  font instance
1673    txt    -  string to measure
1674    len    -  length of the string to render
1675    cords  - the bounding box (modified in place)
1676
1677 =cut
1678 */
1679
1680 static
1681 undef_int
1682 i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[BOUNDING_BOX_COUNT], int utf8 ) {
1683   int upm, casc, cdesc, first;
1684   
1685   int start    = 0;
1686   int width    = 0;
1687   int gdescent = 0;
1688   int gascent  = 0;
1689   int descent  = 0;
1690   int ascent   = 0;
1691   int rightb   = 0;
1692
1693   unsigned long j;
1694   unsigned char *ustr;
1695   ustr=(unsigned char*)txt;
1696
1697   mm_log((1,"i_tt_box_inst(handle 0x%X,inst %d,txt '%.*s', len %d, utf8 %d)\n",handle,inst,len,txt,len, utf8));
1698
1699   upm     = handle->properties.header->Units_Per_EM;
1700   gascent  = ( handle->properties.horizontal->Ascender  * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm;
1701   gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem - upm + 1) / upm;
1702   
1703   width   = 0;
1704   start   = 0;
1705   
1706   mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent));
1707
1708   first=1;
1709   while (len) {
1710     if (utf8) {
1711       j = i_utf8_advance(&txt, &len);
1712       if (j == ~0UL) {
1713         i_push_error(0, "invalid UTF8 character");
1714         return 0;
1715       }
1716     }
1717     else {
1718       j = (unsigned char)*txt++;
1719       --len;
1720     }
1721     if ( i_tt_get_glyph(handle,inst,j) ) {
1722       TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j);
1723       width += gm->advance   / 64;
1724       casc   = (gm->bbox.yMax+63) / 64;
1725       cdesc  = (gm->bbox.yMin-63) / 64;
1726
1727       mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n", 
1728               ((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc));
1729
1730       if (first) {
1731         start    = gm->bbox.xMin / 64;
1732         ascent   = (gm->bbox.yMax+63) / 64;
1733         descent  = (gm->bbox.yMin-63) / 64;
1734         first = 0;
1735       }
1736       if (!len) { /* if at end of string */
1737         /* the right-side bearing - in case the right-side of a 
1738            character goes past the right of the advance width,
1739            as is common for italic fonts
1740         */
1741         rightb = gm->advance - gm->bearingX 
1742           - (gm->bbox.xMax - gm->bbox.xMin);
1743         /* fprintf(stderr, "font info last: %d %d %d %d\n", 
1744            gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */
1745       }
1746
1747       ascent  = (ascent  >  casc ?  ascent : casc );
1748       descent = (descent < cdesc ? descent : cdesc);
1749     }
1750   }
1751   
1752   cords[BBOX_NEG_WIDTH]=start;
1753   cords[BBOX_GLOBAL_DESCENT]=gdescent;
1754   cords[BBOX_POS_WIDTH]=width;
1755   if (rightb < 0)
1756     cords[BBOX_POS_WIDTH] -= rightb / 64;
1757   cords[BBOX_GLOBAL_ASCENT]=gascent;
1758   cords[BBOX_DESCENT]=descent;
1759   cords[BBOX_ASCENT]=ascent;
1760   cords[BBOX_ADVANCE_WIDTH] = width;
1761   cords[BBOX_RIGHT_BEARING] = rightb / 64;
1762
1763   return BBOX_RIGHT_BEARING + 1;
1764 }
1765
1766
1767 /*
1768 =item i_tt_bbox(handle, points, txt, len, cords, utf8)
1769
1770 Interface to get a strings bounding box
1771
1772    handle - pointer to font handle
1773    points - font size to use
1774    txt    - string to render
1775    len    - length of the string to render
1776    cords  - the bounding box (modified in place)
1777
1778 =cut
1779 */
1780
1781 undef_int
1782 i_tt_bbox( TT_Fonthandle *handle, float points,char *txt,int len,int cords[6], int utf8) {
1783   int inst;
1784
1785   i_clear_error();
1786   mm_log((1,"i_tt_box(handle 0x%X,points %f,txt '%.*s', len %d, utf8 %d)\n",handle,points,len,txt,len, utf8));
1787
1788   if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
1789     i_push_errorf(0, "i_tt_get_instance(%g)", points);
1790     mm_log((1,"i_tt_text: get instance failed\n"));
1791     return 0;
1792   }
1793
1794   return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8);
1795 }
1796
1797 /*
1798 =item i_tt_face_name(handle, name_buf, name_buf_size)
1799
1800 Retrieve's the font's postscript name.
1801
1802 This is complicated by the need to handle encodings and so on.
1803
1804 =cut
1805  */
1806 int
1807 i_tt_face_name(TT_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
1808   TT_Face_Properties props;
1809   int name_count;
1810   int i;
1811   TT_UShort platform_id, encoding_id, lang_id, name_id;
1812   TT_UShort name_len;
1813   TT_String *name;
1814   int want_index = -1; /* an acceptable but not perfect name */
1815   int score = 0;
1816
1817   i_clear_error();
1818   
1819   TT_Get_Face_Properties(handle->face, &props);
1820   name_count = props.num_Names;
1821   for (i = 0; i < name_count; ++i) {
1822     TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id, 
1823                    &name_id);
1824
1825     TT_Get_Name_String(handle->face, i, &name, &name_len);
1826
1827     if (platform_id != TT_PLATFORM_APPLE_UNICODE && name_len
1828         && name_id == TT_NAME_ID_PS_NAME) {
1829       int might_want_index = -1;
1830       int might_score = 0;
1831       if ((platform_id == TT_PLATFORM_MACINTOSH && encoding_id == TT_MAC_ID_ROMAN)
1832           ||
1833           (platform_id == TT_PLATFORM_MICROSOFT && encoding_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)) {
1834         /* exactly what we want */
1835         want_index = i;
1836         break;
1837       }
1838       
1839       if (platform_id == TT_PLATFORM_MICROSOFT
1840           && (encoding_id & 0xFF) == TT_MS_LANGID_ENGLISH_GENERAL) {
1841         /* any english is good */
1842         might_want_index = i;
1843         might_score = 9;
1844       }
1845       /* there might be something in between */
1846       else {
1847         /* anything non-unicode is better than nothing */
1848         might_want_index = i;
1849         might_score = 1;
1850       }
1851       if (might_score > score) {
1852         score = might_score;
1853         want_index = might_want_index;
1854       }
1855     }
1856   }
1857
1858   if (want_index != -1) {
1859     TT_Get_Name_String(handle->face, want_index, &name, &name_len);
1860     
1861     strncpy(name_buf, name, name_buf_size);
1862     name_buf[name_buf_size-1] = '\0';
1863
1864     return strlen(name) + 1;
1865   }
1866   else {
1867     i_push_error(0, "no face name present");
1868     return 0;
1869   }
1870 }
1871
1872 void i_tt_dump_names(TT_Fonthandle *handle) {
1873   TT_Face_Properties props;
1874   int name_count;
1875   int i;
1876   TT_UShort platform_id, encoding_id, lang_id, name_id;
1877   TT_UShort name_len;
1878   TT_String *name;
1879   
1880   TT_Get_Face_Properties(handle->face, &props);
1881   name_count = props.num_Names;
1882   for (i = 0; i < name_count; ++i) {
1883     TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id, 
1884                    &name_id);
1885     TT_Get_Name_String(handle->face, i, &name, &name_len);
1886
1887     printf("# %d: plat %d enc %d lang %d name %d value ", i, platform_id,
1888            encoding_id, lang_id, name_id);
1889     if (platform_id == TT_PLATFORM_APPLE_UNICODE) {
1890       printf("(unicode)\n");
1891     }
1892     else {
1893       printf("'%s'\n", name);
1894     }
1895   }
1896 }
1897
1898 int
1899 i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf, 
1900                  size_t name_buf_size) {
1901 #ifdef FTXPOST
1902   TT_Error rc;
1903   TT_String *psname;
1904   TT_UShort index;
1905
1906   i_clear_error();
1907
1908   if (!handle->loaded_names) {
1909     TT_Post post;
1910     mm_log((1, "Loading PS Names"));
1911     handle->load_cond = TT_Load_PS_Names(handle->face, &post);
1912     ++handle->loaded_names;
1913   }
1914
1915   if (handle->load_cond) {
1916     i_push_errorf(rc, "error loading names (%d)", handle->load_cond);
1917     return 0;
1918   }
1919   
1920   index = TT_Char_Index(handle->char_map, ch);
1921   if (!index) {
1922     i_push_error(0, "no such character");
1923     return 0;
1924   }
1925
1926   rc = TT_Get_PS_Name(handle->face, index, &psname);
1927
1928   if (rc) {
1929     i_push_error(rc, "error getting name");
1930     return 0;
1931   }
1932
1933   strncpy(name_buf, psname, name_buf_size);
1934   name_buf[name_buf_size-1] = '\0';
1935
1936   return strlen(psname) + 1;
1937 #else
1938   mm_log((1, "FTXPOST extension not enabled\n"));
1939   i_clear_error();
1940   i_push_error(0, "Use of FTXPOST extension disabled");
1941
1942   return 0;
1943 #endif
1944 }
1945
1946 /*
1947 =item i_tt_push_error(code)
1948
1949 Push an error message and code onto the Imager error stack.
1950
1951 =cut
1952 */
1953 static void
1954 i_tt_push_error(TT_Error rc) {
1955 #ifdef FTXERR18
1956   TT_String const *msg = TT_ErrToString18(rc);
1957
1958   i_push_error(rc, msg);
1959 #else
1960   i_push_errorf(rc, "Error code 0x%04x", (unsigned)rc);
1961 #endif
1962 }
1963
1964 #endif /* HAVE_LIBTT */
1965
1966
1967 /*
1968 =back
1969
1970 =head1 AUTHOR
1971
1972 Arnar M. Hrafnkelsson <addi@umich.edu>
1973
1974 =head1 SEE ALSO
1975
1976 Imager(3)
1977
1978 =cut
1979 */