]> git.imager.perl.org - imager.git/blob - font.c
think about 0.47
[imager.git] / font.c
1 #include "image.h"
2
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <fcntl.h>
6
7 #include <stdio.h>
8 #include <stdlib.h>
9
10 #ifdef HAVE_LIBT1
11 #include <t1lib.h>
12 #endif
13
14
15 /*
16 =head1 NAME
17
18 font.c - implements font handling functions for t1 and truetype fonts
19
20 =head1 SYNOPSIS
21
22   i_init_fonts();
23
24   #ifdef HAVE_LIBT1
25   fontnum = i_t1_new(path_to_pfb, path_to_afm);
26   i_t1_bbox(fontnum, points, "foo", 3, int cords[6]);
27   rc = i_t1_destroy(fontnum);
28   #endif
29
30   #ifdef HAVE_LIBTT
31   handle = i_tt_new(path_to_ttf);
32   rc = i_tt_bbox(handle, points, "foo", 3, int cords[6], utf8);
33   i_tt_destroy(handle);
34
35   // and much more
36
37 =head1 DESCRIPTION
38
39 font.c implements font creation, rendering, bounding box functions and
40 more for Imager.
41
42 =head1 FUNCTION REFERENCE
43
44 Some of these functions are internal.
45
46 =over
47
48 =cut
49
50 */
51
52 /* 
53 =item i_init_fonts()
54
55 Initialize font rendering libraries if they are avaliable.
56
57 =cut
58 */
59
60 undef_int 
61 i_init_fonts(int t1log) {
62   mm_log((1,"Initializing fonts\n"));
63
64 #ifdef HAVE_LIBT1
65   i_init_t1(t1log);
66 #endif
67   
68 #ifdef HAVE_LIBTT
69   i_init_tt();
70 #endif
71
72 #ifdef HAVE_FT2
73   if (!i_ft2_init())
74     return 0;
75 #endif
76
77   return(1); /* FIXME: Always true - check the return values of the init_t1 and init_tt functions */
78 }
79
80
81
82
83 #ifdef HAVE_LIBT1
84
85 static int t1_get_flags(char const *flags);
86 static char *t1_from_utf8(char const *in, int len, int *outlen);
87
88 static void t1_push_error(void);
89
90 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   if (bit->size / bit->rows != bit->cols) {
1088     m_fatal(0, "Integer overflow calculating bitmap size (%d, %d)\n",
1089             bit->width, bit->rows);
1090   }
1091   
1092   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 ));
1093
1094   bit->bitmap = (void *) mymalloc( bit->size ); /* checked 6Nov05 tonyc */
1095   if ( !bit->bitmap ) m_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",bit->size );
1096 }
1097
1098
1099 /*
1100 =item i_tt_clear_raster_map(bit)
1101
1102 Frees the bitmap data and sets pointer to NULL (internal)
1103                  
1104    bit - bitmap to free
1105
1106 =cut
1107 */
1108
1109 static
1110 void
1111 i_tt_done_raster_map( TT_Raster_Map *bit ) {
1112   myfree( bit->bitmap );
1113   bit->bitmap = NULL;
1114 }
1115
1116
1117 /*
1118 =item i_tt_clear_raster_map(bit)
1119
1120 Clears the specified bitmap (internal)
1121                  
1122    bit - bitmap to zero
1123
1124 =cut
1125 */
1126
1127
1128 static
1129 void
1130 i_tt_clear_raster_map( TT_Raster_Map*  bit ) {
1131   memset( bit->bitmap, 0, bit->size );
1132 }
1133
1134
1135 /* 
1136 =item i_tt_blit_or(dst, src, x_off, y_off)
1137
1138 function that blits one raster map into another (internal)
1139                  
1140    dst   - destination bitmap
1141    src   - source bitmap
1142    x_off - x offset into the destination bitmap
1143    y_off - y offset into the destination bitmap
1144
1145 =cut
1146 */
1147
1148 static
1149 void
1150 i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off ) {
1151   int  x,  y;
1152   int  x1, x2, y1, y2;
1153   char *s, *d;
1154   
1155   x1 = x_off < 0 ? -x_off : 0;
1156   y1 = y_off < 0 ? -y_off : 0;
1157   
1158   x2 = (int)dst->cols - x_off;
1159   if ( x2 > src->cols ) x2 = src->cols;
1160   
1161   y2 = (int)dst->rows - y_off;
1162   if ( y2 > src->rows ) y2 = src->rows;
1163
1164   if ( x1 >= x2 ) return;
1165
1166   /* do the real work now */
1167
1168   for ( y = y1; y < y2; ++y ) {
1169     s = ( (char*)src->bitmap ) + y * src->cols + x1;
1170     d = ( (char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
1171     
1172     for ( x = x1; x < x2; ++x ) {
1173       if (*s > *d)
1174         *d = *s;
1175       d++;
1176       s++;
1177     }
1178   }
1179 }
1180
1181 /* useful for debugging */
1182 #if 0
1183
1184 static void dump_raster_map(FILE *out, TT_Raster_Map *bit ) {
1185   int x, y;
1186   fprintf(out, "cols %d rows %d  flow %d\n", bit->cols, bit->rows, bit->flow);
1187   for (y = 0; y < bit->rows; ++y) {
1188     fprintf(out, "%2d:", y);
1189     for (x = 0; x < bit->cols; ++x) {
1190       if ((x & 7) == 0 && x) putc(' ', out);
1191       fprintf(out, "%02x", ((unsigned char *)bit->bitmap)[y*bit->cols+x]);
1192     }
1193     putc('\n', out);
1194   }
1195 }
1196
1197 #endif
1198
1199 /* 
1200 =item i_tt_get_glyph(handle, inst, j) 
1201
1202 Function to see if a glyph exists and if so cache it (internal)
1203                  
1204    handle - pointer to font handle
1205    inst   - font instance
1206    j      - charcode of glyph
1207
1208 =cut
1209 */
1210
1211 static
1212 int
1213 i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j) {
1214   unsigned short load_flags, code;
1215   TT_Error error;
1216
1217   mm_log((1, "i_tt_get_glyph(handle 0x%X, inst %d, j %d (%c))\n",
1218           handle,inst,j, ((j >= ' ' && j <= '~') ? j : '.')));
1219   
1220   /*mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] ));*/
1221
1222   if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)
1223        && handle->instanceh[inst].glyphs[TT_HASH(j)].ch == j) {
1224     mm_log((1,"i_tt_get_glyph: %d in cache\n",j));
1225     return 1;
1226   }
1227
1228   if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) ) {
1229     /* clean up the entry */
1230     TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1231     USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1232     handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
1233   }
1234   
1235   /* Ok - it wasn't cached - try to get it in */
1236   load_flags = TTLOAD_SCALE_GLYPH;
1237   if ( LTT_hinted ) load_flags |= TTLOAD_HINT_GLYPH;
1238   
1239   if ( !TT_VALID(handle->char_map) ) {
1240     code = (j - ' ' + 1) < 0 ? 0 : (j - ' ' + 1);
1241     if ( code >= handle->properties.num_Glyphs ) code = 0;
1242   } else code = TT_Char_Index( handle->char_map, j );
1243   
1244   if ( (error = TT_New_Glyph( handle->face, &handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)) ) {
1245     mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
1246     i_push_error(error, "TT_New_Glyph()");
1247     return 0;
1248   }
1249   if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, code, load_flags)) ) {
1250     mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
1251     /* Don't leak */
1252     TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1253     USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1254     i_push_error(error, "TT_Load_Glyph()");
1255     return 0;
1256   }
1257
1258   /* At this point the glyph should be allocated and loaded */
1259   handle->instanceh[inst].glyphs[TT_HASH(j)].ch = j;
1260
1261   /* Next get the glyph metrics */
1262   error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, 
1263                                 &handle->instanceh[inst].gmetrics[TT_HASH(j)] );
1264   if (error) {
1265     mm_log((1, "TT_Get_Glyph_Metrics: error 0x%x.\n", error ));
1266     TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1267     USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1268     handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
1269     i_push_error(error, "TT_Get_Glyph_Metrics()");
1270     return 0;
1271   }
1272
1273   return 1;
1274 }
1275
1276 /*
1277 =item i_tt_has_chars(handle, text, len, utf8, out)
1278
1279 Check if the given characters are defined by the font.  Note that len
1280 is the number of bytes, not the number of characters (when utf8 is
1281 non-zero).
1282
1283 Returns the number of characters that were checked.
1284
1285 =cut
1286 */
1287
1288 int
1289 i_tt_has_chars(TT_Fonthandle *handle, char const *text, int len, int utf8,
1290                char *out) {
1291   int count = 0;
1292   mm_log((1, "i_tt_has_chars(handle %p, text %p, len %d, utf8 %d)\n", 
1293           handle, text, len, utf8));
1294
1295   while (len) {
1296     unsigned long c;
1297     int index;
1298     if (utf8) {
1299       c = i_utf8_advance(&text, &len);
1300       if (c == ~0UL) {
1301         i_push_error(0, "invalid UTF8 character");
1302         return 0;
1303       }
1304     }
1305     else {
1306       c = (unsigned char)*text++;
1307       --len;
1308     }
1309     
1310     if (TT_VALID(handle->char_map)) {
1311       index = TT_Char_Index(handle->char_map, c);
1312     }
1313     else {
1314       index = (c - ' ' + 1) < 0 ? 0 : (c - ' ' + 1);
1315       if (index >= handle->properties.num_Glyphs)
1316         index = 0;
1317     }
1318     *out++ = index != 0;
1319     ++count;
1320   }
1321
1322   return count;
1323 }
1324
1325 /* 
1326 =item i_tt_destroy(handle)
1327
1328 Clears the data taken by a font including all cached data such as
1329 pixmaps and glyphs
1330                  
1331    handle - pointer to font handle
1332
1333 =cut
1334 */
1335
1336 void
1337 i_tt_destroy( TT_Fonthandle *handle) {
1338   TT_Close_Face( handle->face );
1339   myfree( handle );
1340   
1341   /* FIXME: Should these be freed automatically by the library? 
1342
1343   TT_Done_Instance( instance );
1344   void
1345     i_tt_done_glyphs( void ) {
1346     int  i;
1347
1348     if ( !glyphs ) return;
1349     
1350     for ( i = 0; i < 256; ++i ) TT_Done_Glyph( glyphs[i] );
1351     free( glyphs );
1352     
1353     glyphs = NULL;
1354   }
1355   */
1356 }
1357
1358
1359 /*
1360  * FreeType Rendering functions
1361  */
1362
1363
1364 /* 
1365 =item i_tt_render_glyph(handle, gmetrics, bit, smallbit, x_off, y_off, smooth)
1366
1367 Renders a single glyph into the bit rastermap (internal)
1368
1369    handle   - pointer to font handle
1370    gmetrics - the metrics for the glyph to be rendered
1371    bit      - large bitmap that is the destination for the text
1372    smallbit - small bitmap that is used only if smooth is true
1373    x_off    - x offset of glyph
1374    y_off    - y offset of glyph
1375    smooth   - boolean (True: antialias on, False: antialias is off)
1376
1377 =cut
1378 */
1379
1380 static
1381 void
1382 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 ) {
1383   
1384   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",
1385           USTRCT(glyph), gmetrics, bit, small_bit, x_off,y_off,smooth));
1386   
1387   if ( !smooth ) TT_Get_Glyph_Bitmap( glyph, bit, x_off * 64, y_off * 64);
1388   else {
1389     TT_F26Dot6 xmin, ymin, xmax, ymax;
1390
1391     xmin =  gmetrics->bbox.xMin & -64;
1392     ymin =  gmetrics->bbox.yMin & -64;
1393     xmax = (gmetrics->bbox.xMax + 63) & -64;
1394     ymax = (gmetrics->bbox.yMax + 63) & -64;
1395     
1396     i_tt_clear_raster_map( small_bit );
1397     TT_Get_Glyph_Pixmap( glyph, small_bit, -xmin, -ymin );
1398     i_tt_blit_or( bit, small_bit, xmin/64 + x_off, -ymin/64 - y_off );
1399   }
1400 }
1401
1402
1403 /*
1404 =item i_tt_render_all_glyphs(handle, inst, bit, small_bit, cords, txt, len, smooth)
1405
1406 calls i_tt_render_glyph to render each glyph into the bit rastermap (internal)
1407
1408    handle   - pointer to font handle
1409    inst     - font instance
1410    bit      - large bitmap that is the destination for the text
1411    smallbit - small bitmap that is used only if smooth is true
1412    txt      - string to render
1413    len      - length of the string to render
1414    smooth   - boolean (True: antialias on, False: antialias is off)
1415
1416 =cut
1417 */
1418
1419 static
1420 int
1421 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
1422                         TT_Raster_Map *small_bit, int cords[6], 
1423                         char const* txt, int len, int smooth, int utf8 ) {
1424   unsigned long j;
1425   TT_F26Dot6 x,y;
1426   
1427   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",
1428           handle, inst, bit, small_bit, len, txt, len, smooth, utf8));
1429   
1430   /* 
1431      y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM);
1432   */
1433
1434   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 */
1435   y=-cords[4];
1436   
1437   while (len) {
1438     if (utf8) {
1439       j = i_utf8_advance(&txt, &len);
1440       if (j == ~0UL) {
1441         i_push_error(0, "invalid UTF8 character");
1442         return 0;
1443       }
1444     }
1445     else {
1446       j = (unsigned char)*txt++;
1447       --len;
1448     }
1449     if ( !i_tt_get_glyph(handle,inst,j) ) 
1450       continue;
1451     i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, 
1452                        &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit, 
1453                        small_bit, x, y, smooth );
1454     x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64;
1455   }
1456
1457   return 1;
1458 }
1459
1460
1461 /*
1462  * Functions to render rasters (single channel images) onto images
1463  */
1464
1465 /* 
1466 =item i_tt_dump_raster_map2(im, bit, xb, yb, cl, smooth)
1467
1468 Function to dump a raster onto an image in color used by i_tt_text() (internal).
1469
1470    im     - image to dump raster on
1471    bit    - bitmap that contains the text to be dumped to im
1472    xb, yb - coordinates, left edge and baseline
1473    cl     - color to use for text
1474    smooth - boolean (True: antialias on, False: antialias is off)
1475
1476 =cut
1477 */
1478
1479 static
1480 void
1481 i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, i_color *cl, int smooth ) {
1482   char *bmap;
1483   i_color val;
1484   int c, i, ch, x, y;
1485   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));
1486   
1487   bmap = (char *)bit->bitmap;
1488
1489   if ( smooth ) {
1490
1491     for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1492       c=(255*bmap[y*(bit->cols)+x])/4;
1493       i=255-c;
1494       i_gpix(im,x+xb,y+yb,&val);
1495       for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
1496       i_ppix(im,x+xb,y+yb,&val);
1497     }
1498
1499   } else {
1500
1501     for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1502       c=( bmap[y*bit->cols+x/8] & (128>>(x%8)) ) ? 255 : 0;
1503       i=255-c;
1504       i_gpix(im,x+xb,y+yb,&val);
1505       for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
1506       i_ppix(im,x+xb,y+yb,&val);
1507     }
1508
1509   }
1510 }
1511
1512
1513 /*
1514 =item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth)
1515
1516 Function to dump a raster onto a single channel image in color (internal)
1517
1518    im      - image to dump raster on
1519    bit     - bitmap that contains the text to be dumped to im
1520    xb, yb  - coordinates, left edge and baseline
1521    channel - channel to copy to
1522    smooth  - boolean (True: antialias on, False: antialias is off)
1523
1524 =cut
1525 */
1526
1527 static
1528 void
1529 i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map*  bit, int xb, int yb, int channel, int smooth ) {
1530   char *bmap;
1531   i_color val;
1532   int c,x,y;
1533
1534   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));
1535   
1536   bmap = (char *)bit->bitmap;
1537   
1538   if ( smooth ) {
1539     for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1540       c=(255*bmap[y*(bit->cols)+x])/4;
1541       i_gpix(im,x+xb,y+yb,&val);
1542       val.channel[channel]=c;
1543       i_ppix(im,x+xb,y+yb,&val);
1544     }
1545   } else {
1546     for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1547       c=( bmap[y*bit->cols+x/8] & (128>>(x%8)) ) ? 255 : 0;
1548       i_gpix(im,x+xb,y+yb,&val);
1549       val.channel[channel]=c;
1550       i_ppix(im,x+xb,y+yb,&val);
1551     }
1552   }
1553 }
1554
1555
1556 /* 
1557 =item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth) 
1558
1559 interface for generating single channel raster of text (internal)
1560
1561    handle - pointer to font handle
1562    bit    - the bitmap that is allocated, rendered into and NOT freed
1563    cords  - the bounding box (modified in place)
1564    points - font size to use
1565    txt    - string to render
1566    len    - length of the string to render
1567    smooth - boolean (True: antialias on, False: antialias is off)
1568
1569 =cut
1570 */
1571
1572 static
1573 int
1574 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char const* txt, int len, int smooth, int utf8 ) {
1575   int inst;
1576   int width, height;
1577   TT_Raster_Map small_bit;
1578   
1579   /* find or install an instance */
1580   if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) { 
1581     mm_log((1,"i_tt_rasterize: get instance failed\n"));
1582     return 0;
1583   }
1584   
1585   /* calculate bounding box */
1586   if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 ))
1587     return 0;
1588     
1589   
1590   width  = cords[2]-cords[0];
1591   height = cords[5]-cords[4];
1592   
1593   mm_log((1,"i_tt_rasterize: width=%d, height=%d\n",width, height )); 
1594   
1595   i_tt_init_raster_map ( bit, width, height, smooth );
1596   i_tt_clear_raster_map( bit );
1597   if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth );
1598   
1599   if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len, 
1600                                smooth, utf8 )) {
1601     if ( smooth ) 
1602       i_tt_done_raster_map( &small_bit );
1603     return 0;
1604   }
1605
1606   if ( smooth ) i_tt_done_raster_map( &small_bit );
1607   return 1;
1608 }
1609
1610
1611
1612 /* 
1613  * Exported text rendering interfaces
1614  */
1615
1616
1617 /*
1618 =item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth, utf8)
1619
1620 Interface to text rendering into a single channel in an image
1621
1622    handle  - pointer to font handle
1623    im      - image to render text on to
1624    xb, yb  - coordinates, left edge and baseline
1625    channel - channel to render into
1626    points  - font size to use
1627    txt     - string to render
1628    len     - length of the string to render
1629    smooth  - boolean (True: antialias on, False: antialias is off)
1630
1631 =cut
1632 */
1633
1634 undef_int
1635 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 ) {
1636
1637   int cords[BOUNDING_BOX_COUNT];
1638   int ascent, st_offset, y;
1639   TT_Raster_Map bit;
1640   
1641   i_clear_error();
1642   if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1643   
1644   ascent=cords[BBOX_ASCENT];
1645   st_offset=cords[BBOX_NEG_WIDTH];
1646   y = align ? yb-ascent : yb;
1647
1648   i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , y, channel, smooth );
1649   i_tt_done_raster_map( &bit );
1650
1651   return 1;
1652 }
1653
1654
1655 /* 
1656 =item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8) 
1657
1658 Interface to text rendering in a single color onto an image
1659
1660    handle  - pointer to font handle
1661    im      - image to render text on to
1662    xb, yb  - coordinates, left edge and baseline
1663    cl      - color to use for text
1664    points  - font size to use
1665    txt     - string to render
1666    len     - length of the string to render
1667    smooth  - boolean (True: antialias on, False: antialias is off)
1668
1669 =cut
1670 */
1671
1672 undef_int
1673 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) {
1674   int cords[BOUNDING_BOX_COUNT];
1675   int ascent, st_offset, y;
1676   TT_Raster_Map bit;
1677
1678   i_clear_error();
1679   
1680   if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1681   
1682   ascent=cords[BBOX_ASCENT];
1683   st_offset=cords[BBOX_NEG_WIDTH];
1684   y = align ? yb-ascent : yb;
1685
1686   i_tt_dump_raster_map2( im, &bit, xb+st_offset, y, cl, smooth ); 
1687   i_tt_done_raster_map( &bit );
1688
1689   return 1;
1690 }
1691
1692
1693 /*
1694 =item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8) 
1695
1696 Function to get texts bounding boxes given the instance of the font (internal)
1697
1698    handle - pointer to font handle
1699    inst   -  font instance
1700    txt    -  string to measure
1701    len    -  length of the string to render
1702    cords  - the bounding box (modified in place)
1703
1704 =cut
1705 */
1706
1707 static
1708 undef_int
1709 i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[BOUNDING_BOX_COUNT], int utf8 ) {
1710   int upm, casc, cdesc, first;
1711   
1712   int start    = 0;
1713   int width    = 0;
1714   int gdescent = 0;
1715   int gascent  = 0;
1716   int descent  = 0;
1717   int ascent   = 0;
1718   int rightb   = 0;
1719
1720   unsigned long j;
1721   unsigned char *ustr;
1722   ustr=(unsigned char*)txt;
1723
1724   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));
1725
1726   upm     = handle->properties.header->Units_Per_EM;
1727   gascent  = ( handle->properties.horizontal->Ascender  * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm;
1728   gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem - upm + 1) / upm;
1729   
1730   width   = 0;
1731   start   = 0;
1732   
1733   mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent));
1734
1735   first=1;
1736   while (len) {
1737     if (utf8) {
1738       j = i_utf8_advance(&txt, &len);
1739       if (j == ~0UL) {
1740         i_push_error(0, "invalid UTF8 character");
1741         return 0;
1742       }
1743     }
1744     else {
1745       j = (unsigned char)*txt++;
1746       --len;
1747     }
1748     if ( i_tt_get_glyph(handle,inst,j) ) {
1749       TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j);
1750       width += gm->advance   / 64;
1751       casc   = (gm->bbox.yMax+63) / 64;
1752       cdesc  = (gm->bbox.yMin-63) / 64;
1753
1754       mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n", 
1755               ((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc));
1756
1757       if (first) {
1758         start    = gm->bbox.xMin / 64;
1759         ascent   = (gm->bbox.yMax+63) / 64;
1760         descent  = (gm->bbox.yMin-63) / 64;
1761         first = 0;
1762       }
1763       if (!len) { /* if at end of string */
1764         /* the right-side bearing - in case the right-side of a 
1765            character goes past the right of the advance width,
1766            as is common for italic fonts
1767         */
1768         rightb = gm->advance - gm->bearingX 
1769           - (gm->bbox.xMax - gm->bbox.xMin);
1770         /* fprintf(stderr, "font info last: %d %d %d %d\n", 
1771            gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */
1772       }
1773
1774       ascent  = (ascent  >  casc ?  ascent : casc );
1775       descent = (descent < cdesc ? descent : cdesc);
1776     }
1777   }
1778   
1779   cords[BBOX_NEG_WIDTH]=start;
1780   cords[BBOX_GLOBAL_DESCENT]=gdescent;
1781   cords[BBOX_POS_WIDTH]=width;
1782   if (rightb < 0)
1783     cords[BBOX_POS_WIDTH] -= rightb / 64;
1784   cords[BBOX_GLOBAL_ASCENT]=gascent;
1785   cords[BBOX_DESCENT]=descent;
1786   cords[BBOX_ASCENT]=ascent;
1787   cords[BBOX_ADVANCE_WIDTH] = width;
1788   cords[BBOX_RIGHT_BEARING] = rightb / 64;
1789
1790   return BBOX_RIGHT_BEARING + 1;
1791 }
1792
1793
1794 /*
1795 =item i_tt_bbox(handle, points, txt, len, cords, utf8)
1796
1797 Interface to get a strings bounding box
1798
1799    handle - pointer to font handle
1800    points - font size to use
1801    txt    - string to render
1802    len    - length of the string to render
1803    cords  - the bounding box (modified in place)
1804
1805 =cut
1806 */
1807
1808 undef_int
1809 i_tt_bbox( TT_Fonthandle *handle, float points,char *txt,int len,int cords[6], int utf8) {
1810   int inst;
1811
1812   i_clear_error();
1813   mm_log((1,"i_tt_box(handle 0x%X,points %f,txt '%.*s', len %d, utf8 %d)\n",handle,points,len,txt,len, utf8));
1814
1815   if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
1816     i_push_errorf(0, "i_tt_get_instance(%g)", points);
1817     mm_log((1,"i_tt_text: get instance failed\n"));
1818     return 0;
1819   }
1820
1821   return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8);
1822 }
1823
1824 /*
1825 =item i_tt_face_name(handle, name_buf, name_buf_size)
1826
1827 Retrieve's the font's postscript name.
1828
1829 This is complicated by the need to handle encodings and so on.
1830
1831 =cut
1832  */
1833 int
1834 i_tt_face_name(TT_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
1835   TT_Face_Properties props;
1836   int name_count;
1837   int i;
1838   TT_UShort platform_id, encoding_id, lang_id, name_id;
1839   TT_UShort name_len;
1840   TT_String *name;
1841   int want_index = -1; /* an acceptable but not perfect name */
1842   int score = 0;
1843
1844   i_clear_error();
1845   
1846   TT_Get_Face_Properties(handle->face, &props);
1847   name_count = props.num_Names;
1848   for (i = 0; i < name_count; ++i) {
1849     TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id, 
1850                    &name_id);
1851
1852     TT_Get_Name_String(handle->face, i, &name, &name_len);
1853
1854     if (platform_id != TT_PLATFORM_APPLE_UNICODE && name_len
1855         && name_id == TT_NAME_ID_PS_NAME) {
1856       int might_want_index = -1;
1857       int might_score = 0;
1858       if ((platform_id == TT_PLATFORM_MACINTOSH && encoding_id == TT_MAC_ID_ROMAN)
1859           ||
1860           (platform_id == TT_PLATFORM_MICROSOFT && encoding_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)) {
1861         /* exactly what we want */
1862         want_index = i;
1863         break;
1864       }
1865       
1866       if (platform_id == TT_PLATFORM_MICROSOFT
1867           && (encoding_id & 0xFF) == TT_MS_LANGID_ENGLISH_GENERAL) {
1868         /* any english is good */
1869         might_want_index = i;
1870         might_score = 9;
1871       }
1872       /* there might be something in between */
1873       else {
1874         /* anything non-unicode is better than nothing */
1875         might_want_index = i;
1876         might_score = 1;
1877       }
1878       if (might_score > score) {
1879         score = might_score;
1880         want_index = might_want_index;
1881       }
1882     }
1883   }
1884
1885   if (want_index != -1) {
1886     TT_Get_Name_String(handle->face, want_index, &name, &name_len);
1887     
1888     strncpy(name_buf, name, name_buf_size);
1889     name_buf[name_buf_size-1] = '\0';
1890
1891     return strlen(name) + 1;
1892   }
1893   else {
1894     i_push_error(0, "no face name present");
1895     return 0;
1896   }
1897 }
1898
1899 void i_tt_dump_names(TT_Fonthandle *handle) {
1900   TT_Face_Properties props;
1901   int name_count;
1902   int i;
1903   TT_UShort platform_id, encoding_id, lang_id, name_id;
1904   TT_UShort name_len;
1905   TT_String *name;
1906   
1907   TT_Get_Face_Properties(handle->face, &props);
1908   name_count = props.num_Names;
1909   for (i = 0; i < name_count; ++i) {
1910     TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id, 
1911                    &name_id);
1912     TT_Get_Name_String(handle->face, i, &name, &name_len);
1913
1914     printf("# %d: plat %d enc %d lang %d name %d value ", i, platform_id,
1915            encoding_id, lang_id, name_id);
1916     if (platform_id == TT_PLATFORM_APPLE_UNICODE) {
1917       printf("(unicode)\n");
1918     }
1919     else {
1920       printf("'%s'\n", name);
1921     }
1922   }
1923 }
1924
1925 int
1926 i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf, 
1927                  size_t name_buf_size) {
1928 #ifdef FTXPOST
1929   TT_Error rc;
1930   TT_String *psname;
1931   TT_UShort index;
1932
1933   i_clear_error();
1934
1935   if (!handle->loaded_names) {
1936     TT_Post post;
1937     mm_log((1, "Loading PS Names"));
1938     handle->load_cond = TT_Load_PS_Names(handle->face, &post);
1939     ++handle->loaded_names;
1940   }
1941
1942   if (handle->load_cond) {
1943     i_push_errorf(rc, "error loading names (%d)", handle->load_cond);
1944     return 0;
1945   }
1946   
1947   index = TT_Char_Index(handle->char_map, ch);
1948   if (!index) {
1949     i_push_error(0, "no such character");
1950     return 0;
1951   }
1952
1953   rc = TT_Get_PS_Name(handle->face, index, &psname);
1954
1955   if (rc) {
1956     i_push_error(rc, "error getting name");
1957     return 0;
1958   }
1959
1960   strncpy(name_buf, psname, name_buf_size);
1961   name_buf[name_buf_size-1] = '\0';
1962
1963   return strlen(psname) + 1;
1964 #else
1965   mm_log((1, "FTXPOST extension not enabled\n"));
1966   i_clear_error();
1967   i_push_error(0, "Use of FTXPOST extension disabled");
1968
1969   return 0;
1970 #endif
1971 }
1972
1973 /*
1974 =item i_tt_push_error(code)
1975
1976 Push an error message and code onto the Imager error stack.
1977
1978 =cut
1979 */
1980 static void
1981 i_tt_push_error(TT_Error rc) {
1982 #ifdef FTXERR18
1983   TT_String const *msg = TT_ErrToString18(rc);
1984
1985   i_push_error(rc, msg);
1986 #else
1987   i_push_errorf(rc, "Error code 0x%04x", (unsigned)rc);
1988 #endif
1989 }
1990
1991 #endif /* HAVE_LIBTT */
1992
1993
1994 /*
1995 =back
1996
1997 =head1 AUTHOR
1998
1999 Arnar M. Hrafnkelsson <addi@umich.edu>
2000
2001 =head1 SEE ALSO
2002
2003 Imager(3)
2004
2005 =cut
2006 */