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 );
374 if ( error == TT_Err_Could_Not_Open_File ) {
375 mm_log((1, "Could not find/open %s.\n", fontname ));
378 mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname,
381 i_tt_push_error(error);
385 TT_Get_Face_Properties( handle->face, &(handle->properties) );
387 /* First, look for a Unicode charmap */
388 n = handle->properties.num_CharMaps;
389 USTRCT( handle->char_map )=NULL; /* Invalidate character map */
391 for ( i = 0; i < n; i++ ) {
392 TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
393 if ( (platform == 3 && encoding == 1 )
394 || (platform == 0 && encoding == 0 ) ) {
395 mm_log((2,"i_tt_new - found char map platform %u encoding %u\n",
396 platform, encoding));
397 TT_Get_CharMap( handle->face, i, &(handle->char_map) );
401 if (!USTRCT(handle->char_map) && n != 0) {
402 /* just use the first one */
403 TT_Get_CharMap( handle->face, 0, &(handle->char_map));
406 /* Zero the pointsizes - and ordering */
408 for(i=0;i<TT_CHC;i++) {
409 USTRCT(handle->instanceh[i].instance)=NULL;
410 handle->instanceh[i].order=i;
411 handle->instanceh[i].ptsize=0;
412 handle->instanceh[i].smooth=-1;
416 handle->loaded_names = 0;
419 mm_log((1,"i_tt_new <- %p\n",handle));
426 * raster map management
430 =item i_tt_init_raster_map(bit, width, height, smooth)
432 Allocates internal memory for the bitmap as needed by the parameters (internal)
434 bit - bitmap to allocate into
435 width - width of the bitmap
436 height - height of the bitmap
437 smooth - boolean (True: antialias on, False: antialias is off)
444 i_tt_init_raster_map( TT_Raster_Map* bit, i_img_dim width, i_img_dim height, int smooth ) {
446 mm_log((1,"i_tt_init_raster_map( bit %p, width %" i_DF ", height %" i_DF
447 ", smooth %d)\n", bit, i_DFc(width), i_DFc(height), smooth));
450 bit->width = ( width + 3 ) & -4;
451 bit->flow = TT_Flow_Down;
454 bit->cols = bit->width;
455 bit->size = bit->rows * bit->width;
457 bit->cols = ( bit->width + 7 ) / 8; /* convert to # of bytes */
458 bit->size = bit->rows * bit->cols; /* number of bytes in buffer */
461 /* rows can be 0 for some glyphs, for example ' ' */
462 if (bit->rows && bit->size / bit->rows != bit->cols) {
463 i_fatal(0, "Integer overflow calculating bitmap size (%d, %d)\n",
464 bit->width, bit->rows);
467 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 ));
469 bit->bitmap = (void *) mymalloc( bit->size ); /* checked 6Nov05 tonyc */
470 if ( !bit->bitmap ) i_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",bit->size );
475 =item i_tt_clear_raster_map(bit)
477 Frees the bitmap data and sets pointer to NULL (internal)
486 i_tt_done_raster_map( TT_Raster_Map *bit ) {
487 myfree( bit->bitmap );
493 =item i_tt_clear_raster_map(bit)
495 Clears the specified bitmap (internal)
505 i_tt_clear_raster_map( TT_Raster_Map* bit ) {
506 memset( bit->bitmap, 0, bit->size );
511 =item i_tt_blit_or(dst, src, x_off, y_off)
513 function that blits one raster map into another (internal)
515 dst - destination bitmap
517 x_off - x offset into the destination bitmap
518 y_off - y offset into the destination bitmap
525 i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,i_img_dim x_off, i_img_dim y_off ) {
527 i_img_dim x1, x2, y1, y2;
528 unsigned char *s, *d;
530 x1 = x_off < 0 ? -x_off : 0;
531 y1 = y_off < 0 ? -y_off : 0;
533 x2 = (int)dst->cols - x_off;
534 if ( x2 > src->cols ) x2 = src->cols;
536 y2 = (int)dst->rows - y_off;
537 if ( y2 > src->rows ) y2 = src->rows;
539 if ( x1 >= x2 ) return;
541 /* do the real work now */
543 for ( y = y1; y < y2; ++y ) {
544 s = ( (unsigned char*)src->bitmap ) + y * src->cols + x1;
545 d = ( (unsigned char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
547 for ( x = x1; x < x2; ++x ) {
556 /* useful for debugging */
559 static void dump_raster_map(FILE *out, TT_Raster_Map *bit ) {
561 fprintf(out, "cols %d rows %d flow %d\n", bit->cols, bit->rows, bit->flow);
562 for (y = 0; y < bit->rows; ++y) {
563 fprintf(out, "%2d:", y);
564 for (x = 0; x < bit->cols; ++x) {
565 if ((x & 7) == 0 && x) putc(' ', out);
566 fprintf(out, "%02x", ((unsigned char *)bit->bitmap)[y*bit->cols+x]);
575 =item i_tt_get_glyph(handle, inst, j)
577 Function to see if a glyph exists and if so cache it (internal)
579 handle - pointer to font handle
581 j - charcode of glyph
588 i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j) {
589 unsigned short load_flags, code;
592 mm_log((1, "i_tt_get_glyph(handle %p, inst %d, j %lu (%c))\n",
593 handle,inst,j, (int)((j >= ' ' && j <= '~') ? j : '.')));
595 /*mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] ));*/
597 if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)
598 && handle->instanceh[inst].glyphs[TT_HASH(j)].ch == j) {
599 mm_log((1,"i_tt_get_glyph: %lu in cache\n",j));
603 if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) ) {
604 /* clean up the entry */
605 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
606 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
607 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
610 /* Ok - it wasn't cached - try to get it in */
611 load_flags = TTLOAD_SCALE_GLYPH;
612 if ( LTT_hinted ) load_flags |= TTLOAD_HINT_GLYPH;
614 if ( !TT_VALID(handle->char_map) ) {
615 code = (j - ' ' + 1) < 0 ? 0 : (j - ' ' + 1);
616 if ( code >= handle->properties.num_Glyphs ) code = 0;
617 } else code = TT_Char_Index( handle->char_map, j );
619 if ( (error = TT_New_Glyph( handle->face, &handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)) ) {
620 mm_log((1, "Cannot allocate and load glyph: error %#x.\n", (unsigned)error ));
621 i_push_error(error, "TT_New_Glyph()");
624 if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, code, load_flags)) ) {
625 mm_log((1, "Cannot allocate and load glyph: error %#x.\n", (unsigned)error ));
627 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
628 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
629 i_push_error(error, "TT_Load_Glyph()");
633 /* At this point the glyph should be allocated and loaded */
634 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = j;
636 /* Next get the glyph metrics */
637 error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
638 &handle->instanceh[inst].gmetrics[TT_HASH(j)] );
640 mm_log((1, "TT_Get_Glyph_Metrics: error %#x.\n", (unsigned)error ));
641 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
642 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
643 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
644 i_push_error(error, "TT_Get_Glyph_Metrics()");
652 =item i_tt_has_chars(handle, text, len, utf8, out)
654 Check if the given characters are defined by the font. Note that len
655 is the number of bytes, not the number of characters (when utf8 is
658 Returns the number of characters that were checked.
664 i_tt_has_chars(TT_Fonthandle *handle, char const *text, size_t len, int utf8,
667 mm_log((1, "i_tt_has_chars(handle %p, text %p, len %ld, utf8 %d)\n",
668 handle, text, (long)len, utf8));
674 c = i_utf8_advance(&text, &len);
676 i_push_error(0, "invalid UTF8 character");
681 c = (unsigned char)*text++;
685 if (TT_VALID(handle->char_map)) {
686 index = TT_Char_Index(handle->char_map, c);
689 index = (c - ' ' + 1) < 0 ? 0 : (c - ' ' + 1);
690 if (index >= handle->properties.num_Glyphs)
701 =item i_tt_destroy(handle)
703 Clears the data taken by a font including all cached data such as
706 handle - pointer to font handle
712 i_tt_destroy( TT_Fonthandle *handle) {
713 TT_Close_Face( handle->face );
716 /* FIXME: Should these be freed automatically by the library?
718 TT_Done_Instance( instance );
720 i_tt_done_glyphs( void ) {
723 if ( !glyphs ) return;
725 for ( i = 0; i < 256; ++i ) TT_Done_Glyph( glyphs[i] );
735 * FreeType Rendering functions
740 =item i_tt_render_glyph(handle, gmetrics, bit, smallbit, x_off, y_off, smooth)
742 Renders a single glyph into the bit rastermap (internal)
744 handle - pointer to font handle
745 gmetrics - the metrics for the glyph to be rendered
746 bit - large bitmap that is the destination for the text
747 smallbit - small bitmap that is used only if smooth is true
748 x_off - x offset of glyph
749 y_off - y offset of glyph
750 smooth - boolean (True: antialias on, False: antialias is off)
757 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 ) {
759 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",
760 USTRCT(glyph), gmetrics, bit, small_bit, i_DFc(x_off),
761 i_DFc(y_off), smooth));
763 if ( !smooth ) TT_Get_Glyph_Bitmap( glyph, bit, x_off * 64, y_off * 64);
765 TT_F26Dot6 xmin, ymin, xmax, ymax;
767 xmin = gmetrics->bbox.xMin & -64;
768 ymin = gmetrics->bbox.yMin & -64;
769 xmax = (gmetrics->bbox.xMax + 63) & -64;
770 ymax = (gmetrics->bbox.yMax + 63) & -64;
772 i_tt_clear_raster_map( small_bit );
773 TT_Get_Glyph_Pixmap( glyph, small_bit, -xmin, -ymin );
774 i_tt_blit_or( bit, small_bit, xmin/64 + x_off, -ymin/64 - y_off );
780 =item i_tt_render_all_glyphs(handle, inst, bit, small_bit, cords, txt, len, smooth)
782 calls i_tt_render_glyph to render each glyph into the bit rastermap (internal)
784 handle - pointer to font handle
786 bit - large bitmap that is the destination for the text
787 smallbit - small bitmap that is used only if smooth is true
788 txt - string to render
789 len - length of the string to render
790 smooth - boolean (True: antialias on, False: antialias is off)
797 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
798 TT_Raster_Map *small_bit, i_img_dim cords[6],
799 char const* txt, size_t len, int smooth, int utf8 ) {
803 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",
804 handle, inst, bit, small_bit, (int)len, txt, (long)len, smooth, utf8));
807 y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM);
810 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 */
815 j = i_utf8_advance(&txt, &len);
817 i_push_error(0, "invalid UTF8 character");
822 j = (unsigned char)*txt++;
825 if ( !i_tt_get_glyph(handle,inst,j) )
827 i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
828 &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit,
829 small_bit, x, y, smooth );
830 x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64;
838 * Functions to render rasters (single channel images) onto images
842 =item i_tt_dump_raster_map2(im, bit, xb, yb, cl, smooth)
844 Function to dump a raster onto an image in color used by i_tt_text() (internal).
846 im - image to dump raster on
847 bit - bitmap that contains the text to be dumped to im
848 xb, yb - coordinates, left edge and baseline
849 cl - color to use for text
850 smooth - boolean (True: antialias on, False: antialias is off)
857 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 ) {
860 mm_log((1,"i_tt_dump_raster_map2(im %p, bit %p, xb %" i_DF ", yb %" i_DF ", cl %p)\n",
861 im, bit, i_DFc(xb), i_DFc(yb), cl));
868 i_render_init(&r, im, bit->cols);
869 for(y=0;y<bit->rows;y++) {
870 i_render_color(&r, xb, yb+y, bit->cols, bmap + y*bit->cols, cl);
874 unsigned char *bmp = mymalloc(bit->width);
877 i_render_init(&r, im, bit->width);
879 for(y=0;y<bit->rows;y++) {
880 unsigned mask = 0x80;
881 unsigned char *p = bmap + y * bit->cols;
882 unsigned char *pout = bmp;
884 for(x = 0; x < bit->width; x++) {
885 *pout++ = (*p & mask) ? 0xFF : 0;
893 i_render_color(&r, xb, yb+y, bit->cols, bmp, cl);
903 =item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth)
905 Function to dump a raster onto a single channel image in color (internal)
907 im - image to dump raster on
908 bit - bitmap that contains the text to be dumped to im
909 xb, yb - coordinates, left edge and baseline
910 channel - channel to copy to
911 smooth - boolean (True: antialias on, False: antialias is off)
918 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 ) {
923 int old_mask = im->ch_mask;
924 im->ch_mask = 1 << channel;
926 mm_log((1,"i_tt_dump_raster_channel(im %p, bit %p, xb %" i_DF ", yb %" i_DF ", channel %d)\n",
927 im, bit, i_DFc(xb), i_DFc(yb), channel));
932 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
933 c = bmap[y*(bit->cols)+x];
934 val.channel[channel] = c;
935 i_ppix(im,x+xb,y+yb,&val);
938 for(y=0;y<bit->rows;y++) {
939 unsigned mask = 0x80;
940 unsigned char *p = bmap + y * bit->cols;
942 for(x=0;x<bit->width;x++) {
943 val.channel[channel] = (*p & mask) ? 255 : 0;
944 i_ppix(im,x+xb,y+yb,&val);
954 im->ch_mask = old_mask;
959 =item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth)
961 interface for generating single channel raster of text (internal)
963 handle - pointer to font handle
964 bit - the bitmap that is allocated, rendered into and NOT freed
965 cords - the bounding box (modified in place)
966 points - font size to use
967 txt - string to render
968 len - length of the string to render
969 smooth - boolean (True: antialias on, False: antialias is off)
976 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 ) {
978 i_img_dim width, height;
979 TT_Raster_Map small_bit;
981 /* find or install an instance */
982 if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) {
983 mm_log((1,"i_tt_rasterize: get instance failed\n"));
987 /* calculate bounding box */
988 if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 ))
992 width = cords[2]-cords[0];
993 height = cords[5]-cords[4];
995 mm_log((1,"i_tt_rasterize: width=%" i_DF ", height=%" i_DF "\n",
996 i_DFc(width), i_DFc(height) ));
998 i_tt_init_raster_map ( bit, width, height, smooth );
999 i_tt_clear_raster_map( bit );
1000 if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth );
1002 if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len,
1005 i_tt_done_raster_map( &small_bit );
1009 if ( smooth ) i_tt_done_raster_map( &small_bit );
1016 * Exported text rendering interfaces
1021 =item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth, utf8)
1023 Interface to text rendering into a single channel in an image
1025 handle - pointer to font handle
1026 im - image to render text on to
1027 xb, yb - coordinates, left edge and baseline
1028 channel - channel to render into
1029 points - font size to use
1030 txt - string to render
1031 len - length of the string to render
1032 smooth - boolean (True: antialias on, False: antialias is off)
1038 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 ) {
1040 i_img_dim cords[BOUNDING_BOX_COUNT];
1041 i_img_dim ascent, st_offset, y;
1045 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1047 ascent=cords[BBOX_ASCENT];
1048 st_offset=cords[BBOX_NEG_WIDTH];
1049 y = align ? yb-ascent : yb;
1051 i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , y, channel, smooth );
1052 i_tt_done_raster_map( &bit );
1059 =item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8)
1061 Interface to text rendering in a single color onto an image
1063 handle - pointer to font handle
1064 im - image to render text on to
1065 xb, yb - coordinates, left edge and baseline
1066 cl - color to use for text
1067 points - font size to use
1068 txt - string to render
1069 len - length of the string to render
1070 smooth - boolean (True: antialias on, False: antialias is off)
1076 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) {
1077 i_img_dim cords[BOUNDING_BOX_COUNT];
1078 i_img_dim ascent, st_offset, y;
1083 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1085 ascent=cords[BBOX_ASCENT];
1086 st_offset=cords[BBOX_NEG_WIDTH];
1087 y = align ? yb-ascent : yb;
1089 i_tt_dump_raster_map2( im, &bit, xb+st_offset, y, cl, smooth );
1090 i_tt_done_raster_map( &bit );
1097 =item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8)
1099 Function to get texts bounding boxes given the instance of the font (internal)
1101 handle - pointer to font handle
1102 inst - font instance
1103 txt - string to measure
1104 len - length of the string to render
1105 cords - the bounding box (modified in place)
1112 i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, size_t len, i_img_dim cords[BOUNDING_BOX_COUNT], int utf8 ) {
1113 int upm, casc, cdesc, first;
1116 i_img_dim width = 0;
1124 unsigned char *ustr;
1125 ustr=(unsigned char*)txt;
1127 mm_log((1,"i_tt_box_inst(handle %p,inst %d,txt '%.*s', len %ld, utf8 %d)\n",
1128 handle, inst, (int)len, txt, (long)len, utf8));
1130 upm = handle->properties.header->Units_Per_EM;
1131 gascent = ( handle->properties.horizontal->Ascender * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm;
1132 gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem - upm + 1) / upm;
1137 mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent));
1142 j = i_utf8_advance(&txt, &len);
1144 i_push_error(0, "invalid UTF8 character");
1149 j = (unsigned char)*txt++;
1152 if ( i_tt_get_glyph(handle,inst,j) ) {
1153 TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j);
1154 width += gm->advance / 64;
1155 casc = (gm->bbox.yMax+63) / 64;
1156 cdesc = (gm->bbox.yMin-63) / 64;
1158 mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n",
1159 (int)((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc));
1162 start = gm->bbox.xMin / 64;
1163 ascent = (gm->bbox.yMax+63) / 64;
1164 descent = (gm->bbox.yMin-63) / 64;
1167 if (!len) { /* if at end of string */
1168 /* the right-side bearing - in case the right-side of a
1169 character goes past the right of the advance width,
1170 as is common for italic fonts
1172 rightb = gm->advance - gm->bearingX
1173 - (gm->bbox.xMax - gm->bbox.xMin);
1174 /* fprintf(stderr, "font info last: %d %d %d %d\n",
1175 gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */
1178 ascent = (ascent > casc ? ascent : casc );
1179 descent = (descent < cdesc ? descent : cdesc);
1183 cords[BBOX_NEG_WIDTH]=start;
1184 cords[BBOX_GLOBAL_DESCENT]=gdescent;
1185 cords[BBOX_POS_WIDTH]=width;
1187 cords[BBOX_POS_WIDTH] -= rightb / 64;
1188 cords[BBOX_GLOBAL_ASCENT]=gascent;
1189 cords[BBOX_DESCENT]=descent;
1190 cords[BBOX_ASCENT]=ascent;
1191 cords[BBOX_ADVANCE_WIDTH] = width;
1192 cords[BBOX_RIGHT_BEARING] = rightb / 64;
1194 return BBOX_RIGHT_BEARING + 1;
1199 =item i_tt_bbox(handle, points, txt, len, cords, utf8)
1201 Interface to get a strings bounding box
1203 handle - pointer to font handle
1204 points - font size to use
1205 txt - string to render
1206 len - length of the string to render
1207 cords - the bounding box (modified in place)
1213 i_tt_bbox( TT_Fonthandle *handle, double points,const char *txt,size_t len,i_img_dim cords[6], int utf8) {
1217 mm_log((1,"i_tt_box(handle %p,points %f,txt '%.*s', len %ld, utf8 %d)\n",
1218 handle, points, (int)len, txt, (long)len, utf8));
1220 if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
1221 i_push_errorf(0, "i_tt_get_instance(%g)", points);
1222 mm_log((1,"i_tt_text: get instance failed\n"));
1226 return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8);
1230 =item i_tt_face_name(handle, name_buf, name_buf_size)
1232 Retrieve's the font's postscript name.
1234 This is complicated by the need to handle encodings and so on.
1239 i_tt_face_name(TT_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
1240 TT_Face_Properties props;
1243 TT_UShort platform_id, encoding_id, lang_id, name_id;
1246 int want_index = -1; /* an acceptable but not perfect name */
1251 TT_Get_Face_Properties(handle->face, &props);
1252 name_count = props.num_Names;
1253 for (i = 0; i < name_count; ++i) {
1254 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1257 TT_Get_Name_String(handle->face, i, &name, &name_len);
1259 if (platform_id != TT_PLATFORM_APPLE_UNICODE && name_len
1260 && name_id == TT_NAME_ID_PS_NAME) {
1261 int might_want_index = -1;
1262 int might_score = 0;
1263 if ((platform_id == TT_PLATFORM_MACINTOSH && encoding_id == TT_MAC_ID_ROMAN)
1265 (platform_id == TT_PLATFORM_MICROSOFT && encoding_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)) {
1266 /* exactly what we want */
1271 if (platform_id == TT_PLATFORM_MICROSOFT
1272 && (encoding_id & 0xFF) == TT_MS_LANGID_ENGLISH_GENERAL) {
1273 /* any english is good */
1274 might_want_index = i;
1277 /* there might be something in between */
1279 /* anything non-unicode is better than nothing */
1280 might_want_index = i;
1283 if (might_score > score) {
1284 score = might_score;
1285 want_index = might_want_index;
1290 if (want_index != -1) {
1291 TT_Get_Name_String(handle->face, want_index, &name, &name_len);
1293 strncpy(name_buf, name, name_buf_size);
1294 name_buf[name_buf_size-1] = '\0';
1296 return strlen(name) + 1;
1299 i_push_error(0, "no face name present");
1304 void i_tt_dump_names(TT_Fonthandle *handle) {
1305 TT_Face_Properties props;
1308 TT_UShort platform_id, encoding_id, lang_id, name_id;
1312 TT_Get_Face_Properties(handle->face, &props);
1313 name_count = props.num_Names;
1314 for (i = 0; i < name_count; ++i) {
1315 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1317 TT_Get_Name_String(handle->face, i, &name, &name_len);
1319 printf("# %d: plat %d enc %d lang %d name %d value ", i, platform_id,
1320 encoding_id, lang_id, name_id);
1321 if (platform_id == TT_PLATFORM_APPLE_UNICODE) {
1322 printf("(unicode)\n");
1325 printf("'%s'\n", name);
1332 i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf,
1333 size_t name_buf_size) {
1341 if (!handle->loaded_names) {
1343 mm_log((1, "Loading PS Names"));
1344 handle->load_cond = TT_Load_PS_Names(handle->face, &post);
1345 ++handle->loaded_names;
1348 if (handle->load_cond) {
1349 i_push_errorf(handle->load_cond, "error loading names (%#x)",
1350 (unsigned)handle->load_cond);
1354 index = TT_Char_Index(handle->char_map, ch);
1356 i_push_error(0, "no such character");
1360 rc = TT_Get_PS_Name(handle->face, index, &psname);
1363 i_push_error(rc, "error getting name");
1367 strncpy(name_buf, psname, name_buf_size);
1368 name_buf[name_buf_size-1] = '\0';
1370 return strlen(psname) + 1;
1372 mm_log((1, "FTXPOST extension not enabled\n"));
1374 i_push_error(0, "Use of FTXPOST extension disabled");
1381 =item i_tt_push_error(code)
1383 Push an error message and code onto the Imager error stack.
1388 i_tt_push_error(TT_Error rc) {
1390 TT_String const *msg = TT_ErrToString18(rc);
1392 i_push_error(rc, msg);
1394 i_push_errorf(rc, "Error code 0x%04x", (unsigned)rc);
1404 Arnar M. Hrafnkelsson <addi@umich.edu>