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