]> git.imager.perl.org - imager.git/blob - font.c
document parseiptc()
[imager.git] / font.c
1 #include "imager.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 static int t1_active_fonts = 0;
91 static int t1_initialized = 0;
92
93 /* 
94 =item i_init_t1(t1log)
95
96 Initializes the t1lib font rendering engine.
97
98 =cut
99 */
100
101 undef_int
102 i_init_t1(int t1log) {
103   int init_flags = IGNORE_CONFIGFILE|IGNORE_FONTDATABASE;
104   mm_log((1,"init_t1()\n"));
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   }
114   
115   if (t1log)
116     init_flags |= LOGFILE;
117   if ((T1_InitLib(init_flags) == NULL)){
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 */
123
124   ++t1_initialized;
125
126   return(0);
127 }
128
129
130 /* 
131 =item i_close_t1()
132
133 Shuts the t1lib font rendering engine down.
134
135   This it seems that this function is never used.
136
137 =cut
138 */
139
140 void
141 i_close_t1(void) {
142   T1_CloseLib();
143   t1_initialized = 0;
144 }
145
146
147 /*
148 =item i_t1_new(pfb, afm)
149
150 Loads 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
158 int
159 i_t1_new(char *pfb,char *afm) {
160   int font_id;
161
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   }
173
174   ++t1_active_fonts;
175
176   return font_id;
177 }
178
179 /*
180 =item i_t1_destroy(font_id)
181
182 Frees resources for a t1 font with given font id.
183
184    font_id - number of the font to free
185
186 =cut
187 */
188
189 int
190 i_t1_destroy(int font_id) {
191   mm_log((1,"i_t1_destroy(font_id %d)\n",font_id));
192
193   --t1_active_fonts;
194
195   return T1_DeleteFont(font_id);
196 }
197
198
199 /*
200 =item i_t1_set_aa(st)
201
202 Sets the antialiasing level of the t1 library.
203
204    st - 0 =  NONE, 1 = LOW, 2 =  HIGH.
205
206 =cut
207 */
208
209 void
210 i_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
239 Interface 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
254 undef_int
255 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) {
256   GLYPH *glyph;
257   int xsize,ysize,x,y;
258   i_color val;
259   int mod_flags = t1_get_flags(flags);
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
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   }
274   if (glyph == NULL)
275     return 0;
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
305 function 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
317 int
318 i_t1_bbox(int fontnum,float points,const char *str,int len,int cords[6], int utf8,char const *flags) {
319   BBox bbox;
320   BBox gbbox;
321   int mod_flags = t1_get_flags(flags);
322   int advance;
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 */ 
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,(char *)str,len,0,mod_flags);
334   }
335   gbbox = T1_GetFontBBox(fontnum);
336   advance = T1_GetStringWidth(fontnum, (char *)str, len, 0, mod_flags);
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
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;
355
356   cords[BBOX_ADVANCE_WIDTH] = ((float)advance * points)/1000;
357   cords[BBOX_RIGHT_BEARING] = 
358     cords[BBOX_ADVANCE_WIDTH] - cords[BBOX_POS_WIDTH];
359
360   return BBOX_RIGHT_BEARING+1;
361 }
362
363
364 /*
365 =item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align)
366
367 Interface 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
382 undef_int
383 i_t1_text(i_img *im,int xb,int yb,const i_color *cl,int fontnum,float points,const char* str,int len,int align, int utf8, char const *flags) {
384   GLYPH *glyph;
385   int xsize,ysize,x,y,ch;
386   i_color val;
387   unsigned char c,i;
388   int mod_flags = t1_get_flags(flags);
389
390   if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
391
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     /* T1_AASetString() accepts a char * not a const char */
400     glyph=T1_AASetString( fontnum, (char *)str, len, 0, mod_flags, points, NULL);
401   }
402   if (glyph == NULL)
403     return 0;
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
427 /*
428 =item t1_get_flags(flags)
429
430 Processes the characters in I<flags> to create a mod_flags value used
431 by some T1Lib functions.
432
433 =cut
434  */
435 static int
436 t1_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
454 Produces an unencoded version of I<in> by dropping any Unicode
455 character over 255.
456
457 Returns a newly allocated buffer which should be freed with myfree().
458 Sets *outlen to the number of bytes used in the output string.
459
460 =cut
461 */
462
463 static char *
464 t1_from_utf8(char const *in, int len, int *outlen) {
465   /* at this point len is from a perl SV, so can't approach MAXINT */
466   char *out = mymalloc(len+1); /* checked 5Nov05 tonyc */
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 }
487
488 /*
489 =item i_t1_has_chars(font_num, text, len, utf8, out)
490
491 Check if the given characters are defined by the font.  Note that len
492 is the number of bytes, not the number of characters (when utf8 is
493 non-zero).
494
495 out[char index] will be true if the character exists.
496
497 Accepts UTF-8, but since T1 can only have 256 characters, any chars
498 with values over 255 will simply be returned as false.
499
500 Returns the number of characters that were checked.
501
502 =cut
503 */
504
505 int
506 i_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;
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
557 Copies the face name of the given C<font_num> to C<name_buf>.  Returns
558 the number of characters required to store the name (which can be
559 larger than C<name_buf_size>, including the space required to store
560 the terminating NUL).
561
562 If name_buf is too small (as specified by name_buf_size) then the name
563 will be truncated.  name_buf will always be NUL termintaed.
564
565 =cut
566 */
567
568 int
569 i_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
590 int
591 i_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
620 static void
621 t1_push_error(void) {
622   switch (T1_errno) {
623   case 0: 
624     i_push_error(0, "No error"); 
625     break;
626
627 #ifdef T1ERR_SCAN_FONT_FORMAT
628   case T1ERR_SCAN_FONT_FORMAT:
629     i_push_error(T1ERR_SCAN_FONT_FORMAT, "SCAN_FONT_FORMAT"); 
630     break;
631 #endif
632
633 #ifdef T1ERR_SCAN_FILE_OPEN_ERR
634   case T1ERR_SCAN_FILE_OPEN_ERR:
635     i_push_error(T1ERR_SCAN_FILE_OPEN_ERR, "SCAN_FILE_OPEN_ERR"); 
636     break;
637 #endif
638
639 #ifdef T1ERR_SCAN_OUT_OF_MEMORY
640   case T1ERR_SCAN_OUT_OF_MEMORY:
641     i_push_error(T1ERR_SCAN_OUT_OF_MEMORY, "SCAN_OUT_OF_MEMORY"); 
642     break;
643 #endif
644
645 #ifdef T1ERR_SCAN_ERROR
646   case T1ERR_SCAN_ERROR:
647     i_push_error(T1ERR_SCAN_ERROR, "SCAN_ERROR"); 
648     break;
649 #endif
650
651 #ifdef T1ERR_SCAN_FILE_EOF
652   case T1ERR_SCAN_FILE_EOF:
653     i_push_error(T1ERR_SCAN_FILE_EOF, "SCAN_FILE_EOF"); 
654     break;
655 #endif
656
657 #ifdef T1ERR_PATH_ERROR
658   case T1ERR_PATH_ERROR:
659     i_push_error(T1ERR_PATH_ERROR, "PATH_ERROR"); 
660     break;
661 #endif
662
663 #ifdef T1ERR_PARSE_ERROR
664   case T1ERR_PARSE_ERROR:
665     i_push_error(T1ERR_PARSE_ERROR, "PARSE_ERROR"); 
666     break;
667 #endif
668
669 #ifdef T1ERR_TYPE1_ABORT
670   case T1ERR_TYPE1_ABORT:
671     i_push_error(T1ERR_TYPE1_ABORT, "TYPE1_ABORT"); 
672     break;
673 #endif
674
675 #ifdef T1ERR_INVALID_FONTID
676   case T1ERR_INVALID_FONTID:
677     i_push_error(T1ERR_INVALID_FONTID, "INVALID_FONTID"); 
678     break;
679 #endif
680
681 #ifdef T1ERR_INVALID_PARAMETER
682   case T1ERR_INVALID_PARAMETER:
683     i_push_error(T1ERR_INVALID_PARAMETER, "INVALID_PARAMETER"); 
684     break;
685 #endif
686
687 #ifdef T1ERR_OP_NOT_PERMITTED
688   case T1ERR_OP_NOT_PERMITTED:
689     i_push_error(T1ERR_OP_NOT_PERMITTED, "OP_NOT_PERMITTED"); 
690     break;
691 #endif
692
693 #ifdef T1ERR_ALLOC_MEM
694   case T1ERR_ALLOC_MEM:
695     i_push_error(T1ERR_ALLOC_MEM, "ALLOC_MEM"); 
696     break;
697 #endif
698
699 #ifdef T1ERR_FILE_OPEN_ERR
700   case T1ERR_FILE_OPEN_ERR:
701     i_push_error(T1ERR_FILE_OPEN_ERR, "FILE_OPEN_ERR"); 
702     break;
703 #endif
704
705 #ifdef T1ERR_UNSPECIFIED
706   case T1ERR_UNSPECIFIED:
707     i_push_error(T1ERR_UNSPECIFIED, "UNSPECIFIED"); 
708     break;
709 #endif
710
711 #ifdef T1ERR_NO_AFM_DATA
712   case T1ERR_NO_AFM_DATA:
713     i_push_error(T1ERR_NO_AFM_DATA, "NO_AFM_DATA"); 
714     break;
715 #endif
716
717 #ifdef T1ERR_X11
718   case T1ERR_X11:
719     i_push_error(T1ERR_X11, "X11"); 
720     break;
721 #endif
722
723 #ifdef T1ERR_COMPOSITE_CHAR
724   case T1ERR_COMPOSITE_CHAR:
725     i_push_error(T1ERR_COMPOSITE_CHAR, "COMPOSITE_CHAR"); 
726     break;
727 #endif
728
729   default:
730     i_push_errorf(T1_errno, "unknown error %d", (int)T1_errno);
731   }
732 }
733
734 #endif /* HAVE_LIBT1 */
735
736
737 /* Truetype font support */
738 #ifdef HAVE_LIBTT
739
740 /* These are enabled by default when configuring Freetype 1.x
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
746 #define FTXERR18 1
747
748 #include <freetype.h>
749 #define TT_CHC 5
750
751 #ifdef FTXPOST
752 #include <ftxpost.h>
753 #endif
754
755 #ifdef FTXERR18
756 #include <ftxerr18.h>
757 #endif
758
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
765 /* convert a code point into an index in the glyph cache */
766 #define TT_HASH(x) ((x) & 0xFF)
767
768 typedef struct i_glyph_entry_ {
769   TT_Glyph glyph;
770   unsigned long ch;
771 } i_tt_glyph_entry;
772
773 #define TT_NOCHAR (~0UL)
774
775 struct 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 };
784
785 typedef struct TT_Instancehandle_ TT_Instancehandle;
786
787 struct TT_Fonthandle_ {
788   TT_Face face;
789   TT_Face_Properties properties;
790   TT_Instancehandle instanceh[TT_CHC];
791   TT_CharMap char_map;
792 #ifdef FTXPOST
793   int loaded_names;
794   TT_Error load_cond;
795 #endif
796 };
797
798 /* Defines */
799
800 #define USTRCT(x) ((x).z)
801 #define TT_VALID( handle )  ( ( handle ).z != NULL )
802
803 static void i_tt_push_error(TT_Error rc);
804
805 /* Prototypes */
806
807 static  int i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth );
808 static void i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth );
809 static void i_tt_done_raster_map( TT_Raster_Map *bit );
810 static void i_tt_clear_raster_map( TT_Raster_Map* bit );
811 static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off );
812 static  int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j );
813 static void 
814 i_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 );
817 static int
818 i_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 );
821 static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, const i_color *cl, int smooth );
822 static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth );
823 static  int
824 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], 
825                 float points, char const* txt, int len, int smooth, int utf8 );
826 static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6], int utf8 );
827
828
829 /* static globals needed */
830
831 static TT_Engine    engine;
832 static int  LTT_dpi    = 72; /* FIXME: this ought to be a part of the call interface */
833 static int  LTT_hinted = 1;  /* FIXME: this too */
834
835
836 /*
837  * FreeType interface
838  */
839
840
841 /*
842 =item init_tt()
843
844 Initializes the freetype font rendering engine
845
846 =cut
847 */
848
849 undef_int
850 i_init_tt() {
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   }
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
867   return(0);
868 }
869
870
871 /* 
872 =item i_tt_get_instance(handle, points, smooth)
873
874 Finds a points+smooth instance or if one doesn't exist in the cache
875 allocates 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
885 static
886 int
887 i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) {
888   int i,idx;
889   TT_Error error;
890   
891   mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n",
892           handle,points,smooth));
893   
894   if (smooth == -1) { /* Smooth doesn't matter for this search */
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       }
900     }
901     smooth=1; /* We will be adding a font - add it as smooth then */
902   } else { /* Smooth doesn't matter for this search */
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       }
909     }
910   }
911   
912   /* Found the instance in the cache - return the cache index */
913   
914   for(idx=0;idx<TT_CHC;idx++) {
915     if (!(handle->instanceh[idx].order)) break; /* find the lru item */
916   }
917
918   mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
919   mm_log((1,"i_tt_get_instance: lru pointer 0x%X\n",
920           USTRCT(handle->instanceh[idx].instance) ));
921   
922   if ( USTRCT(handle->instanceh[idx].instance) ) {
923     mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
924
925     /* Free cached glyphs */
926     for(i=0;i<256;i++)
927       if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
928         TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
929
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 );
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
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 */
963
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   }
968   
969   return idx;
970 }
971
972
973 /*
974 =item i_tt_new(fontname)
975
976 Creates a new font handle object, finds a character map and initialise the
977 the font handle's cache
978
979    fontname - path to the font to load
980
981 =cut
982 */
983
984 TT_Fonthandle*
985 i_tt_new(const char *fontname) {
986   TT_Error error;
987   TT_Fonthandle *handle;
988   unsigned short i,n;
989   unsigned short platform,encoding;
990
991   i_clear_error();
992   
993   mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
994   
995   /* allocate memory for the structure */
996   
997   handle = mymalloc( sizeof(TT_Fonthandle) ); /* checked 5Nov05 tonyc */
998
999   /* load the typeface */
1000   error = TT_Open_Face( engine, fontname, &handle->face );
1001   if ( error ) {
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     }
1009     i_tt_push_error(error);
1010     return NULL;
1011   }
1012   
1013   TT_Get_Face_Properties( handle->face, &(handle->properties) );
1014
1015   /* First, look for a Unicode charmap */
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 );
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));
1025       TT_Get_CharMap( handle->face, i, &(handle->char_map) );
1026       break;
1027     }
1028   }
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   }
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
1043 #ifdef FTXPOST
1044   handle->loaded_names = 0;
1045 #endif
1046
1047   mm_log((1,"i_tt_new <- 0x%X\n",handle));
1048   return handle;
1049 }
1050
1051
1052
1053 /*
1054  * raster map management
1055  */
1056
1057 /* 
1058 =item i_tt_init_raster_map(bit, width, height, smooth)
1059
1060 Allocates internal memory for the bitmap as needed by the parameters (internal)
1061                  
1062    bit    - bitmap to allocate into
1063    width  - width of the bitmap
1064    height - height of the bitmap
1065    smooth - boolean (True: antialias on, False: antialias is off)
1066
1067 =cut
1068 */
1069
1070 static
1071 void
1072 i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth ) {
1073
1074   mm_log((1,"i_tt_init_raster_map( bit 08x%08X, width %d, height %d, smooth %d)\n", bit, width, height, smooth));
1075   
1076   bit->rows  = height;
1077   bit->width = ( width + 3 ) & -4;
1078   bit->flow  = TT_Flow_Down;
1079   
1080   if ( smooth ) {
1081     bit->cols  = bit->width;
1082     bit->size  = bit->rows * bit->width;
1083   } else {
1084     bit->cols  = ( bit->width + 7 ) / 8;    /* convert to # of bytes     */
1085     bit->size  = bit->rows * bit->cols;     /* number of bytes in buffer */
1086   }
1087
1088   /* rows can be 0 for some glyphs, for example ' ' */
1089   if (bit->rows && bit->size / bit->rows != bit->cols) {
1090     m_fatal(0, "Integer overflow calculating bitmap size (%d, %d)\n",
1091             bit->width, bit->rows);
1092   }
1093   
1094   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 ));
1095
1096   bit->bitmap = (void *) mymalloc( bit->size ); /* checked 6Nov05 tonyc */
1097   if ( !bit->bitmap ) m_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",bit->size );
1098 }
1099
1100
1101 /*
1102 =item i_tt_clear_raster_map(bit)
1103
1104 Frees the bitmap data and sets pointer to NULL (internal)
1105                  
1106    bit - bitmap to free
1107
1108 =cut
1109 */
1110
1111 static
1112 void
1113 i_tt_done_raster_map( TT_Raster_Map *bit ) {
1114   myfree( bit->bitmap );
1115   bit->bitmap = NULL;
1116 }
1117
1118
1119 /*
1120 =item i_tt_clear_raster_map(bit)
1121
1122 Clears the specified bitmap (internal)
1123                  
1124    bit - bitmap to zero
1125
1126 =cut
1127 */
1128
1129
1130 static
1131 void
1132 i_tt_clear_raster_map( TT_Raster_Map*  bit ) {
1133   memset( bit->bitmap, 0, bit->size );
1134 }
1135
1136
1137 /* 
1138 =item i_tt_blit_or(dst, src, x_off, y_off)
1139
1140 function that blits one raster map into another (internal)
1141                  
1142    dst   - destination bitmap
1143    src   - source bitmap
1144    x_off - x offset into the destination bitmap
1145    y_off - y offset into the destination bitmap
1146
1147 =cut
1148 */
1149
1150 static
1151 void
1152 i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off ) {
1153   int  x,  y;
1154   int  x1, x2, y1, y2;
1155   char *s, *d;
1156   
1157   x1 = x_off < 0 ? -x_off : 0;
1158   y1 = y_off < 0 ? -y_off : 0;
1159   
1160   x2 = (int)dst->cols - x_off;
1161   if ( x2 > src->cols ) x2 = src->cols;
1162   
1163   y2 = (int)dst->rows - y_off;
1164   if ( y2 > src->rows ) y2 = src->rows;
1165
1166   if ( x1 >= x2 ) return;
1167
1168   /* do the real work now */
1169
1170   for ( y = y1; y < y2; ++y ) {
1171     s = ( (char*)src->bitmap ) + y * src->cols + x1;
1172     d = ( (char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
1173     
1174     for ( x = x1; x < x2; ++x ) {
1175       if (*s > *d)
1176         *d = *s;
1177       d++;
1178       s++;
1179     }
1180   }
1181 }
1182
1183 /* useful for debugging */
1184 #if 0
1185
1186 static void dump_raster_map(FILE *out, TT_Raster_Map *bit ) {
1187   int x, y;
1188   fprintf(out, "cols %d rows %d  flow %d\n", bit->cols, bit->rows, bit->flow);
1189   for (y = 0; y < bit->rows; ++y) {
1190     fprintf(out, "%2d:", y);
1191     for (x = 0; x < bit->cols; ++x) {
1192       if ((x & 7) == 0 && x) putc(' ', out);
1193       fprintf(out, "%02x", ((unsigned char *)bit->bitmap)[y*bit->cols+x]);
1194     }
1195     putc('\n', out);
1196   }
1197 }
1198
1199 #endif
1200
1201 /* 
1202 =item i_tt_get_glyph(handle, inst, j) 
1203
1204 Function to see if a glyph exists and if so cache it (internal)
1205                  
1206    handle - pointer to font handle
1207    inst   - font instance
1208    j      - charcode of glyph
1209
1210 =cut
1211 */
1212
1213 static
1214 int
1215 i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j) {
1216   unsigned short load_flags, code;
1217   TT_Error error;
1218
1219   mm_log((1, "i_tt_get_glyph(handle 0x%X, inst %d, j %d (%c))\n",
1220           handle,inst,j, ((j >= ' ' && j <= '~') ? j : '.')));
1221   
1222   /*mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] ));*/
1223
1224   if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)
1225        && handle->instanceh[inst].glyphs[TT_HASH(j)].ch == j) {
1226     mm_log((1,"i_tt_get_glyph: %d in cache\n",j));
1227     return 1;
1228   }
1229
1230   if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) ) {
1231     /* clean up the entry */
1232     TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1233     USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1234     handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
1235   }
1236   
1237   /* Ok - it wasn't cached - try to get it in */
1238   load_flags = TTLOAD_SCALE_GLYPH;
1239   if ( LTT_hinted ) load_flags |= TTLOAD_HINT_GLYPH;
1240   
1241   if ( !TT_VALID(handle->char_map) ) {
1242     code = (j - ' ' + 1) < 0 ? 0 : (j - ' ' + 1);
1243     if ( code >= handle->properties.num_Glyphs ) code = 0;
1244   } else code = TT_Char_Index( handle->char_map, j );
1245   
1246   if ( (error = TT_New_Glyph( handle->face, &handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)) ) {
1247     mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
1248     i_push_error(error, "TT_New_Glyph()");
1249     return 0;
1250   }
1251   if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, code, load_flags)) ) {
1252     mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
1253     /* Don't leak */
1254     TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1255     USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1256     i_push_error(error, "TT_Load_Glyph()");
1257     return 0;
1258   }
1259
1260   /* At this point the glyph should be allocated and loaded */
1261   handle->instanceh[inst].glyphs[TT_HASH(j)].ch = j;
1262
1263   /* Next get the glyph metrics */
1264   error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, 
1265                                 &handle->instanceh[inst].gmetrics[TT_HASH(j)] );
1266   if (error) {
1267     mm_log((1, "TT_Get_Glyph_Metrics: error 0x%x.\n", error ));
1268     TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1269     USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1270     handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
1271     i_push_error(error, "TT_Get_Glyph_Metrics()");
1272     return 0;
1273   }
1274
1275   return 1;
1276 }
1277
1278 /*
1279 =item i_tt_has_chars(handle, text, len, utf8, out)
1280
1281 Check if the given characters are defined by the font.  Note that len
1282 is the number of bytes, not the number of characters (when utf8 is
1283 non-zero).
1284
1285 Returns the number of characters that were checked.
1286
1287 =cut
1288 */
1289
1290 int
1291 i_tt_has_chars(TT_Fonthandle *handle, char const *text, int len, int utf8,
1292                char *out) {
1293   int count = 0;
1294   mm_log((1, "i_tt_has_chars(handle %p, text %p, len %d, utf8 %d)\n", 
1295           handle, text, len, utf8));
1296
1297   while (len) {
1298     unsigned long c;
1299     int index;
1300     if (utf8) {
1301       c = i_utf8_advance(&text, &len);
1302       if (c == ~0UL) {
1303         i_push_error(0, "invalid UTF8 character");
1304         return 0;
1305       }
1306     }
1307     else {
1308       c = (unsigned char)*text++;
1309       --len;
1310     }
1311     
1312     if (TT_VALID(handle->char_map)) {
1313       index = TT_Char_Index(handle->char_map, c);
1314     }
1315     else {
1316       index = (c - ' ' + 1) < 0 ? 0 : (c - ' ' + 1);
1317       if (index >= handle->properties.num_Glyphs)
1318         index = 0;
1319     }
1320     *out++ = index != 0;
1321     ++count;
1322   }
1323
1324   return count;
1325 }
1326
1327 /* 
1328 =item i_tt_destroy(handle)
1329
1330 Clears the data taken by a font including all cached data such as
1331 pixmaps and glyphs
1332                  
1333    handle - pointer to font handle
1334
1335 =cut
1336 */
1337
1338 void
1339 i_tt_destroy( TT_Fonthandle *handle) {
1340   TT_Close_Face( handle->face );
1341   myfree( handle );
1342   
1343   /* FIXME: Should these be freed automatically by the library? 
1344
1345   TT_Done_Instance( instance );
1346   void
1347     i_tt_done_glyphs( void ) {
1348     int  i;
1349
1350     if ( !glyphs ) return;
1351     
1352     for ( i = 0; i < 256; ++i ) TT_Done_Glyph( glyphs[i] );
1353     free( glyphs );
1354     
1355     glyphs = NULL;
1356   }
1357   */
1358 }
1359
1360
1361 /*
1362  * FreeType Rendering functions
1363  */
1364
1365
1366 /* 
1367 =item i_tt_render_glyph(handle, gmetrics, bit, smallbit, x_off, y_off, smooth)
1368
1369 Renders a single glyph into the bit rastermap (internal)
1370
1371    handle   - pointer to font handle
1372    gmetrics - the metrics for the glyph to be rendered
1373    bit      - large bitmap that is the destination for the text
1374    smallbit - small bitmap that is used only if smooth is true
1375    x_off    - x offset of glyph
1376    y_off    - y offset of glyph
1377    smooth   - boolean (True: antialias on, False: antialias is off)
1378
1379 =cut
1380 */
1381
1382 static
1383 void
1384 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 ) {
1385   
1386   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",
1387           USTRCT(glyph), gmetrics, bit, small_bit, x_off,y_off,smooth));
1388   
1389   if ( !smooth ) TT_Get_Glyph_Bitmap( glyph, bit, x_off * 64, y_off * 64);
1390   else {
1391     TT_F26Dot6 xmin, ymin, xmax, ymax;
1392
1393     xmin =  gmetrics->bbox.xMin & -64;
1394     ymin =  gmetrics->bbox.yMin & -64;
1395     xmax = (gmetrics->bbox.xMax + 63) & -64;
1396     ymax = (gmetrics->bbox.yMax + 63) & -64;
1397     
1398     i_tt_clear_raster_map( small_bit );
1399     TT_Get_Glyph_Pixmap( glyph, small_bit, -xmin, -ymin );
1400     i_tt_blit_or( bit, small_bit, xmin/64 + x_off, -ymin/64 - y_off );
1401   }
1402 }
1403
1404
1405 /*
1406 =item i_tt_render_all_glyphs(handle, inst, bit, small_bit, cords, txt, len, smooth)
1407
1408 calls i_tt_render_glyph to render each glyph into the bit rastermap (internal)
1409
1410    handle   - pointer to font handle
1411    inst     - font instance
1412    bit      - large bitmap that is the destination for the text
1413    smallbit - small bitmap that is used only if smooth is true
1414    txt      - string to render
1415    len      - length of the string to render
1416    smooth   - boolean (True: antialias on, False: antialias is off)
1417
1418 =cut
1419 */
1420
1421 static
1422 int
1423 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
1424                         TT_Raster_Map *small_bit, int cords[6], 
1425                         char const* txt, int len, int smooth, int utf8 ) {
1426   unsigned long j;
1427   TT_F26Dot6 x,y;
1428   
1429   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",
1430           handle, inst, bit, small_bit, len, txt, len, smooth, utf8));
1431   
1432   /* 
1433      y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM);
1434   */
1435
1436   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 */
1437   y=-cords[4];
1438   
1439   while (len) {
1440     if (utf8) {
1441       j = i_utf8_advance(&txt, &len);
1442       if (j == ~0UL) {
1443         i_push_error(0, "invalid UTF8 character");
1444         return 0;
1445       }
1446     }
1447     else {
1448       j = (unsigned char)*txt++;
1449       --len;
1450     }
1451     if ( !i_tt_get_glyph(handle,inst,j) ) 
1452       continue;
1453     i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, 
1454                        &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit, 
1455                        small_bit, x, y, smooth );
1456     x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64;
1457   }
1458
1459   return 1;
1460 }
1461
1462
1463 /*
1464  * Functions to render rasters (single channel images) onto images
1465  */
1466
1467 /* 
1468 =item i_tt_dump_raster_map2(im, bit, xb, yb, cl, smooth)
1469
1470 Function to dump a raster onto an image in color used by i_tt_text() (internal).
1471
1472    im     - image to dump raster on
1473    bit    - bitmap that contains the text to be dumped to im
1474    xb, yb - coordinates, left edge and baseline
1475    cl     - color to use for text
1476    smooth - boolean (True: antialias on, False: antialias is off)
1477
1478 =cut
1479 */
1480
1481 static
1482 void
1483 i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, const i_color *cl, int smooth ) {
1484   char *bmap;
1485   i_color val;
1486   int c, i, ch, x, y;
1487   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));
1488   
1489   bmap = (char *)bit->bitmap;
1490
1491   if ( smooth ) {
1492
1493     for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1494       c=(255*bmap[y*(bit->cols)+x])/4;
1495       i=255-c;
1496       i_gpix(im,x+xb,y+yb,&val);
1497       for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
1498       i_ppix(im,x+xb,y+yb,&val);
1499     }
1500
1501   } else {
1502
1503     for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1504       c=( bmap[y*bit->cols+x/8] & (128>>(x%8)) ) ? 255 : 0;
1505       i=255-c;
1506       i_gpix(im,x+xb,y+yb,&val);
1507       for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
1508       i_ppix(im,x+xb,y+yb,&val);
1509     }
1510
1511   }
1512 }
1513
1514
1515 /*
1516 =item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth)
1517
1518 Function to dump a raster onto a single channel image in color (internal)
1519
1520    im      - image to dump raster on
1521    bit     - bitmap that contains the text to be dumped to im
1522    xb, yb  - coordinates, left edge and baseline
1523    channel - channel to copy to
1524    smooth  - boolean (True: antialias on, False: antialias is off)
1525
1526 =cut
1527 */
1528
1529 static
1530 void
1531 i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map*  bit, int xb, int yb, int channel, int smooth ) {
1532   char *bmap;
1533   i_color val;
1534   int c,x,y;
1535
1536   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));
1537   
1538   bmap = (char *)bit->bitmap;
1539   
1540   if ( smooth ) {
1541     for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1542       c=(255*bmap[y*(bit->cols)+x])/4;
1543       i_gpix(im,x+xb,y+yb,&val);
1544       val.channel[channel]=c;
1545       i_ppix(im,x+xb,y+yb,&val);
1546     }
1547   } else {
1548     for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1549       c=( bmap[y*bit->cols+x/8] & (128>>(x%8)) ) ? 255 : 0;
1550       i_gpix(im,x+xb,y+yb,&val);
1551       val.channel[channel]=c;
1552       i_ppix(im,x+xb,y+yb,&val);
1553     }
1554   }
1555 }
1556
1557
1558 /* 
1559 =item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth) 
1560
1561 interface for generating single channel raster of text (internal)
1562
1563    handle - pointer to font handle
1564    bit    - the bitmap that is allocated, rendered into and NOT freed
1565    cords  - the bounding box (modified in place)
1566    points - font size to use
1567    txt    - string to render
1568    len    - length of the string to render
1569    smooth - boolean (True: antialias on, False: antialias is off)
1570
1571 =cut
1572 */
1573
1574 static
1575 int
1576 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char const* txt, int len, int smooth, int utf8 ) {
1577   int inst;
1578   int width, height;
1579   TT_Raster_Map small_bit;
1580   
1581   /* find or install an instance */
1582   if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) { 
1583     mm_log((1,"i_tt_rasterize: get instance failed\n"));
1584     return 0;
1585   }
1586   
1587   /* calculate bounding box */
1588   if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 ))
1589     return 0;
1590     
1591   
1592   width  = cords[2]-cords[0];
1593   height = cords[5]-cords[4];
1594   
1595   mm_log((1,"i_tt_rasterize: width=%d, height=%d\n",width, height )); 
1596   
1597   i_tt_init_raster_map ( bit, width, height, smooth );
1598   i_tt_clear_raster_map( bit );
1599   if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth );
1600   
1601   if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len, 
1602                                smooth, utf8 )) {
1603     if ( smooth ) 
1604       i_tt_done_raster_map( &small_bit );
1605     return 0;
1606   }
1607
1608   if ( smooth ) i_tt_done_raster_map( &small_bit );
1609   return 1;
1610 }
1611
1612
1613
1614 /* 
1615  * Exported text rendering interfaces
1616  */
1617
1618
1619 /*
1620 =item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth, utf8)
1621
1622 Interface to text rendering into a single channel in an image
1623
1624    handle  - pointer to font handle
1625    im      - image to render text on to
1626    xb, yb  - coordinates, left edge and baseline
1627    channel - channel to render into
1628    points  - font size to use
1629    txt     - string to render
1630    len     - length of the string to render
1631    smooth  - boolean (True: antialias on, False: antialias is off)
1632
1633 =cut
1634 */
1635
1636 undef_int
1637 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 ) {
1638
1639   int cords[BOUNDING_BOX_COUNT];
1640   int ascent, st_offset, y;
1641   TT_Raster_Map bit;
1642   
1643   i_clear_error();
1644   if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1645   
1646   ascent=cords[BBOX_ASCENT];
1647   st_offset=cords[BBOX_NEG_WIDTH];
1648   y = align ? yb-ascent : yb;
1649
1650   i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , y, channel, smooth );
1651   i_tt_done_raster_map( &bit );
1652
1653   return 1;
1654 }
1655
1656
1657 /* 
1658 =item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8) 
1659
1660 Interface to text rendering in a single color onto an image
1661
1662    handle  - pointer to font handle
1663    im      - image to render text on to
1664    xb, yb  - coordinates, left edge and baseline
1665    cl      - color to use for text
1666    points  - font size to use
1667    txt     - string to render
1668    len     - length of the string to render
1669    smooth  - boolean (True: antialias on, False: antialias is off)
1670
1671 =cut
1672 */
1673
1674 undef_int
1675 i_tt_text( TT_Fonthandle *handle, i_img *im, int xb, int yb, const i_color *cl, float points, char const* txt, int len, int smooth, int utf8, int align) {
1676   int cords[BOUNDING_BOX_COUNT];
1677   int ascent, st_offset, y;
1678   TT_Raster_Map bit;
1679
1680   i_clear_error();
1681   
1682   if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1683   
1684   ascent=cords[BBOX_ASCENT];
1685   st_offset=cords[BBOX_NEG_WIDTH];
1686   y = align ? yb-ascent : yb;
1687
1688   i_tt_dump_raster_map2( im, &bit, xb+st_offset, y, cl, smooth ); 
1689   i_tt_done_raster_map( &bit );
1690
1691   return 1;
1692 }
1693
1694
1695 /*
1696 =item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8) 
1697
1698 Function to get texts bounding boxes given the instance of the font (internal)
1699
1700    handle - pointer to font handle
1701    inst   -  font instance
1702    txt    -  string to measure
1703    len    -  length of the string to render
1704    cords  - the bounding box (modified in place)
1705
1706 =cut
1707 */
1708
1709 static
1710 undef_int
1711 i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[BOUNDING_BOX_COUNT], int utf8 ) {
1712   int upm, casc, cdesc, first;
1713   
1714   int start    = 0;
1715   int width    = 0;
1716   int gdescent = 0;
1717   int gascent  = 0;
1718   int descent  = 0;
1719   int ascent   = 0;
1720   int rightb   = 0;
1721
1722   unsigned long j;
1723   unsigned char *ustr;
1724   ustr=(unsigned char*)txt;
1725
1726   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));
1727
1728   upm     = handle->properties.header->Units_Per_EM;
1729   gascent  = ( handle->properties.horizontal->Ascender  * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm;
1730   gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem - upm + 1) / upm;
1731   
1732   width   = 0;
1733   start   = 0;
1734   
1735   mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent));
1736
1737   first=1;
1738   while (len) {
1739     if (utf8) {
1740       j = i_utf8_advance(&txt, &len);
1741       if (j == ~0UL) {
1742         i_push_error(0, "invalid UTF8 character");
1743         return 0;
1744       }
1745     }
1746     else {
1747       j = (unsigned char)*txt++;
1748       --len;
1749     }
1750     if ( i_tt_get_glyph(handle,inst,j) ) {
1751       TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j);
1752       width += gm->advance   / 64;
1753       casc   = (gm->bbox.yMax+63) / 64;
1754       cdesc  = (gm->bbox.yMin-63) / 64;
1755
1756       mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n", 
1757               ((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc));
1758
1759       if (first) {
1760         start    = gm->bbox.xMin / 64;
1761         ascent   = (gm->bbox.yMax+63) / 64;
1762         descent  = (gm->bbox.yMin-63) / 64;
1763         first = 0;
1764       }
1765       if (!len) { /* if at end of string */
1766         /* the right-side bearing - in case the right-side of a 
1767            character goes past the right of the advance width,
1768            as is common for italic fonts
1769         */
1770         rightb = gm->advance - gm->bearingX 
1771           - (gm->bbox.xMax - gm->bbox.xMin);
1772         /* fprintf(stderr, "font info last: %d %d %d %d\n", 
1773            gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */
1774       }
1775
1776       ascent  = (ascent  >  casc ?  ascent : casc );
1777       descent = (descent < cdesc ? descent : cdesc);
1778     }
1779   }
1780   
1781   cords[BBOX_NEG_WIDTH]=start;
1782   cords[BBOX_GLOBAL_DESCENT]=gdescent;
1783   cords[BBOX_POS_WIDTH]=width;
1784   if (rightb < 0)
1785     cords[BBOX_POS_WIDTH] -= rightb / 64;
1786   cords[BBOX_GLOBAL_ASCENT]=gascent;
1787   cords[BBOX_DESCENT]=descent;
1788   cords[BBOX_ASCENT]=ascent;
1789   cords[BBOX_ADVANCE_WIDTH] = width;
1790   cords[BBOX_RIGHT_BEARING] = rightb / 64;
1791
1792   return BBOX_RIGHT_BEARING + 1;
1793 }
1794
1795
1796 /*
1797 =item i_tt_bbox(handle, points, txt, len, cords, utf8)
1798
1799 Interface to get a strings bounding box
1800
1801    handle - pointer to font handle
1802    points - font size to use
1803    txt    - string to render
1804    len    - length of the string to render
1805    cords  - the bounding box (modified in place)
1806
1807 =cut
1808 */
1809
1810 undef_int
1811 i_tt_bbox( TT_Fonthandle *handle, float points,const char *txt,int len,int cords[6], int utf8) {
1812   int inst;
1813
1814   i_clear_error();
1815   mm_log((1,"i_tt_box(handle 0x%X,points %f,txt '%.*s', len %d, utf8 %d)\n",handle,points,len,txt,len, utf8));
1816
1817   if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
1818     i_push_errorf(0, "i_tt_get_instance(%g)", points);
1819     mm_log((1,"i_tt_text: get instance failed\n"));
1820     return 0;
1821   }
1822
1823   return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8);
1824 }
1825
1826 /*
1827 =item i_tt_face_name(handle, name_buf, name_buf_size)
1828
1829 Retrieve's the font's postscript name.
1830
1831 This is complicated by the need to handle encodings and so on.
1832
1833 =cut
1834  */
1835 int
1836 i_tt_face_name(TT_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
1837   TT_Face_Properties props;
1838   int name_count;
1839   int i;
1840   TT_UShort platform_id, encoding_id, lang_id, name_id;
1841   TT_UShort name_len;
1842   TT_String *name;
1843   int want_index = -1; /* an acceptable but not perfect name */
1844   int score = 0;
1845
1846   i_clear_error();
1847   
1848   TT_Get_Face_Properties(handle->face, &props);
1849   name_count = props.num_Names;
1850   for (i = 0; i < name_count; ++i) {
1851     TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id, 
1852                    &name_id);
1853
1854     TT_Get_Name_String(handle->face, i, &name, &name_len);
1855
1856     if (platform_id != TT_PLATFORM_APPLE_UNICODE && name_len
1857         && name_id == TT_NAME_ID_PS_NAME) {
1858       int might_want_index = -1;
1859       int might_score = 0;
1860       if ((platform_id == TT_PLATFORM_MACINTOSH && encoding_id == TT_MAC_ID_ROMAN)
1861           ||
1862           (platform_id == TT_PLATFORM_MICROSOFT && encoding_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)) {
1863         /* exactly what we want */
1864         want_index = i;
1865         break;
1866       }
1867       
1868       if (platform_id == TT_PLATFORM_MICROSOFT
1869           && (encoding_id & 0xFF) == TT_MS_LANGID_ENGLISH_GENERAL) {
1870         /* any english is good */
1871         might_want_index = i;
1872         might_score = 9;
1873       }
1874       /* there might be something in between */
1875       else {
1876         /* anything non-unicode is better than nothing */
1877         might_want_index = i;
1878         might_score = 1;
1879       }
1880       if (might_score > score) {
1881         score = might_score;
1882         want_index = might_want_index;
1883       }
1884     }
1885   }
1886
1887   if (want_index != -1) {
1888     TT_Get_Name_String(handle->face, want_index, &name, &name_len);
1889     
1890     strncpy(name_buf, name, name_buf_size);
1891     name_buf[name_buf_size-1] = '\0';
1892
1893     return strlen(name) + 1;
1894   }
1895   else {
1896     i_push_error(0, "no face name present");
1897     return 0;
1898   }
1899 }
1900
1901 void i_tt_dump_names(TT_Fonthandle *handle) {
1902   TT_Face_Properties props;
1903   int name_count;
1904   int i;
1905   TT_UShort platform_id, encoding_id, lang_id, name_id;
1906   TT_UShort name_len;
1907   TT_String *name;
1908   
1909   TT_Get_Face_Properties(handle->face, &props);
1910   name_count = props.num_Names;
1911   for (i = 0; i < name_count; ++i) {
1912     TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id, 
1913                    &name_id);
1914     TT_Get_Name_String(handle->face, i, &name, &name_len);
1915
1916     printf("# %d: plat %d enc %d lang %d name %d value ", i, platform_id,
1917            encoding_id, lang_id, name_id);
1918     if (platform_id == TT_PLATFORM_APPLE_UNICODE) {
1919       printf("(unicode)\n");
1920     }
1921     else {
1922       printf("'%s'\n", name);
1923     }
1924   }
1925 }
1926
1927 int
1928 i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf, 
1929                  size_t name_buf_size) {
1930 #ifdef FTXPOST
1931   TT_Error rc;
1932   TT_String *psname;
1933   TT_UShort index;
1934
1935   i_clear_error();
1936
1937   if (!handle->loaded_names) {
1938     TT_Post post;
1939     mm_log((1, "Loading PS Names"));
1940     handle->load_cond = TT_Load_PS_Names(handle->face, &post);
1941     ++handle->loaded_names;
1942   }
1943
1944   if (handle->load_cond) {
1945     i_push_errorf(rc, "error loading names (%d)", handle->load_cond);
1946     return 0;
1947   }
1948   
1949   index = TT_Char_Index(handle->char_map, ch);
1950   if (!index) {
1951     i_push_error(0, "no such character");
1952     return 0;
1953   }
1954
1955   rc = TT_Get_PS_Name(handle->face, index, &psname);
1956
1957   if (rc) {
1958     i_push_error(rc, "error getting name");
1959     return 0;
1960   }
1961
1962   strncpy(name_buf, psname, name_buf_size);
1963   name_buf[name_buf_size-1] = '\0';
1964
1965   return strlen(psname) + 1;
1966 #else
1967   mm_log((1, "FTXPOST extension not enabled\n"));
1968   i_clear_error();
1969   i_push_error(0, "Use of FTXPOST extension disabled");
1970
1971   return 0;
1972 #endif
1973 }
1974
1975 /*
1976 =item i_tt_push_error(code)
1977
1978 Push an error message and code onto the Imager error stack.
1979
1980 =cut
1981 */
1982 static void
1983 i_tt_push_error(TT_Error rc) {
1984 #ifdef FTXERR18
1985   TT_String const *msg = TT_ErrToString18(rc);
1986
1987   i_push_error(rc, msg);
1988 #else
1989   i_push_errorf(rc, "Error code 0x%04x", (unsigned)rc);
1990 #endif
1991 }
1992
1993 #endif /* HAVE_LIBTT */
1994
1995
1996 /*
1997 =back
1998
1999 =head1 AUTHOR
2000
2001 Arnar M. Hrafnkelsson <addi@umich.edu>
2002
2003 =head1 SEE ALSO
2004
2005 Imager(3)
2006
2007 =cut
2008 */