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 */
153 im_context_t ctx = im_get_context();
155 slot = im_context_slot_new(i_tt_uninit);
162 Initializes the freetype font rendering engine (if needed)
170 im_context_t ctx = im_get_context();
171 TT_Byte palette[] = { 0, 64, 127, 191, 255 };
172 i_tt_engine *result = im_context_slot_get(ctx, slot);
176 if (result == NULL) {
177 result = mymalloc(sizeof(i_tt_engine));
178 memset(result, 0, sizeof(*result));
179 im_context_slot_set(ctx, slot, result);
180 mm_log((1, "allocated FT1 state %p\n", result));
183 mm_log((1,"init_tt()\n"));
185 if (result->initialized)
188 error = TT_Init_FreeType( &result->engine );
190 mm_log((1,"Initialization of freetype failed, code = 0x%x\n",
192 i_tt_push_error(error);
193 i_push_error(0, "Could not initialize freetype 1.x");
198 error = TT_Init_Post_Extension( result->engine );
200 mm_log((1, "Initialization of Post extension failed = 0x%x\n",
203 i_tt_push_error(error);
204 i_push_error(0, "Could not initialize FT 1.x POST extension");
209 error = TT_Set_Raster_Gray_Palette(result->engine, palette);
211 mm_log((1, "Initialization of gray levels failed = 0x%x\n",
213 i_tt_push_error(error);
214 i_push_error(0, "Could not initialize FT 1.x POST extension");
218 mm_log((1, "initialized FT1 state %p\n", result));
220 result->initialized = 1;
226 i_tt_uninit(void *p) {
227 i_tt_engine *tteng = p;
229 if (tteng->initialized) {
230 mm_log((1, "finalizing FT1 state %p\n", tteng));
231 TT_Done_FreeType(tteng->engine);
233 mm_log((1, "freeing FT1 state %p\n", tteng));
238 =item i_tt_get_instance(handle, points, smooth)
240 Finds a points+smooth instance or if one doesn't exist in the cache
241 allocates room and returns its cache entry
243 fontname - path to the font to load
244 handle - handle to the font.
245 points - points of the requested font
246 smooth - boolean (True: antialias on, False: antialias is off)
253 i_tt_get_instance( TT_Fonthandle *handle, i_img_dim points, int smooth ) {
257 mm_log((1,"i_tt_get_instance(handle %p, points %" i_DF ", smooth %d)\n",
258 handle, i_DFc(points), smooth));
260 if (smooth == -1) { /* Smooth doesn't matter for this search */
261 for(i=0;i<TT_CHC;i++) {
262 if (handle->instanceh[i].ptsize==points) {
263 mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
267 smooth=1; /* We will be adding a font - add it as smooth then */
268 } else { /* Smooth doesn't matter for this search */
269 for(i=0;i<TT_CHC;i++) {
270 if (handle->instanceh[i].ptsize == points
271 && handle->instanceh[i].smooth == smooth) {
272 mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
278 /* Found the instance in the cache - return the cache index */
280 for(idx=0;idx<TT_CHC;idx++) {
281 if (!(handle->instanceh[idx].order)) break; /* find the lru item */
284 mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
285 mm_log((1,"i_tt_get_instance: lru pointer %p\n",
286 USTRCT(handle->instanceh[idx].instance) ));
288 if ( USTRCT(handle->instanceh[idx].instance) ) {
289 mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
291 /* Free cached glyphs */
293 if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
294 TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
297 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
298 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
301 /* Free instance if needed */
302 TT_Done_Instance( handle->instanceh[idx].instance );
305 /* create and initialize instance */
306 /* FIXME: probably a memory leak on fail */
308 (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) ||
309 ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
310 ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
313 mm_log((1, "Could not create and initialize instance: error %x.\n",
318 /* Now that the instance should the inplace we need to lower all of the
319 ru counts and put `this' one with the highest entry */
321 for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
323 handle->instanceh[idx].order=TT_CHC-1;
324 handle->instanceh[idx].ptsize=points;
325 handle->instanceh[idx].smooth=smooth;
326 TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
328 /* Zero the memory for the glyph storage so they are not thought as
329 cached if they haven't been cached since this new font was loaded */
332 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
333 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
341 =item i_tt_new(fontname)
343 Creates a new font handle object, finds a character map and initialise the
344 the font handle's cache
346 fontname - path to the font to load
352 i_tt_new(const char *fontname) {
354 TT_Fonthandle *handle;
356 unsigned short platform,encoding;
359 if ((tteng = i_init_tt()) == NULL) {
360 i_push_error(0, "Could not initialize FT1 engine");
366 mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
368 /* allocate memory for the structure */
370 handle = mymalloc( sizeof(TT_Fonthandle) ); /* checked 5Nov05 tonyc */
372 /* load the typeface */
373 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 ? 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 ? 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, xmax, ymax;
768 xmin = gmetrics->bbox.xMin & -64;
769 ymin = gmetrics->bbox.yMin & -64;
770 xmax = (gmetrics->bbox.xMax + 63) & -64;
771 ymax = (gmetrics->bbox.yMax + 63) & -64;
773 i_tt_clear_raster_map( small_bit );
774 TT_Get_Glyph_Pixmap( glyph, small_bit, -xmin, -ymin );
775 i_tt_blit_or( bit, small_bit, xmin/64 + x_off, -ymin/64 - y_off );
781 =item i_tt_render_all_glyphs(handle, inst, bit, small_bit, cords, txt, len, smooth)
783 calls i_tt_render_glyph to render each glyph into the bit rastermap (internal)
785 handle - pointer to font handle
787 bit - large bitmap that is the destination for the text
788 smallbit - small bitmap that is used only if smooth is true
789 txt - string to render
790 len - length of the string to render
791 smooth - boolean (True: antialias on, False: antialias is off)
798 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
799 TT_Raster_Map *small_bit, i_img_dim cords[6],
800 char const* txt, size_t len, int smooth, int utf8 ) {
804 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",
805 handle, inst, bit, small_bit, (int)len, txt, (long)len, smooth, utf8));
808 y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM);
811 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 */
816 j = i_utf8_advance(&txt, &len);
818 i_push_error(0, "invalid UTF8 character");
823 j = (unsigned char)*txt++;
826 if ( !i_tt_get_glyph(handle,inst,j) )
828 i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
829 &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit,
830 small_bit, x, y, smooth );
831 x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64;
839 * Functions to render rasters (single channel images) onto images
843 =item i_tt_dump_raster_map2(im, bit, xb, yb, cl, smooth)
845 Function to dump a raster onto an image in color used by i_tt_text() (internal).
847 im - image to dump raster on
848 bit - bitmap that contains the text to be dumped to im
849 xb, yb - coordinates, left edge and baseline
850 cl - color to use for text
851 smooth - boolean (True: antialias on, False: antialias is off)
858 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 ) {
861 mm_log((1,"i_tt_dump_raster_map2(im %p, bit %p, xb %" i_DF ", yb %" i_DF ", cl %p)\n",
862 im, bit, i_DFc(xb), i_DFc(yb), cl));
869 i_render_init(&r, im, bit->cols);
870 for(y=0;y<bit->rows;y++) {
871 i_render_color(&r, xb, yb+y, bit->cols, bmap + y*bit->cols, cl);
875 for(y=0;y<bit->rows;y++) {
876 unsigned mask = 0x80;
877 unsigned char *p = bmap + y * bit->cols;
879 for(x = 0; x < bit->width; x++) {
881 i_ppix(im, x+xb, y+yb, cl);
896 =item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth)
898 Function to dump a raster onto a single channel image in color (internal)
900 im - image to dump raster on
901 bit - bitmap that contains the text to be dumped to im
902 xb, yb - coordinates, left edge and baseline
903 channel - channel to copy to
904 smooth - boolean (True: antialias on, False: antialias is off)
911 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 ) {
916 int old_mask = im->ch_mask;
917 im->ch_mask = 1 << channel;
919 mm_log((1,"i_tt_dump_raster_channel(im %p, bit %p, xb %" i_DF ", yb %" i_DF ", channel %d)\n",
920 im, bit, i_DFc(xb), i_DFc(yb), channel));
925 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
926 c = bmap[y*(bit->cols)+x];
927 val.channel[channel] = c;
928 i_ppix(im,x+xb,y+yb,&val);
931 for(y=0;y<bit->rows;y++) {
932 unsigned mask = 0x80;
933 unsigned char *p = bmap + y * bit->cols;
935 for(x=0;x<bit->width;x++) {
936 val.channel[channel] = (*p & mask) ? 255 : 0;
937 i_ppix(im,x+xb,y+yb,&val);
947 im->ch_mask = old_mask;
952 =item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth)
954 interface for generating single channel raster of text (internal)
956 handle - pointer to font handle
957 bit - the bitmap that is allocated, rendered into and NOT freed
958 cords - the bounding box (modified in place)
959 points - font size to use
960 txt - string to render
961 len - length of the string to render
962 smooth - boolean (True: antialias on, False: antialias is off)
969 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 ) {
971 i_img_dim width, height;
972 TT_Raster_Map small_bit;
974 /* find or install an instance */
975 if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) {
976 mm_log((1,"i_tt_rasterize: get instance failed\n"));
980 /* calculate bounding box */
981 if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 ))
985 width = cords[2]-cords[0];
986 height = cords[5]-cords[4];
988 mm_log((1,"i_tt_rasterize: width=%" i_DF ", height=%" i_DF "\n",
989 i_DFc(width), i_DFc(height) ));
991 i_tt_init_raster_map ( bit, width, height, smooth );
992 i_tt_clear_raster_map( bit );
993 if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth );
995 if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len,
998 i_tt_done_raster_map( &small_bit );
1002 if ( smooth ) i_tt_done_raster_map( &small_bit );
1009 * Exported text rendering interfaces
1014 =item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth, utf8)
1016 Interface to text rendering into a single channel in an image
1018 handle - pointer to font handle
1019 im - image to render text on to
1020 xb, yb - coordinates, left edge and baseline
1021 channel - channel to render into
1022 points - font size to use
1023 txt - string to render
1024 len - length of the string to render
1025 smooth - boolean (True: antialias on, False: antialias is off)
1031 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 ) {
1033 i_img_dim cords[BOUNDING_BOX_COUNT];
1034 i_img_dim ascent, st_offset, y;
1038 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1040 ascent=cords[BBOX_ASCENT];
1041 st_offset=cords[BBOX_NEG_WIDTH];
1042 y = align ? yb-ascent : yb;
1044 i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , y, channel, smooth );
1045 i_tt_done_raster_map( &bit );
1052 =item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8)
1054 Interface to text rendering in a single color onto an image
1056 handle - pointer to font handle
1057 im - image to render text on to
1058 xb, yb - coordinates, left edge and baseline
1059 cl - color to use for text
1060 points - font size to use
1061 txt - string to render
1062 len - length of the string to render
1063 smooth - boolean (True: antialias on, False: antialias is off)
1069 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) {
1070 i_img_dim cords[BOUNDING_BOX_COUNT];
1071 i_img_dim ascent, st_offset, y;
1076 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1078 ascent=cords[BBOX_ASCENT];
1079 st_offset=cords[BBOX_NEG_WIDTH];
1080 y = align ? yb-ascent : yb;
1082 i_tt_dump_raster_map2( im, &bit, xb+st_offset, y, cl, smooth );
1083 i_tt_done_raster_map( &bit );
1090 =item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8)
1092 Function to get texts bounding boxes given the instance of the font (internal)
1094 handle - pointer to font handle
1095 inst - font instance
1096 txt - string to measure
1097 len - length of the string to render
1098 cords - the bounding box (modified in place)
1105 i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, size_t len, i_img_dim cords[BOUNDING_BOX_COUNT], int utf8 ) {
1106 int upm, casc, cdesc, first;
1109 i_img_dim width = 0;
1117 unsigned char *ustr;
1118 ustr=(unsigned char*)txt;
1120 mm_log((1,"i_tt_box_inst(handle %p,inst %d,txt '%.*s', len %ld, utf8 %d)\n",
1121 handle, inst, (int)len, txt, (long)len, utf8));
1123 upm = handle->properties.header->Units_Per_EM;
1124 gascent = ( handle->properties.horizontal->Ascender * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm;
1125 gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem - upm + 1) / upm;
1130 mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent));
1135 j = i_utf8_advance(&txt, &len);
1137 i_push_error(0, "invalid UTF8 character");
1142 j = (unsigned char)*txt++;
1145 if ( i_tt_get_glyph(handle,inst,j) ) {
1146 TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j);
1147 width += gm->advance / 64;
1148 casc = (gm->bbox.yMax+63) / 64;
1149 cdesc = (gm->bbox.yMin-63) / 64;
1151 mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n",
1152 (int)((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc));
1155 start = gm->bbox.xMin / 64;
1156 ascent = (gm->bbox.yMax+63) / 64;
1157 descent = (gm->bbox.yMin-63) / 64;
1160 if (!len) { /* if at end of string */
1161 /* the right-side bearing - in case the right-side of a
1162 character goes past the right of the advance width,
1163 as is common for italic fonts
1165 rightb = gm->advance - gm->bearingX
1166 - (gm->bbox.xMax - gm->bbox.xMin);
1167 /* fprintf(stderr, "font info last: %d %d %d %d\n",
1168 gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */
1171 ascent = (ascent > casc ? ascent : casc );
1172 descent = (descent < cdesc ? descent : cdesc);
1176 cords[BBOX_NEG_WIDTH]=start;
1177 cords[BBOX_GLOBAL_DESCENT]=gdescent;
1178 cords[BBOX_POS_WIDTH]=width;
1180 cords[BBOX_POS_WIDTH] -= rightb / 64;
1181 cords[BBOX_GLOBAL_ASCENT]=gascent;
1182 cords[BBOX_DESCENT]=descent;
1183 cords[BBOX_ASCENT]=ascent;
1184 cords[BBOX_ADVANCE_WIDTH] = width;
1185 cords[BBOX_RIGHT_BEARING] = rightb / 64;
1187 return BBOX_RIGHT_BEARING + 1;
1192 =item i_tt_bbox(handle, points, txt, len, cords, utf8)
1194 Interface to get a strings bounding box
1196 handle - pointer to font handle
1197 points - font size to use
1198 txt - string to render
1199 len - length of the string to render
1200 cords - the bounding box (modified in place)
1206 i_tt_bbox( TT_Fonthandle *handle, double points,const char *txt,size_t len,i_img_dim cords[6], int utf8) {
1210 mm_log((1,"i_tt_box(handle %p,points %f,txt '%.*s', len %ld, utf8 %d)\n",
1211 handle, points, (int)len, txt, (long)len, utf8));
1213 if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
1214 i_push_errorf(0, "i_tt_get_instance(%g)", points);
1215 mm_log((1,"i_tt_text: get instance failed\n"));
1219 return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8);
1223 =item i_tt_face_name(handle, name_buf, name_buf_size)
1225 Retrieve's the font's postscript name.
1227 This is complicated by the need to handle encodings and so on.
1232 i_tt_face_name(TT_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
1233 TT_Face_Properties props;
1236 TT_UShort platform_id, encoding_id, lang_id, name_id;
1239 int want_index = -1; /* an acceptable but not perfect name */
1244 TT_Get_Face_Properties(handle->face, &props);
1245 name_count = props.num_Names;
1246 for (i = 0; i < name_count; ++i) {
1247 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1250 TT_Get_Name_String(handle->face, i, &name, &name_len);
1252 if (platform_id != TT_PLATFORM_APPLE_UNICODE && name_len
1253 && name_id == TT_NAME_ID_PS_NAME) {
1254 int might_want_index = -1;
1255 int might_score = 0;
1256 if ((platform_id == TT_PLATFORM_MACINTOSH && encoding_id == TT_MAC_ID_ROMAN)
1258 (platform_id == TT_PLATFORM_MICROSOFT && encoding_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)) {
1259 /* exactly what we want */
1264 if (platform_id == TT_PLATFORM_MICROSOFT
1265 && (encoding_id & 0xFF) == TT_MS_LANGID_ENGLISH_GENERAL) {
1266 /* any english is good */
1267 might_want_index = i;
1270 /* there might be something in between */
1272 /* anything non-unicode is better than nothing */
1273 might_want_index = i;
1276 if (might_score > score) {
1277 score = might_score;
1278 want_index = might_want_index;
1283 if (want_index != -1) {
1284 TT_Get_Name_String(handle->face, want_index, &name, &name_len);
1286 strncpy(name_buf, name, name_buf_size);
1287 name_buf[name_buf_size-1] = '\0';
1289 return strlen(name) + 1;
1292 i_push_error(0, "no face name present");
1297 void i_tt_dump_names(TT_Fonthandle *handle) {
1298 TT_Face_Properties props;
1301 TT_UShort platform_id, encoding_id, lang_id, name_id;
1305 TT_Get_Face_Properties(handle->face, &props);
1306 name_count = props.num_Names;
1307 for (i = 0; i < name_count; ++i) {
1308 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1310 TT_Get_Name_String(handle->face, i, &name, &name_len);
1312 printf("# %d: plat %d enc %d lang %d name %d value ", i, platform_id,
1313 encoding_id, lang_id, name_id);
1314 if (platform_id == TT_PLATFORM_APPLE_UNICODE) {
1315 printf("(unicode)\n");
1318 printf("'%s'\n", name);
1325 i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf,
1326 size_t name_buf_size) {
1334 if (!handle->loaded_names) {
1336 mm_log((1, "Loading PS Names"));
1337 handle->load_cond = TT_Load_PS_Names(handle->face, &post);
1338 ++handle->loaded_names;
1341 if (handle->load_cond) {
1342 i_push_errorf(handle->load_cond, "error loading names (%#x)",
1343 (unsigned)handle->load_cond);
1347 index = TT_Char_Index(handle->char_map, ch);
1349 i_push_error(0, "no such character");
1353 rc = TT_Get_PS_Name(handle->face, index, &psname);
1356 i_push_error(rc, "error getting name");
1360 strncpy(name_buf, psname, name_buf_size);
1361 name_buf[name_buf_size-1] = '\0';
1363 return strlen(psname) + 1;
1365 mm_log((1, "FTXPOST extension not enabled\n"));
1367 i_push_error(0, "Use of FTXPOST extension disabled");
1374 =item i_tt_push_error(code)
1376 Push an error message and code onto the Imager error stack.
1381 i_tt_push_error(TT_Error rc) {
1383 TT_String const *msg = TT_ErrToString18(rc);
1385 i_push_error(rc, msg);
1387 i_push_errorf(rc, "Error code 0x%04x", (unsigned)rc);
1397 Arnar M. Hrafnkelsson <addi@umich.edu>