13 font.c - implements font handling functions for t1 and truetype fonts
20 fontnum = i_t1_new(path_to_pfb, path_to_afm);
21 i_t1_bbox(fontnum, points, "foo", 3, int cords[6]);
22 rc = i_t1_destroy(fontnum);
26 handle = i_tt_new(path_to_ttf);
27 rc = i_tt_bbox(handle, points, "foo", 3, int cords[6], utf8);
34 font.c implements font creation, rendering, bounding box functions and
37 =head1 FUNCTION REFERENCE
39 Some of these functions are internal.
58 Initialize font rendering libraries if they are avaliable.
64 i_init_fonts(int t1log) {
65 mm_log((1,"Initializing fonts\n"));
80 return(1); /* FIXME: Always true - check the return values of the init_t1 and init_tt functions */
88 static int t1_get_flags(char const *flags);
89 static char *t1_from_utf8(char const *in, int len, int *outlen);
92 =item i_init_t1(t1log)
94 Initializes the t1lib font rendering engine.
100 i_init_t1(int t1log) {
101 int init_flags = IGNORE_CONFIGFILE|IGNORE_FONTDATABASE;
102 mm_log((1,"init_t1()\n"));
105 init_flags |= LOGFILE;
106 if ((T1_InitLib(init_flags) == NULL)){
107 mm_log((1,"Initialization of t1lib failed\n"));
110 T1_SetLogLevel(T1LOG_DEBUG);
111 i_t1_set_aa(1); /* Default Antialias value */
119 Shuts the t1lib font rendering engine down.
121 This it seems that this function is never used.
133 =item i_t1_new(pfb, afm)
135 Loads the fonts with the given filenames, returns its font id
137 pfb - path to pfb file for font
138 afm - path to afm file for font
144 i_t1_new(char *pfb,char *afm) {
147 mm_log((1,"i_t1_new(pfb %s,afm %s)\n",pfb,(afm?afm:"NULL")));
148 font_id = T1_AddFont(pfb);
150 mm_log((1,"i_t1_new: Failed to load pfb file '%s' - return code %d.\n",pfb,font_id));
155 mm_log((1,"i_t1_new: requesting afm file '%s'.\n",afm));
156 if (T1_SetAfmFileName(font_id,afm)<0) mm_log((1,"i_t1_new: afm loading of '%s' failed.\n",afm));
163 =item i_t1_destroy(font_id)
165 Frees resources for a t1 font with given font id.
167 font_id - number of the font to free
173 i_t1_destroy(int font_id) {
174 mm_log((1,"i_t1_destroy(font_id %d)\n",font_id));
175 return T1_DeleteFont(font_id);
180 =item i_t1_set_aa(st)
182 Sets the antialiasing level of the t1 library.
184 st - 0 = NONE, 1 = LOW, 2 = HIGH.
190 i_t1_set_aa(int st) {
192 unsigned long cst[17];
195 T1_AASetBitsPerPixel( 8 );
196 T1_AASetLevel( T1_AA_NONE );
197 T1_AANSetGrayValues( 0, 255 );
198 mm_log((1,"setting T1 antialias to none\n"));
201 T1_AASetBitsPerPixel( 8 );
202 T1_AASetLevel( T1_AA_LOW );
203 T1_AASetGrayValues( 0,65,127,191,255 );
204 mm_log((1,"setting T1 antialias to low\n"));
207 T1_AASetBitsPerPixel(8);
208 T1_AASetLevel(T1_AA_HIGH);
209 for(i=0;i<17;i++) cst[i]=(i*255)/16;
210 T1_AAHSetGrayValues( cst );
211 mm_log((1,"setting T1 antialias to high\n"));
217 =item i_t1_cp(im, xb, yb, channel, fontnum, points, str, len, align)
219 Interface to text rendering into a single channel in an image
221 im pointer to image structure
222 xb x coordinate of start of string
223 yb y coordinate of start of string ( see align )
224 channel - destination channel
225 fontnum - t1 library font id
226 points - number of points in fontheight
227 str - string to render
229 align - (0 - top of font glyph | 1 - baseline )
235 i_t1_cp(i_img *im,int xb,int yb,int channel,int fontnum,float points,char* str,int len,int align, int utf8, char const *flags) {
239 int mod_flags = t1_get_flags(flags);
241 unsigned int ch_mask_store;
243 if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
247 char *work = t1_from_utf8(str, len, &worklen);
248 glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
252 glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL);
257 mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
258 mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
259 mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
260 mm_log((1,"bpp: %d\n",glyph->bpp));
262 xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
263 ysize=glyph->metrics.ascent-glyph->metrics.descent;
265 mm_log((1,"width: %d height: %d\n",xsize,ysize));
267 ch_mask_store=im->ch_mask;
268 im->ch_mask=1<<channel;
270 if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
272 for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
273 val.channel[channel]=glyph->bits[y*xsize+x];
274 i_ppix(im,x+xb,y+yb,&val);
277 im->ch_mask=ch_mask_store;
283 =item i_t1_bbox(handle, fontnum, points, str, len, cords)
285 function to get a strings bounding box given the font id and sizes
287 handle - pointer to font handle
288 fontnum - t1 library font id
289 points - number of points in fontheight
290 str - string to measure
292 cords - the bounding box (modified in place)
298 i_t1_bbox(int fontnum,float points,char *str,int len,int cords[6], int utf8,char const *flags) {
301 int mod_flags = t1_get_flags(flags);
303 mm_log((1,"i_t1_bbox(fontnum %d,points %.2f,str '%.*s', len %d)\n",fontnum,points,len,str,len));
304 T1_LoadFont(fontnum); /* FIXME: Here a return code is ignored - haw haw haw */
307 char *work = t1_from_utf8(str, len, &worklen);
308 bbox = T1_GetStringBBox(fontnum,work,worklen,0,mod_flags);
312 bbox = T1_GetStringBBox(fontnum,str,len,0,mod_flags);
314 gbbox = T1_GetFontBBox(fontnum);
316 mm_log((1,"bbox: (%d,%d,%d,%d)\n",
317 (int)(bbox.llx*points/1000),
318 (int)(gbbox.lly*points/1000),
319 (int)(bbox.urx*points/1000),
320 (int)(gbbox.ury*points/1000),
321 (int)(bbox.lly*points/1000),
322 (int)(bbox.ury*points/1000) ));
325 cords[0]=((float)bbox.llx*points)/1000;
326 cords[2]=((float)bbox.urx*points)/1000;
328 cords[1]=((float)gbbox.lly*points)/1000;
329 cords[3]=((float)gbbox.ury*points)/1000;
331 cords[4]=((float)bbox.lly*points)/1000;
332 cords[5]=((float)bbox.ury*points)/1000;
337 =item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align)
339 Interface to text rendering in a single color onto an image
341 im - pointer to image structure
342 xb - x coordinate of start of string
343 yb - y coordinate of start of string ( see align )
344 cl - color to draw the text in
345 fontnum - t1 library font id
346 points - number of points in fontheight
347 str - char pointer to string to render
349 align - (0 - top of font glyph | 1 - baseline )
355 i_t1_text(i_img *im,int xb,int yb,i_color *cl,int fontnum,float points,char* str,int len,int align, int utf8, char const *flags) {
357 int xsize,ysize,x,y,ch;
360 int mod_flags = t1_get_flags(flags);
362 if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
366 char *work = t1_from_utf8(str, len, &worklen);
367 glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
371 glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL);
376 mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
377 mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
378 mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
379 mm_log((1,"bpp: %d\n",glyph->bpp));
381 xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
382 ysize=glyph->metrics.ascent-glyph->metrics.descent;
384 mm_log((1,"width: %d height: %d\n",xsize,ysize));
386 if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
388 for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
389 c=glyph->bits[y*xsize+x];
391 i_gpix(im,x+xb,y+yb,&val);
392 for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
393 i_ppix(im,x+xb,y+yb,&val);
399 =item t1_get_flags(flags)
401 Processes the characters in I<flags> to create a mod_flags value used
402 by some T1Lib functions.
407 t1_get_flags(char const *flags) {
408 int mod_flags = T1_KERNING;
412 case 'u': case 'U': mod_flags |= T1_UNDERLINE; break;
413 case 'o': case 'O': mod_flags |= T1_OVERLINE; break;
414 case 's': case 'S': mod_flags |= T1_OVERSTRIKE; break;
415 /* ignore anything we don't recognize */
423 =item t1_from_utf8(char const *in, int len, int *outlen)
425 Produces an unencoded version of I<in> by dropping any Unicode
428 Returns a newly allocated buffer which should be freed with myfree().
429 Sets *outlen to the number of bytes used in the output string.
435 t1_from_utf8(char const *in, int len, int *outlen) {
436 char *out = mymalloc(len+1);
441 c = i_utf8_advance(&in, &len);
444 i_push_error(0, "invalid UTF8 character");
447 /* yeah, just drop them */
458 #endif /* HAVE_LIBT1 */
461 /* Truetype font support */
465 #include <freetype.h>
468 /* convert a code point into an index in the glyph cache */
469 #define TT_HASH(x) ((x) & 0xFF)
471 typedef struct i_glyph_entry_ {
476 #define TT_NOCHAR (~0UL)
478 struct TT_Instancehandle_ {
479 TT_Instance instance;
480 TT_Instance_Metrics imetrics;
481 TT_Glyph_Metrics gmetrics[256];
482 i_tt_glyph_entry glyphs[256];
488 typedef struct TT_Instancehandle_ TT_Instancehandle;
490 struct TT_Fonthandle_ {
492 TT_Face_Properties properties;
493 TT_Instancehandle instanceh[TT_CHC];
499 #define USTRCT(x) ((x).z)
500 #define TT_VALID( handle ) ( ( handle ).z != NULL )
505 static int i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth );
506 static void i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth );
507 static void i_tt_done_raster_map( TT_Raster_Map *bit );
508 static void i_tt_clear_raster_map( TT_Raster_Map* bit );
509 static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off );
510 static int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j );
512 i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics,
513 TT_Raster_Map *bit, TT_Raster_Map *small_bit,
514 int x_off, int y_off, int smooth );
516 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
517 TT_Raster_Map *small_bit, int cords[6],
518 char const* txt, int len, int smooth, int utf8 );
519 static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, i_color *cl, int smooth );
520 static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth );
522 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6],
523 float points, char const* txt, int len, int smooth, int utf8 );
524 static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6], int utf8 );
527 /* static globals needed */
529 static TT_Engine engine;
530 static int LTT_dpi = 72; /* FIXME: this ought to be a part of the call interface */
531 static int LTT_hinted = 1; /* FIXME: this too */
542 Initializes the freetype font rendering engine
550 mm_log((1,"init_tt()\n"));
551 error = TT_Init_FreeType( &engine );
553 mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error));
561 =item i_tt_get_instance(handle, points, smooth)
563 Finds a points+smooth instance or if one doesn't exist in the cache
564 allocates room and returns its cache entry
566 fontname - path to the font to load
567 handle - handle to the font.
568 points - points of the requested font
569 smooth - boolean (True: antialias on, False: antialias is off)
576 i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) {
580 mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n",
581 handle,points,smooth));
583 if (smooth == -1) { /* Smooth doesn't matter for this search */
584 for(i=0;i<TT_CHC;i++) {
585 if (handle->instanceh[i].ptsize==points) {
586 mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
590 smooth=1; /* We will be adding a font - add it as smooth then */
591 } else { /* Smooth doesn't matter for this search */
592 for(i=0;i<TT_CHC;i++) {
593 if (handle->instanceh[i].ptsize == points
594 && handle->instanceh[i].smooth == smooth) {
595 mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
601 /* Found the instance in the cache - return the cache index */
603 for(idx=0;idx<TT_CHC;idx++) {
604 if (!(handle->instanceh[idx].order)) break; /* find the lru item */
607 mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
608 mm_log((1,"i_tt_get_instance: lru pointer 0x%X\n",
609 USTRCT(handle->instanceh[idx].instance) ));
611 if ( USTRCT(handle->instanceh[idx].instance) ) {
612 mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
614 /* Free cached glyphs */
616 if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
617 TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
620 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
621 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
624 /* Free instance if needed */
625 TT_Done_Instance( handle->instanceh[idx].instance );
628 /* create and initialize instance */
629 /* FIXME: probably a memory leak on fail */
631 (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) ||
632 ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
633 ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
636 mm_log((1, "Could not create and initialize instance: error 0x%x.\n",error ));
640 /* Now that the instance should the inplace we need to lower all of the
641 ru counts and put `this' one with the highest entry */
643 for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
645 handle->instanceh[idx].order=TT_CHC-1;
646 handle->instanceh[idx].ptsize=points;
647 handle->instanceh[idx].smooth=smooth;
648 TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
650 /* Zero the memory for the glyph storage so they are not thought as
651 cached if they haven't been cached since this new font was loaded */
654 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
655 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
663 =item i_tt_new(fontname)
665 Creates a new font handle object, finds a character map and initialise the
666 the font handle's cache
668 fontname - path to the font to load
674 i_tt_new(char *fontname) {
676 TT_Fonthandle *handle;
678 unsigned short platform,encoding;
680 mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
682 /* allocate memory for the structure */
684 handle = mymalloc( sizeof(TT_Fonthandle) );
686 /* load the typeface */
687 error = TT_Open_Face( engine, fontname, &handle->face );
689 if ( error == TT_Err_Could_Not_Open_File ) {
690 mm_log((1, "Could not find/open %s.\n", fontname ));
693 mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname,
699 TT_Get_Face_Properties( handle->face, &(handle->properties) );
701 /* First, look for a Unicode charmap */
702 n = handle->properties.num_CharMaps;
703 USTRCT( handle->char_map )=NULL; /* Invalidate character map */
705 for ( i = 0; i < n; i++ ) {
706 TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
707 if ( (platform == 3 && encoding == 1 )
708 || (platform == 0 && encoding == 0 ) ) {
709 mm_log((2,"i_tt_new - found char map platform %u encoding %u\n",
710 platform, encoding));
711 TT_Get_CharMap( handle->face, i, &(handle->char_map) );
715 if (!USTRCT(handle->char_map) && n != 0) {
716 /* just use the first one */
717 TT_Get_CharMap( handle->face, 0, &(handle->char_map));
720 /* Zero the pointsizes - and ordering */
722 for(i=0;i<TT_CHC;i++) {
723 USTRCT(handle->instanceh[i].instance)=NULL;
724 handle->instanceh[i].order=i;
725 handle->instanceh[i].ptsize=0;
726 handle->instanceh[i].smooth=-1;
729 mm_log((1,"i_tt_new <- 0x%X\n",handle));
736 * raster map management
740 =item i_tt_init_raster_map(bit, width, height, smooth)
742 Allocates internal memory for the bitmap as needed by the parameters (internal)
744 bit - bitmap to allocate into
745 width - width of the bitmap
746 height - height of the bitmap
747 smooth - boolean (True: antialias on, False: antialias is off)
754 i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth ) {
756 mm_log((1,"i_tt_init_raster_map( bit 08x%08X, width %d, height %d, smooth %d)\n", bit, width, height, smooth));
759 bit->width = ( width + 3 ) & -4;
760 bit->flow = TT_Flow_Down;
763 bit->cols = bit->width;
764 bit->size = bit->rows * bit->width;
766 bit->cols = ( bit->width + 7 ) / 8; /* convert to # of bytes */
767 bit->size = bit->rows * bit->cols; /* number of bytes in buffer */
770 mm_log((1,"i_tt_init_raster_map: bit->width %d, bit->cols %d, bit->rows %d, bit->size %d)\n", bit->width, bit->cols, bit->rows, bit->size ));
772 bit->bitmap = (void *) mymalloc( bit->size );
773 if ( !bit->bitmap ) m_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",bit->size );
778 =item i_tt_clear_raster_map(bit)
780 Frees the bitmap data and sets pointer to NULL (internal)
789 i_tt_done_raster_map( TT_Raster_Map *bit ) {
790 myfree( bit->bitmap );
796 =item i_tt_clear_raster_map(bit)
798 Clears the specified bitmap (internal)
808 i_tt_clear_raster_map( TT_Raster_Map* bit ) {
809 memset( bit->bitmap, 0, bit->size );
814 =item i_tt_blit_or(dst, src, x_off, y_off)
816 function that blits one raster map into another (internal)
818 dst - destination bitmap
820 x_off - x offset into the destination bitmap
821 y_off - y offset into the destination bitmap
828 i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off ) {
833 x1 = x_off < 0 ? -x_off : 0;
834 y1 = y_off < 0 ? -y_off : 0;
836 x2 = (int)dst->cols - x_off;
837 if ( x2 > src->cols ) x2 = src->cols;
839 y2 = (int)dst->rows - y_off;
840 if ( y2 > src->rows ) y2 = src->rows;
842 if ( x1 >= x2 ) return;
844 /* do the real work now */
846 for ( y = y1; y < y2; ++y ) {
847 s = ( (char*)src->bitmap ) + y * src->cols + x1;
848 d = ( (char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
850 for ( x = x1; x < x2; ++x ) {
859 /* useful for debugging */
862 static void dump_raster_map(FILE *out, TT_Raster_Map *bit ) {
864 fprintf(out, "cols %d rows %d flow %d\n", bit->cols, bit->rows, bit->flow);
865 for (y = 0; y < bit->rows; ++y) {
866 fprintf(out, "%2d:", y);
867 for (x = 0; x < bit->cols; ++x) {
868 if ((x & 7) == 0 && x) putc(' ', out);
869 fprintf(out, "%02x", ((unsigned char *)bit->bitmap)[y*bit->cols+x]);
878 =item i_tt_get_glyph(handle, inst, j)
880 Function to see if a glyph exists and if so cache it (internal)
882 handle - pointer to font handle
884 j - charcode of glyph
891 i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j) {
892 unsigned short load_flags, code;
895 mm_log((1, "i_tt_get_glyph(handle 0x%X, inst %d, j %d (%c))\n",
896 handle,inst,j, ((j >= ' ' && j <= '~') ? j : '.')));
898 /*mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] ));*/
900 if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)
901 && handle->instanceh[inst].glyphs[TT_HASH(j)].ch == j) {
902 mm_log((1,"i_tt_get_glyph: %d in cache\n",j));
906 if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) ) {
907 /* clean up the entry */
908 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
909 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
910 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
913 /* Ok - it wasn't cached - try to get it in */
914 load_flags = TTLOAD_SCALE_GLYPH;
915 if ( LTT_hinted ) load_flags |= TTLOAD_HINT_GLYPH;
917 if ( !TT_VALID(handle->char_map) ) {
918 code = (j - ' ' + 1) < 0 ? 0 : (j - ' ' + 1);
919 if ( code >= handle->properties.num_Glyphs ) code = 0;
920 } else code = TT_Char_Index( handle->char_map, j );
922 if ( (error = TT_New_Glyph( handle->face, &handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)) ) {
923 mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
924 i_push_error(error, "TT_New_Glyph()");
927 if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, code, load_flags)) ) {
928 mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
930 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
931 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
932 i_push_error(error, "TT_Load_Glyph()");
936 /* At this point the glyph should be allocated and loaded */
937 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = j;
939 /* Next get the glyph metrics */
940 error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
941 &handle->instanceh[inst].gmetrics[TT_HASH(j)] );
943 mm_log((1, "TT_Get_Glyph_Metrics: error 0x%x.\n", error ));
944 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
945 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
946 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
947 i_push_error(error, "TT_Get_Glyph_Metrics()");
955 =item i_tt_has_chars(handle, text, len, utf8, out)
957 Check if the given characters are defined by the font. Note that len
958 is the number of bytes, not the number of characters (when utf8 is
961 Returns the number of characters that were checked.
967 i_tt_has_chars(TT_Fonthandle *handle, char const *text, int len, int utf8,
971 mm_log((1, "i_ft2_has_chars(handle %p, text %p, len %d, utf8 %d)\n",
972 handle, text, len, utf8));
978 c = i_utf8_advance(&text, &len);
980 i_push_error(0, "invalid UTF8 character");
985 c = (unsigned char)*text++;
989 if (TT_VALID(handle->char_map)) {
990 index = TT_Char_Index(handle->char_map, c);
993 index = (c - ' ' + 1) < 0 ? 0 : (c - ' ' + 1);
994 if (index >= handle->properties.num_Glyphs)
1005 =item i_tt_destroy(handle)
1007 Clears the data taken by a font including all cached data such as
1010 handle - pointer to font handle
1016 i_tt_destroy( TT_Fonthandle *handle) {
1017 TT_Close_Face( handle->face );
1020 /* FIXME: Should these be freed automatically by the library?
1022 TT_Done_Instance( instance );
1024 i_tt_done_glyphs( void ) {
1027 if ( !glyphs ) return;
1029 for ( i = 0; i < 256; ++i ) TT_Done_Glyph( glyphs[i] );
1039 * FreeType Rendering functions
1044 =item i_tt_render_glyph(handle, gmetrics, bit, smallbit, x_off, y_off, smooth)
1046 Renders a single glyph into the bit rastermap (internal)
1048 handle - pointer to font handle
1049 gmetrics - the metrics for the glyph to be rendered
1050 bit - large bitmap that is the destination for the text
1051 smallbit - small bitmap that is used only if smooth is true
1052 x_off - x offset of glyph
1053 y_off - y offset of glyph
1054 smooth - boolean (True: antialias on, False: antialias is off)
1061 i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics, TT_Raster_Map *bit, TT_Raster_Map *small_bit, int x_off, int y_off, int smooth ) {
1063 mm_log((1,"i_tt_render_glyph(glyph 0x0%X, gmetrics 0x0%X, bit 0x%X, small_bit 0x%X, x_off %d, y_off %d, smooth %d)\n",
1064 USTRCT(glyph), gmetrics, bit, small_bit, x_off,y_off,smooth));
1066 if ( !smooth ) TT_Get_Glyph_Bitmap( glyph, bit, x_off * 64, y_off * 64);
1068 TT_F26Dot6 xmin, ymin, xmax, ymax;
1070 xmin = gmetrics->bbox.xMin & -64;
1071 ymin = gmetrics->bbox.yMin & -64;
1072 xmax = (gmetrics->bbox.xMax + 63) & -64;
1073 ymax = (gmetrics->bbox.yMax + 63) & -64;
1075 i_tt_clear_raster_map( small_bit );
1076 TT_Get_Glyph_Pixmap( glyph, small_bit, -xmin, -ymin );
1077 i_tt_blit_or( bit, small_bit, xmin/64 + x_off, -ymin/64 - y_off );
1083 =item i_tt_render_all_glyphs(handle, inst, bit, small_bit, cords, txt, len, smooth)
1085 calls i_tt_render_glyph to render each glyph into the bit rastermap (internal)
1087 handle - pointer to font handle
1088 inst - font instance
1089 bit - large bitmap that is the destination for the text
1090 smallbit - small bitmap that is used only if smooth is true
1091 txt - string to render
1092 len - length of the string to render
1093 smooth - boolean (True: antialias on, False: antialias is off)
1100 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
1101 TT_Raster_Map *small_bit, int cords[6],
1102 char const* txt, int len, int smooth, int utf8 ) {
1107 mm_log((1,"i_tt_render_all_glyphs( handle 0x%X, inst %d, bit 0x%X, small_bit 0x%X, txt '%.*s', len %d, smooth %d, utf8 %d)\n",
1108 handle, inst, bit, small_bit, len, txt, len, smooth, utf8));
1111 y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM);
1114 x=-cords[0]; /* FIXME: If you font is antialiased this should be expanded by one to allow for aa expansion and the allocation too - do before passing here */
1119 j = i_utf8_advance(&txt, &len);
1121 i_push_error(0, "invalid UTF8 character");
1126 j = (unsigned char)*txt++;
1129 if ( !i_tt_get_glyph(handle,inst,j) )
1131 i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
1132 &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit,
1133 small_bit, x, y, smooth );
1134 x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64;
1142 * Functions to render rasters (single channel images) onto images
1146 =item i_tt_dump_raster_map2(im, bit, xb, yb, cl, smooth)
1148 Function to dump a raster onto an image in color used by i_tt_text() (internal).
1150 im - image to dump raster on
1151 bit - bitmap that contains the text to be dumped to im
1152 xb, yb - coordinates, left edge and baseline
1153 cl - color to use for text
1154 smooth - boolean (True: antialias on, False: antialias is off)
1161 i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, i_color *cl, int smooth ) {
1165 mm_log((1,"i_tt_dump_raster_map2(im 0x%x, bit 0x%X, xb %d, yb %d, cl 0x%X)\n",im,bit,xb,yb,cl));
1167 bmap = (char *)bit->bitmap;
1171 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1172 c=(255*bmap[y*(bit->cols)+x])/4;
1174 i_gpix(im,x+xb,y+yb,&val);
1175 for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
1176 i_ppix(im,x+xb,y+yb,&val);
1181 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1182 c=( bmap[y*bit->cols+x/8] & (128>>(x%8)) ) ? 255 : 0;
1184 i_gpix(im,x+xb,y+yb,&val);
1185 for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
1186 i_ppix(im,x+xb,y+yb,&val);
1194 =item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth)
1196 Function to dump a raster onto a single channel image in color (internal)
1198 im - image to dump raster on
1199 bit - bitmap that contains the text to be dumped to im
1200 xb, yb - coordinates, left edge and baseline
1201 channel - channel to copy to
1202 smooth - boolean (True: antialias on, False: antialias is off)
1209 i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth ) {
1214 mm_log((1,"i_tt_dump_raster_channel(im 0x%x, bit 0x%X, xb %d, yb %d, channel %d)\n",im,bit,xb,yb,channel));
1216 bmap = (char *)bit->bitmap;
1219 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1220 c=(255*bmap[y*(bit->cols)+x])/4;
1221 i_gpix(im,x+xb,y+yb,&val);
1222 val.channel[channel]=c;
1223 i_ppix(im,x+xb,y+yb,&val);
1226 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1227 c=( bmap[y*bit->cols+x/8] & (128>>(x%8)) ) ? 255 : 0;
1228 i_gpix(im,x+xb,y+yb,&val);
1229 val.channel[channel]=c;
1230 i_ppix(im,x+xb,y+yb,&val);
1237 =item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth)
1239 interface for generating single channel raster of text (internal)
1241 handle - pointer to font handle
1242 bit - the bitmap that is allocated, rendered into and NOT freed
1243 cords - the bounding box (modified in place)
1244 points - font size to use
1245 txt - string to render
1246 len - length of the string to render
1247 smooth - boolean (True: antialias on, False: antialias is off)
1254 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char const* txt, int len, int smooth, int utf8 ) {
1257 TT_Raster_Map small_bit;
1259 /* find or install an instance */
1260 if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) {
1261 mm_log((1,"i_tt_rasterize: get instance failed\n"));
1265 /* calculate bounding box */
1266 if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 ))
1270 width = cords[2]-cords[0];
1271 height = cords[5]-cords[4];
1273 mm_log((1,"i_tt_rasterize: width=%d, height=%d\n",width, height ));
1275 i_tt_init_raster_map ( bit, width, height, smooth );
1276 i_tt_clear_raster_map( bit );
1277 if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth );
1279 if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len,
1282 i_tt_done_raster_map( &small_bit );
1286 /* ascent = ( handle->properties.horizontal->Ascender * handle->instanceh[inst].imetrics.y_ppem ) / handle->properties.header->Units_Per_EM; */
1288 if ( smooth ) i_tt_done_raster_map( &small_bit );
1295 * Exported text rendering interfaces
1300 =item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth, utf8)
1302 Interface to text rendering into a single channel in an image
1304 handle - pointer to font handle
1305 im - image to render text on to
1306 xb, yb - coordinates, left edge and baseline
1307 channel - channel to render into
1308 points - font size to use
1309 txt - string to render
1310 len - length of the string to render
1311 smooth - boolean (True: antialias on, False: antialias is off)
1317 i_tt_cp( TT_Fonthandle *handle, i_img *im, int xb, int yb, int channel, float points, char const* txt, int len, int smooth, int utf8 ) {
1320 int ascent, st_offset;
1324 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1329 i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , yb-ascent, channel, smooth );
1330 i_tt_done_raster_map( &bit );
1337 =item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8)
1339 Interface to text rendering in a single color onto an image
1341 handle - pointer to font handle
1342 im - image to render text on to
1343 xb, yb - coordinates, left edge and baseline
1344 cl - color to use for text
1345 points - font size to use
1346 txt - string to render
1347 len - length of the string to render
1348 smooth - boolean (True: antialias on, False: antialias is off)
1354 i_tt_text( TT_Fonthandle *handle, i_img *im, int xb, int yb, i_color *cl, float points, char const* txt, int len, int smooth, int utf8) {
1356 int ascent, st_offset;
1361 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1366 i_tt_dump_raster_map2( im, &bit, xb+st_offset, yb-ascent, cl, smooth );
1367 i_tt_done_raster_map( &bit );
1374 =item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8)
1376 Function to get texts bounding boxes given the instance of the font (internal)
1378 handle - pointer to font handle
1379 inst - font instance
1380 txt - string to measure
1381 len - length of the string to render
1382 cords - the bounding box (modified in place)
1389 i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6], int utf8 ) {
1390 int i, upm, casc, cdesc, first;
1401 unsigned char *ustr;
1402 ustr=(unsigned char*)txt;
1404 mm_log((1,"i_tt_box_inst(handle 0x%X,inst %d,txt '%.*s', len %d, utf8 %d)\n",handle,inst,len,txt,len, utf8));
1406 upm = handle->properties.header->Units_Per_EM;
1407 gascent = ( handle->properties.horizontal->Ascender * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm;
1408 gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem - upm + 1) / upm;
1413 mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent));
1418 j = i_utf8_advance(&txt, &len);
1420 i_push_error(0, "invalid UTF8 character");
1425 j = (unsigned char)*txt++;
1428 if ( i_tt_get_glyph(handle,inst,j) ) {
1429 TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j);
1430 width += gm->advance / 64;
1431 casc = (gm->bbox.yMax+63) / 64;
1432 cdesc = (gm->bbox.yMin-63) / 64;
1434 mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n",
1435 ((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc));
1438 start = gm->bbox.xMin / 64;
1439 ascent = (gm->bbox.yMax+63) / 64;
1440 descent = (gm->bbox.yMin-63) / 64;
1444 /* the right-side bearing - in case the right-side of a
1445 character goes past the right of the advance width,
1446 as is common for italic fonts
1448 int rightb = gm->advance - gm->bearingX
1449 - (gm->bbox.xMax - gm->bbox.xMin);
1450 /* fprintf(stderr, "font info last: %d %d %d %d\n",
1451 gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */
1456 ascent = (ascent > casc ? ascent : casc );
1457 descent = (descent < cdesc ? descent : cdesc);
1472 =item i_tt_bbox(handle, points, txt, len, cords, utf8)
1474 Interface to get a strings bounding box
1476 handle - pointer to font handle
1477 points - font size to use
1478 txt - string to render
1479 len - length of the string to render
1480 cords - the bounding box (modified in place)
1486 i_tt_bbox( TT_Fonthandle *handle, float points,char *txt,int len,int cords[6], int utf8) {
1490 mm_log((1,"i_tt_box(handle 0x%X,points %f,txt '%.*s', len %d, utf8 %d)\n",handle,points,len,txt,len, utf8));
1492 if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
1493 i_push_errorf(0, "i_tt_get_instance(%g)", points);
1494 mm_log((1,"i_tt_text: get instance failed\n"));
1498 return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8);
1503 #endif /* HAVE_LIBTT */
1511 Arnar M. Hrafnkelsson <addi@umich.edu>