]> git.imager.perl.org - imager.git/blob - font.c
temporary scaling test code, proper defaults for crop().
[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 /*
11 =head1 NAME
12
13 font.c - implements font handling functions for t1 and truetype fonts
14
15 =head1 SYNOPSIS
16
17   i_init_fonts();
18
19   #ifdef HAVE_LIBT1
20   fontnum = i_t1_new(path_to_pfb, path_to_afm);
21   i_t1_bbox(fontnum, points, "foo", 3, int cords[6]);
22   rc = i_t1_destroy(fontnum);
23   #endif
24
25   #ifdef HAVE_LIBTT
26   handle = i_tt_new(path_to_ttf);
27   rc = i_tt_bbox(handle, points, "foo", 3, int cords[6], utf8);
28   i_tt_destroy(handle);
29
30   // and much more
31
32 =head1 DESCRIPTION
33
34 font.c implements font creation, rendering, bounding box functions and
35 more for Imager.
36
37 =head1 FUNCTION REFERENCE
38
39 Some of these functions are internal.
40
41 =over
42
43 =cut
44
45 */
46
47
48
49
50
51
52
53
54
55 /* 
56 =item i_init_fonts()
57
58 Initialize font rendering libraries if they are avaliable.
59
60 =cut
61 */
62
63 undef_int 
64 i_init_fonts(int t1log) {
65   mm_log((1,"Initializing fonts\n"));
66
67 #ifdef HAVE_LIBT1
68   i_init_t1(t1log);
69 #endif
70   
71 #ifdef HAVE_LIBTT
72   i_init_tt();
73 #endif
74
75 #ifdef HAVE_FT2
76   if (!i_ft2_init())
77     return 0;
78 #endif
79
80   return(1); /* FIXME: Always true - check the return values of the init_t1 and init_tt functions */
81 }
82
83
84
85
86 #ifdef HAVE_LIBT1
87
88 static int t1_get_flags(char const *flags);
89 static char *t1_from_utf8(char const *in, int len, int *outlen);
90
91 /* 
92 =item i_init_t1(t1log)
93
94 Initializes the t1lib font rendering engine.
95
96 =cut
97 */
98
99 undef_int
100 i_init_t1(int t1log) {
101   int init_flags = IGNORE_CONFIGFILE|IGNORE_FONTDATABASE;
102   mm_log((1,"init_t1()\n"));
103   
104   if (t1log)
105     init_flags |= LOGFILE;
106   if ((T1_InitLib(init_flags) == NULL)){
107     mm_log((1,"Initialization of t1lib failed\n"));
108     return(1);
109   }
110   T1_SetLogLevel(T1LOG_DEBUG);
111   i_t1_set_aa(1); /* Default Antialias value */
112   return(0);
113 }
114
115
116 /* 
117 =item i_close_t1()
118
119 Shuts the t1lib font rendering engine down.
120
121   This it seems that this function is never used.
122
123 =cut
124 */
125
126 void
127 i_close_t1(void) {
128   T1_CloseLib();
129 }
130
131
132 /*
133 =item i_t1_new(pfb, afm)
134
135 Loads the fonts with the given filenames, returns its font id
136
137  pfb -  path to pfb file for font
138  afm -  path to afm file for font
139
140 =cut
141 */
142
143 int
144 i_t1_new(char *pfb,char *afm) {
145   int font_id;
146
147   mm_log((1,"i_t1_new(pfb %s,afm %s)\n",pfb,(afm?afm:"NULL")));
148   font_id = T1_AddFont(pfb);
149   if (font_id<0) {
150     mm_log((1,"i_t1_new: Failed to load pfb file '%s' - return code %d.\n",pfb,font_id));
151     return font_id;
152   }
153   
154   if (afm != NULL) {
155     mm_log((1,"i_t1_new: requesting afm file '%s'.\n",afm));
156     if (T1_SetAfmFileName(font_id,afm)<0) mm_log((1,"i_t1_new: afm loading of '%s' failed.\n",afm));
157   }
158
159   return font_id;
160 }
161
162 /*
163 =item i_t1_destroy(font_id)
164
165 Frees resources for a t1 font with given font id.
166
167    font_id - number of the font to free
168
169 =cut
170 */
171
172 int
173 i_t1_destroy(int font_id) {
174   mm_log((1,"i_t1_destroy(font_id %d)\n",font_id));
175   return T1_DeleteFont(font_id);
176 }
177
178
179 /*
180 =item i_t1_set_aa(st)
181
182 Sets the antialiasing level of the t1 library.
183
184    st - 0 =  NONE, 1 = LOW, 2 =  HIGH.
185
186 =cut
187 */
188
189 void
190 i_t1_set_aa(int st) {
191   int i;
192   unsigned long cst[17];
193   switch(st) {
194   case 0:
195     T1_AASetBitsPerPixel( 8 );
196     T1_AASetLevel( T1_AA_NONE );
197     T1_AANSetGrayValues( 0, 255 );
198     mm_log((1,"setting T1 antialias to none\n"));
199     break;
200   case 1:
201     T1_AASetBitsPerPixel( 8 );
202     T1_AASetLevel( T1_AA_LOW );
203     T1_AASetGrayValues( 0,65,127,191,255 );
204     mm_log((1,"setting T1 antialias to low\n"));
205     break;
206   case 2:
207     T1_AASetBitsPerPixel(8);
208     T1_AASetLevel(T1_AA_HIGH);
209     for(i=0;i<17;i++) cst[i]=(i*255)/16;
210     T1_AAHSetGrayValues( cst );
211     mm_log((1,"setting T1 antialias to high\n"));
212   }
213 }
214
215
216 /* 
217 =item i_t1_cp(im, xb, yb, channel, fontnum, points, str, len, align)
218
219 Interface to text rendering into a single channel in an image
220
221    im        pointer to image structure
222    xb        x coordinate of start of string
223    yb        y coordinate of start of string ( see align )
224    channel - destination channel
225    fontnum - t1 library font id
226    points  - number of points in fontheight
227    str     - string to render
228    len     - string length
229    align   - (0 - top of font glyph | 1 - baseline )
230
231 =cut
232 */
233
234 undef_int
235 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) {
236   GLYPH *glyph;
237   int xsize,ysize,x,y;
238   i_color val;
239   int mod_flags = t1_get_flags(flags);
240
241   unsigned int ch_mask_store;
242   
243   if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
244
245   if (utf8) {
246     int worklen;
247     char *work = t1_from_utf8(str, len, &worklen);
248     glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
249     myfree(work);
250   }
251   else {
252     glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL);
253   }
254   if (glyph == NULL)
255     return 0;
256
257   mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
258   mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
259   mm_log((1," advanceX: %d  advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
260   mm_log((1,"bpp: %d\n",glyph->bpp));
261   
262   xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
263   ysize=glyph->metrics.ascent-glyph->metrics.descent;
264   
265   mm_log((1,"width: %d height: %d\n",xsize,ysize));
266
267   ch_mask_store=im->ch_mask;
268   im->ch_mask=1<<channel;
269
270   if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
271   
272   for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
273     val.channel[channel]=glyph->bits[y*xsize+x];
274     i_ppix(im,x+xb,y+yb,&val);
275   }
276   
277   im->ch_mask=ch_mask_store;
278   return 1;
279 }
280
281
282 /*
283 =item i_t1_bbox(handle, fontnum, points, str, len, cords)
284
285 function to get a strings bounding box given the font id and sizes
286
287    handle  - pointer to font handle   
288    fontnum - t1 library font id
289    points  - number of points in fontheight
290    str     - string to measure
291    len     - string length
292    cords   - the bounding box (modified in place)
293
294 =cut
295 */
296
297 void
298 i_t1_bbox(int fontnum,float points,char *str,int len,int cords[6], int utf8,char const *flags) {
299   BBox bbox;
300   BBox gbbox;
301   int mod_flags = t1_get_flags(flags);
302   
303   mm_log((1,"i_t1_bbox(fontnum %d,points %.2f,str '%.*s', len %d)\n",fontnum,points,len,str,len));
304   T1_LoadFont(fontnum);  /* FIXME: Here a return code is ignored - haw haw haw */ 
305   if (utf8) {
306     int worklen;
307     char *work = t1_from_utf8(str, len, &worklen);
308     bbox = T1_GetStringBBox(fontnum,work,worklen,0,mod_flags);
309     myfree(work);
310   }
311   else {
312     bbox = T1_GetStringBBox(fontnum,str,len,0,mod_flags);
313   }
314   gbbox = T1_GetFontBBox(fontnum);
315   
316   mm_log((1,"bbox: (%d,%d,%d,%d)\n",
317           (int)(bbox.llx*points/1000),
318           (int)(gbbox.lly*points/1000),
319           (int)(bbox.urx*points/1000),
320           (int)(gbbox.ury*points/1000),
321           (int)(bbox.lly*points/1000),
322           (int)(bbox.ury*points/1000) ));
323
324
325   cords[0]=((float)bbox.llx*points)/1000;
326   cords[2]=((float)bbox.urx*points)/1000;
327
328   cords[1]=((float)gbbox.lly*points)/1000;
329   cords[3]=((float)gbbox.ury*points)/1000;
330
331   cords[4]=((float)bbox.lly*points)/1000;
332   cords[5]=((float)bbox.ury*points)/1000;
333 }
334
335
336 /*
337 =item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align)
338
339 Interface to text rendering in a single color onto an image
340
341    im      - pointer to image structure
342    xb      - x coordinate of start of string
343    yb      - y coordinate of start of string ( see align )
344    cl      - color to draw the text in
345    fontnum - t1 library font id
346    points  - number of points in fontheight
347    str     - char pointer to string to render
348    len     - string length
349    align   - (0 - top of font glyph | 1 - baseline )
350
351 =cut
352 */
353
354 undef_int
355 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) {
356   GLYPH *glyph;
357   int xsize,ysize,x,y,ch;
358   i_color val;
359   unsigned char c,i;
360   int mod_flags = t1_get_flags(flags);
361
362   if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
363
364   if (utf8) {
365     int worklen;
366     char *work = t1_from_utf8(str, len, &worklen);
367     glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
368     myfree(work);
369   }
370   else {
371     glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL);
372   }
373   if (glyph == NULL)
374     return 0;
375
376   mm_log((1,"metrics:  ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
377   mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
378   mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
379   mm_log((1,"bpp: %d\n",glyph->bpp));
380   
381   xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
382   ysize=glyph->metrics.ascent-glyph->metrics.descent;
383   
384   mm_log((1,"width: %d height: %d\n",xsize,ysize));
385
386   if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
387   
388   for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
389     c=glyph->bits[y*xsize+x];
390     i=255-c;
391     i_gpix(im,x+xb,y+yb,&val);
392     for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
393     i_ppix(im,x+xb,y+yb,&val);
394   }
395   return 1;
396 }
397
398 /*
399 =item t1_get_flags(flags)
400
401 Processes the characters in I<flags> to create a mod_flags value used
402 by some T1Lib functions.
403
404 =cut
405  */
406 static int
407 t1_get_flags(char const *flags) {
408   int mod_flags = T1_KERNING;
409
410   while (*flags) {
411     switch (*flags++) {
412     case 'u': case 'U': mod_flags |= T1_UNDERLINE; break;
413     case 'o': case 'O': mod_flags |= T1_OVERLINE; break;
414     case 's': case 'S': mod_flags |= T1_OVERSTRIKE; break;
415       /* ignore anything we don't recognize */
416     }
417   }
418
419   return mod_flags;
420 }
421
422 /*
423 =item t1_from_utf8(char const *in, int len, int *outlen)
424
425 Produces an unencoded version of I<in> by dropping any Unicode
426 character over 255.
427
428 Returns a newly allocated buffer which should be freed with myfree().
429 Sets *outlen to the number of bytes used in the output string.
430
431 =cut
432 */
433
434 static char *
435 t1_from_utf8(char const *in, int len, int *outlen) {
436   char *out = mymalloc(len+1);
437   char *p = out;
438   unsigned long c;
439
440   while (len) {
441     c = i_utf8_advance(&in, &len);
442     if (c == ~0UL) {
443       myfree(out);
444       i_push_error(0, "invalid UTF8 character");
445       return 0;
446     }
447     /* yeah, just drop them */
448     if (c < 0x100) {
449       *p++ = (char)c;
450     }
451   }
452   *p = '\0';
453   *outlen = p - out;
454
455   return out;
456 }
457
458 #endif /* HAVE_LIBT1 */
459
460
461 /* Truetype font support */
462
463 #ifdef HAVE_LIBTT
464
465 #include <freetype.h>
466 #define TT_CHC 5
467
468 /* convert a code point into an index in the glyph cache */
469 #define TT_HASH(x) ((x) & 0xFF)
470
471 typedef struct i_glyph_entry_ {
472   TT_Glyph glyph;
473   unsigned long ch;
474 } i_tt_glyph_entry;
475
476 #define TT_NOCHAR (~0UL)
477
478 struct TT_Instancehandle_ {
479   TT_Instance instance;
480   TT_Instance_Metrics imetrics;
481   TT_Glyph_Metrics gmetrics[256];
482   i_tt_glyph_entry glyphs[256];
483   int smooth;
484   int ptsize;
485   int order;
486 };
487
488 typedef struct TT_Instancehandle_ TT_Instancehandle;
489
490 struct TT_Fonthandle_ {
491   TT_Face face;
492   TT_Face_Properties properties;
493   TT_Instancehandle instanceh[TT_CHC];
494   TT_CharMap char_map;
495 };
496
497 /* Defines */
498
499 #define USTRCT(x) ((x).z)
500 #define TT_VALID( handle )  ( ( handle ).z != NULL )
501
502
503 /* Prototypes */
504
505 static  int i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth );
506 static void i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth );
507 static void i_tt_done_raster_map( TT_Raster_Map *bit );
508 static void i_tt_clear_raster_map( TT_Raster_Map* bit );
509 static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off );
510 static  int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j );
511 static void 
512 i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics, 
513                    TT_Raster_Map *bit, TT_Raster_Map *small_bit, 
514                    int x_off, int y_off, int smooth );
515 static int
516 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit, 
517                         TT_Raster_Map *small_bit, int cords[6], 
518                         char const* txt, int len, int smooth, int utf8 );
519 static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, i_color *cl, int smooth );
520 static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth );
521 static  int
522 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], 
523                 float points, char const* txt, int len, int smooth, int utf8 );
524 static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6], int utf8 );
525
526
527 /* static globals needed */
528
529 static TT_Engine    engine;
530 static int  LTT_dpi    = 72; /* FIXME: this ought to be a part of the call interface */
531 static int  LTT_hinted = 1;  /* FIXME: this too */
532
533
534 /*
535  * FreeType interface
536  */
537
538
539 /*
540 =item init_tt()
541
542 Initializes the freetype font rendering engine
543
544 =cut
545 */
546
547 undef_int
548 i_init_tt() {
549   TT_Error  error;
550   mm_log((1,"init_tt()\n"));
551   error = TT_Init_FreeType( &engine );
552   if ( error ){
553     mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error));
554     return(1);
555   }
556   return(0);
557 }
558
559
560 /* 
561 =item i_tt_get_instance(handle, points, smooth)
562
563 Finds a points+smooth instance or if one doesn't exist in the cache
564 allocates room and returns its cache entry
565
566    fontname - path to the font to load
567    handle   - handle to the font.
568    points   - points of the requested font
569    smooth   - boolean (True: antialias on, False: antialias is off)
570
571 =cut
572 */
573
574 static
575 int
576 i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) {
577   int i,idx;
578   TT_Error error;
579   
580   mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n",
581           handle,points,smooth));
582   
583   if (smooth == -1) { /* Smooth doesn't matter for this search */
584     for(i=0;i<TT_CHC;i++) {
585       if (handle->instanceh[i].ptsize==points) {
586         mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
587         return i;
588       }
589     }
590     smooth=1; /* We will be adding a font - add it as smooth then */
591   } else { /* Smooth doesn't matter for this search */
592     for(i=0;i<TT_CHC;i++) {
593       if (handle->instanceh[i].ptsize == points 
594           && handle->instanceh[i].smooth == smooth) {
595         mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
596         return i;
597       }
598     }
599   }
600   
601   /* Found the instance in the cache - return the cache index */
602   
603   for(idx=0;idx<TT_CHC;idx++) {
604     if (!(handle->instanceh[idx].order)) break; /* find the lru item */
605   }
606
607   mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
608   mm_log((1,"i_tt_get_instance: lru pointer 0x%X\n",
609           USTRCT(handle->instanceh[idx].instance) ));
610   
611   if ( USTRCT(handle->instanceh[idx].instance) ) {
612     mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
613
614     /* Free cached glyphs */
615     for(i=0;i<256;i++)
616       if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
617         TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
618
619     for(i=0;i<256;i++) {
620       handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
621       USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
622     }
623
624     /* Free instance if needed */
625     TT_Done_Instance( handle->instanceh[idx].instance );
626   }
627   
628   /* create and initialize instance */
629   /* FIXME: probably a memory leak on fail */
630   
631   (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) || 
632           ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
633           ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
634   
635   if ( error ) {
636     mm_log((1, "Could not create and initialize instance: error 0x%x.\n",error ));
637     return -1;
638   }
639   
640   /* Now that the instance should the inplace we need to lower all of the
641      ru counts and put `this' one with the highest entry */
642   
643   for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
644
645   handle->instanceh[idx].order=TT_CHC-1;
646   handle->instanceh[idx].ptsize=points;
647   handle->instanceh[idx].smooth=smooth;
648   TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
649
650   /* Zero the memory for the glyph storage so they are not thought as
651      cached if they haven't been cached since this new font was loaded */
652
653   for(i=0;i<256;i++) {
654     handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
655     USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
656   }
657   
658   return idx;
659 }
660
661
662 /*
663 =item i_tt_new(fontname)
664
665 Creates a new font handle object, finds a character map and initialise the
666 the font handle's cache
667
668    fontname - path to the font to load
669
670 =cut
671 */
672
673 TT_Fonthandle*
674 i_tt_new(char *fontname) {
675   TT_Error error;
676   TT_Fonthandle *handle;
677   unsigned short i,n;
678   unsigned short platform,encoding;
679   
680   mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
681   
682   /* allocate memory for the structure */
683   
684   handle = mymalloc( sizeof(TT_Fonthandle) );
685
686   /* load the typeface */
687   error = TT_Open_Face( engine, fontname, &handle->face );
688   if ( error ) {
689     if ( error == TT_Err_Could_Not_Open_File ) {
690       mm_log((1, "Could not find/open %s.\n", fontname ));
691     }
692     else {
693       mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname, 
694               error )); 
695     }
696     return NULL;
697   }
698   
699   TT_Get_Face_Properties( handle->face, &(handle->properties) );
700
701   /* First, look for a Unicode charmap */
702   n = handle->properties.num_CharMaps;
703   USTRCT( handle->char_map )=NULL; /* Invalidate character map */
704   
705   for ( i = 0; i < n; i++ ) {
706     TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
707     if ( (platform == 3 && encoding == 1 ) 
708          || (platform == 0 && encoding == 0 ) ) {
709       mm_log((2,"i_tt_new - found char map platform %u encoding %u\n", 
710               platform, encoding));
711       TT_Get_CharMap( handle->face, i, &(handle->char_map) );
712       break;
713     }
714   }
715   if (!USTRCT(handle->char_map) && n != 0) {
716     /* just use the first one */
717     TT_Get_CharMap( handle->face, 0, &(handle->char_map));
718   }
719
720   /* Zero the pointsizes - and ordering */
721   
722   for(i=0;i<TT_CHC;i++) {
723     USTRCT(handle->instanceh[i].instance)=NULL;
724     handle->instanceh[i].order=i;
725     handle->instanceh[i].ptsize=0;
726     handle->instanceh[i].smooth=-1;
727   }
728
729   mm_log((1,"i_tt_new <- 0x%X\n",handle));
730   return handle;
731 }
732
733
734
735 /*
736  * raster map management
737  */
738
739 /* 
740 =item i_tt_init_raster_map(bit, width, height, smooth)
741
742 Allocates internal memory for the bitmap as needed by the parameters (internal)
743                  
744    bit    - bitmap to allocate into
745    width  - width of the bitmap
746    height - height of the bitmap
747    smooth - boolean (True: antialias on, False: antialias is off)
748
749 =cut
750 */
751
752 static
753 void
754 i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth ) {
755
756   mm_log((1,"i_tt_init_raster_map( bit 08x%08X, width %d, height %d, smooth %d)\n", bit, width, height, smooth));
757   
758   bit->rows  = height;
759   bit->width = ( width + 3 ) & -4;
760   bit->flow  = TT_Flow_Down;
761   
762   if ( smooth ) {
763     bit->cols  = bit->width;
764     bit->size  = bit->rows * bit->width;
765   } else {
766     bit->cols  = ( bit->width + 7 ) / 8;    /* convert to # of bytes     */
767     bit->size  = bit->rows * bit->cols;     /* number of bytes in buffer */
768   }
769   
770   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 ));
771
772   bit->bitmap = (void *) mymalloc( bit->size );
773   if ( !bit->bitmap ) m_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",bit->size );
774 }
775
776
777 /*
778 =item i_tt_clear_raster_map(bit)
779
780 Frees the bitmap data and sets pointer to NULL (internal)
781                  
782    bit - bitmap to free
783
784 =cut
785 */
786
787 static
788 void
789 i_tt_done_raster_map( TT_Raster_Map *bit ) {
790   myfree( bit->bitmap );
791   bit->bitmap = NULL;
792 }
793
794
795 /*
796 =item i_tt_clear_raster_map(bit)
797
798 Clears the specified bitmap (internal)
799                  
800    bit - bitmap to zero
801
802 =cut
803 */
804
805
806 static
807 void
808 i_tt_clear_raster_map( TT_Raster_Map*  bit ) {
809   memset( bit->bitmap, 0, bit->size );
810 }
811
812
813 /* 
814 =item i_tt_blit_or(dst, src, x_off, y_off)
815
816 function that blits one raster map into another (internal)
817                  
818    dst   - destination bitmap
819    src   - source bitmap
820    x_off - x offset into the destination bitmap
821    y_off - y offset into the destination bitmap
822
823 =cut
824 */
825
826 static
827 void
828 i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off ) {
829   int  x,  y;
830   int  x1, x2, y1, y2;
831   char *s, *d;
832   
833   x1 = x_off < 0 ? -x_off : 0;
834   y1 = y_off < 0 ? -y_off : 0;
835   
836   x2 = (int)dst->cols - x_off;
837   if ( x2 > src->cols ) x2 = src->cols;
838   
839   y2 = (int)dst->rows - y_off;
840   if ( y2 > src->rows ) y2 = src->rows;
841
842   if ( x1 >= x2 ) return;
843
844   /* do the real work now */
845
846   for ( y = y1; y < y2; ++y ) {
847     s = ( (char*)src->bitmap ) + y * src->cols + x1;
848     d = ( (char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
849     
850     for ( x = x1; x < x2; ++x ) {
851       if (*s > *d)
852         *d = *s;
853       d++;
854       s++;
855     }
856   }
857 }
858
859 /* useful for debugging */
860 #if 0
861
862 static void dump_raster_map(FILE *out, TT_Raster_Map *bit ) {
863   int x, y;
864   fprintf(out, "cols %d rows %d  flow %d\n", bit->cols, bit->rows, bit->flow);
865   for (y = 0; y < bit->rows; ++y) {
866     fprintf(out, "%2d:", y);
867     for (x = 0; x < bit->cols; ++x) {
868       if ((x & 7) == 0 && x) putc(' ', out);
869       fprintf(out, "%02x", ((unsigned char *)bit->bitmap)[y*bit->cols+x]);
870     }
871     putc('\n', out);
872   }
873 }
874
875 #endif
876
877 /* 
878 =item i_tt_get_glyph(handle, inst, j) 
879
880 Function to see if a glyph exists and if so cache it (internal)
881                  
882    handle - pointer to font handle
883    inst   - font instance
884    j      - charcode of glyph
885
886 =cut
887 */
888
889 static
890 int
891 i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j) {
892   unsigned short load_flags, code;
893   TT_Error error;
894
895   mm_log((1, "i_tt_get_glyph(handle 0x%X, inst %d, j %d (%c))\n",
896           handle,inst,j, ((j >= ' ' && j <= '~') ? j : '.')));
897   
898   /*mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] ));*/
899
900   if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)
901        && handle->instanceh[inst].glyphs[TT_HASH(j)].ch == j) {
902     mm_log((1,"i_tt_get_glyph: %d in cache\n",j));
903     return 1;
904   }
905
906   if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) ) {
907     /* clean up the entry */
908     TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
909     USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
910     handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
911   }
912   
913   /* Ok - it wasn't cached - try to get it in */
914   load_flags = TTLOAD_SCALE_GLYPH;
915   if ( LTT_hinted ) load_flags |= TTLOAD_HINT_GLYPH;
916   
917   if ( !TT_VALID(handle->char_map) ) {
918     code = (j - ' ' + 1) < 0 ? 0 : (j - ' ' + 1);
919     if ( code >= handle->properties.num_Glyphs ) code = 0;
920   } else code = TT_Char_Index( handle->char_map, j );
921   
922   if ( (error = TT_New_Glyph( handle->face, &handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)) ) {
923     mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
924     i_push_error(error, "TT_New_Glyph()");
925     return 0;
926   }
927   if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, code, load_flags)) ) {
928     mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
929     /* Don't leak */
930     TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
931     USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
932     i_push_error(error, "TT_Load_Glyph()");
933     return 0;
934   }
935
936   /* At this point the glyph should be allocated and loaded */
937   handle->instanceh[inst].glyphs[TT_HASH(j)].ch = j;
938
939   /* Next get the glyph metrics */
940   error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, 
941                                 &handle->instanceh[inst].gmetrics[TT_HASH(j)] );
942   if (error) {
943     mm_log((1, "TT_Get_Glyph_Metrics: error 0x%x.\n", error ));
944     TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
945     USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
946     handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
947     i_push_error(error, "TT_Get_Glyph_Metrics()");
948     return 0;
949   }
950
951   return 1;
952 }
953
954 /*
955 =item i_tt_has_chars(handle, text, len, utf8, out)
956
957 Check if the given characters are defined by the font.  Note that len
958 is the number of bytes, not the number of characters (when utf8 is
959 non-zero).
960
961 Returns the number of characters that were checked.
962
963 =cut
964 */
965
966 int
967 i_tt_has_chars(TT_Fonthandle *handle, char const *text, int len, int utf8,
968                char *out) {
969   int count = 0;
970   int inst;
971   mm_log((1, "i_ft2_has_chars(handle %p, text %p, len %d, utf8 %d)\n", 
972           handle, text, len, utf8));
973
974   while (len) {
975     unsigned long c;
976     int index;
977     if (utf8) {
978       c = i_utf8_advance(&text, &len);
979       if (c == ~0UL) {
980         i_push_error(0, "invalid UTF8 character");
981         return 0;
982       }
983     }
984     else {
985       c = (unsigned char)*text++;
986       --len;
987     }
988     
989     if (TT_VALID(handle->char_map)) {
990       index = TT_Char_Index(handle->char_map, c);
991     }
992     else {
993       index = (c - ' ' + 1) < 0 ? 0 : (c - ' ' + 1);
994       if (index >= handle->properties.num_Glyphs)
995         index = 0;
996     }
997     *out++ = index != 0;
998     ++count;
999   }
1000
1001   return count;
1002 }
1003
1004 /* 
1005 =item i_tt_destroy(handle)
1006
1007 Clears the data taken by a font including all cached data such as
1008 pixmaps and glyphs
1009                  
1010    handle - pointer to font handle
1011
1012 =cut
1013 */
1014
1015 void
1016 i_tt_destroy( TT_Fonthandle *handle) {
1017   TT_Close_Face( handle->face );
1018   myfree( handle );
1019   
1020   /* FIXME: Should these be freed automatically by the library? 
1021
1022   TT_Done_Instance( instance );
1023   void
1024     i_tt_done_glyphs( void ) {
1025     int  i;
1026
1027     if ( !glyphs ) return;
1028     
1029     for ( i = 0; i < 256; ++i ) TT_Done_Glyph( glyphs[i] );
1030     free( glyphs );
1031     
1032     glyphs = NULL;
1033   }
1034   */
1035 }
1036
1037
1038 /*
1039  * FreeType Rendering functions
1040  */
1041
1042
1043 /* 
1044 =item i_tt_render_glyph(handle, gmetrics, bit, smallbit, x_off, y_off, smooth)
1045
1046 Renders a single glyph into the bit rastermap (internal)
1047
1048    handle   - pointer to font handle
1049    gmetrics - the metrics for the glyph to be rendered
1050    bit      - large bitmap that is the destination for the text
1051    smallbit - small bitmap that is used only if smooth is true
1052    x_off    - x offset of glyph
1053    y_off    - y offset of glyph
1054    smooth   - boolean (True: antialias on, False: antialias is off)
1055
1056 =cut
1057 */
1058
1059 static
1060 void
1061 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 ) {
1062   
1063   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",
1064           USTRCT(glyph), gmetrics, bit, small_bit, x_off,y_off,smooth));
1065   
1066   if ( !smooth ) TT_Get_Glyph_Bitmap( glyph, bit, x_off * 64, y_off * 64);
1067   else {
1068     TT_F26Dot6 xmin, ymin, xmax, ymax;
1069
1070     xmin =  gmetrics->bbox.xMin & -64;
1071     ymin =  gmetrics->bbox.yMin & -64;
1072     xmax = (gmetrics->bbox.xMax + 63) & -64;
1073     ymax = (gmetrics->bbox.yMax + 63) & -64;
1074     
1075     i_tt_clear_raster_map( small_bit );
1076     TT_Get_Glyph_Pixmap( glyph, small_bit, -xmin, -ymin );
1077     i_tt_blit_or( bit, small_bit, xmin/64 + x_off, -ymin/64 - y_off );
1078   }
1079 }
1080
1081
1082 /*
1083 =item i_tt_render_all_glyphs(handle, inst, bit, small_bit, cords, txt, len, smooth)
1084
1085 calls i_tt_render_glyph to render each glyph into the bit rastermap (internal)
1086
1087    handle   - pointer to font handle
1088    inst     - font instance
1089    bit      - large bitmap that is the destination for the text
1090    smallbit - small bitmap that is used only if smooth is true
1091    txt      - string to render
1092    len      - length of the string to render
1093    smooth   - boolean (True: antialias on, False: antialias is off)
1094
1095 =cut
1096 */
1097
1098 static
1099 int
1100 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
1101                         TT_Raster_Map *small_bit, int cords[6], 
1102                         char const* txt, int len, int smooth, int utf8 ) {
1103   unsigned long j;
1104   int i;
1105   TT_F26Dot6 x,y;
1106   
1107   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",
1108           handle, inst, bit, small_bit, len, txt, len, smooth, utf8));
1109   
1110   /* 
1111      y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM);
1112   */
1113
1114   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 */
1115   y=-cords[4];
1116   
1117   while (len) {
1118     if (utf8) {
1119       j = i_utf8_advance(&txt, &len);
1120       if (j == ~0UL) {
1121         i_push_error(0, "invalid UTF8 character");
1122         return 0;
1123       }
1124     }
1125     else {
1126       j = (unsigned char)*txt++;
1127       --len;
1128     }
1129     if ( !i_tt_get_glyph(handle,inst,j) ) 
1130       continue;
1131     i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, 
1132                        &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit, 
1133                        small_bit, x, y, smooth );
1134     x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64;
1135   }
1136
1137   return 1;
1138 }
1139
1140
1141 /*
1142  * Functions to render rasters (single channel images) onto images
1143  */
1144
1145 /* 
1146 =item i_tt_dump_raster_map2(im, bit, xb, yb, cl, smooth)
1147
1148 Function to dump a raster onto an image in color used by i_tt_text() (internal).
1149
1150    im     - image to dump raster on
1151    bit    - bitmap that contains the text to be dumped to im
1152    xb, yb - coordinates, left edge and baseline
1153    cl     - color to use for text
1154    smooth - boolean (True: antialias on, False: antialias is off)
1155
1156 =cut
1157 */
1158
1159 static
1160 void
1161 i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, i_color *cl, int smooth ) {
1162   char *bmap;
1163   i_color val;
1164   int c, i, ch, x, y;
1165   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));
1166   
1167   bmap = (char *)bit->bitmap;
1168
1169   if ( smooth ) {
1170
1171     for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1172       c=(255*bmap[y*(bit->cols)+x])/4;
1173       i=255-c;
1174       i_gpix(im,x+xb,y+yb,&val);
1175       for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
1176       i_ppix(im,x+xb,y+yb,&val);
1177     }
1178
1179   } else {
1180
1181     for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1182       c=( bmap[y*bit->cols+x/8] & (128>>(x%8)) ) ? 255 : 0;
1183       i=255-c;
1184       i_gpix(im,x+xb,y+yb,&val);
1185       for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
1186       i_ppix(im,x+xb,y+yb,&val);
1187     }
1188
1189   }
1190 }
1191
1192
1193 /*
1194 =item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth)
1195
1196 Function to dump a raster onto a single channel image in color (internal)
1197
1198    im      - image to dump raster on
1199    bit     - bitmap that contains the text to be dumped to im
1200    xb, yb  - coordinates, left edge and baseline
1201    channel - channel to copy to
1202    smooth  - boolean (True: antialias on, False: antialias is off)
1203
1204 =cut
1205 */
1206
1207 static
1208 void
1209 i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map*  bit, int xb, int yb, int channel, int smooth ) {
1210   char *bmap;
1211   i_color val;
1212   int c,x,y;
1213
1214   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));
1215   
1216   bmap = (char *)bit->bitmap;
1217   
1218   if ( smooth ) {
1219     for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1220       c=(255*bmap[y*(bit->cols)+x])/4;
1221       i_gpix(im,x+xb,y+yb,&val);
1222       val.channel[channel]=c;
1223       i_ppix(im,x+xb,y+yb,&val);
1224     }
1225   } else {
1226     for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1227       c=( bmap[y*bit->cols+x/8] & (128>>(x%8)) ) ? 255 : 0;
1228       i_gpix(im,x+xb,y+yb,&val);
1229       val.channel[channel]=c;
1230       i_ppix(im,x+xb,y+yb,&val);
1231     }
1232   }
1233 }
1234
1235
1236 /* 
1237 =item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth) 
1238
1239 interface for generating single channel raster of text (internal)
1240
1241    handle - pointer to font handle
1242    bit    - the bitmap that is allocated, rendered into and NOT freed
1243    cords  - the bounding box (modified in place)
1244    points - font size to use
1245    txt    - string to render
1246    len    - length of the string to render
1247    smooth - boolean (True: antialias on, False: antialias is off)
1248
1249 =cut
1250 */
1251
1252 static
1253 int
1254 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char const* txt, int len, int smooth, int utf8 ) {
1255   int inst;
1256   int width, height;
1257   TT_Raster_Map small_bit;
1258   
1259   /* find or install an instance */
1260   if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) { 
1261     mm_log((1,"i_tt_rasterize: get instance failed\n"));
1262     return 0;
1263   }
1264   
1265   /* calculate bounding box */
1266   if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 ))
1267     return 0;
1268     
1269   
1270   width  = cords[2]-cords[0];
1271   height = cords[5]-cords[4];
1272   
1273   mm_log((1,"i_tt_rasterize: width=%d, height=%d\n",width, height )); 
1274   
1275   i_tt_init_raster_map ( bit, width, height, smooth );
1276   i_tt_clear_raster_map( bit );
1277   if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth );
1278   
1279   if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len, 
1280                                smooth, utf8 )) {
1281     if ( smooth ) 
1282       i_tt_done_raster_map( &small_bit );
1283     return 0;
1284   }
1285
1286   /*  ascent = ( handle->properties.horizontal->Ascender  * handle->instanceh[inst].imetrics.y_ppem ) / handle->properties.header->Units_Per_EM; */
1287   
1288   if ( smooth ) i_tt_done_raster_map( &small_bit );
1289   return 1;
1290 }
1291
1292
1293
1294 /* 
1295  * Exported text rendering interfaces
1296  */
1297
1298
1299 /*
1300 =item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth, utf8)
1301
1302 Interface to text rendering into a single channel in an image
1303
1304    handle  - pointer to font handle
1305    im      - image to render text on to
1306    xb, yb  - coordinates, left edge and baseline
1307    channel - channel to render into
1308    points  - font size to use
1309    txt     - string to render
1310    len     - length of the string to render
1311    smooth  - boolean (True: antialias on, False: antialias is off)
1312
1313 =cut
1314 */
1315
1316 undef_int
1317 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 ) {
1318
1319   int cords[6];
1320   int ascent, st_offset;
1321   TT_Raster_Map bit;
1322   
1323   i_clear_error();
1324   if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1325   
1326   ascent=cords[5];
1327   st_offset=cords[0];
1328
1329   i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , yb-ascent, channel, smooth );
1330   i_tt_done_raster_map( &bit );
1331
1332   return 1;
1333 }
1334
1335
1336 /* 
1337 =item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8) 
1338
1339 Interface to text rendering in a single color onto an image
1340
1341    handle  - pointer to font handle
1342    im      - image to render text on to
1343    xb, yb  - coordinates, left edge and baseline
1344    cl      - color to use for text
1345    points  - font size to use
1346    txt     - string to render
1347    len     - length of the string to render
1348    smooth  - boolean (True: antialias on, False: antialias is off)
1349
1350 =cut
1351 */
1352
1353 undef_int
1354 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) {
1355   int cords[6];
1356   int ascent, st_offset;
1357   TT_Raster_Map bit;
1358
1359   i_clear_error();
1360   
1361   if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1362   
1363   ascent=cords[5];
1364   st_offset=cords[0];
1365
1366   i_tt_dump_raster_map2( im, &bit, xb+st_offset, yb-ascent, cl, smooth ); 
1367   i_tt_done_raster_map( &bit );
1368
1369   return 1;
1370 }
1371
1372
1373 /*
1374 =item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8) 
1375
1376 Function to get texts bounding boxes given the instance of the font (internal)
1377
1378    handle - pointer to font handle
1379    inst   -  font instance
1380    txt    -  string to measure
1381    len    -  length of the string to render
1382    cords  - the bounding box (modified in place)
1383
1384 =cut
1385 */
1386
1387 static
1388 undef_int
1389 i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6], int utf8 ) {
1390   int i, upm, casc, cdesc, first;
1391   
1392   int start    = 0;
1393   int width    = 0;
1394   int gdescent = 0;
1395   int gascent  = 0;
1396   int descent  = 0;
1397   int ascent   = 0;
1398   
1399
1400   unsigned long j;
1401   unsigned char *ustr;
1402   ustr=(unsigned char*)txt;
1403
1404   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));
1405
1406   upm     = handle->properties.header->Units_Per_EM;
1407   gascent  = ( handle->properties.horizontal->Ascender  * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm;
1408   gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem - upm + 1) / upm;
1409   
1410   width   = 0;
1411   start   = 0;
1412   
1413   mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent));
1414
1415   first=1;
1416   while (len) {
1417     if (utf8) {
1418       j = i_utf8_advance(&txt, &len);
1419       if (j == ~0UL) {
1420         i_push_error(0, "invalid UTF8 character");
1421         return 0;
1422       }
1423     }
1424     else {
1425       j = (unsigned char)*txt++;
1426       --len;
1427     }
1428     if ( i_tt_get_glyph(handle,inst,j) ) {
1429       TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j);
1430       width += gm->advance   / 64;
1431       casc   = (gm->bbox.yMax+63) / 64;
1432       cdesc  = (gm->bbox.yMin-63) / 64;
1433
1434       mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n", 
1435               ((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc));
1436
1437       if (first) {
1438         start    = gm->bbox.xMin / 64;
1439         ascent   = (gm->bbox.yMax+63) / 64;
1440         descent  = (gm->bbox.yMin-63) / 64;
1441         first = 0;
1442       }
1443       if (i == len-1) {
1444         /* the right-side bearing - in case the right-side of a 
1445            character goes past the right of the advance width,
1446            as is common for italic fonts
1447         */
1448         int rightb = gm->advance - gm->bearingX 
1449           - (gm->bbox.xMax - gm->bbox.xMin);
1450         /* fprintf(stderr, "font info last: %d %d %d %d\n", 
1451            gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */
1452         if (rightb < 0)
1453           width -= rightb/64;
1454       }
1455
1456       ascent  = (ascent  >  casc ?  ascent : casc );
1457       descent = (descent < cdesc ? descent : cdesc);
1458     }
1459   }
1460   
1461   cords[0]=start;
1462   cords[1]=gdescent;
1463   cords[2]=width;
1464   cords[3]=gascent;
1465   cords[4]=descent;
1466   cords[5]=ascent;
1467   return 1;
1468 }
1469
1470
1471 /*
1472 =item i_tt_bbox(handle, points, txt, len, cords, utf8)
1473
1474 Interface to get a strings bounding box
1475
1476    handle - pointer to font handle
1477    points - font size to use
1478    txt    - string to render
1479    len    - length of the string to render
1480    cords  - the bounding box (modified in place)
1481
1482 =cut
1483 */
1484
1485 undef_int
1486 i_tt_bbox( TT_Fonthandle *handle, float points,char *txt,int len,int cords[6], int utf8) {
1487   int inst;
1488
1489   i_clear_error();
1490   mm_log((1,"i_tt_box(handle 0x%X,points %f,txt '%.*s', len %d, utf8 %d)\n",handle,points,len,txt,len, utf8));
1491
1492   if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
1493     i_push_errorf(0, "i_tt_get_instance(%g)", points);
1494     mm_log((1,"i_tt_text: get instance failed\n"));
1495     return 0;
1496   }
1497
1498   return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8);
1499 }
1500
1501
1502
1503 #endif /* HAVE_LIBTT */
1504
1505
1506 /*
1507 =back
1508
1509 =head1 AUTHOR
1510
1511 Arnar M. Hrafnkelsson <addi@umich.edu>
1512
1513 =head1 SEE ALSO
1514
1515 Imager(3)
1516
1517 =cut
1518 */