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 /* convert a code point into an index in the glyph cache */
70 #define TT_HASH(x) ((x) & 0xFF)
72 typedef struct i_glyph_entry_ {
77 #define TT_NOCHAR (~0UL)
79 struct TT_Instancehandle_ {
81 TT_Instance_Metrics imetrics;
82 TT_Glyph_Metrics gmetrics[256];
83 i_tt_glyph_entry glyphs[256];
89 typedef struct TT_Instancehandle_ TT_Instancehandle;
91 struct TT_Fonthandle_ {
93 TT_Face_Properties properties;
94 TT_Instancehandle instanceh[TT_CHC];
104 #define USTRCT(x) ((x).z)
105 #define TT_VALID( handle ) ( ( handle ).z != NULL )
107 static void i_tt_push_error(TT_Error rc);
111 static int i_tt_get_instance( TT_Fonthandle *handle, i_img_dim points, int smooth );
112 static void i_tt_init_raster_map( TT_Raster_Map* bit, i_img_dim width, i_img_dim height, int smooth );
113 static void i_tt_done_raster_map( TT_Raster_Map *bit );
114 static void i_tt_clear_raster_map( TT_Raster_Map* bit );
115 static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,i_img_dim x_off, i_img_dim y_off );
116 static int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j );
118 i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics,
119 TT_Raster_Map *bit, TT_Raster_Map *small_bit,
120 i_img_dim x_off, i_img_dim y_off, int smooth );
122 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
123 TT_Raster_Map *small_bit, i_img_dim cords[6],
124 char const* txt, size_t len, int smooth, int utf8 );
125 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 );
126 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 );
128 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, i_img_dim cords[6],
129 double points, char const* txt, size_t len, int smooth, int utf8 );
130 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 );
133 /* static globals needed */
135 static int TT_initialized = 0;
136 static TT_Engine engine;
137 static int LTT_dpi = 72; /* FIXME: this ought to be a part of the call interface */
138 static int LTT_hinted = 1; /* FIXME: this too */
149 Initializes the freetype font rendering engine
157 TT_Byte palette[] = { 0, 64, 127, 191, 255 };
161 mm_log((1,"init_tt()\n"));
162 error = TT_Init_FreeType( &engine );
164 mm_log((1,"Initialization of freetype failed, code = 0x%x\n",
166 i_tt_push_error(error);
167 i_push_error(0, "Could not initialize freetype 1.x");
172 error = TT_Init_Post_Extension( engine );
174 mm_log((1, "Initialization of Post extension failed = 0x%x\n",
177 i_tt_push_error(error);
178 i_push_error(0, "Could not initialize FT 1.x POST extension");
183 error = TT_Set_Raster_Gray_Palette(engine, palette);
185 mm_log((1, "Initialization of gray levels failed = 0x%x\n",
187 i_tt_push_error(error);
188 i_push_error(0, "Could not initialize FT 1.x POST extension");
199 =item i_tt_get_instance(handle, points, smooth)
201 Finds a points+smooth instance or if one doesn't exist in the cache
202 allocates room and returns its cache entry
204 fontname - path to the font to load
205 handle - handle to the font.
206 points - points of the requested font
207 smooth - boolean (True: antialias on, False: antialias is off)
214 i_tt_get_instance( TT_Fonthandle *handle, i_img_dim points, int smooth ) {
218 mm_log((1,"i_tt_get_instance(handle %p, points %" i_DF ", smooth %d)\n",
219 handle, i_DFc(points), smooth));
221 if (smooth == -1) { /* Smooth doesn't matter for this search */
222 for(i=0;i<TT_CHC;i++) {
223 if (handle->instanceh[i].ptsize==points) {
224 mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
228 smooth=1; /* We will be adding a font - add it as smooth then */
229 } else { /* Smooth doesn't matter for this search */
230 for(i=0;i<TT_CHC;i++) {
231 if (handle->instanceh[i].ptsize == points
232 && handle->instanceh[i].smooth == smooth) {
233 mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
239 /* Found the instance in the cache - return the cache index */
241 for(idx=0;idx<TT_CHC;idx++) {
242 if (!(handle->instanceh[idx].order)) break; /* find the lru item */
245 mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
246 mm_log((1,"i_tt_get_instance: lru pointer %p\n",
247 USTRCT(handle->instanceh[idx].instance) ));
249 if ( USTRCT(handle->instanceh[idx].instance) ) {
250 mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
252 /* Free cached glyphs */
254 if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
255 TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
258 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
259 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
262 /* Free instance if needed */
263 TT_Done_Instance( handle->instanceh[idx].instance );
266 /* create and initialize instance */
267 /* FIXME: probably a memory leak on fail */
269 (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) ||
270 ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
271 ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
274 mm_log((1, "Could not create and initialize instance: error %x.\n",
279 /* Now that the instance should the inplace we need to lower all of the
280 ru counts and put `this' one with the highest entry */
282 for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
284 handle->instanceh[idx].order=TT_CHC-1;
285 handle->instanceh[idx].ptsize=points;
286 handle->instanceh[idx].smooth=smooth;
287 TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
289 /* Zero the memory for the glyph storage so they are not thought as
290 cached if they haven't been cached since this new font was loaded */
293 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
294 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
302 =item i_tt_new(fontname)
304 Creates a new font handle object, finds a character map and initialise the
305 the font handle's cache
307 fontname - path to the font to load
313 i_tt_new(const char *fontname) {
315 TT_Fonthandle *handle;
317 unsigned short platform,encoding;
319 if (!TT_initialized && i_init_tt()) {
320 i_push_error(0, "Could not initialize FT1 engine");
326 mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
328 /* allocate memory for the structure */
330 handle = mymalloc( sizeof(TT_Fonthandle) ); /* checked 5Nov05 tonyc */
332 /* load the typeface */
333 error = TT_Open_Face( engine, fontname, &handle->face );
335 if ( error == TT_Err_Could_Not_Open_File ) {
336 mm_log((1, "Could not find/open %s.\n", fontname ));
339 mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname,
342 i_tt_push_error(error);
346 TT_Get_Face_Properties( handle->face, &(handle->properties) );
348 /* First, look for a Unicode charmap */
349 n = handle->properties.num_CharMaps;
350 USTRCT( handle->char_map )=NULL; /* Invalidate character map */
352 for ( i = 0; i < n; i++ ) {
353 TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
354 if ( (platform == 3 && encoding == 1 )
355 || (platform == 0 && encoding == 0 ) ) {
356 mm_log((2,"i_tt_new - found char map platform %u encoding %u\n",
357 platform, encoding));
358 TT_Get_CharMap( handle->face, i, &(handle->char_map) );
362 if (!USTRCT(handle->char_map) && n != 0) {
363 /* just use the first one */
364 TT_Get_CharMap( handle->face, 0, &(handle->char_map));
367 /* Zero the pointsizes - and ordering */
369 for(i=0;i<TT_CHC;i++) {
370 USTRCT(handle->instanceh[i].instance)=NULL;
371 handle->instanceh[i].order=i;
372 handle->instanceh[i].ptsize=0;
373 handle->instanceh[i].smooth=-1;
377 handle->loaded_names = 0;
380 mm_log((1,"i_tt_new <- %p\n",handle));
387 * raster map management
391 =item i_tt_init_raster_map(bit, width, height, smooth)
393 Allocates internal memory for the bitmap as needed by the parameters (internal)
395 bit - bitmap to allocate into
396 width - width of the bitmap
397 height - height of the bitmap
398 smooth - boolean (True: antialias on, False: antialias is off)
405 i_tt_init_raster_map( TT_Raster_Map* bit, i_img_dim width, i_img_dim height, int smooth ) {
407 mm_log((1,"i_tt_init_raster_map( bit %p, width %" i_DF ", height %" i_DF
408 ", smooth %d)\n", bit, i_DFc(width), i_DFc(height), smooth));
411 bit->width = ( width + 3 ) & -4;
412 bit->flow = TT_Flow_Down;
415 bit->cols = bit->width;
416 bit->size = bit->rows * bit->width;
418 bit->cols = ( bit->width + 7 ) / 8; /* convert to # of bytes */
419 bit->size = bit->rows * bit->cols; /* number of bytes in buffer */
422 /* rows can be 0 for some glyphs, for example ' ' */
423 if (bit->rows && bit->size / bit->rows != bit->cols) {
424 i_fatal(0, "Integer overflow calculating bitmap size (%d, %d)\n",
425 bit->width, bit->rows);
428 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 ));
430 bit->bitmap = (void *) mymalloc( bit->size ); /* checked 6Nov05 tonyc */
431 if ( !bit->bitmap ) i_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",bit->size );
436 =item i_tt_clear_raster_map(bit)
438 Frees the bitmap data and sets pointer to NULL (internal)
447 i_tt_done_raster_map( TT_Raster_Map *bit ) {
448 myfree( bit->bitmap );
454 =item i_tt_clear_raster_map(bit)
456 Clears the specified bitmap (internal)
466 i_tt_clear_raster_map( TT_Raster_Map* bit ) {
467 memset( bit->bitmap, 0, bit->size );
472 =item i_tt_blit_or(dst, src, x_off, y_off)
474 function that blits one raster map into another (internal)
476 dst - destination bitmap
478 x_off - x offset into the destination bitmap
479 y_off - y offset into the destination bitmap
486 i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,i_img_dim x_off, i_img_dim y_off ) {
488 i_img_dim x1, x2, y1, y2;
489 unsigned char *s, *d;
491 x1 = x_off < 0 ? -x_off : 0;
492 y1 = y_off < 0 ? -y_off : 0;
494 x2 = (int)dst->cols - x_off;
495 if ( x2 > src->cols ) x2 = src->cols;
497 y2 = (int)dst->rows - y_off;
498 if ( y2 > src->rows ) y2 = src->rows;
500 if ( x1 >= x2 ) return;
502 /* do the real work now */
504 for ( y = y1; y < y2; ++y ) {
505 s = ( (unsigned char*)src->bitmap ) + y * src->cols + x1;
506 d = ( (unsigned char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
508 for ( x = x1; x < x2; ++x ) {
517 /* useful for debugging */
520 static void dump_raster_map(FILE *out, TT_Raster_Map *bit ) {
522 fprintf(out, "cols %d rows %d flow %d\n", bit->cols, bit->rows, bit->flow);
523 for (y = 0; y < bit->rows; ++y) {
524 fprintf(out, "%2d:", y);
525 for (x = 0; x < bit->cols; ++x) {
526 if ((x & 7) == 0 && x) putc(' ', out);
527 fprintf(out, "%02x", ((unsigned char *)bit->bitmap)[y*bit->cols+x]);
536 =item i_tt_get_glyph(handle, inst, j)
538 Function to see if a glyph exists and if so cache it (internal)
540 handle - pointer to font handle
542 j - charcode of glyph
549 i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j) {
550 unsigned short load_flags, code;
553 mm_log((1, "i_tt_get_glyph(handle %p, inst %d, j %lu (%c))\n",
554 handle,inst,j, (int)((j >= ' ' && j <= '~') ? j : '.')));
556 /*mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] ));*/
558 if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)
559 && handle->instanceh[inst].glyphs[TT_HASH(j)].ch == j) {
560 mm_log((1,"i_tt_get_glyph: %lu in cache\n",j));
564 if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) ) {
565 /* clean up the entry */
566 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
567 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
568 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
571 /* Ok - it wasn't cached - try to get it in */
572 load_flags = TTLOAD_SCALE_GLYPH;
573 if ( LTT_hinted ) load_flags |= TTLOAD_HINT_GLYPH;
575 if ( !TT_VALID(handle->char_map) ) {
576 code = (j - ' ' + 1) < 0 ? 0 : (j - ' ' + 1);
577 if ( code >= handle->properties.num_Glyphs ) code = 0;
578 } else code = TT_Char_Index( handle->char_map, j );
580 if ( (error = TT_New_Glyph( handle->face, &handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)) ) {
581 mm_log((1, "Cannot allocate and load glyph: error %#x.\n", (unsigned)error ));
582 i_push_error(error, "TT_New_Glyph()");
585 if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, code, load_flags)) ) {
586 mm_log((1, "Cannot allocate and load glyph: error %#x.\n", (unsigned)error ));
588 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
589 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
590 i_push_error(error, "TT_Load_Glyph()");
594 /* At this point the glyph should be allocated and loaded */
595 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = j;
597 /* Next get the glyph metrics */
598 error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
599 &handle->instanceh[inst].gmetrics[TT_HASH(j)] );
601 mm_log((1, "TT_Get_Glyph_Metrics: error %#x.\n", (unsigned)error ));
602 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
603 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
604 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
605 i_push_error(error, "TT_Get_Glyph_Metrics()");
613 =item i_tt_has_chars(handle, text, len, utf8, out)
615 Check if the given characters are defined by the font. Note that len
616 is the number of bytes, not the number of characters (when utf8 is
619 Returns the number of characters that were checked.
625 i_tt_has_chars(TT_Fonthandle *handle, char const *text, size_t len, int utf8,
628 mm_log((1, "i_tt_has_chars(handle %p, text %p, len %ld, utf8 %d)\n",
629 handle, text, (long)len, utf8));
635 c = i_utf8_advance(&text, &len);
637 i_push_error(0, "invalid UTF8 character");
642 c = (unsigned char)*text++;
646 if (TT_VALID(handle->char_map)) {
647 index = TT_Char_Index(handle->char_map, c);
650 index = (c - ' ' + 1) < 0 ? 0 : (c - ' ' + 1);
651 if (index >= handle->properties.num_Glyphs)
662 =item i_tt_destroy(handle)
664 Clears the data taken by a font including all cached data such as
667 handle - pointer to font handle
673 i_tt_destroy( TT_Fonthandle *handle) {
674 TT_Close_Face( handle->face );
677 /* FIXME: Should these be freed automatically by the library?
679 TT_Done_Instance( instance );
681 i_tt_done_glyphs( void ) {
684 if ( !glyphs ) return;
686 for ( i = 0; i < 256; ++i ) TT_Done_Glyph( glyphs[i] );
696 * FreeType Rendering functions
701 =item i_tt_render_glyph(handle, gmetrics, bit, smallbit, x_off, y_off, smooth)
703 Renders a single glyph into the bit rastermap (internal)
705 handle - pointer to font handle
706 gmetrics - the metrics for the glyph to be rendered
707 bit - large bitmap that is the destination for the text
708 smallbit - small bitmap that is used only if smooth is true
709 x_off - x offset of glyph
710 y_off - y offset of glyph
711 smooth - boolean (True: antialias on, False: antialias is off)
718 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 ) {
720 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",
721 USTRCT(glyph), gmetrics, bit, small_bit, i_DFc(x_off),
722 i_DFc(y_off), smooth));
724 if ( !smooth ) TT_Get_Glyph_Bitmap( glyph, bit, x_off * 64, y_off * 64);
726 TT_F26Dot6 xmin, ymin, xmax, ymax;
728 xmin = gmetrics->bbox.xMin & -64;
729 ymin = gmetrics->bbox.yMin & -64;
730 xmax = (gmetrics->bbox.xMax + 63) & -64;
731 ymax = (gmetrics->bbox.yMax + 63) & -64;
733 i_tt_clear_raster_map( small_bit );
734 TT_Get_Glyph_Pixmap( glyph, small_bit, -xmin, -ymin );
735 i_tt_blit_or( bit, small_bit, xmin/64 + x_off, -ymin/64 - y_off );
741 =item i_tt_render_all_glyphs(handle, inst, bit, small_bit, cords, txt, len, smooth)
743 calls i_tt_render_glyph to render each glyph into the bit rastermap (internal)
745 handle - pointer to font handle
747 bit - large bitmap that is the destination for the text
748 smallbit - small bitmap that is used only if smooth is true
749 txt - string to render
750 len - length of the string to render
751 smooth - boolean (True: antialias on, False: antialias is off)
758 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
759 TT_Raster_Map *small_bit, i_img_dim cords[6],
760 char const* txt, size_t len, int smooth, int utf8 ) {
764 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",
765 handle, inst, bit, small_bit, (int)len, txt, (long)len, smooth, utf8));
768 y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM);
771 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 */
776 j = i_utf8_advance(&txt, &len);
778 i_push_error(0, "invalid UTF8 character");
783 j = (unsigned char)*txt++;
786 if ( !i_tt_get_glyph(handle,inst,j) )
788 i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
789 &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit,
790 small_bit, x, y, smooth );
791 x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64;
799 * Functions to render rasters (single channel images) onto images
803 =item i_tt_dump_raster_map2(im, bit, xb, yb, cl, smooth)
805 Function to dump a raster onto an image in color used by i_tt_text() (internal).
807 im - image to dump raster on
808 bit - bitmap that contains the text to be dumped to im
809 xb, yb - coordinates, left edge and baseline
810 cl - color to use for text
811 smooth - boolean (True: antialias on, False: antialias is off)
818 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 ) {
821 mm_log((1,"i_tt_dump_raster_map2(im %p, bit %p, xb %" i_DF ", yb %" i_DF ", cl %p)\n",
822 im, bit, i_DFc(xb), i_DFc(yb), cl));
829 i_render_init(&r, im, bit->cols);
830 for(y=0;y<bit->rows;y++) {
831 i_render_color(&r, xb, yb+y, bit->cols, bmap + y*bit->cols, cl);
835 for(y=0;y<bit->rows;y++) {
836 unsigned mask = 0x80;
837 unsigned char *p = bmap + y * bit->cols;
839 for(x = 0; x < bit->width; x++) {
841 i_ppix(im, x+xb, y+yb, cl);
856 =item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth)
858 Function to dump a raster onto a single channel image in color (internal)
860 im - image to dump raster on
861 bit - bitmap that contains the text to be dumped to im
862 xb, yb - coordinates, left edge and baseline
863 channel - channel to copy to
864 smooth - boolean (True: antialias on, False: antialias is off)
871 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 ) {
876 int old_mask = im->ch_mask;
877 im->ch_mask = 1 << channel;
879 mm_log((1,"i_tt_dump_raster_channel(im %p, bit %p, xb %" i_DF ", yb %" i_DF ", channel %d)\n",
880 im, bit, i_DFc(xb), i_DFc(yb), channel));
885 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
886 c = bmap[y*(bit->cols)+x];
887 val.channel[channel] = c;
888 i_ppix(im,x+xb,y+yb,&val);
891 for(y=0;y<bit->rows;y++) {
892 unsigned mask = 0x80;
893 unsigned char *p = bmap + y * bit->cols;
895 for(x=0;x<bit->width;x++) {
896 val.channel[channel] = (*p & mask) ? 255 : 0;
897 i_ppix(im,x+xb,y+yb,&val);
907 im->ch_mask = old_mask;
912 =item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth)
914 interface for generating single channel raster of text (internal)
916 handle - pointer to font handle
917 bit - the bitmap that is allocated, rendered into and NOT freed
918 cords - the bounding box (modified in place)
919 points - font size to use
920 txt - string to render
921 len - length of the string to render
922 smooth - boolean (True: antialias on, False: antialias is off)
929 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 ) {
931 i_img_dim width, height;
932 TT_Raster_Map small_bit;
934 /* find or install an instance */
935 if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) {
936 mm_log((1,"i_tt_rasterize: get instance failed\n"));
940 /* calculate bounding box */
941 if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 ))
945 width = cords[2]-cords[0];
946 height = cords[5]-cords[4];
948 mm_log((1,"i_tt_rasterize: width=%" i_DF ", height=%" i_DF "\n",
949 i_DFc(width), i_DFc(height) ));
951 i_tt_init_raster_map ( bit, width, height, smooth );
952 i_tt_clear_raster_map( bit );
953 if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth );
955 if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len,
958 i_tt_done_raster_map( &small_bit );
962 if ( smooth ) i_tt_done_raster_map( &small_bit );
969 * Exported text rendering interfaces
974 =item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth, utf8)
976 Interface to text rendering into a single channel in an image
978 handle - pointer to font handle
979 im - image to render text on to
980 xb, yb - coordinates, left edge and baseline
981 channel - channel to render into
982 points - font size to use
983 txt - string to render
984 len - length of the string to render
985 smooth - boolean (True: antialias on, False: antialias is off)
991 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 ) {
993 i_img_dim cords[BOUNDING_BOX_COUNT];
994 i_img_dim ascent, st_offset, y;
998 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1000 ascent=cords[BBOX_ASCENT];
1001 st_offset=cords[BBOX_NEG_WIDTH];
1002 y = align ? yb-ascent : yb;
1004 i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , y, channel, smooth );
1005 i_tt_done_raster_map( &bit );
1012 =item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8)
1014 Interface to text rendering in a single color onto an image
1016 handle - pointer to font handle
1017 im - image to render text on to
1018 xb, yb - coordinates, left edge and baseline
1019 cl - color to use for text
1020 points - font size to use
1021 txt - string to render
1022 len - length of the string to render
1023 smooth - boolean (True: antialias on, False: antialias is off)
1029 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) {
1030 i_img_dim cords[BOUNDING_BOX_COUNT];
1031 i_img_dim ascent, st_offset, y;
1036 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1038 ascent=cords[BBOX_ASCENT];
1039 st_offset=cords[BBOX_NEG_WIDTH];
1040 y = align ? yb-ascent : yb;
1042 i_tt_dump_raster_map2( im, &bit, xb+st_offset, y, cl, smooth );
1043 i_tt_done_raster_map( &bit );
1050 =item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8)
1052 Function to get texts bounding boxes given the instance of the font (internal)
1054 handle - pointer to font handle
1055 inst - font instance
1056 txt - string to measure
1057 len - length of the string to render
1058 cords - the bounding box (modified in place)
1065 i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, size_t len, i_img_dim cords[BOUNDING_BOX_COUNT], int utf8 ) {
1066 int upm, casc, cdesc, first;
1069 i_img_dim width = 0;
1077 unsigned char *ustr;
1078 ustr=(unsigned char*)txt;
1080 mm_log((1,"i_tt_box_inst(handle %p,inst %d,txt '%.*s', len %ld, utf8 %d)\n",
1081 handle, inst, (int)len, txt, (long)len, utf8));
1083 upm = handle->properties.header->Units_Per_EM;
1084 gascent = ( handle->properties.horizontal->Ascender * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm;
1085 gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem - upm + 1) / upm;
1090 mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent));
1095 j = i_utf8_advance(&txt, &len);
1097 i_push_error(0, "invalid UTF8 character");
1102 j = (unsigned char)*txt++;
1105 if ( i_tt_get_glyph(handle,inst,j) ) {
1106 TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j);
1107 width += gm->advance / 64;
1108 casc = (gm->bbox.yMax+63) / 64;
1109 cdesc = (gm->bbox.yMin-63) / 64;
1111 mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n",
1112 (int)((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc));
1115 start = gm->bbox.xMin / 64;
1116 ascent = (gm->bbox.yMax+63) / 64;
1117 descent = (gm->bbox.yMin-63) / 64;
1120 if (!len) { /* if at end of string */
1121 /* the right-side bearing - in case the right-side of a
1122 character goes past the right of the advance width,
1123 as is common for italic fonts
1125 rightb = gm->advance - gm->bearingX
1126 - (gm->bbox.xMax - gm->bbox.xMin);
1127 /* fprintf(stderr, "font info last: %d %d %d %d\n",
1128 gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */
1131 ascent = (ascent > casc ? ascent : casc );
1132 descent = (descent < cdesc ? descent : cdesc);
1136 cords[BBOX_NEG_WIDTH]=start;
1137 cords[BBOX_GLOBAL_DESCENT]=gdescent;
1138 cords[BBOX_POS_WIDTH]=width;
1140 cords[BBOX_POS_WIDTH] -= rightb / 64;
1141 cords[BBOX_GLOBAL_ASCENT]=gascent;
1142 cords[BBOX_DESCENT]=descent;
1143 cords[BBOX_ASCENT]=ascent;
1144 cords[BBOX_ADVANCE_WIDTH] = width;
1145 cords[BBOX_RIGHT_BEARING] = rightb / 64;
1147 return BBOX_RIGHT_BEARING + 1;
1152 =item i_tt_bbox(handle, points, txt, len, cords, utf8)
1154 Interface to get a strings bounding box
1156 handle - pointer to font handle
1157 points - font size to use
1158 txt - string to render
1159 len - length of the string to render
1160 cords - the bounding box (modified in place)
1166 i_tt_bbox( TT_Fonthandle *handle, double points,const char *txt,size_t len,i_img_dim cords[6], int utf8) {
1170 mm_log((1,"i_tt_box(handle %p,points %f,txt '%.*s', len %ld, utf8 %d)\n",
1171 handle, points, (int)len, txt, (long)len, utf8));
1173 if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
1174 i_push_errorf(0, "i_tt_get_instance(%g)", points);
1175 mm_log((1,"i_tt_text: get instance failed\n"));
1179 return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8);
1183 =item i_tt_face_name(handle, name_buf, name_buf_size)
1185 Retrieve's the font's postscript name.
1187 This is complicated by the need to handle encodings and so on.
1192 i_tt_face_name(TT_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
1193 TT_Face_Properties props;
1196 TT_UShort platform_id, encoding_id, lang_id, name_id;
1199 int want_index = -1; /* an acceptable but not perfect name */
1204 TT_Get_Face_Properties(handle->face, &props);
1205 name_count = props.num_Names;
1206 for (i = 0; i < name_count; ++i) {
1207 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1210 TT_Get_Name_String(handle->face, i, &name, &name_len);
1212 if (platform_id != TT_PLATFORM_APPLE_UNICODE && name_len
1213 && name_id == TT_NAME_ID_PS_NAME) {
1214 int might_want_index = -1;
1215 int might_score = 0;
1216 if ((platform_id == TT_PLATFORM_MACINTOSH && encoding_id == TT_MAC_ID_ROMAN)
1218 (platform_id == TT_PLATFORM_MICROSOFT && encoding_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)) {
1219 /* exactly what we want */
1224 if (platform_id == TT_PLATFORM_MICROSOFT
1225 && (encoding_id & 0xFF) == TT_MS_LANGID_ENGLISH_GENERAL) {
1226 /* any english is good */
1227 might_want_index = i;
1230 /* there might be something in between */
1232 /* anything non-unicode is better than nothing */
1233 might_want_index = i;
1236 if (might_score > score) {
1237 score = might_score;
1238 want_index = might_want_index;
1243 if (want_index != -1) {
1244 TT_Get_Name_String(handle->face, want_index, &name, &name_len);
1246 strncpy(name_buf, name, name_buf_size);
1247 name_buf[name_buf_size-1] = '\0';
1249 return strlen(name) + 1;
1252 i_push_error(0, "no face name present");
1257 void i_tt_dump_names(TT_Fonthandle *handle) {
1258 TT_Face_Properties props;
1261 TT_UShort platform_id, encoding_id, lang_id, name_id;
1265 TT_Get_Face_Properties(handle->face, &props);
1266 name_count = props.num_Names;
1267 for (i = 0; i < name_count; ++i) {
1268 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1270 TT_Get_Name_String(handle->face, i, &name, &name_len);
1272 printf("# %d: plat %d enc %d lang %d name %d value ", i, platform_id,
1273 encoding_id, lang_id, name_id);
1274 if (platform_id == TT_PLATFORM_APPLE_UNICODE) {
1275 printf("(unicode)\n");
1278 printf("'%s'\n", name);
1285 i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf,
1286 size_t name_buf_size) {
1294 if (!handle->loaded_names) {
1296 mm_log((1, "Loading PS Names"));
1297 handle->load_cond = TT_Load_PS_Names(handle->face, &post);
1298 ++handle->loaded_names;
1301 if (handle->load_cond) {
1302 i_push_errorf(handle->load_cond, "error loading names (%#x)",
1303 (unsigned)handle->load_cond);
1307 index = TT_Char_Index(handle->char_map, ch);
1309 i_push_error(0, "no such character");
1313 rc = TT_Get_PS_Name(handle->face, index, &psname);
1316 i_push_error(rc, "error getting name");
1320 strncpy(name_buf, psname, name_buf_size);
1321 name_buf[name_buf_size-1] = '\0';
1323 return strlen(psname) + 1;
1325 mm_log((1, "FTXPOST extension not enabled\n"));
1327 i_push_error(0, "Use of FTXPOST extension disabled");
1334 =item i_tt_push_error(code)
1336 Push an error message and code onto the Imager error stack.
1341 i_tt_push_error(TT_Error rc) {
1343 TT_String const *msg = TT_ErrToString18(rc);
1345 i_push_error(rc, msg);
1347 i_push_errorf(rc, "Error code 0x%04x", (unsigned)rc);
1357 Arnar M. Hrafnkelsson <addi@umich.edu>