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