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;
767 xmin = gmetrics->bbox.xMin & -64;
768 ymin = gmetrics->bbox.yMin & -64;
770 i_tt_clear_raster_map( small_bit );
771 TT_Get_Glyph_Pixmap( glyph, small_bit, -xmin, -ymin );
772 i_tt_blit_or( bit, small_bit, xmin/64 + x_off, -ymin/64 - y_off );
778 =item i_tt_render_all_glyphs(handle, inst, bit, small_bit, cords, txt, len, smooth)
780 calls i_tt_render_glyph to render each glyph into the bit rastermap (internal)
782 handle - pointer to font handle
784 bit - large bitmap that is the destination for the text
785 smallbit - small bitmap that is used only if smooth is true
786 txt - string to render
787 len - length of the string to render
788 smooth - boolean (True: antialias on, False: antialias is off)
795 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
796 TT_Raster_Map *small_bit, i_img_dim cords[6],
797 char const* txt, size_t len, int smooth, int utf8 ) {
801 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",
802 handle, inst, bit, small_bit, (int)len, txt, (long)len, smooth, utf8));
805 y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM);
808 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 */
813 j = i_utf8_advance(&txt, &len);
815 i_push_error(0, "invalid UTF8 character");
820 j = (unsigned char)*txt++;
823 if ( !i_tt_get_glyph(handle,inst,j) )
825 i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
826 &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit,
827 small_bit, x, y, smooth );
828 x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64;
836 * Functions to render rasters (single channel images) onto images
840 =item i_tt_dump_raster_map2(im, bit, xb, yb, cl, smooth)
842 Function to dump a raster onto an image in color used by i_tt_text() (internal).
844 im - image to dump raster on
845 bit - bitmap that contains the text to be dumped to im
846 xb, yb - coordinates, left edge and baseline
847 cl - color to use for text
848 smooth - boolean (True: antialias on, False: antialias is off)
855 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 ) {
858 mm_log((1,"i_tt_dump_raster_map2(im %p, bit %p, xb %" i_DF ", yb %" i_DF ", cl %p)\n",
859 im, bit, i_DFc(xb), i_DFc(yb), cl));
866 i_render_init(&r, im, bit->cols);
867 for(y=0;y<bit->rows;y++) {
868 i_render_color(&r, xb, yb+y, bit->cols, bmap + y*bit->cols, cl);
872 unsigned char *bmp = mymalloc(bit->width);
875 i_render_init(&r, im, bit->width);
877 for(y=0;y<bit->rows;y++) {
878 unsigned mask = 0x80;
879 unsigned char *p = bmap + y * bit->cols;
880 unsigned char *pout = bmp;
882 for(x = 0; x < bit->width; x++) {
883 *pout++ = (*p & mask) ? 0xFF : 0;
891 i_render_color(&r, xb, yb+y, bit->cols, bmp, cl);
901 =item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth)
903 Function to dump a raster onto a single channel image in color (internal)
905 im - image to dump raster on
906 bit - bitmap that contains the text to be dumped to im
907 xb, yb - coordinates, left edge and baseline
908 channel - channel to copy to
909 smooth - boolean (True: antialias on, False: antialias is off)
916 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 ) {
921 int old_mask = im->ch_mask;
922 im->ch_mask = 1 << channel;
924 mm_log((1,"i_tt_dump_raster_channel(im %p, bit %p, xb %" i_DF ", yb %" i_DF ", channel %d)\n",
925 im, bit, i_DFc(xb), i_DFc(yb), channel));
930 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
931 c = bmap[y*(bit->cols)+x];
932 val.channel[channel] = c;
933 i_ppix(im,x+xb,y+yb,&val);
936 for(y=0;y<bit->rows;y++) {
937 unsigned mask = 0x80;
938 unsigned char *p = bmap + y * bit->cols;
940 for(x=0;x<bit->width;x++) {
941 val.channel[channel] = (*p & mask) ? 255 : 0;
942 i_ppix(im,x+xb,y+yb,&val);
952 im->ch_mask = old_mask;
957 =item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth)
959 interface for generating single channel raster of text (internal)
961 handle - pointer to font handle
962 bit - the bitmap that is allocated, rendered into and NOT freed
963 cords - the bounding box (modified in place)
964 points - font size to use
965 txt - string to render
966 len - length of the string to render
967 smooth - boolean (True: antialias on, False: antialias is off)
974 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 ) {
976 i_img_dim width, height;
977 TT_Raster_Map small_bit;
979 /* find or install an instance */
980 if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) {
981 mm_log((1,"i_tt_rasterize: get instance failed\n"));
985 /* calculate bounding box */
986 if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 ))
990 width = cords[2]-cords[0];
991 height = cords[5]-cords[4];
993 mm_log((1,"i_tt_rasterize: width=%" i_DF ", height=%" i_DF "\n",
994 i_DFc(width), i_DFc(height) ));
996 i_tt_init_raster_map ( bit, width, height, smooth );
997 i_tt_clear_raster_map( bit );
998 if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth );
1000 if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len,
1003 i_tt_done_raster_map( &small_bit );
1007 if ( smooth ) i_tt_done_raster_map( &small_bit );
1014 * Exported text rendering interfaces
1019 =item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth, utf8)
1021 Interface to text rendering into a single channel in an image
1023 handle - pointer to font handle
1024 im - image to render text on to
1025 xb, yb - coordinates, left edge and baseline
1026 channel - channel to render into
1027 points - font size to use
1028 txt - string to render
1029 len - length of the string to render
1030 smooth - boolean (True: antialias on, False: antialias is off)
1036 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 ) {
1038 i_img_dim cords[BOUNDING_BOX_COUNT];
1039 i_img_dim ascent, st_offset, y;
1043 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1045 ascent=cords[BBOX_ASCENT];
1046 st_offset=cords[BBOX_NEG_WIDTH];
1047 y = align ? yb-ascent : yb;
1049 i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , y, channel, smooth );
1050 i_tt_done_raster_map( &bit );
1057 =item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8)
1059 Interface to text rendering in a single color onto an image
1061 handle - pointer to font handle
1062 im - image to render text on to
1063 xb, yb - coordinates, left edge and baseline
1064 cl - color to use for text
1065 points - font size to use
1066 txt - string to render
1067 len - length of the string to render
1068 smooth - boolean (True: antialias on, False: antialias is off)
1074 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) {
1075 i_img_dim cords[BOUNDING_BOX_COUNT];
1076 i_img_dim ascent, st_offset, y;
1081 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1083 ascent=cords[BBOX_ASCENT];
1084 st_offset=cords[BBOX_NEG_WIDTH];
1085 y = align ? yb-ascent : yb;
1087 i_tt_dump_raster_map2( im, &bit, xb+st_offset, y, cl, smooth );
1088 i_tt_done_raster_map( &bit );
1095 =item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8)
1097 Function to get texts bounding boxes given the instance of the font (internal)
1099 handle - pointer to font handle
1100 inst - font instance
1101 txt - string to measure
1102 len - length of the string to render
1103 cords - the bounding box (modified in place)
1110 i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, size_t len, i_img_dim cords[BOUNDING_BOX_COUNT], int utf8 ) {
1111 int upm, casc, cdesc, first;
1114 i_img_dim width = 0;
1123 mm_log((1,"i_tt_box_inst(handle %p,inst %d,txt '%.*s', len %ld, utf8 %d)\n",
1124 handle, inst, (int)len, txt, (long)len, utf8));
1126 upm = handle->properties.header->Units_Per_EM;
1127 gascent = ( handle->properties.horizontal->Ascender * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm;
1128 gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem - upm + 1) / upm;
1133 mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent));
1138 j = i_utf8_advance(&txt, &len);
1140 i_push_error(0, "invalid UTF8 character");
1145 j = (unsigned char)*txt++;
1148 if ( i_tt_get_glyph(handle,inst,j) ) {
1149 TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j);
1150 width += gm->advance / 64;
1151 casc = (gm->bbox.yMax+63) / 64;
1152 cdesc = (gm->bbox.yMin-63) / 64;
1154 mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n",
1155 (int)((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc));
1158 start = gm->bbox.xMin / 64;
1159 ascent = (gm->bbox.yMax+63) / 64;
1160 descent = (gm->bbox.yMin-63) / 64;
1163 if (!len) { /* if at end of string */
1164 /* the right-side bearing - in case the right-side of a
1165 character goes past the right of the advance width,
1166 as is common for italic fonts
1168 rightb = gm->advance - gm->bearingX
1169 - (gm->bbox.xMax - gm->bbox.xMin);
1170 /* fprintf(stderr, "font info last: %d %d %d %d\n",
1171 gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */
1174 ascent = (ascent > casc ? ascent : casc );
1175 descent = (descent < cdesc ? descent : cdesc);
1179 cords[BBOX_NEG_WIDTH]=start;
1180 cords[BBOX_GLOBAL_DESCENT]=gdescent;
1181 cords[BBOX_POS_WIDTH]=width;
1183 cords[BBOX_POS_WIDTH] -= rightb / 64;
1184 cords[BBOX_GLOBAL_ASCENT]=gascent;
1185 cords[BBOX_DESCENT]=descent;
1186 cords[BBOX_ASCENT]=ascent;
1187 cords[BBOX_ADVANCE_WIDTH] = width;
1188 cords[BBOX_RIGHT_BEARING] = rightb / 64;
1190 return BBOX_RIGHT_BEARING + 1;
1195 =item i_tt_bbox(handle, points, txt, len, cords, utf8)
1197 Interface to get a strings bounding box
1199 handle - pointer to font handle
1200 points - font size to use
1201 txt - string to render
1202 len - length of the string to render
1203 cords - the bounding box (modified in place)
1209 i_tt_bbox( TT_Fonthandle *handle, double points,const char *txt,size_t len,i_img_dim cords[6], int utf8) {
1213 mm_log((1,"i_tt_box(handle %p,points %f,txt '%.*s', len %ld, utf8 %d)\n",
1214 handle, points, (int)len, txt, (long)len, utf8));
1216 if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
1217 i_push_errorf(0, "i_tt_get_instance(%g)", points);
1218 mm_log((1,"i_tt_text: get instance failed\n"));
1222 return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8);
1226 =item i_tt_face_name(handle, name_buf, name_buf_size)
1228 Retrieve's the font's postscript name.
1230 This is complicated by the need to handle encodings and so on.
1235 i_tt_face_name(TT_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
1236 TT_Face_Properties props;
1239 TT_UShort platform_id, encoding_id, lang_id, name_id;
1242 int want_index = -1; /* an acceptable but not perfect name */
1247 TT_Get_Face_Properties(handle->face, &props);
1248 name_count = props.num_Names;
1249 for (i = 0; i < name_count; ++i) {
1250 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1253 TT_Get_Name_String(handle->face, i, &name, &name_len);
1255 if (platform_id != TT_PLATFORM_APPLE_UNICODE && name_len
1256 && name_id == TT_NAME_ID_PS_NAME) {
1257 int might_want_index = -1;
1258 int might_score = 0;
1259 if ((platform_id == TT_PLATFORM_MACINTOSH && encoding_id == TT_MAC_ID_ROMAN)
1261 (platform_id == TT_PLATFORM_MICROSOFT && encoding_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)) {
1262 /* exactly what we want */
1267 if (platform_id == TT_PLATFORM_MICROSOFT
1268 && (encoding_id & 0xFF) == TT_MS_LANGID_ENGLISH_GENERAL) {
1269 /* any english is good */
1270 might_want_index = i;
1273 /* there might be something in between */
1275 /* anything non-unicode is better than nothing */
1276 might_want_index = i;
1279 if (might_score > score) {
1280 score = might_score;
1281 want_index = might_want_index;
1286 if (want_index != -1) {
1287 TT_Get_Name_String(handle->face, want_index, &name, &name_len);
1289 strncpy(name_buf, name, name_buf_size);
1290 name_buf[name_buf_size-1] = '\0';
1292 return strlen(name) + 1;
1295 i_push_error(0, "no face name present");
1300 void i_tt_dump_names(TT_Fonthandle *handle) {
1301 TT_Face_Properties props;
1304 TT_UShort platform_id, encoding_id, lang_id, name_id;
1308 TT_Get_Face_Properties(handle->face, &props);
1309 name_count = props.num_Names;
1310 for (i = 0; i < name_count; ++i) {
1311 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1313 TT_Get_Name_String(handle->face, i, &name, &name_len);
1315 printf("# %d: plat %d enc %d lang %d name %d value ", i, platform_id,
1316 encoding_id, lang_id, name_id);
1317 if (platform_id == TT_PLATFORM_APPLE_UNICODE) {
1318 printf("(unicode)\n");
1321 printf("'%s'\n", name);
1328 i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf,
1329 size_t name_buf_size) {
1337 if (!handle->loaded_names) {
1339 mm_log((1, "Loading PS Names"));
1340 handle->load_cond = TT_Load_PS_Names(handle->face, &post);
1341 ++handle->loaded_names;
1344 if (handle->load_cond) {
1345 i_push_errorf(handle->load_cond, "error loading names (%#x)",
1346 (unsigned)handle->load_cond);
1350 index = TT_Char_Index(handle->char_map, ch);
1352 i_push_error(0, "no such character");
1356 rc = TT_Get_PS_Name(handle->face, index, &psname);
1359 i_push_error(rc, "error getting name");
1363 strncpy(name_buf, psname, name_buf_size);
1364 name_buf[name_buf_size-1] = '\0';
1366 return strlen(psname) + 1;
1368 mm_log((1, "FTXPOST extension not enabled\n"));
1370 i_push_error(0, "Use of FTXPOST extension disabled");
1377 =item i_tt_push_error(code)
1379 Push an error message and code onto the Imager error stack.
1384 i_tt_push_error(TT_Error rc) {
1386 TT_String const *msg = TT_ErrToString18(rc);
1388 i_push_error(rc, msg);
1390 i_push_errorf(rc, "Error code 0x%04x", (unsigned)rc);
1400 Arnar M. Hrafnkelsson <addi@umich.edu>