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, i_img_dim cords[BOUNDING_BOX_COUNT]);
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, i_img_dim points, int smooth );
124 static void i_tt_init_raster_map( TT_Raster_Map* bit, i_img_dim width, i_img_dim 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,i_img_dim x_off, i_img_dim 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 i_img_dim x_off, i_img_dim 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, i_img_dim 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, i_img_dim xb, i_img_dim yb, const i_color *cl, int smooth );
138 static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, i_img_dim xb, i_img_dim yb, int channel, int smooth );
140 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, i_img_dim cords[6],
141 double 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, i_img_dim 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",
178 i_tt_push_error(error);
179 i_push_error(0, "Could not initialize freetype 1.x");
184 error = TT_Init_Post_Extension( engine );
186 mm_log((1, "Initialization of Post extension failed = 0x%x\n",
189 i_tt_push_error(error);
190 i_push_error(0, "Could not initialize FT 1.x POST extension");
195 error = TT_Set_Raster_Gray_Palette(engine, palette);
197 mm_log((1, "Initialization of gray levels failed = 0x%x\n",
199 i_tt_push_error(error);
200 i_push_error(0, "Could not initialize FT 1.x POST extension");
211 =item i_tt_get_instance(handle, points, smooth)
213 Finds a points+smooth instance or if one doesn't exist in the cache
214 allocates room and returns its cache entry
216 fontname - path to the font to load
217 handle - handle to the font.
218 points - points of the requested font
219 smooth - boolean (True: antialias on, False: antialias is off)
226 i_tt_get_instance( TT_Fonthandle *handle, i_img_dim points, int smooth ) {
230 mm_log((1,"i_tt_get_instance(handle %p, points %" i_DF ", smooth %d)\n",
231 handle, i_DFc(points), smooth));
233 if (smooth == -1) { /* Smooth doesn't matter for this search */
234 for(i=0;i<TT_CHC;i++) {
235 if (handle->instanceh[i].ptsize==points) {
236 mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
240 smooth=1; /* We will be adding a font - add it as smooth then */
241 } else { /* Smooth doesn't matter for this search */
242 for(i=0;i<TT_CHC;i++) {
243 if (handle->instanceh[i].ptsize == points
244 && handle->instanceh[i].smooth == smooth) {
245 mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
251 /* Found the instance in the cache - return the cache index */
253 for(idx=0;idx<TT_CHC;idx++) {
254 if (!(handle->instanceh[idx].order)) break; /* find the lru item */
257 mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
258 mm_log((1,"i_tt_get_instance: lru pointer %p\n",
259 USTRCT(handle->instanceh[idx].instance) ));
261 if ( USTRCT(handle->instanceh[idx].instance) ) {
262 mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
264 /* Free cached glyphs */
266 if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
267 TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
270 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
271 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
274 /* Free instance if needed */
275 TT_Done_Instance( handle->instanceh[idx].instance );
278 /* create and initialize instance */
279 /* FIXME: probably a memory leak on fail */
281 (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) ||
282 ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
283 ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
286 mm_log((1, "Could not create and initialize instance: error %x.\n",
291 /* Now that the instance should the inplace we need to lower all of the
292 ru counts and put `this' one with the highest entry */
294 for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
296 handle->instanceh[idx].order=TT_CHC-1;
297 handle->instanceh[idx].ptsize=points;
298 handle->instanceh[idx].smooth=smooth;
299 TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
301 /* Zero the memory for the glyph storage so they are not thought as
302 cached if they haven't been cached since this new font was loaded */
305 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
306 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
314 =item i_tt_new(fontname)
316 Creates a new font handle object, finds a character map and initialise the
317 the font handle's cache
319 fontname - path to the font to load
325 i_tt_new(const char *fontname) {
327 TT_Fonthandle *handle;
329 unsigned short platform,encoding;
331 if (!TT_initialized && i_init_tt()) {
332 i_push_error(0, "Could not initialize FT1 engine");
338 mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
340 /* allocate memory for the structure */
342 handle = mymalloc( sizeof(TT_Fonthandle) ); /* checked 5Nov05 tonyc */
344 /* load the typeface */
345 error = TT_Open_Face( engine, fontname, &handle->face );
347 if ( error == TT_Err_Could_Not_Open_File ) {
348 mm_log((1, "Could not find/open %s.\n", fontname ));
351 mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname,
354 i_tt_push_error(error);
358 TT_Get_Face_Properties( handle->face, &(handle->properties) );
360 /* First, look for a Unicode charmap */
361 n = handle->properties.num_CharMaps;
362 USTRCT( handle->char_map )=NULL; /* Invalidate character map */
364 for ( i = 0; i < n; i++ ) {
365 TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
366 if ( (platform == 3 && encoding == 1 )
367 || (platform == 0 && encoding == 0 ) ) {
368 mm_log((2,"i_tt_new - found char map platform %u encoding %u\n",
369 platform, encoding));
370 TT_Get_CharMap( handle->face, i, &(handle->char_map) );
374 if (!USTRCT(handle->char_map) && n != 0) {
375 /* just use the first one */
376 TT_Get_CharMap( handle->face, 0, &(handle->char_map));
379 /* Zero the pointsizes - and ordering */
381 for(i=0;i<TT_CHC;i++) {
382 USTRCT(handle->instanceh[i].instance)=NULL;
383 handle->instanceh[i].order=i;
384 handle->instanceh[i].ptsize=0;
385 handle->instanceh[i].smooth=-1;
389 handle->loaded_names = 0;
392 mm_log((1,"i_tt_new <- %p\n",handle));
399 *Â raster map management
403 =item i_tt_init_raster_map(bit, width, height, smooth)
405 Allocates internal memory for the bitmap as needed by the parameters (internal)
407 bit - bitmap to allocate into
408 width - width of the bitmap
409 height - height of the bitmap
410 smooth - boolean (True: antialias on, False: antialias is off)
417 i_tt_init_raster_map( TT_Raster_Map* bit, i_img_dim width, i_img_dim height, int smooth ) {
419 mm_log((1,"i_tt_init_raster_map( bit %p, width %" i_DF ", height %" i_DF
420 ", smooth %d)\n", bit, i_DFc(width), i_DFc(height), smooth));
423 bit->width = ( width + 3 ) & -4;
424 bit->flow = TT_Flow_Down;
427 bit->cols = bit->width;
428 bit->size = bit->rows * bit->width;
430 bit->cols = ( bit->width + 7 ) / 8; /* convert to # of bytes */
431 bit->size = bit->rows * bit->cols; /* number of bytes in buffer */
434 /* rows can be 0 for some glyphs, for example ' ' */
435 if (bit->rows && bit->size / bit->rows != bit->cols) {
436 i_fatal(0, "Integer overflow calculating bitmap size (%d, %d)\n",
437 bit->width, bit->rows);
440 mm_log((1,"i_tt_init_raster_map: bit->width %d, bit->cols %d, bit->rows %d, bit->size %ld)\n", bit->width, bit->cols, bit->rows, bit->size ));
442 bit->bitmap = (void *) mymalloc( bit->size ); /* checked 6Nov05 tonyc */
443 if ( !bit->bitmap ) i_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",bit->size );
448 =item i_tt_clear_raster_map(bit)
450 Frees the bitmap data and sets pointer to NULL (internal)
459 i_tt_done_raster_map( TT_Raster_Map *bit ) {
460 myfree( bit->bitmap );
466 =item i_tt_clear_raster_map(bit)
468 Clears the specified bitmap (internal)
478 i_tt_clear_raster_map( TT_Raster_Map* bit ) {
479 memset( bit->bitmap, 0, bit->size );
484 =item i_tt_blit_or(dst, src, x_off, y_off)
486 function that blits one raster map into another (internal)
488 dst - destination bitmap
490 x_off - x offset into the destination bitmap
491 y_off - y offset into the destination bitmap
498 i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,i_img_dim x_off, i_img_dim y_off ) {
500 i_img_dim x1, x2, y1, y2;
501 unsigned char *s, *d;
503 x1 = x_off < 0 ? -x_off : 0;
504 y1 = y_off < 0 ? -y_off : 0;
506 x2 = (int)dst->cols - x_off;
507 if ( x2 > src->cols ) x2 = src->cols;
509 y2 = (int)dst->rows - y_off;
510 if ( y2 > src->rows ) y2 = src->rows;
512 if ( x1 >= x2 ) return;
514 /* do the real work now */
516 for ( y = y1; y < y2; ++y ) {
517 s = ( (unsigned char*)src->bitmap ) + y * src->cols + x1;
518 d = ( (unsigned char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
520 for ( x = x1; x < x2; ++x ) {
529 /* useful for debugging */
532 static void dump_raster_map(FILE *out, TT_Raster_Map *bit ) {
534 fprintf(out, "cols %d rows %d flow %d\n", bit->cols, bit->rows, bit->flow);
535 for (y = 0; y < bit->rows; ++y) {
536 fprintf(out, "%2d:", y);
537 for (x = 0; x < bit->cols; ++x) {
538 if ((x & 7) == 0 && x) putc(' ', out);
539 fprintf(out, "%02x", ((unsigned char *)bit->bitmap)[y*bit->cols+x]);
548 =item i_tt_get_glyph(handle, inst, j)
550 Function to see if a glyph exists and if so cache it (internal)
552 handle - pointer to font handle
554 j - charcode of glyph
561 i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j) {
562 unsigned short load_flags, code;
565 mm_log((1, "i_tt_get_glyph(handle %p, inst %d, j %lu (%c))\n",
566 handle,inst,j, (int)((j >= ' ' && j <= '~') ? j : '.')));
568 /*mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] ));*/
570 if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)
571 && handle->instanceh[inst].glyphs[TT_HASH(j)].ch == j) {
572 mm_log((1,"i_tt_get_glyph: %lu in cache\n",j));
576 if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) ) {
577 /* clean up the entry */
578 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
579 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
580 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
583 /* Ok - it wasn't cached - try to get it in */
584 load_flags = TTLOAD_SCALE_GLYPH;
585 if ( LTT_hinted ) load_flags |= TTLOAD_HINT_GLYPH;
587 if ( !TT_VALID(handle->char_map) ) {
588 code = (j - ' ' + 1) < 0 ? 0 : (j - ' ' + 1);
589 if ( code >= handle->properties.num_Glyphs ) code = 0;
590 } else code = TT_Char_Index( handle->char_map, j );
592 if ( (error = TT_New_Glyph( handle->face, &handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)) ) {
593 mm_log((1, "Cannot allocate and load glyph: error %#x.\n", (unsigned)error ));
594 i_push_error(error, "TT_New_Glyph()");
597 if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, code, load_flags)) ) {
598 mm_log((1, "Cannot allocate and load glyph: error %#x.\n", (unsigned)error ));
600 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
601 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
602 i_push_error(error, "TT_Load_Glyph()");
606 /* At this point the glyph should be allocated and loaded */
607 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = j;
609 /* Next get the glyph metrics */
610 error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
611 &handle->instanceh[inst].gmetrics[TT_HASH(j)] );
613 mm_log((1, "TT_Get_Glyph_Metrics: error %#x.\n", (unsigned)error ));
614 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
615 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
616 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
617 i_push_error(error, "TT_Get_Glyph_Metrics()");
625 =item i_tt_has_chars(handle, text, len, utf8, out)
627 Check if the given characters are defined by the font. Note that len
628 is the number of bytes, not the number of characters (when utf8 is
631 Returns the number of characters that were checked.
637 i_tt_has_chars(TT_Fonthandle *handle, char const *text, size_t len, int utf8,
640 mm_log((1, "i_tt_has_chars(handle %p, text %p, len %ld, utf8 %d)\n",
641 handle, text, (long)len, utf8));
647 c = i_utf8_advance(&text, &len);
649 i_push_error(0, "invalid UTF8 character");
654 c = (unsigned char)*text++;
658 if (TT_VALID(handle->char_map)) {
659 index = TT_Char_Index(handle->char_map, c);
662 index = (c - ' ' + 1) < 0 ? 0 : (c - ' ' + 1);
663 if (index >= handle->properties.num_Glyphs)
674 =item i_tt_destroy(handle)
676 Clears the data taken by a font including all cached data such as
679 handle - pointer to font handle
685 i_tt_destroy( TT_Fonthandle *handle) {
686 TT_Close_Face( handle->face );
689 /* FIXME: Should these be freed automatically by the library?
691 TT_Done_Instance( instance );
693 i_tt_done_glyphs( void ) {
696 if ( !glyphs ) return;
698 for ( i = 0; i < 256; ++i ) TT_Done_Glyph( glyphs[i] );
708 * FreeType Rendering functions
713 =item i_tt_render_glyph(handle, gmetrics, bit, smallbit, x_off, y_off, smooth)
715 Renders a single glyph into the bit rastermap (internal)
717 handle - pointer to font handle
718 gmetrics - the metrics for the glyph to be rendered
719 bit - large bitmap that is the destination for the text
720 smallbit - small bitmap that is used only if smooth is true
721 x_off - x offset of glyph
722 y_off - y offset of glyph
723 smooth - boolean (True: antialias on, False: antialias is off)
730 i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics, TT_Raster_Map *bit, TT_Raster_Map *small_bit, i_img_dim x_off, i_img_dim y_off, int smooth ) {
732 mm_log((1,"i_tt_render_glyph(glyph %p, gmetrics %p, bit %p, small_bit %p, x_off %" i_DF ", y_off %" i_DF ", smooth %d)\n",
733 USTRCT(glyph), gmetrics, bit, small_bit, i_DFc(x_off),
734 i_DFc(y_off), smooth));
736 if ( !smooth ) TT_Get_Glyph_Bitmap( glyph, bit, x_off * 64, y_off * 64);
738 TT_F26Dot6 xmin, ymin, xmax, ymax;
740 xmin = gmetrics->bbox.xMin & -64;
741 ymin = gmetrics->bbox.yMin & -64;
742 xmax = (gmetrics->bbox.xMax + 63) & -64;
743 ymax = (gmetrics->bbox.yMax + 63) & -64;
745 i_tt_clear_raster_map( small_bit );
746 TT_Get_Glyph_Pixmap( glyph, small_bit, -xmin, -ymin );
747 i_tt_blit_or( bit, small_bit, xmin/64 + x_off, -ymin/64 - y_off );
753 =item i_tt_render_all_glyphs(handle, inst, bit, small_bit, cords, txt, len, smooth)
755 calls i_tt_render_glyph to render each glyph into the bit rastermap (internal)
757 handle - pointer to font handle
759 bit - large bitmap that is the destination for the text
760 smallbit - small bitmap that is used only if smooth is true
761 txt - string to render
762 len - length of the string to render
763 smooth - boolean (True: antialias on, False: antialias is off)
770 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
771 TT_Raster_Map *small_bit, i_img_dim cords[6],
772 char const* txt, size_t len, int smooth, int utf8 ) {
776 mm_log((1,"i_tt_render_all_glyphs( handle %p, inst %d, bit %p, small_bit %p, txt '%.*s', len %ld, smooth %d, utf8 %d)\n",
777 handle, inst, bit, small_bit, (int)len, txt, (long)len, smooth, utf8));
780 y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM);
783 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 */
788 j = i_utf8_advance(&txt, &len);
790 i_push_error(0, "invalid UTF8 character");
795 j = (unsigned char)*txt++;
798 if ( !i_tt_get_glyph(handle,inst,j) )
800 i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
801 &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit,
802 small_bit, x, y, smooth );
803 x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64;
811 * Functions to render rasters (single channel images) onto images
815 =item i_tt_dump_raster_map2(im, bit, xb, yb, cl, smooth)
817 Function to dump a raster onto an image in color used by i_tt_text() (internal).
819 im - image to dump raster on
820 bit - bitmap that contains the text to be dumped to im
821 xb, yb - coordinates, left edge and baseline
822 cl - color to use for text
823 smooth - boolean (True: antialias on, False: antialias is off)
830 i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, i_img_dim xb, i_img_dim yb, const i_color *cl, int smooth ) {
833 mm_log((1,"i_tt_dump_raster_map2(im %p, bit %p, xb %" i_DF ", yb %" i_DF ", cl %p)\n",
834 im, bit, i_DFc(xb), i_DFc(yb), cl));
841 i_render_init(&r, im, bit->cols);
842 for(y=0;y<bit->rows;y++) {
843 i_render_color(&r, xb, yb+y, bit->cols, bmap + y*bit->cols, cl);
847 for(y=0;y<bit->rows;y++) {
848 unsigned mask = 0x80;
849 unsigned char *p = bmap + y * bit->cols;
851 for(x = 0; x < bit->width; x++) {
853 i_ppix(im, x+xb, y+yb, cl);
868 =item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth)
870 Function to dump a raster onto a single channel image in color (internal)
872 im - image to dump raster on
873 bit - bitmap that contains the text to be dumped to im
874 xb, yb - coordinates, left edge and baseline
875 channel - channel to copy to
876 smooth - boolean (True: antialias on, False: antialias is off)
883 i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, i_img_dim xb, i_img_dim yb, int channel, int smooth ) {
888 int old_mask = im->ch_mask;
889 im->ch_mask = 1 << channel;
891 mm_log((1,"i_tt_dump_raster_channel(im %p, bit %p, xb %" i_DF ", yb %" i_DF ", channel %d)\n",
892 im, bit, i_DFc(xb), i_DFc(yb), channel));
897 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
898 c = bmap[y*(bit->cols)+x];
899 val.channel[channel] = c;
900 i_ppix(im,x+xb,y+yb,&val);
903 for(y=0;y<bit->rows;y++) {
904 unsigned mask = 0x80;
905 unsigned char *p = bmap + y * bit->cols;
907 for(x=0;x<bit->width;x++) {
908 val.channel[channel] = (*p & mask) ? 255 : 0;
909 i_ppix(im,x+xb,y+yb,&val);
919 im->ch_mask = old_mask;
924 =item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth)
926 interface for generating single channel raster of text (internal)
928 handle - pointer to font handle
929 bit - the bitmap that is allocated, rendered into and NOT freed
930 cords - the bounding box (modified in place)
931 points - font size to use
932 txt - string to render
933 len - length of the string to render
934 smooth - boolean (True: antialias on, False: antialias is off)
941 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, i_img_dim cords[6], double points, char const* txt, size_t len, int smooth, int utf8 ) {
943 i_img_dim width, height;
944 TT_Raster_Map small_bit;
946 /* find or install an instance */
947 if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) {
948 mm_log((1,"i_tt_rasterize: get instance failed\n"));
952 /* calculate bounding box */
953 if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 ))
957 width = cords[2]-cords[0];
958 height = cords[5]-cords[4];
960 mm_log((1,"i_tt_rasterize: width=%" i_DF ", height=%" i_DF "\n",
961 i_DFc(width), i_DFc(height) ));
963 i_tt_init_raster_map ( bit, width, height, smooth );
964 i_tt_clear_raster_map( bit );
965 if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth );
967 if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len,
970 i_tt_done_raster_map( &small_bit );
974 if ( smooth ) i_tt_done_raster_map( &small_bit );
981 * Exported text rendering interfaces
986 =item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth, utf8)
988 Interface to text rendering into a single channel in an image
990 handle - pointer to font handle
991 im - image to render text on to
992 xb, yb - coordinates, left edge and baseline
993 channel - channel to render into
994 points - font size to use
995 txt - string to render
996 len - length of the string to render
997 smooth - boolean (True: antialias on, False: antialias is off)
1003 i_tt_cp( TT_Fonthandle *handle, i_img *im, i_img_dim xb, i_img_dim yb, int channel, double points, char const* txt, size_t len, int smooth, int utf8, int align ) {
1005 i_img_dim cords[BOUNDING_BOX_COUNT];
1006 i_img_dim ascent, st_offset, y;
1010 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1012 ascent=cords[BBOX_ASCENT];
1013 st_offset=cords[BBOX_NEG_WIDTH];
1014 y = align ? yb-ascent : yb;
1016 i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , y, channel, smooth );
1017 i_tt_done_raster_map( &bit );
1024 =item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8)
1026 Interface to text rendering in a single color onto an image
1028 handle - pointer to font handle
1029 im - image to render text on to
1030 xb, yb - coordinates, left edge and baseline
1031 cl - color to use for text
1032 points - font size to use
1033 txt - string to render
1034 len - length of the string to render
1035 smooth - boolean (True: antialias on, False: antialias is off)
1041 i_tt_text( TT_Fonthandle *handle, i_img *im, i_img_dim xb, i_img_dim yb, const i_color *cl, double points, char const* txt, size_t len, int smooth, int utf8, int align) {
1042 i_img_dim cords[BOUNDING_BOX_COUNT];
1043 i_img_dim ascent, st_offset, y;
1048 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1050 ascent=cords[BBOX_ASCENT];
1051 st_offset=cords[BBOX_NEG_WIDTH];
1052 y = align ? yb-ascent : yb;
1054 i_tt_dump_raster_map2( im, &bit, xb+st_offset, y, cl, smooth );
1055 i_tt_done_raster_map( &bit );
1062 =item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8)
1064 Function to get texts bounding boxes given the instance of the font (internal)
1066 handle - pointer to font handle
1067 inst - font instance
1068 txt - string to measure
1069 len - length of the string to render
1070 cords - the bounding box (modified in place)
1077 i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, size_t len, i_img_dim cords[BOUNDING_BOX_COUNT], int utf8 ) {
1078 int upm, casc, cdesc, first;
1081 i_img_dim width = 0;
1089 unsigned char *ustr;
1090 ustr=(unsigned char*)txt;
1092 mm_log((1,"i_tt_box_inst(handle %p,inst %d,txt '%.*s', len %ld, utf8 %d)\n",
1093 handle, inst, (int)len, txt, (long)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 (int)((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, double points,const char *txt,size_t len,i_img_dim cords[6], int utf8) {
1182 mm_log((1,"i_tt_box(handle %p,points %f,txt '%.*s', len %ld, utf8 %d)\n",
1183 handle, points, (int)len, txt, (long)len, utf8));
1185 if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
1186 i_push_errorf(0, "i_tt_get_instance(%g)", points);
1187 mm_log((1,"i_tt_text: get instance failed\n"));
1191 return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8);
1195 =item i_tt_face_name(handle, name_buf, name_buf_size)
1197 Retrieve's the font's postscript name.
1199 This is complicated by the need to handle encodings and so on.
1204 i_tt_face_name(TT_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
1205 TT_Face_Properties props;
1208 TT_UShort platform_id, encoding_id, lang_id, name_id;
1211 int want_index = -1; /* an acceptable but not perfect name */
1216 TT_Get_Face_Properties(handle->face, &props);
1217 name_count = props.num_Names;
1218 for (i = 0; i < name_count; ++i) {
1219 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1222 TT_Get_Name_String(handle->face, i, &name, &name_len);
1224 if (platform_id != TT_PLATFORM_APPLE_UNICODE && name_len
1225 && name_id == TT_NAME_ID_PS_NAME) {
1226 int might_want_index = -1;
1227 int might_score = 0;
1228 if ((platform_id == TT_PLATFORM_MACINTOSH && encoding_id == TT_MAC_ID_ROMAN)
1230 (platform_id == TT_PLATFORM_MICROSOFT && encoding_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)) {
1231 /* exactly what we want */
1236 if (platform_id == TT_PLATFORM_MICROSOFT
1237 && (encoding_id & 0xFF) == TT_MS_LANGID_ENGLISH_GENERAL) {
1238 /* any english is good */
1239 might_want_index = i;
1242 /* there might be something in between */
1244 /* anything non-unicode is better than nothing */
1245 might_want_index = i;
1248 if (might_score > score) {
1249 score = might_score;
1250 want_index = might_want_index;
1255 if (want_index != -1) {
1256 TT_Get_Name_String(handle->face, want_index, &name, &name_len);
1258 strncpy(name_buf, name, name_buf_size);
1259 name_buf[name_buf_size-1] = '\0';
1261 return strlen(name) + 1;
1264 i_push_error(0, "no face name present");
1269 void i_tt_dump_names(TT_Fonthandle *handle) {
1270 TT_Face_Properties props;
1273 TT_UShort platform_id, encoding_id, lang_id, name_id;
1277 TT_Get_Face_Properties(handle->face, &props);
1278 name_count = props.num_Names;
1279 for (i = 0; i < name_count; ++i) {
1280 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1282 TT_Get_Name_String(handle->face, i, &name, &name_len);
1284 printf("# %d: plat %d enc %d lang %d name %d value ", i, platform_id,
1285 encoding_id, lang_id, name_id);
1286 if (platform_id == TT_PLATFORM_APPLE_UNICODE) {
1287 printf("(unicode)\n");
1290 printf("'%s'\n", name);
1297 i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf,
1298 size_t name_buf_size) {
1306 if (!handle->loaded_names) {
1308 mm_log((1, "Loading PS Names"));
1309 handle->load_cond = TT_Load_PS_Names(handle->face, &post);
1310 ++handle->loaded_names;
1313 if (handle->load_cond) {
1314 i_push_errorf(handle->load_cond, "error loading names (%#x)",
1315 (unsigned)handle->load_cond);
1319 index = TT_Char_Index(handle->char_map, ch);
1321 i_push_error(0, "no such character");
1325 rc = TT_Get_PS_Name(handle->face, index, &psname);
1328 i_push_error(rc, "error getting name");
1332 strncpy(name_buf, psname, name_buf_size);
1333 name_buf[name_buf_size-1] = '\0';
1335 return strlen(psname) + 1;
1337 mm_log((1, "FTXPOST extension not enabled\n"));
1339 i_push_error(0, "Use of FTXPOST extension disabled");
1346 =item i_tt_push_error(code)
1348 Push an error message and code onto the Imager error stack.
1353 i_tt_push_error(TT_Error rc) {
1355 TT_String const *msg = TT_ErrToString18(rc);
1357 i_push_error(rc, msg);
1359 i_push_errorf(rc, "Error code 0x%04x", (unsigned)rc);
1363 #endif /* HAVE_LIBTT */
1371 Arnar M. Hrafnkelsson <addi@umich.edu>