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, "TT");
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 for(y=0;y<bit->rows;y++) {
875 unsigned mask = 0x80;
876 unsigned char *p = bmap + y * bit->cols;
878 for(x = 0; x < bit->width; x++) {
880 i_ppix(im, x+xb, y+yb, cl);
895 =item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth)
897 Function to dump a raster onto a single channel image in color (internal)
899 im - image to dump raster on
900 bit - bitmap that contains the text to be dumped to im
901 xb, yb - coordinates, left edge and baseline
902 channel - channel to copy to
903 smooth - boolean (True: antialias on, False: antialias is off)
910 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 ) {
915 int old_mask = im->ch_mask;
916 im->ch_mask = 1 << channel;
918 mm_log((1,"i_tt_dump_raster_channel(im %p, bit %p, xb %" i_DF ", yb %" i_DF ", channel %d)\n",
919 im, bit, i_DFc(xb), i_DFc(yb), channel));
924 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
925 c = bmap[y*(bit->cols)+x];
926 val.channel[channel] = c;
927 i_ppix(im,x+xb,y+yb,&val);
930 for(y=0;y<bit->rows;y++) {
931 unsigned mask = 0x80;
932 unsigned char *p = bmap + y * bit->cols;
934 for(x=0;x<bit->width;x++) {
935 val.channel[channel] = (*p & mask) ? 255 : 0;
936 i_ppix(im,x+xb,y+yb,&val);
946 im->ch_mask = old_mask;
951 =item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth)
953 interface for generating single channel raster of text (internal)
955 handle - pointer to font handle
956 bit - the bitmap that is allocated, rendered into and NOT freed
957 cords - the bounding box (modified in place)
958 points - font size to use
959 txt - string to render
960 len - length of the string to render
961 smooth - boolean (True: antialias on, False: antialias is off)
968 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 ) {
970 i_img_dim width, height;
971 TT_Raster_Map small_bit;
973 /* find or install an instance */
974 if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) {
975 mm_log((1,"i_tt_rasterize: get instance failed\n"));
979 /* calculate bounding box */
980 if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 ))
984 width = cords[2]-cords[0];
985 height = cords[5]-cords[4];
987 mm_log((1,"i_tt_rasterize: width=%" i_DF ", height=%" i_DF "\n",
988 i_DFc(width), i_DFc(height) ));
990 i_tt_init_raster_map ( bit, width, height, smooth );
991 i_tt_clear_raster_map( bit );
992 if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth );
994 if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len,
997 i_tt_done_raster_map( &small_bit );
1001 if ( smooth ) i_tt_done_raster_map( &small_bit );
1008 * Exported text rendering interfaces
1013 =item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth, utf8)
1015 Interface to text rendering into a single channel in an image
1017 handle - pointer to font handle
1018 im - image to render text on to
1019 xb, yb - coordinates, left edge and baseline
1020 channel - channel to render into
1021 points - font size to use
1022 txt - string to render
1023 len - length of the string to render
1024 smooth - boolean (True: antialias on, False: antialias is off)
1030 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 ) {
1032 i_img_dim cords[BOUNDING_BOX_COUNT];
1033 i_img_dim ascent, st_offset, y;
1037 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1039 ascent=cords[BBOX_ASCENT];
1040 st_offset=cords[BBOX_NEG_WIDTH];
1041 y = align ? yb-ascent : yb;
1043 i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , y, channel, smooth );
1044 i_tt_done_raster_map( &bit );
1051 =item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8)
1053 Interface to text rendering in a single color onto an image
1055 handle - pointer to font handle
1056 im - image to render text on to
1057 xb, yb - coordinates, left edge and baseline
1058 cl - color to use for text
1059 points - font size to use
1060 txt - string to render
1061 len - length of the string to render
1062 smooth - boolean (True: antialias on, False: antialias is off)
1068 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) {
1069 i_img_dim cords[BOUNDING_BOX_COUNT];
1070 i_img_dim ascent, st_offset, y;
1075 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1077 ascent=cords[BBOX_ASCENT];
1078 st_offset=cords[BBOX_NEG_WIDTH];
1079 y = align ? yb-ascent : yb;
1081 i_tt_dump_raster_map2( im, &bit, xb+st_offset, y, cl, smooth );
1082 i_tt_done_raster_map( &bit );
1089 =item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8)
1091 Function to get texts bounding boxes given the instance of the font (internal)
1093 handle - pointer to font handle
1094 inst - font instance
1095 txt - string to measure
1096 len - length of the string to render
1097 cords - the bounding box (modified in place)
1104 i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, size_t len, i_img_dim cords[BOUNDING_BOX_COUNT], int utf8 ) {
1105 int upm, casc, cdesc, first;
1108 i_img_dim width = 0;
1116 unsigned char *ustr;
1117 ustr=(unsigned char*)txt;
1119 mm_log((1,"i_tt_box_inst(handle %p,inst %d,txt '%.*s', len %ld, utf8 %d)\n",
1120 handle, inst, (int)len, txt, (long)len, utf8));
1122 upm = handle->properties.header->Units_Per_EM;
1123 gascent = ( handle->properties.horizontal->Ascender * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm;
1124 gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem - upm + 1) / upm;
1129 mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent));
1134 j = i_utf8_advance(&txt, &len);
1136 i_push_error(0, "invalid UTF8 character");
1141 j = (unsigned char)*txt++;
1144 if ( i_tt_get_glyph(handle,inst,j) ) {
1145 TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j);
1146 width += gm->advance / 64;
1147 casc = (gm->bbox.yMax+63) / 64;
1148 cdesc = (gm->bbox.yMin-63) / 64;
1150 mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n",
1151 (int)((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc));
1154 start = gm->bbox.xMin / 64;
1155 ascent = (gm->bbox.yMax+63) / 64;
1156 descent = (gm->bbox.yMin-63) / 64;
1159 if (!len) { /* if at end of string */
1160 /* the right-side bearing - in case the right-side of a
1161 character goes past the right of the advance width,
1162 as is common for italic fonts
1164 rightb = gm->advance - gm->bearingX
1165 - (gm->bbox.xMax - gm->bbox.xMin);
1166 /* fprintf(stderr, "font info last: %d %d %d %d\n",
1167 gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */
1170 ascent = (ascent > casc ? ascent : casc );
1171 descent = (descent < cdesc ? descent : cdesc);
1175 cords[BBOX_NEG_WIDTH]=start;
1176 cords[BBOX_GLOBAL_DESCENT]=gdescent;
1177 cords[BBOX_POS_WIDTH]=width;
1179 cords[BBOX_POS_WIDTH] -= rightb / 64;
1180 cords[BBOX_GLOBAL_ASCENT]=gascent;
1181 cords[BBOX_DESCENT]=descent;
1182 cords[BBOX_ASCENT]=ascent;
1183 cords[BBOX_ADVANCE_WIDTH] = width;
1184 cords[BBOX_RIGHT_BEARING] = rightb / 64;
1186 return BBOX_RIGHT_BEARING + 1;
1191 =item i_tt_bbox(handle, points, txt, len, cords, utf8)
1193 Interface to get a strings bounding box
1195 handle - pointer to font handle
1196 points - font size to use
1197 txt - string to render
1198 len - length of the string to render
1199 cords - the bounding box (modified in place)
1205 i_tt_bbox( TT_Fonthandle *handle, double points,const char *txt,size_t len,i_img_dim cords[6], int utf8) {
1209 mm_log((1,"i_tt_box(handle %p,points %f,txt '%.*s', len %ld, utf8 %d)\n",
1210 handle, points, (int)len, txt, (long)len, utf8));
1212 if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
1213 i_push_errorf(0, "i_tt_get_instance(%g)", points);
1214 mm_log((1,"i_tt_text: get instance failed\n"));
1218 return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8);
1222 =item i_tt_face_name(handle, name_buf, name_buf_size)
1224 Retrieve's the font's postscript name.
1226 This is complicated by the need to handle encodings and so on.
1231 i_tt_face_name(TT_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
1232 TT_Face_Properties props;
1235 TT_UShort platform_id, encoding_id, lang_id, name_id;
1238 int want_index = -1; /* an acceptable but not perfect name */
1243 TT_Get_Face_Properties(handle->face, &props);
1244 name_count = props.num_Names;
1245 for (i = 0; i < name_count; ++i) {
1246 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1249 TT_Get_Name_String(handle->face, i, &name, &name_len);
1251 if (platform_id != TT_PLATFORM_APPLE_UNICODE && name_len
1252 && name_id == TT_NAME_ID_PS_NAME) {
1253 int might_want_index = -1;
1254 int might_score = 0;
1255 if ((platform_id == TT_PLATFORM_MACINTOSH && encoding_id == TT_MAC_ID_ROMAN)
1257 (platform_id == TT_PLATFORM_MICROSOFT && encoding_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)) {
1258 /* exactly what we want */
1263 if (platform_id == TT_PLATFORM_MICROSOFT
1264 && (encoding_id & 0xFF) == TT_MS_LANGID_ENGLISH_GENERAL) {
1265 /* any english is good */
1266 might_want_index = i;
1269 /* there might be something in between */
1271 /* anything non-unicode is better than nothing */
1272 might_want_index = i;
1275 if (might_score > score) {
1276 score = might_score;
1277 want_index = might_want_index;
1282 if (want_index != -1) {
1283 TT_Get_Name_String(handle->face, want_index, &name, &name_len);
1285 strncpy(name_buf, name, name_buf_size);
1286 name_buf[name_buf_size-1] = '\0';
1288 return strlen(name) + 1;
1291 i_push_error(0, "no face name present");
1296 void i_tt_dump_names(TT_Fonthandle *handle) {
1297 TT_Face_Properties props;
1300 TT_UShort platform_id, encoding_id, lang_id, name_id;
1304 TT_Get_Face_Properties(handle->face, &props);
1305 name_count = props.num_Names;
1306 for (i = 0; i < name_count; ++i) {
1307 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1309 TT_Get_Name_String(handle->face, i, &name, &name_len);
1311 printf("# %d: plat %d enc %d lang %d name %d value ", i, platform_id,
1312 encoding_id, lang_id, name_id);
1313 if (platform_id == TT_PLATFORM_APPLE_UNICODE) {
1314 printf("(unicode)\n");
1317 printf("'%s'\n", name);
1324 i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf,
1325 size_t name_buf_size) {
1333 if (!handle->loaded_names) {
1335 mm_log((1, "Loading PS Names"));
1336 handle->load_cond = TT_Load_PS_Names(handle->face, &post);
1337 ++handle->loaded_names;
1340 if (handle->load_cond) {
1341 i_push_errorf(handle->load_cond, "error loading names (%#x)",
1342 (unsigned)handle->load_cond);
1346 index = TT_Char_Index(handle->char_map, ch);
1348 i_push_error(0, "no such character");
1352 rc = TT_Get_PS_Name(handle->face, index, &psname);
1355 i_push_error(rc, "error getting name");
1359 strncpy(name_buf, psname, name_buf_size);
1360 name_buf[name_buf_size-1] = '\0';
1362 return strlen(psname) + 1;
1364 mm_log((1, "FTXPOST extension not enabled\n"));
1366 i_push_error(0, "Use of FTXPOST extension disabled");
1373 =item i_tt_push_error(code)
1375 Push an error message and code onto the Imager error stack.
1380 i_tt_push_error(TT_Error rc) {
1382 TT_String const *msg = TT_ErrToString18(rc);
1384 i_push_error(rc, msg);
1386 i_push_errorf(rc, "Error code 0x%04x", (unsigned)rc);
1396 Arnar M. Hrafnkelsson <addi@umich.edu>