]> git.imager.perl.org - imager.git/blob - font.c
since we use fork(), autoflush will help it work...
[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
12
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]);
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 4
47
48 =cut
49
50 */
51
52
53
54
55
56
57
58
59
60 /* 
61 =item i_init_fonts()
62
63 Initialize font rendering libraries if they are avaliable.
64
65 =cut
66 */
67
68 undef_int 
69 i_init_fonts() {
70   mm_log((1,"Initializing fonts\n"));
71
72 #ifdef HAVE_LIBT1
73   init_t1();
74 #endif
75   
76 #ifdef HAVE_LIBTT
77   init_tt();
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
89
90 /* 
91 =item i_init_t1()
92
93 Initializes the t1lib font rendering engine.
94
95 =cut
96 */
97
98 undef_int
99 init_t1() {
100   mm_log((1,"init_t1()\n"));
101   if ((T1_InitLib(LOGFILE|IGNORE_CONFIGFILE|IGNORE_FONTDATABASE) == NULL)){
102     mm_log((1,"Initialization of t1lib failed\n"));
103     return(1);
104   }
105   T1_SetLogLevel(T1LOG_DEBUG);
106   i_t1_set_aa(1); /* Default Antialias value */
107   return(0);
108 }
109
110
111 /* 
112 =item i_close_t1()
113
114 Shuts the t1lib font rendering engine down.
115
116   This it seems that this function is never used.
117
118 =cut
119 */
120
121 void
122 i_close_t1() {
123   T1_CloseLib();
124 }
125
126
127 /*
128 =item i_t1_new(pfb, afm)
129
130 Loads the fonts with the given filenames, returns its font id
131
132  pfb -  path to pfb file for font
133  afm -  path to afm file for font
134
135 =cut
136 */
137
138 int
139 i_t1_new(char *pfb,char *afm) {
140   int font_id;
141   mm_log((1,"i_t1_new(pfb %s,afm %s)\n",pfb,(afm?afm:"NULL")));
142   font_id = T1_AddFont(pfb);
143   if (font_id<0) {
144     mm_log((1,"i_t1_new: Failed to load pfb file '%s' - return code %d.\n",pfb,font_id));
145     return font_id;
146   }
147   
148   if (afm != NULL) {
149     mm_log((1,"i_t1_new: requesting afm file '%s'.\n",afm));
150     if (T1_SetAfmFileName(font_id,afm)<0) mm_log((1,"i_t1_new: afm loading of '%s' failed.\n",afm));
151   }
152   return font_id;
153 }
154
155 /*
156 =item i_t1_destroy(font_id)
157
158 Frees resources for a t1 font with given font id.
159
160    font_id - number of the font to free
161
162 =cut
163 */
164
165 int
166 i_t1_destroy(int font_id) {
167   mm_log((1,"i_t1_destroy(font_id %d)\n",font_id));
168   return T1_DeleteFont(font_id);
169 }
170
171
172 /*
173 =item i_t1_set_aa(st)
174
175 Sets the antialiasing level of the t1 library.
176
177    st - 0 =  NONE, 1 = LOW, 2 =  HIGH.
178
179 =cut
180 */
181
182 void
183 i_t1_set_aa(int st) {
184   int i;
185   unsigned long cst[17];
186   switch(st) {
187   case 0:
188     T1_AASetBitsPerPixel( 8 );
189     T1_AASetLevel( T1_AA_NONE );
190     T1_AANSetGrayValues( 0, 255 );
191     mm_log((1,"setting T1 antialias to none\n"));
192     break;
193   case 1:
194     T1_AASetBitsPerPixel( 8 );
195     T1_AASetLevel( T1_AA_LOW );
196     T1_AASetGrayValues( 0,65,127,191,255 );
197     mm_log((1,"setting T1 antialias to low\n"));
198     break;
199   case 2:
200     T1_AASetBitsPerPixel(8);
201     T1_AASetLevel(T1_AA_HIGH);
202     for(i=0;i<17;i++) cst[i]=(i*255)/16;
203     T1_AAHSetGrayValues( cst );
204     mm_log((1,"setting T1 antialias to high\n"));
205   }
206 }
207
208
209 /* 
210 =item i_t1_cp(im, xb, yb, channel, fontnum, points, str, len, align)
211
212 Interface to text rendering into a single channel in an image
213
214    im        pointer to image structure
215    xb        x coordinate of start of string
216    yb        y coordinate of start of string ( see align )
217    channel - destination channel
218    fontnum - t1 library font id
219    points  - number of points in fontheight
220    str     - string to render
221    len     - string length
222    align   - (0 - top of font glyph | 1 - baseline )
223
224 =cut
225 */
226
227 undef_int
228 i_t1_cp(i_img *im,int xb,int yb,int channel,int fontnum,float points,char* str,int len,int align) {
229   GLYPH *glyph;
230   int xsize,ysize,x,y;
231   i_color val;
232
233   unsigned int ch_mask_store;
234   
235   if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
236
237   glyph=T1_AASetString( fontnum, str, len, 0, T1_KERNING, points, NULL);
238
239   mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
240   mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
241   mm_log((1," advanceX: %d  advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
242   mm_log((1,"bpp: %d\n",glyph->bpp));
243   
244   xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
245   ysize=glyph->metrics.ascent-glyph->metrics.descent;
246   
247   mm_log((1,"width: %d height: %d\n",xsize,ysize));
248
249   ch_mask_store=im->ch_mask;
250   im->ch_mask=1<<channel;
251
252   if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
253   
254   for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
255     val.channel[channel]=glyph->bits[y*xsize+x];
256     i_ppix(im,x+xb,y+yb,&val);
257   }
258   
259   im->ch_mask=ch_mask_store;
260   return 1;
261 }
262
263
264 /*
265 =item i_t1_bbox(handle, fontnum, points, str, len, cords)
266
267 function to get a strings bounding box given the font id and sizes
268
269    handle  - pointer to font handle   
270    fontnum - t1 library font id
271    points  - number of points in fontheight
272    str     - string to measure
273    len     - string length
274    cords   - the bounding box (modified in place)
275
276 =cut
277 */
278
279 void
280 i_t1_bbox(int fontnum,float points,char *str,int len,int cords[6]) {
281   BBox bbox;
282   BBox gbbox;
283   
284   mm_log((1,"i_t1_bbox(fontnum %d,points %.2f,str '%.*s', len %d)\n",fontnum,points,len,str,len));
285   T1_LoadFont(fontnum);  /* FIXME: Here a return code is ignored - haw haw haw */ 
286   bbox = T1_GetStringBBox(fontnum,str,len,0,T1_KERNING);
287   gbbox = T1_GetFontBBox(fontnum);
288   
289   mm_log((1,"bbox: (%d,%d,%d,%d)\n",
290           (int)(bbox.llx*points/1000),
291           (int)(gbbox.lly*points/1000),
292           (int)(bbox.urx*points/1000),
293           (int)(gbbox.ury*points/1000),
294           (int)(bbox.lly*points/1000),
295           (int)(bbox.ury*points/1000) ));
296
297
298   cords[0]=((float)bbox.llx*points)/1000;
299   cords[2]=((float)bbox.urx*points)/1000;
300
301   cords[1]=((float)gbbox.lly*points)/1000;
302   cords[3]=((float)gbbox.ury*points)/1000;
303
304   cords[4]=((float)bbox.lly*points)/1000;
305   cords[5]=((float)bbox.ury*points)/1000;
306 }
307
308
309 /*
310 =item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align)
311
312 Interface to text rendering in a single color onto an image
313
314    im      - pointer to image structure
315    xb      - x coordinate of start of string
316    yb      - y coordinate of start of string ( see align )
317    cl      - color to draw the text in
318    fontnum - t1 library font id
319    points  - number of points in fontheight
320    str     - char pointer to string to render
321    len     - string length
322    align   - (0 - top of font glyph | 1 - baseline )
323
324 =cut
325 */
326
327 undef_int
328 i_t1_text(i_img *im,int xb,int yb,i_color *cl,int fontnum,float points,char* str,int len,int align) {
329   GLYPH *glyph;
330   int xsize,ysize,x,y,ch;
331   i_color val;
332   unsigned char c,i;
333
334   if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
335
336   glyph=T1_AASetString( fontnum, str, len, 0, T1_KERNING, points, NULL);
337
338   mm_log((1,"metrics:  ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
339   mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
340   mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
341   mm_log((1,"bpp: %d\n",glyph->bpp));
342   
343   xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
344   ysize=glyph->metrics.ascent-glyph->metrics.descent;
345   
346   mm_log((1,"width: %d height: %d\n",xsize,ysize));
347
348   if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
349   
350   for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
351     c=glyph->bits[y*xsize+x];
352     i=255-c;
353     i_gpix(im,x+xb,y+yb,&val);
354     for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
355     i_ppix(im,x+xb,y+yb,&val);
356   }
357   return 1;
358 }
359
360
361 #endif /* HAVE_LIBT1 */
362
363
364
365
366
367
368
369
370
371
372 /* Truetype font support */
373
374 #ifdef HAVE_LIBTT
375
376
377 /* Defines */
378
379 #define USTRCT(x) ((x).z)
380 #define TT_VALID( handle )  ( ( handle ).z != NULL )
381
382
383 /* Prototypes */
384
385 static  int i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth );
386 static void i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth );
387 static void i_tt_done_raster_map( TT_Raster_Map *bit );
388 static void i_tt_clear_raster_map( TT_Raster_Map* bit );
389 static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off );
390 static  int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned char j );
391 static void 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 );
392 static void i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit, TT_Raster_Map *small_bit, int cords[6], char* txt, int len, int smooth );
393 static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, i_color *cl, int smooth );
394 static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth );
395 static  int i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char* txt, int len, int smooth );
396 static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6] );
397
398
399 /* static globals needed */
400
401 static TT_Engine    engine;
402 static int  LTT_dpi    = 72; /* FIXME: this ought to be a part of the call interface */
403 static int  LTT_hinted = 1;  /* FIXME: this too */
404
405
406 /*
407  * FreeType interface
408  */
409
410
411 /*
412 =item init_tt()
413
414 Initializes the freetype font rendering engine
415
416 =cut
417 */
418
419 undef_int
420 init_tt() {
421   TT_Error  error;
422   mm_log((1,"init_tt()\n"));
423   error = TT_Init_FreeType( &engine );
424   if ( error ){
425     mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error));
426     return(1);
427   }
428   return(0);
429 }
430
431
432 /* 
433 =item i_tt_get_instance(handle, points, smooth)
434
435 Finds a points+smooth instance or if one doesn't exist in the cache
436 allocates room and returns its cache entry
437
438    fontname - path to the font to load
439    handle   - handle to the font.
440    points   - points of the requested font
441    smooth   - boolean (True: antialias on, False: antialias is off)
442
443 =cut
444 */
445
446 static
447 int
448 i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) {
449   int i,idx;
450   TT_Error error;
451   
452   mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n",handle,points,smooth));
453   
454   if (smooth == -1) { /* Smooth doesn't matter for this search */
455     for(i=0;i<TT_CHC;i++) if (handle->instanceh[i].ptsize==points) {
456       mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
457       return i;
458     }
459     smooth=1; /* We will be adding a font - add it as smooth then */
460   } else { /* Smooth doesn't matter for this search */
461     for(i=0;i<TT_CHC;i++) if (handle->instanceh[i].ptsize==points && handle->instanceh[i].smooth==smooth) {
462       mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
463       return i;
464     }
465   }
466   
467   /* Found the instance in the cache - return the cache index */
468   
469   for(idx=0;idx<TT_CHC;idx++) if (!(handle->instanceh[idx].order)) break; /* find the lru item */
470
471   mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
472   mm_log((1,"i_tt_get_instance: lru pointer 0x%X\n",USTRCT(handle->instanceh[idx].instance) ));
473   
474   if ( USTRCT(handle->instanceh[idx].instance) ) {
475     mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
476     TT_Done_Instance( handle->instanceh[idx].instance ); /* Free instance if needed */
477   }
478   
479   /* create and initialize instance */
480   /* FIXME: probably a memory leak on fail */
481   
482   (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) || 
483           ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
484           ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
485   
486   if ( error ) {
487     mm_log((1, "Could not create and initialize instance: error 0x%x.\n",error ));
488     return -1;
489   }
490   
491   /* Now that the instance should the inplace we need to lower all of the
492      ru counts and put `this' one with the highest entry */
493   
494   for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
495
496   handle->instanceh[idx].order=TT_CHC-1;
497   handle->instanceh[idx].ptsize=points;
498   handle->instanceh[idx].smooth=smooth;
499   TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
500
501   /* Zero the memory for the glyph storage so they are not thought as cached if they haven't been cached
502      since this new font was loaded */
503
504   for(i=0;i<256;i++) USTRCT(handle->instanceh[idx].glyphs[i])=NULL;
505   
506   return idx;
507 }
508
509
510 /*
511 =item i_tt_new(fontname)
512
513 Creates a new font handle object, finds a character map and initialise the
514 the font handle's cache
515
516    fontname - path to the font to load
517
518 =cut
519 */
520
521 TT_Fonthandle*
522 i_tt_new(char *fontname) {
523   TT_Error error;
524   TT_Fonthandle *handle;
525   unsigned short i,n;
526   unsigned short platform,encoding;
527   
528   mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
529   
530   /* allocate memory for the structure */
531   
532   handle=mymalloc( sizeof(TT_Fonthandle) );
533
534   /* load the typeface */
535   error = TT_Open_Face( engine, fontname, &handle->face );
536   if ( error ) {
537     if ( error == TT_Err_Could_Not_Open_File ) mm_log((1, "Could not find/open %s.\n", fontname ))
538     else mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname, error ));
539     return NULL;
540   }
541   
542   TT_Get_Face_Properties( handle->face, &(handle->properties) );
543   /* First, look for a Unicode charmap */
544   
545   n = handle->properties.num_CharMaps;
546   USTRCT( handle->char_map )=NULL; /* Invalidate character map */
547   
548   for ( i = 0; i < n; i++ ) {
549     TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
550     if ( (platform == 3 && encoding == 1 ) || (platform == 0 && encoding == 0 ) ) {
551       TT_Get_CharMap( handle->face, i, &(handle->char_map) );
552       break;
553     }
554   }
555
556   /* Zero the pointsizes - and ordering */
557   
558   for(i=0;i<TT_CHC;i++) {
559     USTRCT(handle->instanceh[i].instance)=NULL;
560     handle->instanceh[i].order=i;
561     handle->instanceh[i].ptsize=0;
562     handle->instanceh[i].smooth=-1;
563   }
564
565   mm_log((1,"i_tt_new <- 0x%X\n",handle));
566   return handle;
567 }
568
569
570
571 /*
572  * raster map management
573  */
574
575 /* 
576 =item i_tt_init_raster_map(bit, width, height, smooth)
577
578 Allocates internal memory for the bitmap as needed by the parameters (internal)
579                  
580    bit    - bitmap to allocate into
581    width  - width of the bitmap
582    height - height of the bitmap
583    smooth - boolean (True: antialias on, False: antialias is off)
584
585 =cut
586 */
587
588 static
589 void
590 i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth ) {
591
592   mm_log((1,"i_tt_init_raster_map( bit 08x%08X, width %d, height %d, smooth %d)\n", bit, width, height, smooth));
593   
594   bit->rows  = height;
595   bit->width = ( width + 3 ) & -4;
596   bit->flow  = TT_Flow_Down;
597   
598   if ( smooth ) {
599     bit->cols  = bit->width;
600     bit->size  = bit->rows * bit->width;
601   } else {
602     bit->cols  = ( bit->width + 7 ) / 8;    /* convert to # of bytes     */
603     bit->size  = bit->rows * bit->cols;     /* number of bytes in buffer */
604   }
605   
606   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 ));
607
608   bit->bitmap = (void *) mymalloc( bit->size );
609   if ( !bit->bitmap ) m_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",bit->size );
610 }
611
612
613 /*
614 =item i_tt_clear_raster_map(bit)
615
616 Frees the bitmap data and sets pointer to NULL (internal)
617                  
618    bit - bitmap to free
619
620 =cut
621 */
622
623 static
624 void
625 i_tt_done_raster_map( TT_Raster_Map *bit ) {
626   myfree( bit->bitmap );
627   bit->bitmap = NULL;
628 }
629
630
631 /*
632 =item i_tt_clear_raster_map(bit)
633
634 Clears the specified bitmap (internal)
635                  
636    bit - bitmap to zero
637
638 =cut
639 */
640
641
642 static
643 void
644 i_tt_clear_raster_map( TT_Raster_Map*  bit ) {
645   memset( bit->bitmap, 0, bit->size );
646 }
647
648
649 /* 
650 =item i_tt_blit_or(dst, src, x_off, y_off)
651
652 function that blits one raster map into another (internal)
653                  
654    dst   - destination bitmap
655    src   - source bitmap
656    x_off - x offset into the destination bitmap
657    y_off - y offset into the destination bitmap
658
659 =cut
660 */
661
662 static
663 void
664 i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off ) {
665   int  x,  y;
666   int  x1, x2, y1, y2;
667   char *s, *d;
668   
669   x1 = x_off < 0 ? -x_off : 0;
670   y1 = y_off < 0 ? -y_off : 0;
671   
672   x2 = (int)dst->cols - x_off;
673   if ( x2 > src->cols ) x2 = src->cols;
674   
675   y2 = (int)dst->rows - y_off;
676   if ( y2 > src->rows ) y2 = src->rows;
677
678   if ( x1 >= x2 ) return;
679
680   /* do the real work now */
681
682   for ( y = y1; y < y2; ++y ) {
683     s = ( (char*)src->bitmap ) + y * src->cols + x1;
684     d = ( (char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
685     
686     for ( x = x1; x < x2; ++x ) *d++ |= *s++;
687   }
688 }
689
690
691 /* 
692 =item i_tt_get_glyph(handle, inst, j) 
693
694 Function to see if a glyph exists and if so cache it (internal)
695                  
696    handle - pointer to font handle
697    inst   - font instance
698    j      - charcode of glyph
699
700 =cut
701 */
702
703 static
704 int
705 i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned char j) { /* FIXME: Check if unsigned char is enough */
706   unsigned short load_flags, code;
707   TT_Error error;
708
709   mm_log((1, "i_tt_get_glyph(handle 0x%X, inst %d, j %d (%c))\n",handle,inst,j,j));
710   mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] ));
711
712   if ( TT_VALID(handle->instanceh[inst].glyphs[j]) ) {
713     mm_log((1,"i_tt_get_glyph: %d in cache\n",j));
714     return 1;
715   }
716   
717   /* Ok - it wasn't cached - try to get it in */
718   load_flags = TTLOAD_SCALE_GLYPH;
719   if ( LTT_hinted ) load_flags |= TTLOAD_HINT_GLYPH;
720   
721   if ( !TT_VALID(handle->char_map) ) {
722     code = (j - ' ' + 1) < 0 ? 0 : (j - ' ' + 1);
723     if ( code >= handle->properties.num_Glyphs ) code = 0;
724   } else code = TT_Char_Index( handle->char_map, j );
725   
726   if ( (error = TT_New_Glyph( handle->face, &handle->instanceh[inst].glyphs[j])) ) 
727     mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
728   if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[j], code, load_flags)) )
729     mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
730   
731   /* At this point the glyph should be allocated and loaded */
732   /* Next get the glyph metrics */
733   
734   error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[j], &handle->instanceh[inst].gmetrics[j] );
735   mm_log((1, "TT_Get_Glyph_Metrics: error 0x%x.\n", error ));
736   return 1;
737 }
738
739
740 /* 
741 =item i_tt_destroy(handle)
742
743 Clears the data taken by a font including all cached data such as
744 pixmaps and glyphs
745                  
746    handle - pointer to font handle
747
748 =cut
749 */
750
751 void
752 i_tt_destroy( TT_Fonthandle *handle) {
753   TT_Close_Face( handle->face );
754   
755   /* FIXME: Should these be freed automatically by the library? 
756
757   TT_Done_Instance( instance );
758   void
759     i_tt_done_glyphs( void ) {
760     int  i;
761
762     if ( !glyphs ) return;
763     
764     for ( i = 0; i < 256; ++i ) TT_Done_Glyph( glyphs[i] );
765     free( glyphs );
766     
767     glyphs = NULL;
768   }
769   */
770 }
771
772
773 /*
774  * FreeType Rendering functions
775  */
776
777
778 /* 
779 =item i_tt_render_glyph(handle, gmetrics, bit, smallbit, x_off, y_off, smooth)
780
781 Renders a single glyph into the bit rastermap (internal)
782
783    handle   - pointer to font handle
784    gmetrics - the metrics for the glyph to be rendered
785    bit      - large bitmap that is the destination for the text
786    smallbit - small bitmap that is used only if smooth is true
787    x_off    - x offset of glyph
788    y_off    - y offset of glyph
789    smooth   - boolean (True: antialias on, False: antialias is off)
790
791 =cut
792 */
793
794 static
795 void
796 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 ) {
797   
798   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",
799           USTRCT(glyph), gmetrics, bit, small_bit, x_off,y_off,smooth));
800   
801   if ( !smooth ) TT_Get_Glyph_Bitmap( glyph, bit, x_off * 64, y_off * 64);
802   else {
803     TT_F26Dot6 xmin, ymin, xmax, ymax;
804     
805     xmin =  gmetrics->bbox.xMin & -64;
806     ymin =  gmetrics->bbox.yMin & -64;
807     xmax = (gmetrics->bbox.xMax + 63) & -64;
808     ymax = (gmetrics->bbox.yMax + 63) & -64;
809     
810     i_tt_clear_raster_map( small_bit );
811     TT_Get_Glyph_Pixmap( glyph, small_bit, -xmin, -ymin );
812     i_tt_blit_or( bit, small_bit, xmin/64 + x_off, -ymin/64 - y_off );
813   }
814 }
815
816
817 /*
818 =item i_tt_render_all_glyphs(handle, inst, bit, small_bit, cords, txt, len, smooth)
819
820 calls i_tt_render_glyph to render each glyph into the bit rastermap (internal)
821
822    handle   - pointer to font handle
823    inst     - font instance
824    bit      - large bitmap that is the destination for the text
825    smallbit - small bitmap that is used only if smooth is true
826    txt      - string to render
827    len      - length of the string to render
828    smooth   - boolean (True: antialias on, False: antialias is off)
829
830 =cut
831 */
832
833 static
834 void
835 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit, TT_Raster_Map *small_bit, int cords[6], char* txt, int len, int smooth ) {
836   unsigned char j;
837   int i;
838   TT_F26Dot6 x,y;
839   
840   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)\n",
841           handle, inst, bit, small_bit, len, txt, len, smooth));
842   
843   /* 
844      y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM);
845   */
846
847   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 */
848   y=-cords[1];
849   
850   for ( i = 0; i < len; i++ ) {
851     j = txt[i];
852     if ( !i_tt_get_glyph(handle,inst,j) ) continue;
853     i_tt_render_glyph( handle->instanceh[inst].glyphs[j], &handle->instanceh[inst].gmetrics[j], bit, small_bit, x, y, smooth );
854     x += handle->instanceh[inst].gmetrics[j].advance / 64;
855   }
856 }
857
858
859 /*
860  * Functions to render rasters (single channel images) onto images
861  */
862
863 /* 
864 =item i_tt_dump_raster_map2(im, bit, xb, yb, cl, smooth)
865
866 Function to dump a raster onto an image in color used by i_tt_text() (internal).
867
868    im     - image to dump raster on
869    bit    - bitmap that contains the text to be dumped to im
870    xb, yb - coordinates, left edge and baseline
871    cl     - color to use for text
872    smooth - boolean (True: antialias on, False: antialias is off)
873
874 =cut
875 */
876
877 static
878 void
879 i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, i_color *cl, int smooth ) {
880   char *bmap;
881   i_color val;
882   int c, i, ch, x, y;
883   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));
884   
885   bmap = (char *)bit->bitmap;
886
887   if ( smooth ) {
888
889     for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
890       c=(255*bmap[y*(bit->cols)+x])/4;
891       i=255-c;
892       i_gpix(im,x+xb,y+yb,&val);
893       for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
894       i_ppix(im,x+xb,y+yb,&val);
895     }
896
897   } else {
898
899     for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
900       c=( bmap[y*bit->cols+x/8] & (128>>(x%8)) ) ? 255 : 0;
901       i=255-c;
902       i_gpix(im,x+xb,y+yb,&val);
903       for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
904       i_ppix(im,x+xb,y+yb,&val);
905     }
906
907   }
908 }
909
910
911 /*
912 =item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth)
913
914 Function to dump a raster onto a single channel image in color (internal)
915
916    im      - image to dump raster on
917    bit     - bitmap that contains the text to be dumped to im
918    xb, yb  - coordinates, left edge and baseline
919    channel - channel to copy to
920    smooth  - boolean (True: antialias on, False: antialias is off)
921
922 =cut
923 */
924
925 static
926 void
927 i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map*  bit, int xb, int yb, int channel, int smooth ) {
928   char *bmap;
929   i_color val;
930   int c,x,y;
931
932   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));
933   
934   bmap = (char *)bit->bitmap;
935   
936   if ( smooth ) {
937     for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
938       c=(255*bmap[y*(bit->cols)+x])/4;
939       i_gpix(im,x+xb,y+yb,&val);
940       val.channel[channel]=c;
941       i_ppix(im,x+xb,y+yb,&val);
942     }
943   } else {
944     for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
945       c=( bmap[y*bit->cols+x/8] & (128>>(x%8)) ) ? 255 : 0;
946       i_gpix(im,x+xb,y+yb,&val);
947       val.channel[channel]=c;
948       i_ppix(im,x+xb,y+yb,&val);
949     }
950   }
951 }
952
953
954 /* 
955 =item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth) 
956
957 interface for generating single channel raster of text (internal)
958
959    handle - pointer to font handle
960    bit    - the bitmap that is allocated, rendered into and NOT freed
961    cords  - the bounding box (modified in place)
962    points - font size to use
963    txt    - string to render
964    len    - length of the string to render
965    smooth - boolean (True: antialias on, False: antialias is off)
966
967 =cut
968 */
969
970 static
971 int
972 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char* txt, int len, int smooth ) {
973   int inst;
974   int width, height;
975   TT_Raster_Map small_bit;
976   
977   /* find or install an instance */
978   if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) { 
979     mm_log((1,"i_tt_rasterize: get instance failed\n"));
980     return 0;
981   }
982   
983   /* calculate bounding box */
984   i_tt_bbox_inst( handle, inst, txt, len, cords );
985   
986   width  = cords[2]-cords[0];
987   height = cords[3]-cords[1];
988   
989   mm_log((1,"i_tt_rasterize: width=%d, height=%d\n",width, height )); 
990   
991   i_tt_init_raster_map ( bit, width, height, smooth );
992   i_tt_clear_raster_map( bit );
993   if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth );
994   
995   i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len, smooth );
996
997   /*  ascent = ( handle->properties.horizontal->Ascender  * handle->instanceh[inst].imetrics.y_ppem ) / handle->properties.header->Units_Per_EM; */
998   
999   if ( smooth ) i_tt_done_raster_map( &small_bit );
1000   return 1;
1001 }
1002
1003
1004
1005 /* 
1006  * Exported text rendering interfaces
1007  */
1008
1009
1010 /*
1011 =item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth)
1012
1013 Interface to text rendering into a single channel in an image
1014
1015    handle  - pointer to font handle
1016    im      - image to render text on to
1017    xb, yb  - coordinates, left edge and baseline
1018    channel - channel to render into
1019    points  - font size to use
1020    txt     - string to render
1021    len     - length of the string to render
1022    smooth  - boolean (True: antialias on, False: antialias is off)
1023
1024 =cut
1025 */
1026
1027 undef_int
1028 i_tt_cp( TT_Fonthandle *handle, i_img *im, int xb, int yb, int channel, float points, char* txt, int len, int smooth ) {
1029
1030   int cords[6];
1031   int ascent, st_offset;
1032   TT_Raster_Map bit;
1033   
1034   if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth ) ) return 0;
1035   
1036   ascent=cords[3];
1037   st_offset=cords[0];
1038
1039   i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , yb-ascent, channel, smooth );
1040   i_tt_done_raster_map( &bit );
1041
1042   return 1;
1043 }
1044
1045
1046 /* 
1047 =item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth) 
1048
1049 Interface to text rendering in a single color onto an image
1050
1051    handle  - pointer to font handle
1052    im      - image to render text on to
1053    xb, yb  - coordinates, left edge and baseline
1054    cl      - color to use for text
1055    points  - font size to use
1056    txt     - string to render
1057    len     - length of the string to render
1058    smooth  - boolean (True: antialias on, False: antialias is off)
1059
1060 =cut
1061 */
1062
1063 undef_int
1064 i_tt_text( TT_Fonthandle *handle, i_img *im, int xb, int yb, i_color *cl, float points, char* txt, int len, int smooth) {
1065   int cords[6];
1066   int ascent, st_offset;
1067   TT_Raster_Map bit;
1068   
1069   if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth ) ) return 0;
1070   
1071   ascent=cords[3];
1072   st_offset=cords[0];
1073
1074   i_tt_dump_raster_map2( im, &bit, xb-st_offset, yb-ascent, cl, smooth ); 
1075   i_tt_done_raster_map( &bit );
1076
1077   return 1;
1078 }
1079
1080
1081 /*
1082 =item i_tt_bbox_inst(handle, inst, txt, len, cords) 
1083
1084 Function to get texts bounding boxes given the instance of the font (internal)
1085
1086    handle - pointer to font handle
1087    inst   -  font instance
1088    txt    -  string to measure
1089    len    -  length of the string to render
1090    cords  - the bounding box (modified in place)
1091
1092 =cut
1093 */
1094
1095 static
1096 undef_int
1097 i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6] ) {
1098   int i, upm, ascent, descent, gascent, gdescent, width, casc, cdesc, first, start;
1099   unsigned int j;
1100   unsigned char *ustr;
1101   ustr=(unsigned char*)txt;
1102   
1103   mm_log((1,"i_tt_box_inst(handle 0x%X,inst %d,txt '%.*s', len %d)\n",handle,inst,len,txt,len));
1104
1105   upm     = handle->properties.header->Units_Per_EM;
1106   gascent  = ( handle->properties.horizontal->Ascender  * handle->instanceh[inst].imetrics.y_ppem ) / upm;
1107   gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem ) / upm;
1108   
1109   width   = 0;
1110   start   = 0;
1111   
1112   mm_log((1, "i_tt_box_inst: glyph='%c' ascent=%d descent=%d\n", j, ascent, descent));
1113
1114   first=1;
1115   for ( i = 0; i < len; ++i ) {
1116     j = ustr[i];
1117     if ( i_tt_get_glyph(handle,inst,j) ) {
1118       width += handle->instanceh[inst].gmetrics[j].advance   / 64;
1119       casc   = handle->instanceh[inst].gmetrics[j].bbox.yMax / 64;
1120       cdesc  = handle->instanceh[inst].gmetrics[j].bbox.yMin / 64;
1121
1122       mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n", j, casc, cdesc));
1123
1124       if (first) {
1125         start    = handle->instanceh[inst].gmetrics[j].bbox.xMin / 64;
1126         ascent   = handle->instanceh[inst].gmetrics[j].bbox.yMax / 64;
1127         descent  = handle->instanceh[inst].gmetrics[j].bbox.yMin / 64;
1128         first = 0;
1129       }
1130
1131       ascent  = (ascent  >  casc ?  ascent : casc );
1132       descent = (descent < cdesc ? descent : cdesc);
1133     }
1134   }
1135   
1136   cords[0]=start;
1137   cords[1]=gdescent;
1138   cords[2]=width+start;
1139   cords[3]=gascent;
1140   cords[4]=descent;
1141   cords[5]=ascent;
1142   return 1;
1143 }
1144
1145
1146 /*
1147 =item i_tt_bbox(handle, points, txt, len, cords)
1148
1149 Interface to get a strings bounding box
1150
1151    handle - pointer to font handle
1152    points - font size to use
1153    txt    - string to render
1154    len    - length of the string to render
1155    cords  - the bounding box (modified in place)
1156
1157 =cut
1158 */
1159
1160 undef_int
1161 i_tt_bbox( TT_Fonthandle *handle, float points,char *txt,int len,int cords[6]) {
1162   int inst;
1163   
1164   mm_log((1,"i_tt_box(handle 0x%X,points %f,txt '%.*s', len %d)\n",handle,points,len,txt,len));
1165
1166   if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
1167     mm_log((1,"i_tt_text: get instance failed\n"));
1168     return 0;
1169   }
1170
1171   return i_tt_bbox_inst(handle, inst, txt, len, cords);
1172 }
1173
1174
1175
1176 #endif /* HAVE_LIBTT */