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