15 fontft1.c - Freetype 1.x font driver for Imager
19 handle = i_tt_new(path_to_ttf);
20 rc = i_tt_bbox(handle, points, "foo", 3, int cords[6], utf8);
27 fontft1.c implements font creation, rendering, bounding box functions and
28 more for Imager using Freetype 1.x.
30 In general this driver should be ignored in favour of the FT2 driver.
32 =head1 FUNCTION REFERENCE
34 Some of these functions are internal.
43 /* Truetype font support */
44 /* These are enabled by default when configuring Freetype 1.x
45 I haven't a clue how to reliably detect it at compile time.
47 We need a compilation probe in Makefile.PL
63 /* some versions of FT1.x don't seem to define this - it's font defined
65 #ifndef TT_MS_LANGID_ENGLISH_GENERAL
66 #define TT_MS_LANGID_ENGLISH_GENERAL 0x0409
69 static im_slot_t slot = -1;
71 /* convert a code point into an index in the glyph cache */
72 #define TT_HASH(x) ((x) & 0xFF)
79 typedef struct i_glyph_entry_ {
84 #define TT_NOCHAR (~0UL)
86 struct TT_Instancehandle_ {
88 TT_Instance_Metrics imetrics;
89 TT_Glyph_Metrics gmetrics[256];
90 i_tt_glyph_entry glyphs[256];
96 typedef struct TT_Instancehandle_ TT_Instancehandle;
98 struct TT_Fonthandle_ {
100 TT_Face_Properties properties;
101 TT_Instancehandle instanceh[TT_CHC];
111 #define USTRCT(x) ((x).z)
112 #define TT_VALID( handle ) ( ( handle ).z != NULL )
114 static void i_tt_push_error(TT_Error rc);
115 static void i_tt_uninit(void *);
119 static int i_tt_get_instance( TT_Fonthandle *handle, i_img_dim points, int smooth );
120 static void i_tt_init_raster_map( TT_Raster_Map* bit, i_img_dim width, i_img_dim height, int smooth );
121 static void i_tt_done_raster_map( TT_Raster_Map *bit );
122 static void i_tt_clear_raster_map( TT_Raster_Map* bit );
123 static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,i_img_dim x_off, i_img_dim y_off );
124 static int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j );
126 i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics,
127 TT_Raster_Map *bit, TT_Raster_Map *small_bit,
128 i_img_dim x_off, i_img_dim y_off, int smooth );
130 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
131 TT_Raster_Map *small_bit, i_img_dim cords[6],
132 char const* txt, size_t len, int smooth, int utf8 );
133 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 );
134 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 );
136 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, i_img_dim cords[6],
137 double points, char const* txt, size_t len, int smooth, int utf8 );
138 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 );
141 /* static globals needed */
143 static int LTT_dpi = 72; /* FIXME: this ought to be a part of the call interface */
144 static int LTT_hinted = 1; /* FIXME: this too */
154 slot = im_context_slot_new(i_tt_uninit);
161 Initializes the freetype font rendering engine (if needed)
169 im_context_t ctx = im_get_context();
170 TT_Byte palette[] = { 0, 64, 127, 191, 255 };
171 i_tt_engine *result = im_context_slot_get(ctx, slot);
175 if (result == NULL) {
176 result = mymalloc(sizeof(i_tt_engine));
177 memset(result, 0, sizeof(*result));
178 im_context_slot_set(ctx, slot, result);
179 mm_log((1, "allocated FT1 state %p\n", result));
182 mm_log((1,"init_tt()\n"));
184 if (result->initialized)
187 error = TT_Init_FreeType( &result->engine );
189 mm_log((1,"Initialization of freetype failed, code = 0x%x\n",
191 i_tt_push_error(error);
192 i_push_error(0, "Could not initialize freetype 1.x");
197 error = TT_Init_Post_Extension( result->engine );
199 mm_log((1, "Initialization of Post extension failed = 0x%x\n",
202 i_tt_push_error(error);
203 i_push_error(0, "Could not initialize FT 1.x POST extension");
208 error = TT_Set_Raster_Gray_Palette(result->engine, palette);
210 mm_log((1, "Initialization of gray levels failed = 0x%x\n",
212 i_tt_push_error(error);
213 i_push_error(0, "Could not initialize FT 1.x POST extension");
217 mm_log((1, "initialized FT1 state %p\n", result));
219 result->initialized = 1;
225 i_tt_uninit(void *p) {
226 i_tt_engine *tteng = p;
228 if (tteng->initialized) {
229 mm_log((1, "finalizing FT1 state %p\n", tteng));
230 TT_Done_FreeType(tteng->engine);
232 mm_log((1, "freeing FT1 state %p\n", tteng));
237 =item i_tt_get_instance(handle, points, smooth)
239 Finds a points+smooth instance or if one doesn't exist in the cache
240 allocates room and returns its cache entry
242 fontname - path to the font to load
243 handle - handle to the font.
244 points - points of the requested font
245 smooth - boolean (True: antialias on, False: antialias is off)
252 i_tt_get_instance( TT_Fonthandle *handle, i_img_dim points, int smooth ) {
256 mm_log((1,"i_tt_get_instance(handle %p, points %" i_DF ", smooth %d)\n",
257 handle, i_DFc(points), smooth));
259 if (smooth == -1) { /* Smooth doesn't matter for this search */
260 for(i=0;i<TT_CHC;i++) {
261 if (handle->instanceh[i].ptsize==points) {
262 mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
266 smooth=1; /* We will be adding a font - add it as smooth then */
267 } else { /* Smooth doesn't matter for this search */
268 for(i=0;i<TT_CHC;i++) {
269 if (handle->instanceh[i].ptsize == points
270 && handle->instanceh[i].smooth == smooth) {
271 mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
277 /* Found the instance in the cache - return the cache index */
279 for(idx=0;idx<TT_CHC;idx++) {
280 if (!(handle->instanceh[idx].order)) break; /* find the lru item */
283 mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
284 mm_log((1,"i_tt_get_instance: lru pointer %p\n",
285 USTRCT(handle->instanceh[idx].instance) ));
287 if ( USTRCT(handle->instanceh[idx].instance) ) {
288 mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
290 /* Free cached glyphs */
292 if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
293 TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
296 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
297 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
300 /* Free instance if needed */
301 TT_Done_Instance( handle->instanceh[idx].instance );
304 /* create and initialize instance */
305 /* FIXME: probably a memory leak on fail */
307 (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) ||
308 ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
309 ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
312 mm_log((1, "Could not create and initialize instance: error %x.\n",
317 /* Now that the instance should the inplace we need to lower all of the
318 ru counts and put `this' one with the highest entry */
320 for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
322 handle->instanceh[idx].order=TT_CHC-1;
323 handle->instanceh[idx].ptsize=points;
324 handle->instanceh[idx].smooth=smooth;
325 TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
327 /* Zero the memory for the glyph storage so they are not thought as
328 cached if they haven't been cached since this new font was loaded */
331 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
332 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
340 =item i_tt_new(fontname)
342 Creates a new font handle object, finds a character map and initialise the
343 the font handle's cache
345 fontname - path to the font to load
351 i_tt_new(const char *fontname) {
353 TT_Fonthandle *handle;
355 unsigned short platform,encoding;
358 if ((tteng = i_init_tt()) == NULL) {
359 i_push_error(0, "Could not initialize FT1 engine");
365 mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
367 /* allocate memory for the structure */
369 handle = mymalloc( sizeof(TT_Fonthandle) ); /* checked 5Nov05 tonyc */
371 /* load the typeface */
372 error = TT_Open_Face( tteng->engine, fontname, &handle->face );
375 if ( error == TT_Err_Could_Not_Open_File ) {
376 mm_log((1, "Could not find/open %s.\n", fontname ));
379 mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname,
382 i_tt_push_error(error);
386 TT_Get_Face_Properties( handle->face, &(handle->properties) );
388 /* First, look for a Unicode charmap */
389 n = handle->properties.num_CharMaps;
390 USTRCT( handle->char_map )=NULL; /* Invalidate character map */
392 for ( i = 0; i < n; i++ ) {
393 TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
394 if ( (platform == 3 && encoding == 1 )
395 || (platform == 0 && encoding == 0 ) ) {
396 mm_log((2,"i_tt_new - found char map platform %u encoding %u\n",
397 platform, encoding));
398 TT_Get_CharMap( handle->face, i, &(handle->char_map) );
402 if (!USTRCT(handle->char_map) && n != 0) {
403 /* just use the first one */
404 TT_Get_CharMap( handle->face, 0, &(handle->char_map));
407 /* Zero the pointsizes - and ordering */
409 for(i=0;i<TT_CHC;i++) {
410 USTRCT(handle->instanceh[i].instance)=NULL;
411 handle->instanceh[i].order=i;
412 handle->instanceh[i].ptsize=0;
413 handle->instanceh[i].smooth=-1;
417 handle->loaded_names = 0;
420 mm_log((1,"i_tt_new <- %p\n",handle));
427 * raster map management
431 =item i_tt_init_raster_map(bit, width, height, smooth)
433 Allocates internal memory for the bitmap as needed by the parameters (internal)
435 bit - bitmap to allocate into
436 width - width of the bitmap
437 height - height of the bitmap
438 smooth - boolean (True: antialias on, False: antialias is off)
445 i_tt_init_raster_map( TT_Raster_Map* bit, i_img_dim width, i_img_dim height, int smooth ) {
447 mm_log((1,"i_tt_init_raster_map( bit %p, width %" i_DF ", height %" i_DF
448 ", smooth %d)\n", bit, i_DFc(width), i_DFc(height), smooth));
451 bit->width = ( width + 3 ) & -4;
452 bit->flow = TT_Flow_Down;
455 bit->cols = bit->width;
456 bit->size = bit->rows * bit->width;
458 bit->cols = ( bit->width + 7 ) / 8; /* convert to # of bytes */
459 bit->size = bit->rows * bit->cols; /* number of bytes in buffer */
462 /* rows can be 0 for some glyphs, for example ' ' */
463 if (bit->rows && bit->size / bit->rows != bit->cols) {
464 i_fatal(0, "Integer overflow calculating bitmap size (%d, %d)\n",
465 bit->width, bit->rows);
468 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 ));
470 bit->bitmap = (void *) mymalloc( bit->size ); /* checked 6Nov05 tonyc */
471 if ( !bit->bitmap ) i_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",bit->size );
476 =item i_tt_clear_raster_map(bit)
478 Frees the bitmap data and sets pointer to NULL (internal)
487 i_tt_done_raster_map( TT_Raster_Map *bit ) {
488 myfree( bit->bitmap );
494 =item i_tt_clear_raster_map(bit)
496 Clears the specified bitmap (internal)
506 i_tt_clear_raster_map( TT_Raster_Map* bit ) {
507 memset( bit->bitmap, 0, bit->size );
512 =item i_tt_blit_or(dst, src, x_off, y_off)
514 function that blits one raster map into another (internal)
516 dst - destination bitmap
518 x_off - x offset into the destination bitmap
519 y_off - y offset into the destination bitmap
526 i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,i_img_dim x_off, i_img_dim y_off ) {
528 i_img_dim x1, x2, y1, y2;
529 unsigned char *s, *d;
531 x1 = x_off < 0 ? -x_off : 0;
532 y1 = y_off < 0 ? -y_off : 0;
534 x2 = (int)dst->cols - x_off;
535 if ( x2 > src->cols ) x2 = src->cols;
537 y2 = (int)dst->rows - y_off;
538 if ( y2 > src->rows ) y2 = src->rows;
540 if ( x1 >= x2 ) return;
542 /* do the real work now */
544 for ( y = y1; y < y2; ++y ) {
545 s = ( (unsigned char*)src->bitmap ) + y * src->cols + x1;
546 d = ( (unsigned char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
548 for ( x = x1; x < x2; ++x ) {
557 /* useful for debugging */
560 static void dump_raster_map(FILE *out, TT_Raster_Map *bit ) {
562 fprintf(out, "cols %d rows %d flow %d\n", bit->cols, bit->rows, bit->flow);
563 for (y = 0; y < bit->rows; ++y) {
564 fprintf(out, "%2d:", y);
565 for (x = 0; x < bit->cols; ++x) {
566 if ((x & 7) == 0 && x) putc(' ', out);
567 fprintf(out, "%02x", ((unsigned char *)bit->bitmap)[y*bit->cols+x]);
576 =item i_tt_get_glyph(handle, inst, j)
578 Function to see if a glyph exists and if so cache it (internal)
580 handle - pointer to font handle
582 j - charcode of glyph
589 i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j) {
590 unsigned short load_flags, code;
593 mm_log((1, "i_tt_get_glyph(handle %p, inst %d, j %lu (%c))\n",
594 handle,inst,j, (int)((j >= ' ' && j <= '~') ? j : '.')));
596 /*mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] ));*/
598 if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)
599 && handle->instanceh[inst].glyphs[TT_HASH(j)].ch == j) {
600 mm_log((1,"i_tt_get_glyph: %lu in cache\n",j));
604 if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) ) {
605 /* clean up the entry */
606 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
607 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
608 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
611 /* Ok - it wasn't cached - try to get it in */
612 load_flags = TTLOAD_SCALE_GLYPH;
613 if ( LTT_hinted ) load_flags |= TTLOAD_HINT_GLYPH;
615 if ( !TT_VALID(handle->char_map) ) {
616 code = (j < ' ' - 1) ? 0 : (j - (' ' - 1));
617 if ( code >= handle->properties.num_Glyphs ) code = 0;
618 } else code = TT_Char_Index( handle->char_map, j );
620 if ( (error = TT_New_Glyph( handle->face, &handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)) ) {
621 mm_log((1, "Cannot allocate and load glyph: error %#x.\n", (unsigned)error ));
622 i_push_error(error, "TT_New_Glyph()");
625 if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, code, load_flags)) ) {
626 mm_log((1, "Cannot allocate and load glyph: error %#x.\n", (unsigned)error ));
628 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
629 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
630 i_push_error(error, "TT_Load_Glyph()");
634 /* At this point the glyph should be allocated and loaded */
635 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = j;
637 /* Next get the glyph metrics */
638 error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
639 &handle->instanceh[inst].gmetrics[TT_HASH(j)] );
641 mm_log((1, "TT_Get_Glyph_Metrics: error %#x.\n", (unsigned)error ));
642 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
643 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
644 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
645 i_push_error(error, "TT_Get_Glyph_Metrics()");
653 =item i_tt_has_chars(handle, text, len, utf8, out)
655 Check if the given characters are defined by the font. Note that len
656 is the number of bytes, not the number of characters (when utf8 is
659 Returns the number of characters that were checked.
665 i_tt_has_chars(TT_Fonthandle *handle, char const *text, size_t len, int utf8,
668 mm_log((1, "i_tt_has_chars(handle %p, text %p, len %ld, utf8 %d)\n",
669 handle, text, (long)len, utf8));
675 c = i_utf8_advance(&text, &len);
677 i_push_error(0, "invalid UTF8 character");
682 c = (unsigned char)*text++;
686 if (TT_VALID(handle->char_map)) {
687 index = TT_Char_Index(handle->char_map, c);
690 index = (c < ' ' - 1) ? 0 : (c - (' ' - 1));
691 if (index >= handle->properties.num_Glyphs)
702 =item i_tt_destroy(handle)
704 Clears the data taken by a font including all cached data such as
707 handle - pointer to font handle
713 i_tt_destroy( TT_Fonthandle *handle) {
714 TT_Close_Face( handle->face );
717 /* FIXME: Should these be freed automatically by the library?
719 TT_Done_Instance( instance );
721 i_tt_done_glyphs( void ) {
724 if ( !glyphs ) return;
726 for ( i = 0; i < 256; ++i ) TT_Done_Glyph( glyphs[i] );
736 * FreeType Rendering functions
741 =item i_tt_render_glyph(handle, gmetrics, bit, smallbit, x_off, y_off, smooth)
743 Renders a single glyph into the bit rastermap (internal)
745 handle - pointer to font handle
746 gmetrics - the metrics for the glyph to be rendered
747 bit - large bitmap that is the destination for the text
748 smallbit - small bitmap that is used only if smooth is true
749 x_off - x offset of glyph
750 y_off - y offset of glyph
751 smooth - boolean (True: antialias on, False: antialias is off)
758 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 ) {
760 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",
761 USTRCT(glyph), gmetrics, bit, small_bit, i_DFc(x_off),
762 i_DFc(y_off), smooth));
764 if ( !smooth ) TT_Get_Glyph_Bitmap( glyph, bit, x_off * 64, y_off * 64);
766 TT_F26Dot6 xmin, ymin;
768 xmin = gmetrics->bbox.xMin & -64;
769 ymin = gmetrics->bbox.yMin & -64;
771 i_tt_clear_raster_map( small_bit );
772 TT_Get_Glyph_Pixmap( glyph, small_bit, -xmin, -ymin );
773 i_tt_blit_or( bit, small_bit, xmin/64 + x_off, -ymin/64 - y_off );
779 =item i_tt_render_all_glyphs(handle, inst, bit, small_bit, cords, txt, len, smooth)
781 calls i_tt_render_glyph to render each glyph into the bit rastermap (internal)
783 handle - pointer to font handle
785 bit - large bitmap that is the destination for the text
786 smallbit - small bitmap that is used only if smooth is true
787 txt - string to render
788 len - length of the string to render
789 smooth - boolean (True: antialias on, False: antialias is off)
796 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
797 TT_Raster_Map *small_bit, i_img_dim cords[6],
798 char const* txt, size_t len, int smooth, int utf8 ) {
802 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",
803 handle, inst, bit, small_bit, (int)len, txt, (long)len, smooth, utf8));
806 y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM);
809 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 */
814 j = i_utf8_advance(&txt, &len);
816 i_push_error(0, "invalid UTF8 character");
821 j = (unsigned char)*txt++;
824 if ( !i_tt_get_glyph(handle,inst,j) )
826 i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
827 &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit,
828 small_bit, x, y, smooth );
829 x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64;
837 * Functions to render rasters (single channel images) onto images
841 =item i_tt_dump_raster_map2(im, bit, xb, yb, cl, smooth)
843 Function to dump a raster onto an image in color used by i_tt_text() (internal).
845 im - image to dump raster on
846 bit - bitmap that contains the text to be dumped to im
847 xb, yb - coordinates, left edge and baseline
848 cl - color to use for text
849 smooth - boolean (True: antialias on, False: antialias is off)
856 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 ) {
859 mm_log((1,"i_tt_dump_raster_map2(im %p, bit %p, xb %" i_DF ", yb %" i_DF ", cl %p)\n",
860 im, bit, i_DFc(xb), i_DFc(yb), cl));
867 i_render_init(&r, im, bit->cols);
868 for(y=0;y<bit->rows;y++) {
869 i_render_color(&r, xb, yb+y, bit->cols, bmap + y*bit->cols, cl);
873 unsigned char *bmp = mymalloc(bit->width);
876 i_render_init(&r, im, bit->width);
878 for(y=0;y<bit->rows;y++) {
879 unsigned mask = 0x80;
880 unsigned char *p = bmap + y * bit->cols;
881 unsigned char *pout = bmp;
883 for(x = 0; x < bit->width; x++) {
884 *pout++ = (*p & mask) ? 0xFF : 0;
892 i_render_color(&r, xb, yb+y, bit->width, bmp, cl);
902 =item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth)
904 Function to dump a raster onto a single channel image in color (internal)
906 im - image to dump raster on
907 bit - bitmap that contains the text to be dumped to im
908 xb, yb - coordinates, left edge and baseline
909 channel - channel to copy to
910 smooth - boolean (True: antialias on, False: antialias is off)
917 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 ) {
922 int old_mask = im->ch_mask;
923 im->ch_mask = 1 << channel;
925 mm_log((1,"i_tt_dump_raster_channel(im %p, bit %p, xb %" i_DF ", yb %" i_DF ", channel %d)\n",
926 im, bit, i_DFc(xb), i_DFc(yb), channel));
931 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
932 c = bmap[y*(bit->cols)+x];
933 val.channel[channel] = c;
934 i_ppix(im,x+xb,y+yb,&val);
937 for(y=0;y<bit->rows;y++) {
938 unsigned mask = 0x80;
939 unsigned char *p = bmap + y * bit->cols;
941 for(x=0;x<bit->width;x++) {
942 val.channel[channel] = (*p & mask) ? 255 : 0;
943 i_ppix(im,x+xb,y+yb,&val);
953 im->ch_mask = old_mask;
958 =item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth)
960 interface for generating single channel raster of text (internal)
962 handle - pointer to font handle
963 bit - the bitmap that is allocated, rendered into and NOT freed
964 cords - the bounding box (modified in place)
965 points - font size to use
966 txt - string to render
967 len - length of the string to render
968 smooth - boolean (True: antialias on, False: antialias is off)
975 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, i_img_dim *cords, double points, char const* txt, size_t len, int smooth, int utf8 ) {
977 i_img_dim width, height;
978 TT_Raster_Map small_bit;
980 /* find or install an instance */
981 if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) {
982 mm_log((1,"i_tt_rasterize: get instance failed\n"));
986 /* calculate bounding box */
987 if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 ))
991 width = cords[2]-cords[0];
992 height = cords[5]-cords[4];
994 mm_log((1,"i_tt_rasterize: width=%" i_DF ", height=%" i_DF "\n",
995 i_DFc(width), i_DFc(height) ));
997 i_tt_init_raster_map ( bit, width, height, smooth );
998 i_tt_clear_raster_map( bit );
999 if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth );
1001 if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len,
1004 i_tt_done_raster_map( &small_bit );
1008 if ( smooth ) i_tt_done_raster_map( &small_bit );
1015 * Exported text rendering interfaces
1020 =item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth, utf8)
1022 Interface to text rendering into a single channel in an image
1024 handle - pointer to font handle
1025 im - image to render text on to
1026 xb, yb - coordinates, left edge and baseline
1027 channel - channel to render into
1028 points - font size to use
1029 txt - string to render
1030 len - length of the string to render
1031 smooth - boolean (True: antialias on, False: antialias is off)
1037 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 ) {
1039 i_img_dim cords[BOUNDING_BOX_COUNT];
1040 i_img_dim ascent, st_offset, y;
1044 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1046 ascent=cords[BBOX_ASCENT];
1047 st_offset=cords[BBOX_NEG_WIDTH];
1048 y = align ? yb-ascent : yb;
1050 i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , y, channel, smooth );
1051 i_tt_done_raster_map( &bit );
1058 =item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8)
1060 Interface to text rendering in a single color onto an image
1062 handle - pointer to font handle
1063 im - image to render text on to
1064 xb, yb - coordinates, left edge and baseline
1065 cl - color to use for text
1066 points - font size to use
1067 txt - string to render
1068 len - length of the string to render
1069 smooth - boolean (True: antialias on, False: antialias is off)
1075 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) {
1076 i_img_dim cords[BOUNDING_BOX_COUNT];
1077 i_img_dim ascent, st_offset, y;
1082 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1084 ascent=cords[BBOX_ASCENT];
1085 st_offset=cords[BBOX_NEG_WIDTH];
1086 y = align ? yb-ascent : yb;
1088 i_tt_dump_raster_map2( im, &bit, xb+st_offset, y, cl, smooth );
1089 i_tt_done_raster_map( &bit );
1096 =item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8)
1098 Function to get texts bounding boxes given the instance of the font (internal)
1100 handle - pointer to font handle
1101 inst - font instance
1102 txt - string to measure
1103 len - length of the string to render
1104 cords - the bounding box (modified in place)
1111 i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, size_t len, i_img_dim *cords, int utf8 ) {
1112 int upm, casc, cdesc, first;
1115 i_img_dim width = 0;
1124 mm_log((1,"i_tt_box_inst(handle %p,inst %d,txt '%.*s', len %ld, utf8 %d)\n",
1125 handle, inst, (int)len, txt, (long)len, utf8));
1127 upm = handle->properties.header->Units_Per_EM;
1128 gascent = ( handle->properties.horizontal->Ascender * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm;
1129 gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem - upm + 1) / upm;
1134 mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent));
1139 j = i_utf8_advance(&txt, &len);
1141 i_push_error(0, "invalid UTF8 character");
1146 j = (unsigned char)*txt++;
1149 if ( i_tt_get_glyph(handle,inst,j) ) {
1150 TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j);
1151 width += gm->advance / 64;
1152 casc = (gm->bbox.yMax+63) / 64;
1153 cdesc = (gm->bbox.yMin-63) / 64;
1155 mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n",
1156 (int)((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc));
1159 start = gm->bbox.xMin / 64;
1160 ascent = (gm->bbox.yMax+63) / 64;
1161 descent = (gm->bbox.yMin-63) / 64;
1164 if (!len) { /* if at end of string */
1165 /* the right-side bearing - in case the right-side of a
1166 character goes past the right of the advance width,
1167 as is common for italic fonts
1169 rightb = gm->advance - gm->bearingX
1170 - (gm->bbox.xMax - gm->bbox.xMin);
1171 /* fprintf(stderr, "font info last: %d %d %d %d\n",
1172 gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */
1175 ascent = (ascent > casc ? ascent : casc );
1176 descent = (descent < cdesc ? descent : cdesc);
1180 cords[BBOX_NEG_WIDTH]=start;
1181 cords[BBOX_GLOBAL_DESCENT]=gdescent;
1182 cords[BBOX_POS_WIDTH]=width;
1184 cords[BBOX_POS_WIDTH] -= rightb / 64;
1185 cords[BBOX_GLOBAL_ASCENT]=gascent;
1186 cords[BBOX_DESCENT]=descent;
1187 cords[BBOX_ASCENT]=ascent;
1188 cords[BBOX_ADVANCE_WIDTH] = width;
1189 cords[BBOX_RIGHT_BEARING] = rightb / 64;
1191 return BBOX_RIGHT_BEARING + 1;
1196 =item i_tt_bbox(handle, points, txt, len, cords, utf8)
1198 Interface to get a strings bounding box
1200 handle - pointer to font handle
1201 points - font size to use
1202 txt - string to render
1203 len - length of the string to render
1204 cords - the bounding box (modified in place)
1210 i_tt_bbox( TT_Fonthandle *handle, double points,const char *txt,size_t len,i_img_dim *cords, int utf8) {
1214 mm_log((1,"i_tt_box(handle %p,points %f,txt '%.*s', len %ld, utf8 %d)\n",
1215 handle, points, (int)len, txt, (long)len, utf8));
1217 if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
1218 i_push_errorf(0, "i_tt_get_instance(%g)", points);
1219 mm_log((1,"i_tt_text: get instance failed\n"));
1223 return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8);
1227 =item i_tt_face_name(handle, name_buf, name_buf_size)
1229 Retrieve's the font's postscript name.
1231 This is complicated by the need to handle encodings and so on.
1236 i_tt_face_name(TT_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
1237 TT_Face_Properties props;
1240 TT_UShort platform_id, encoding_id, lang_id, name_id;
1243 int want_index = -1; /* an acceptable but not perfect name */
1248 TT_Get_Face_Properties(handle->face, &props);
1249 name_count = props.num_Names;
1250 for (i = 0; i < name_count; ++i) {
1251 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1254 TT_Get_Name_String(handle->face, i, &name, &name_len);
1256 if (platform_id != TT_PLATFORM_APPLE_UNICODE && name_len
1257 && name_id == TT_NAME_ID_PS_NAME) {
1258 int might_want_index = -1;
1259 int might_score = 0;
1260 if ((platform_id == TT_PLATFORM_MACINTOSH && encoding_id == TT_MAC_ID_ROMAN)
1262 (platform_id == TT_PLATFORM_MICROSOFT && encoding_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)) {
1263 /* exactly what we want */
1268 if (platform_id == TT_PLATFORM_MICROSOFT
1269 && (encoding_id & 0xFF) == TT_MS_LANGID_ENGLISH_GENERAL) {
1270 /* any english is good */
1271 might_want_index = i;
1274 /* there might be something in between */
1276 /* anything non-unicode is better than nothing */
1277 might_want_index = i;
1280 if (might_score > score) {
1281 score = might_score;
1282 want_index = might_want_index;
1287 if (want_index != -1) {
1288 TT_Get_Name_String(handle->face, want_index, &name, &name_len);
1290 strncpy(name_buf, name, name_buf_size);
1291 name_buf[name_buf_size-1] = '\0';
1293 return strlen(name) + 1;
1296 i_push_error(0, "no face name present");
1301 void i_tt_dump_names(TT_Fonthandle *handle) {
1302 TT_Face_Properties props;
1305 TT_UShort platform_id, encoding_id, lang_id, name_id;
1309 TT_Get_Face_Properties(handle->face, &props);
1310 name_count = props.num_Names;
1311 for (i = 0; i < name_count; ++i) {
1312 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1314 TT_Get_Name_String(handle->face, i, &name, &name_len);
1316 printf("# %d: plat %d enc %d lang %d name %d value ", i, platform_id,
1317 encoding_id, lang_id, name_id);
1318 if (platform_id == TT_PLATFORM_APPLE_UNICODE) {
1319 printf("(unicode)\n");
1322 printf("'%s'\n", name);
1329 i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf,
1330 size_t name_buf_size) {
1338 if (!handle->loaded_names) {
1340 mm_log((1, "Loading PS Names"));
1341 handle->load_cond = TT_Load_PS_Names(handle->face, &post);
1342 ++handle->loaded_names;
1345 if (handle->load_cond) {
1346 i_push_errorf(handle->load_cond, "error loading names (%#x)",
1347 (unsigned)handle->load_cond);
1351 index = TT_Char_Index(handle->char_map, ch);
1353 i_push_error(0, "no such character");
1357 rc = TT_Get_PS_Name(handle->face, index, &psname);
1360 i_push_error(rc, "error getting name");
1364 strncpy(name_buf, psname, name_buf_size);
1365 name_buf[name_buf_size-1] = '\0';
1367 return strlen(psname) + 1;
1369 mm_log((1, "FTXPOST extension not enabled\n"));
1371 i_push_error(0, "Use of FTXPOST extension disabled");
1378 =item i_tt_push_error(code)
1380 Push an error message and code onto the Imager error stack.
1385 i_tt_push_error(TT_Error rc) {
1387 TT_String const *msg = TT_ErrToString18(rc);
1389 i_push_error(rc, msg);
1391 i_push_errorf(rc, "Error code 0x%04x", (unsigned)rc);
1401 Arnar M. Hrafnkelsson <addi@umich.edu>