18 font.c - implements font handling functions for t1 and truetype fonts
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);
31 handle = i_tt_new(path_to_ttf);
32 rc = i_tt_bbox(handle, points, "foo", 3, int cords[6], utf8);
39 font.c implements font creation, rendering, bounding box functions and
42 =head1 FUNCTION REFERENCE
44 Some of these functions are internal.
53 /* Truetype font support */
56 /* These are enabled by default when configuring Freetype 1.x
57 I haven't a clue how to reliably detect it at compile time.
59 We need a compilation probe in Makefile.PL
75 /* some versions of FT1.x don't seem to define this - it's font defined
77 #ifndef TT_MS_LANGID_ENGLISH_GENERAL
78 #define TT_MS_LANGID_ENGLISH_GENERAL 0x0409
81 /* convert a code point into an index in the glyph cache */
82 #define TT_HASH(x) ((x) & 0xFF)
84 typedef struct i_glyph_entry_ {
89 #define TT_NOCHAR (~0UL)
91 struct TT_Instancehandle_ {
93 TT_Instance_Metrics imetrics;
94 TT_Glyph_Metrics gmetrics[256];
95 i_tt_glyph_entry glyphs[256];
101 typedef struct TT_Instancehandle_ TT_Instancehandle;
103 struct TT_Fonthandle_ {
105 TT_Face_Properties properties;
106 TT_Instancehandle instanceh[TT_CHC];
116 #define USTRCT(x) ((x).z)
117 #define TT_VALID( handle ) ( ( handle ).z != NULL )
119 static void i_tt_push_error(TT_Error rc);
123 static int i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth );
124 static void i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth );
125 static void i_tt_done_raster_map( TT_Raster_Map *bit );
126 static void i_tt_clear_raster_map( TT_Raster_Map* bit );
127 static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off );
128 static int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j );
130 i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics,
131 TT_Raster_Map *bit, TT_Raster_Map *small_bit,
132 int x_off, int y_off, int smooth );
134 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
135 TT_Raster_Map *small_bit, int cords[6],
136 char const* txt, size_t len, int smooth, int utf8 );
137 static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, const i_color *cl, int smooth );
138 static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth );
140 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6],
141 float points, char const* txt, size_t len, int smooth, int utf8 );
142 static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, size_t len, int cords[6], int utf8 );
145 /* static globals needed */
147 static int TT_initialized = 0;
148 static TT_Engine engine;
149 static int LTT_dpi = 72; /* FIXME: this ought to be a part of the call interface */
150 static int LTT_hinted = 1; /* FIXME: this too */
161 Initializes the freetype font rendering engine
169 TT_Byte palette[] = { 0, 64, 127, 191, 255 };
173 mm_log((1,"init_tt()\n"));
174 error = TT_Init_FreeType( &engine );
176 mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error));
177 i_tt_push_error(error);
178 i_push_error(0, "Could not initialize freetype 1.x");
183 error = TT_Init_Post_Extension( engine );
185 mm_log((1, "Initialization of Post extension failed = 0x%x\n", error));
187 i_tt_push_error(error);
188 i_push_error(0, "Could not initialize FT 1.x POST extension");
193 error = TT_Set_Raster_Gray_Palette(engine, palette);
195 mm_log((1, "Initialization of gray levels failed = 0x%x\n", error));
196 i_tt_push_error(error);
197 i_push_error(0, "Could not initialize FT 1.x POST extension");
208 =item i_tt_get_instance(handle, points, smooth)
210 Finds a points+smooth instance or if one doesn't exist in the cache
211 allocates room and returns its cache entry
213 fontname - path to the font to load
214 handle - handle to the font.
215 points - points of the requested font
216 smooth - boolean (True: antialias on, False: antialias is off)
223 i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) {
227 mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n",
228 handle,points,smooth));
230 if (smooth == -1) { /* Smooth doesn't matter for this search */
231 for(i=0;i<TT_CHC;i++) {
232 if (handle->instanceh[i].ptsize==points) {
233 mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
237 smooth=1; /* We will be adding a font - add it as smooth then */
238 } else { /* Smooth doesn't matter for this search */
239 for(i=0;i<TT_CHC;i++) {
240 if (handle->instanceh[i].ptsize == points
241 && handle->instanceh[i].smooth == smooth) {
242 mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
248 /* Found the instance in the cache - return the cache index */
250 for(idx=0;idx<TT_CHC;idx++) {
251 if (!(handle->instanceh[idx].order)) break; /* find the lru item */
254 mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
255 mm_log((1,"i_tt_get_instance: lru pointer 0x%X\n",
256 USTRCT(handle->instanceh[idx].instance) ));
258 if ( USTRCT(handle->instanceh[idx].instance) ) {
259 mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
261 /* Free cached glyphs */
263 if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
264 TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
267 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
268 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
271 /* Free instance if needed */
272 TT_Done_Instance( handle->instanceh[idx].instance );
275 /* create and initialize instance */
276 /* FIXME: probably a memory leak on fail */
278 (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) ||
279 ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
280 ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
283 mm_log((1, "Could not create and initialize instance: error 0x%x.\n",error ));
287 /* Now that the instance should the inplace we need to lower all of the
288 ru counts and put `this' one with the highest entry */
290 for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
292 handle->instanceh[idx].order=TT_CHC-1;
293 handle->instanceh[idx].ptsize=points;
294 handle->instanceh[idx].smooth=smooth;
295 TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
297 /* Zero the memory for the glyph storage so they are not thought as
298 cached if they haven't been cached since this new font was loaded */
301 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
302 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
310 =item i_tt_new(fontname)
312 Creates a new font handle object, finds a character map and initialise the
313 the font handle's cache
315 fontname - path to the font to load
321 i_tt_new(const char *fontname) {
323 TT_Fonthandle *handle;
325 unsigned short platform,encoding;
327 if (!TT_initialized && i_init_tt()) {
328 i_push_error(0, "Could not initialize FT1 engine");
334 mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
336 /* allocate memory for the structure */
338 handle = mymalloc( sizeof(TT_Fonthandle) ); /* checked 5Nov05 tonyc */
340 /* load the typeface */
341 error = TT_Open_Face( engine, fontname, &handle->face );
343 if ( error == TT_Err_Could_Not_Open_File ) {
344 mm_log((1, "Could not find/open %s.\n", fontname ));
347 mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname,
350 i_tt_push_error(error);
354 TT_Get_Face_Properties( handle->face, &(handle->properties) );
356 /* First, look for a Unicode charmap */
357 n = handle->properties.num_CharMaps;
358 USTRCT( handle->char_map )=NULL; /* Invalidate character map */
360 for ( i = 0; i < n; i++ ) {
361 TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
362 if ( (platform == 3 && encoding == 1 )
363 || (platform == 0 && encoding == 0 ) ) {
364 mm_log((2,"i_tt_new - found char map platform %u encoding %u\n",
365 platform, encoding));
366 TT_Get_CharMap( handle->face, i, &(handle->char_map) );
370 if (!USTRCT(handle->char_map) && n != 0) {
371 /* just use the first one */
372 TT_Get_CharMap( handle->face, 0, &(handle->char_map));
375 /* Zero the pointsizes - and ordering */
377 for(i=0;i<TT_CHC;i++) {
378 USTRCT(handle->instanceh[i].instance)=NULL;
379 handle->instanceh[i].order=i;
380 handle->instanceh[i].ptsize=0;
381 handle->instanceh[i].smooth=-1;
385 handle->loaded_names = 0;
388 mm_log((1,"i_tt_new <- 0x%X\n",handle));
395 * raster map management
399 =item i_tt_init_raster_map(bit, width, height, smooth)
401 Allocates internal memory for the bitmap as needed by the parameters (internal)
403 bit - bitmap to allocate into
404 width - width of the bitmap
405 height - height of the bitmap
406 smooth - boolean (True: antialias on, False: antialias is off)
413 i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth ) {
415 mm_log((1,"i_tt_init_raster_map( bit 08x%08X, width %d, height %d, smooth %d)\n", bit, width, height, smooth));
418 bit->width = ( width + 3 ) & -4;
419 bit->flow = TT_Flow_Down;
422 bit->cols = bit->width;
423 bit->size = bit->rows * bit->width;
425 bit->cols = ( bit->width + 7 ) / 8; /* convert to # of bytes */
426 bit->size = bit->rows * bit->cols; /* number of bytes in buffer */
429 /* rows can be 0 for some glyphs, for example ' ' */
430 if (bit->rows && bit->size / bit->rows != bit->cols) {
431 i_fatal(0, "Integer overflow calculating bitmap size (%d, %d)\n",
432 bit->width, bit->rows);
435 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 ));
437 bit->bitmap = (void *) mymalloc( bit->size ); /* checked 6Nov05 tonyc */
438 if ( !bit->bitmap ) i_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",bit->size );
443 =item i_tt_clear_raster_map(bit)
445 Frees the bitmap data and sets pointer to NULL (internal)
454 i_tt_done_raster_map( TT_Raster_Map *bit ) {
455 myfree( bit->bitmap );
461 =item i_tt_clear_raster_map(bit)
463 Clears the specified bitmap (internal)
473 i_tt_clear_raster_map( TT_Raster_Map* bit ) {
474 memset( bit->bitmap, 0, bit->size );
479 =item i_tt_blit_or(dst, src, x_off, y_off)
481 function that blits one raster map into another (internal)
483 dst - destination bitmap
485 x_off - x offset into the destination bitmap
486 y_off - y offset into the destination bitmap
493 i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off ) {
496 unsigned char *s, *d;
498 x1 = x_off < 0 ? -x_off : 0;
499 y1 = y_off < 0 ? -y_off : 0;
501 x2 = (int)dst->cols - x_off;
502 if ( x2 > src->cols ) x2 = src->cols;
504 y2 = (int)dst->rows - y_off;
505 if ( y2 > src->rows ) y2 = src->rows;
507 if ( x1 >= x2 ) return;
509 /* do the real work now */
511 for ( y = y1; y < y2; ++y ) {
512 s = ( (unsigned char*)src->bitmap ) + y * src->cols + x1;
513 d = ( (unsigned char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
515 for ( x = x1; x < x2; ++x ) {
524 /* useful for debugging */
527 static void dump_raster_map(FILE *out, TT_Raster_Map *bit ) {
529 fprintf(out, "cols %d rows %d flow %d\n", bit->cols, bit->rows, bit->flow);
530 for (y = 0; y < bit->rows; ++y) {
531 fprintf(out, "%2d:", y);
532 for (x = 0; x < bit->cols; ++x) {
533 if ((x & 7) == 0 && x) putc(' ', out);
534 fprintf(out, "%02x", ((unsigned char *)bit->bitmap)[y*bit->cols+x]);
543 =item i_tt_get_glyph(handle, inst, j)
545 Function to see if a glyph exists and if so cache it (internal)
547 handle - pointer to font handle
549 j - charcode of glyph
556 i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j) {
557 unsigned short load_flags, code;
560 mm_log((1, "i_tt_get_glyph(handle 0x%X, inst %d, j %d (%c))\n",
561 handle,inst,j, ((j >= ' ' && j <= '~') ? j : '.')));
563 /*mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] ));*/
565 if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)
566 && handle->instanceh[inst].glyphs[TT_HASH(j)].ch == j) {
567 mm_log((1,"i_tt_get_glyph: %d in cache\n",j));
571 if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) ) {
572 /* clean up the entry */
573 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
574 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
575 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
578 /* Ok - it wasn't cached - try to get it in */
579 load_flags = TTLOAD_SCALE_GLYPH;
580 if ( LTT_hinted ) load_flags |= TTLOAD_HINT_GLYPH;
582 if ( !TT_VALID(handle->char_map) ) {
583 code = (j - ' ' + 1) < 0 ? 0 : (j - ' ' + 1);
584 if ( code >= handle->properties.num_Glyphs ) code = 0;
585 } else code = TT_Char_Index( handle->char_map, j );
587 if ( (error = TT_New_Glyph( handle->face, &handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)) ) {
588 mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
589 i_push_error(error, "TT_New_Glyph()");
592 if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, code, load_flags)) ) {
593 mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
595 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
596 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
597 i_push_error(error, "TT_Load_Glyph()");
601 /* At this point the glyph should be allocated and loaded */
602 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = j;
604 /* Next get the glyph metrics */
605 error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
606 &handle->instanceh[inst].gmetrics[TT_HASH(j)] );
608 mm_log((1, "TT_Get_Glyph_Metrics: error 0x%x.\n", error ));
609 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
610 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
611 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
612 i_push_error(error, "TT_Get_Glyph_Metrics()");
620 =item i_tt_has_chars(handle, text, len, utf8, out)
622 Check if the given characters are defined by the font. Note that len
623 is the number of bytes, not the number of characters (when utf8 is
626 Returns the number of characters that were checked.
632 i_tt_has_chars(TT_Fonthandle *handle, char const *text, size_t len, int utf8,
635 mm_log((1, "i_tt_has_chars(handle %p, text %p, len %d, utf8 %d)\n",
636 handle, text, len, utf8));
642 c = i_utf8_advance(&text, &len);
644 i_push_error(0, "invalid UTF8 character");
649 c = (unsigned char)*text++;
653 if (TT_VALID(handle->char_map)) {
654 index = TT_Char_Index(handle->char_map, c);
657 index = (c - ' ' + 1) < 0 ? 0 : (c - ' ' + 1);
658 if (index >= handle->properties.num_Glyphs)
669 =item i_tt_destroy(handle)
671 Clears the data taken by a font including all cached data such as
674 handle - pointer to font handle
680 i_tt_destroy( TT_Fonthandle *handle) {
681 TT_Close_Face( handle->face );
684 /* FIXME: Should these be freed automatically by the library?
686 TT_Done_Instance( instance );
688 i_tt_done_glyphs( void ) {
691 if ( !glyphs ) return;
693 for ( i = 0; i < 256; ++i ) TT_Done_Glyph( glyphs[i] );
703 * FreeType Rendering functions
708 =item i_tt_render_glyph(handle, gmetrics, bit, smallbit, x_off, y_off, smooth)
710 Renders a single glyph into the bit rastermap (internal)
712 handle - pointer to font handle
713 gmetrics - the metrics for the glyph to be rendered
714 bit - large bitmap that is the destination for the text
715 smallbit - small bitmap that is used only if smooth is true
716 x_off - x offset of glyph
717 y_off - y offset of glyph
718 smooth - boolean (True: antialias on, False: antialias is off)
725 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 ) {
727 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",
728 USTRCT(glyph), gmetrics, bit, small_bit, x_off,y_off,smooth));
730 if ( !smooth ) TT_Get_Glyph_Bitmap( glyph, bit, x_off * 64, y_off * 64);
732 TT_F26Dot6 xmin, ymin, xmax, ymax;
734 xmin = gmetrics->bbox.xMin & -64;
735 ymin = gmetrics->bbox.yMin & -64;
736 xmax = (gmetrics->bbox.xMax + 63) & -64;
737 ymax = (gmetrics->bbox.yMax + 63) & -64;
739 i_tt_clear_raster_map( small_bit );
740 TT_Get_Glyph_Pixmap( glyph, small_bit, -xmin, -ymin );
741 i_tt_blit_or( bit, small_bit, xmin/64 + x_off, -ymin/64 - y_off );
747 =item i_tt_render_all_glyphs(handle, inst, bit, small_bit, cords, txt, len, smooth)
749 calls i_tt_render_glyph to render each glyph into the bit rastermap (internal)
751 handle - pointer to font handle
753 bit - large bitmap that is the destination for the text
754 smallbit - small bitmap that is used only if smooth is true
755 txt - string to render
756 len - length of the string to render
757 smooth - boolean (True: antialias on, False: antialias is off)
764 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
765 TT_Raster_Map *small_bit, int cords[6],
766 char const* txt, size_t len, int smooth, int utf8 ) {
770 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",
771 handle, inst, bit, small_bit, len, txt, len, smooth, utf8));
774 y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM);
777 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 */
782 j = i_utf8_advance(&txt, &len);
784 i_push_error(0, "invalid UTF8 character");
789 j = (unsigned char)*txt++;
792 if ( !i_tt_get_glyph(handle,inst,j) )
794 i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
795 &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit,
796 small_bit, x, y, smooth );
797 x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64;
805 * Functions to render rasters (single channel images) onto images
809 =item i_tt_dump_raster_map2(im, bit, xb, yb, cl, smooth)
811 Function to dump a raster onto an image in color used by i_tt_text() (internal).
813 im - image to dump raster on
814 bit - bitmap that contains the text to be dumped to im
815 xb, yb - coordinates, left edge and baseline
816 cl - color to use for text
817 smooth - boolean (True: antialias on, False: antialias is off)
824 i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, const i_color *cl, int smooth ) {
827 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));
834 i_render_init(&r, im, bit->cols);
835 for(y=0;y<bit->rows;y++) {
837 for(x=0;x<bit->width;x++) {
838 c = (unsigned char)bmap[y*(bit->cols)+x];
840 i_gpix(im,x+xb,y+yb,&val);
841 for(ch=0;ch<im->channels;ch++)
842 val.channel[ch] = (c*cl->channel[ch]+i*val.channel[ch])/255;
843 i_ppix(im,x+xb,y+yb,&val);
846 i_render_color(&r, xb, yb+y, bit->cols, bmap + y*bit->cols, cl);
851 for(y=0;y<bit->rows;y++) {
852 unsigned mask = 0x80;
853 unsigned char *p = bmap + y * bit->cols;
855 for(x = 0; x < bit->width; x++) {
857 i_ppix(im, x+xb, y+yb, cl);
872 =item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth)
874 Function to dump a raster onto a single channel image in color (internal)
876 im - image to dump raster on
877 bit - bitmap that contains the text to be dumped to im
878 xb, yb - coordinates, left edge and baseline
879 channel - channel to copy to
880 smooth - boolean (True: antialias on, False: antialias is off)
887 i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth ) {
891 int old_mask = im->ch_mask;
892 im->ch_mask = 1 << channel;
894 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));
899 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
900 c = bmap[y*(bit->cols)+x];
901 val.channel[channel] = c;
902 i_ppix(im,x+xb,y+yb,&val);
905 for(y=0;y<bit->rows;y++) {
906 unsigned mask = 0x80;
907 unsigned char *p = bmap + y * bit->cols;
909 for(x=0;x<bit->width;x++) {
910 val.channel[channel] = (*p & mask) ? 255 : 0;
911 i_ppix(im,x+xb,y+yb,&val);
921 im->ch_mask = old_mask;
926 =item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth)
928 interface for generating single channel raster of text (internal)
930 handle - pointer to font handle
931 bit - the bitmap that is allocated, rendered into and NOT freed
932 cords - the bounding box (modified in place)
933 points - font size to use
934 txt - string to render
935 len - length of the string to render
936 smooth - boolean (True: antialias on, False: antialias is off)
943 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char const* txt, size_t len, int smooth, int utf8 ) {
946 TT_Raster_Map small_bit;
948 /* find or install an instance */
949 if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) {
950 mm_log((1,"i_tt_rasterize: get instance failed\n"));
954 /* calculate bounding box */
955 if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 ))
959 width = cords[2]-cords[0];
960 height = cords[5]-cords[4];
962 mm_log((1,"i_tt_rasterize: width=%d, height=%d\n",width, height ));
964 i_tt_init_raster_map ( bit, width, height, smooth );
965 i_tt_clear_raster_map( bit );
966 if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth );
968 if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len,
971 i_tt_done_raster_map( &small_bit );
975 if ( smooth ) i_tt_done_raster_map( &small_bit );
982 * Exported text rendering interfaces
987 =item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth, utf8)
989 Interface to text rendering into a single channel in an image
991 handle - pointer to font handle
992 im - image to render text on to
993 xb, yb - coordinates, left edge and baseline
994 channel - channel to render into
995 points - font size to use
996 txt - string to render
997 len - length of the string to render
998 smooth - boolean (True: antialias on, False: antialias is off)
1004 i_tt_cp( TT_Fonthandle *handle, i_img *im, int xb, int yb, int channel, float points, char const* txt, size_t len, int smooth, int utf8, int align ) {
1006 int cords[BOUNDING_BOX_COUNT];
1007 int ascent, st_offset, y;
1011 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1013 ascent=cords[BBOX_ASCENT];
1014 st_offset=cords[BBOX_NEG_WIDTH];
1015 y = align ? yb-ascent : yb;
1017 i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , y, channel, smooth );
1018 i_tt_done_raster_map( &bit );
1025 =item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8)
1027 Interface to text rendering in a single color onto an image
1029 handle - pointer to font handle
1030 im - image to render text on to
1031 xb, yb - coordinates, left edge and baseline
1032 cl - color to use for text
1033 points - font size to use
1034 txt - string to render
1035 len - length of the string to render
1036 smooth - boolean (True: antialias on, False: antialias is off)
1042 i_tt_text( TT_Fonthandle *handle, i_img *im, int xb, int yb, const i_color *cl, float points, char const* txt, size_t len, int smooth, int utf8, int align) {
1043 int cords[BOUNDING_BOX_COUNT];
1044 int ascent, st_offset, y;
1049 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1051 ascent=cords[BBOX_ASCENT];
1052 st_offset=cords[BBOX_NEG_WIDTH];
1053 y = align ? yb-ascent : yb;
1055 i_tt_dump_raster_map2( im, &bit, xb+st_offset, y, cl, smooth );
1056 i_tt_done_raster_map( &bit );
1063 =item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8)
1065 Function to get texts bounding boxes given the instance of the font (internal)
1067 handle - pointer to font handle
1068 inst - font instance
1069 txt - string to measure
1070 len - length of the string to render
1071 cords - the bounding box (modified in place)
1078 i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, size_t len, int cords[BOUNDING_BOX_COUNT], int utf8 ) {
1079 int upm, casc, cdesc, first;
1090 unsigned char *ustr;
1091 ustr=(unsigned char*)txt;
1093 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));
1095 upm = handle->properties.header->Units_Per_EM;
1096 gascent = ( handle->properties.horizontal->Ascender * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm;
1097 gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem - upm + 1) / upm;
1102 mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent));
1107 j = i_utf8_advance(&txt, &len);
1109 i_push_error(0, "invalid UTF8 character");
1114 j = (unsigned char)*txt++;
1117 if ( i_tt_get_glyph(handle,inst,j) ) {
1118 TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j);
1119 width += gm->advance / 64;
1120 casc = (gm->bbox.yMax+63) / 64;
1121 cdesc = (gm->bbox.yMin-63) / 64;
1123 mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n",
1124 ((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc));
1127 start = gm->bbox.xMin / 64;
1128 ascent = (gm->bbox.yMax+63) / 64;
1129 descent = (gm->bbox.yMin-63) / 64;
1132 if (!len) { /* if at end of string */
1133 /* the right-side bearing - in case the right-side of a
1134 character goes past the right of the advance width,
1135 as is common for italic fonts
1137 rightb = gm->advance - gm->bearingX
1138 - (gm->bbox.xMax - gm->bbox.xMin);
1139 /* fprintf(stderr, "font info last: %d %d %d %d\n",
1140 gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */
1143 ascent = (ascent > casc ? ascent : casc );
1144 descent = (descent < cdesc ? descent : cdesc);
1148 cords[BBOX_NEG_WIDTH]=start;
1149 cords[BBOX_GLOBAL_DESCENT]=gdescent;
1150 cords[BBOX_POS_WIDTH]=width;
1152 cords[BBOX_POS_WIDTH] -= rightb / 64;
1153 cords[BBOX_GLOBAL_ASCENT]=gascent;
1154 cords[BBOX_DESCENT]=descent;
1155 cords[BBOX_ASCENT]=ascent;
1156 cords[BBOX_ADVANCE_WIDTH] = width;
1157 cords[BBOX_RIGHT_BEARING] = rightb / 64;
1159 return BBOX_RIGHT_BEARING + 1;
1164 =item i_tt_bbox(handle, points, txt, len, cords, utf8)
1166 Interface to get a strings bounding box
1168 handle - pointer to font handle
1169 points - font size to use
1170 txt - string to render
1171 len - length of the string to render
1172 cords - the bounding box (modified in place)
1178 i_tt_bbox( TT_Fonthandle *handle, float points,const char *txt,size_t len,int cords[6], int utf8) {
1182 mm_log((1,"i_tt_box(handle 0x%X,points %f,txt '%.*s', len %d, utf8 %d)\n",handle,points,len,txt,len, utf8));
1184 if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
1185 i_push_errorf(0, "i_tt_get_instance(%g)", points);
1186 mm_log((1,"i_tt_text: get instance failed\n"));
1190 return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8);
1194 =item i_tt_face_name(handle, name_buf, name_buf_size)
1196 Retrieve's the font's postscript name.
1198 This is complicated by the need to handle encodings and so on.
1203 i_tt_face_name(TT_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
1204 TT_Face_Properties props;
1207 TT_UShort platform_id, encoding_id, lang_id, name_id;
1210 int want_index = -1; /* an acceptable but not perfect name */
1215 TT_Get_Face_Properties(handle->face, &props);
1216 name_count = props.num_Names;
1217 for (i = 0; i < name_count; ++i) {
1218 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1221 TT_Get_Name_String(handle->face, i, &name, &name_len);
1223 if (platform_id != TT_PLATFORM_APPLE_UNICODE && name_len
1224 && name_id == TT_NAME_ID_PS_NAME) {
1225 int might_want_index = -1;
1226 int might_score = 0;
1227 if ((platform_id == TT_PLATFORM_MACINTOSH && encoding_id == TT_MAC_ID_ROMAN)
1229 (platform_id == TT_PLATFORM_MICROSOFT && encoding_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)) {
1230 /* exactly what we want */
1235 if (platform_id == TT_PLATFORM_MICROSOFT
1236 && (encoding_id & 0xFF) == TT_MS_LANGID_ENGLISH_GENERAL) {
1237 /* any english is good */
1238 might_want_index = i;
1241 /* there might be something in between */
1243 /* anything non-unicode is better than nothing */
1244 might_want_index = i;
1247 if (might_score > score) {
1248 score = might_score;
1249 want_index = might_want_index;
1254 if (want_index != -1) {
1255 TT_Get_Name_String(handle->face, want_index, &name, &name_len);
1257 strncpy(name_buf, name, name_buf_size);
1258 name_buf[name_buf_size-1] = '\0';
1260 return strlen(name) + 1;
1263 i_push_error(0, "no face name present");
1268 void i_tt_dump_names(TT_Fonthandle *handle) {
1269 TT_Face_Properties props;
1272 TT_UShort platform_id, encoding_id, lang_id, name_id;
1276 TT_Get_Face_Properties(handle->face, &props);
1277 name_count = props.num_Names;
1278 for (i = 0; i < name_count; ++i) {
1279 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1281 TT_Get_Name_String(handle->face, i, &name, &name_len);
1283 printf("# %d: plat %d enc %d lang %d name %d value ", i, platform_id,
1284 encoding_id, lang_id, name_id);
1285 if (platform_id == TT_PLATFORM_APPLE_UNICODE) {
1286 printf("(unicode)\n");
1289 printf("'%s'\n", name);
1296 i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf,
1297 size_t name_buf_size) {
1305 if (!handle->loaded_names) {
1307 mm_log((1, "Loading PS Names"));
1308 handle->load_cond = TT_Load_PS_Names(handle->face, &post);
1309 ++handle->loaded_names;
1312 if (handle->load_cond) {
1313 i_push_errorf(handle->load_cond, "error loading names (%d)", handle->load_cond);
1317 index = TT_Char_Index(handle->char_map, ch);
1319 i_push_error(0, "no such character");
1323 rc = TT_Get_PS_Name(handle->face, index, &psname);
1326 i_push_error(rc, "error getting name");
1330 strncpy(name_buf, psname, name_buf_size);
1331 name_buf[name_buf_size-1] = '\0';
1333 return strlen(psname) + 1;
1335 mm_log((1, "FTXPOST extension not enabled\n"));
1337 i_push_error(0, "Use of FTXPOST extension disabled");
1344 =item i_tt_push_error(code)
1346 Push an error message and code onto the Imager error stack.
1351 i_tt_push_error(TT_Error rc) {
1353 TT_String const *msg = TT_ErrToString18(rc);
1355 i_push_error(rc, msg);
1357 i_push_errorf(rc, "Error code 0x%04x", (unsigned)rc);
1361 #endif /* HAVE_LIBTT */
1369 Arnar M. Hrafnkelsson <addi@umich.edu>