Flines/Makefile.PL
Flines/t/t00flines.t
flip.im
-font.c
+fontft1.c
fontfiles/dodge.ttf
fontfiles/ExistenceTest.ttf generated using pfaedit
fontfiles/ImUgly.ttf
if (defined $Config{'d_dlsymun'}) { $OSDEF .= ' -DDLSYMUN'; }
my @objs = qw(Imager.o context.o draw.o polygon.o image.o io.o iolayer.o
- log.o gaussian.o conv.o pnm.o raw.o feat.o font.o combine.o
+ log.o gaussian.o conv.o pnm.o raw.o feat.o combine.o
filters.o dynaload.o stackmach.o datatypes.o
regmach.o trans2.o quant.o error.o convert.o
map.o tags.o palimg.o maskimg.o img8.o img16.o rotate.o
&& !-e catfile($_[0], 'fterrors.h') },
libcheck=>sub { $_[0] eq "libttf$aext" or $_[0] eq "libttf.$lext" },
libfiles=>'-lttf',
- objfiles=>'',
+ objfiles=>'fontft1.o',
code => \&freetype1_probe,
docs=>q{
Truetype fonts are scalable fonts. They can include
+++ /dev/null
-#include "imager.h"
-#include "imrender.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#ifdef HAVE_LIBT1
-#endif
-
-
-/*
-=head1 NAME
-
-font.c - implements font handling functions for t1 and truetype fonts
-
-=head1 SYNOPSIS
-
- i_init_fonts();
-
- #ifdef HAVE_LIBT1
- fontnum = i_t1_new(path_to_pfb, path_to_afm);
- i_t1_bbox(fontnum, points, "foo", 3, i_img_dim cords[BOUNDING_BOX_COUNT]);
- rc = i_t1_destroy(fontnum);
- #endif
-
- #ifdef HAVE_LIBTT
- handle = i_tt_new(path_to_ttf);
- rc = i_tt_bbox(handle, points, "foo", 3, int cords[6], utf8);
- i_tt_destroy(handle);
-
- // and much more
-
-=head1 DESCRIPTION
-
-font.c implements font creation, rendering, bounding box functions and
-more for Imager.
-
-=head1 FUNCTION REFERENCE
-
-Some of these functions are internal.
-
-=over
-
-=cut
-
-*/
-
-
-/* Truetype font support */
-#ifdef HAVE_LIBTT
-
-/* These are enabled by default when configuring Freetype 1.x
- I haven't a clue how to reliably detect it at compile time.
-
- We need a compilation probe in Makefile.PL
-*/
-#define FTXPOST 1
-#define FTXERR18 1
-
-#include <freetype.h>
-#define TT_CHC 5
-
-#ifdef FTXPOST
-#include <ftxpost.h>
-#endif
-
-#ifdef FTXERR18
-#include <ftxerr18.h>
-#endif
-
-/* some versions of FT1.x don't seem to define this - it's font defined
- so it won't change */
-#ifndef TT_MS_LANGID_ENGLISH_GENERAL
-#define TT_MS_LANGID_ENGLISH_GENERAL 0x0409
-#endif
-
-/* convert a code point into an index in the glyph cache */
-#define TT_HASH(x) ((x) & 0xFF)
-
-typedef struct i_glyph_entry_ {
- TT_Glyph glyph;
- unsigned long ch;
-} i_tt_glyph_entry;
-
-#define TT_NOCHAR (~0UL)
-
-struct TT_Instancehandle_ {
- TT_Instance instance;
- TT_Instance_Metrics imetrics;
- TT_Glyph_Metrics gmetrics[256];
- i_tt_glyph_entry glyphs[256];
- int smooth;
- int order;
- i_img_dim ptsize;
-};
-
-typedef struct TT_Instancehandle_ TT_Instancehandle;
-
-struct TT_Fonthandle_ {
- TT_Face face;
- TT_Face_Properties properties;
- TT_Instancehandle instanceh[TT_CHC];
- TT_CharMap char_map;
-#ifdef FTXPOST
- int loaded_names;
- TT_Error load_cond;
-#endif
-};
-
-/* Defines */
-
-#define USTRCT(x) ((x).z)
-#define TT_VALID( handle ) ( ( handle ).z != NULL )
-
-static void i_tt_push_error(TT_Error rc);
-
-/* Prototypes */
-
-static int i_tt_get_instance( TT_Fonthandle *handle, i_img_dim points, int smooth );
-static void i_tt_init_raster_map( TT_Raster_Map* bit, i_img_dim width, i_img_dim height, int smooth );
-static void i_tt_done_raster_map( TT_Raster_Map *bit );
-static void i_tt_clear_raster_map( TT_Raster_Map* bit );
-static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,i_img_dim x_off, i_img_dim y_off );
-static int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j );
-static void
-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 );
-static int
-i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
- TT_Raster_Map *small_bit, i_img_dim cords[6],
- char const* txt, size_t len, int smooth, int utf8 );
-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 );
-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 );
-static int
-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 );
-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 );
-
-
-/* static globals needed */
-
-static int TT_initialized = 0;
-static TT_Engine engine;
-static int LTT_dpi = 72; /* FIXME: this ought to be a part of the call interface */
-static int LTT_hinted = 1; /* FIXME: this too */
-
-
-/*
- * FreeType interface
- */
-
-
-/*
-=item init_tt()
-
-Initializes the freetype font rendering engine
-
-=cut
-*/
-
-static undef_int
-i_init_tt(void) {
- TT_Error error;
- TT_Byte palette[] = { 0, 64, 127, 191, 255 };
-
- i_clear_error();
-
- mm_log((1,"init_tt()\n"));
- error = TT_Init_FreeType( &engine );
- if ( error ){
- mm_log((1,"Initialization of freetype failed, code = 0x%x\n",
- (unsigned)error));
- i_tt_push_error(error);
- i_push_error(0, "Could not initialize freetype 1.x");
- return(1);
- }
-
-#ifdef FTXPOST
- error = TT_Init_Post_Extension( engine );
- if (error) {
- mm_log((1, "Initialization of Post extension failed = 0x%x\n",
- (unsigned)error));
-
- i_tt_push_error(error);
- i_push_error(0, "Could not initialize FT 1.x POST extension");
- return 1;
- }
-#endif
-
- error = TT_Set_Raster_Gray_Palette(engine, palette);
- if (error) {
- mm_log((1, "Initialization of gray levels failed = 0x%x\n",
- (unsigned)error));
- i_tt_push_error(error);
- i_push_error(0, "Could not initialize FT 1.x POST extension");
- return 1;
- }
-
- TT_initialized = 1;
-
- return(0);
-}
-
-
-/*
-=item i_tt_get_instance(handle, points, smooth)
-
-Finds a points+smooth instance or if one doesn't exist in the cache
-allocates room and returns its cache entry
-
- fontname - path to the font to load
- handle - handle to the font.
- points - points of the requested font
- smooth - boolean (True: antialias on, False: antialias is off)
-
-=cut
-*/
-
-static
-int
-i_tt_get_instance( TT_Fonthandle *handle, i_img_dim points, int smooth ) {
- int i,idx;
- TT_Error error;
-
- mm_log((1,"i_tt_get_instance(handle %p, points %" i_DF ", smooth %d)\n",
- handle, i_DFc(points), smooth));
-
- if (smooth == -1) { /* Smooth doesn't matter for this search */
- for(i=0;i<TT_CHC;i++) {
- if (handle->instanceh[i].ptsize==points) {
- mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
- return i;
- }
- }
- smooth=1; /* We will be adding a font - add it as smooth then */
- } else { /* Smooth doesn't matter for this search */
- for(i=0;i<TT_CHC;i++) {
- if (handle->instanceh[i].ptsize == points
- && handle->instanceh[i].smooth == smooth) {
- mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
- return i;
- }
- }
- }
-
- /* Found the instance in the cache - return the cache index */
-
- for(idx=0;idx<TT_CHC;idx++) {
- if (!(handle->instanceh[idx].order)) break; /* find the lru item */
- }
-
- mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
- mm_log((1,"i_tt_get_instance: lru pointer %p\n",
- USTRCT(handle->instanceh[idx].instance) ));
-
- if ( USTRCT(handle->instanceh[idx].instance) ) {
- mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
-
- /* Free cached glyphs */
- for(i=0;i<256;i++)
- if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
- TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
-
- for(i=0;i<256;i++) {
- handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
- USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
- }
-
- /* Free instance if needed */
- TT_Done_Instance( handle->instanceh[idx].instance );
- }
-
- /* create and initialize instance */
- /* FIXME: probably a memory leak on fail */
-
- (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) ||
- ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
- ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
-
- if ( error ) {
- mm_log((1, "Could not create and initialize instance: error %x.\n",
- (unsigned)error ));
- return -1;
- }
-
- /* Now that the instance should the inplace we need to lower all of the
- ru counts and put `this' one with the highest entry */
-
- for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
-
- handle->instanceh[idx].order=TT_CHC-1;
- handle->instanceh[idx].ptsize=points;
- handle->instanceh[idx].smooth=smooth;
- TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
-
- /* Zero the memory for the glyph storage so they are not thought as
- cached if they haven't been cached since this new font was loaded */
-
- for(i=0;i<256;i++) {
- handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
- USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
- }
-
- return idx;
-}
-
-
-/*
-=item i_tt_new(fontname)
-
-Creates a new font handle object, finds a character map and initialise the
-the font handle's cache
-
- fontname - path to the font to load
-
-=cut
-*/
-
-TT_Fonthandle*
-i_tt_new(const char *fontname) {
- TT_Error error;
- TT_Fonthandle *handle;
- unsigned short i,n;
- unsigned short platform,encoding;
-
- if (!TT_initialized && i_init_tt()) {
- i_push_error(0, "Could not initialize FT1 engine");
- return NULL;
- }
-
- i_clear_error();
-
- mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
-
- /* allocate memory for the structure */
-
- handle = mymalloc( sizeof(TT_Fonthandle) ); /* checked 5Nov05 tonyc */
-
- /* load the typeface */
- error = TT_Open_Face( engine, fontname, &handle->face );
- if ( error ) {
- if ( error == TT_Err_Could_Not_Open_File ) {
- mm_log((1, "Could not find/open %s.\n", fontname ));
- }
- else {
- mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname,
- (unsigned)error ));
- }
- i_tt_push_error(error);
- return NULL;
- }
-
- TT_Get_Face_Properties( handle->face, &(handle->properties) );
-
- /* First, look for a Unicode charmap */
- n = handle->properties.num_CharMaps;
- USTRCT( handle->char_map )=NULL; /* Invalidate character map */
-
- for ( i = 0; i < n; i++ ) {
- TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
- if ( (platform == 3 && encoding == 1 )
- || (platform == 0 && encoding == 0 ) ) {
- mm_log((2,"i_tt_new - found char map platform %u encoding %u\n",
- platform, encoding));
- TT_Get_CharMap( handle->face, i, &(handle->char_map) );
- break;
- }
- }
- if (!USTRCT(handle->char_map) && n != 0) {
- /* just use the first one */
- TT_Get_CharMap( handle->face, 0, &(handle->char_map));
- }
-
- /* Zero the pointsizes - and ordering */
-
- for(i=0;i<TT_CHC;i++) {
- USTRCT(handle->instanceh[i].instance)=NULL;
- handle->instanceh[i].order=i;
- handle->instanceh[i].ptsize=0;
- handle->instanceh[i].smooth=-1;
- }
-
-#ifdef FTXPOST
- handle->loaded_names = 0;
-#endif
-
- mm_log((1,"i_tt_new <- %p\n",handle));
- return handle;
-}
-
-
-
-/*
- *Â raster map management
- */
-
-/*
-=item i_tt_init_raster_map(bit, width, height, smooth)
-
-Allocates internal memory for the bitmap as needed by the parameters (internal)
-
- bit - bitmap to allocate into
- width - width of the bitmap
- height - height of the bitmap
- smooth - boolean (True: antialias on, False: antialias is off)
-
-=cut
-*/
-
-static
-void
-i_tt_init_raster_map( TT_Raster_Map* bit, i_img_dim width, i_img_dim height, int smooth ) {
-
- mm_log((1,"i_tt_init_raster_map( bit %p, width %" i_DF ", height %" i_DF
- ", smooth %d)\n", bit, i_DFc(width), i_DFc(height), smooth));
-
- bit->rows = height;
- bit->width = ( width + 3 ) & -4;
- bit->flow = TT_Flow_Down;
-
- if ( smooth ) {
- bit->cols = bit->width;
- bit->size = bit->rows * bit->width;
- } else {
- bit->cols = ( bit->width + 7 ) / 8; /* convert to # of bytes */
- bit->size = bit->rows * bit->cols; /* number of bytes in buffer */
- }
-
- /* rows can be 0 for some glyphs, for example ' ' */
- if (bit->rows && bit->size / bit->rows != bit->cols) {
- i_fatal(0, "Integer overflow calculating bitmap size (%d, %d)\n",
- bit->width, bit->rows);
- }
-
- 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 ));
-
- bit->bitmap = (void *) mymalloc( bit->size ); /* checked 6Nov05 tonyc */
- if ( !bit->bitmap ) i_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",bit->size );
-}
-
-
-/*
-=item i_tt_clear_raster_map(bit)
-
-Frees the bitmap data and sets pointer to NULL (internal)
-
- bit - bitmap to free
-
-=cut
-*/
-
-static
-void
-i_tt_done_raster_map( TT_Raster_Map *bit ) {
- myfree( bit->bitmap );
- bit->bitmap = NULL;
-}
-
-
-/*
-=item i_tt_clear_raster_map(bit)
-
-Clears the specified bitmap (internal)
-
- bit - bitmap to zero
-
-=cut
-*/
-
-
-static
-void
-i_tt_clear_raster_map( TT_Raster_Map* bit ) {
- memset( bit->bitmap, 0, bit->size );
-}
-
-
-/*
-=item i_tt_blit_or(dst, src, x_off, y_off)
-
-function that blits one raster map into another (internal)
-
- dst - destination bitmap
- src - source bitmap
- x_off - x offset into the destination bitmap
- y_off - y offset into the destination bitmap
-
-=cut
-*/
-
-static
-void
-i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,i_img_dim x_off, i_img_dim y_off ) {
- i_img_dim x, y;
- i_img_dim x1, x2, y1, y2;
- unsigned char *s, *d;
-
- x1 = x_off < 0 ? -x_off : 0;
- y1 = y_off < 0 ? -y_off : 0;
-
- x2 = (int)dst->cols - x_off;
- if ( x2 > src->cols ) x2 = src->cols;
-
- y2 = (int)dst->rows - y_off;
- if ( y2 > src->rows ) y2 = src->rows;
-
- if ( x1 >= x2 ) return;
-
- /* do the real work now */
-
- for ( y = y1; y < y2; ++y ) {
- s = ( (unsigned char*)src->bitmap ) + y * src->cols + x1;
- d = ( (unsigned char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
-
- for ( x = x1; x < x2; ++x ) {
- if (*s > *d)
- *d = *s;
- d++;
- s++;
- }
- }
-}
-
-/* useful for debugging */
-#if 0
-
-static void dump_raster_map(FILE *out, TT_Raster_Map *bit ) {
- int x, y;
- fprintf(out, "cols %d rows %d flow %d\n", bit->cols, bit->rows, bit->flow);
- for (y = 0; y < bit->rows; ++y) {
- fprintf(out, "%2d:", y);
- for (x = 0; x < bit->cols; ++x) {
- if ((x & 7) == 0 && x) putc(' ', out);
- fprintf(out, "%02x", ((unsigned char *)bit->bitmap)[y*bit->cols+x]);
- }
- putc('\n', out);
- }
-}
-
-#endif
-
-/*
-=item i_tt_get_glyph(handle, inst, j)
-
-Function to see if a glyph exists and if so cache it (internal)
-
- handle - pointer to font handle
- inst - font instance
- j - charcode of glyph
-
-=cut
-*/
-
-static
-int
-i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j) {
- unsigned short load_flags, code;
- TT_Error error;
-
- mm_log((1, "i_tt_get_glyph(handle %p, inst %d, j %lu (%c))\n",
- handle,inst,j, (int)((j >= ' ' && j <= '~') ? j : '.')));
-
- /*mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] ));*/
-
- if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)
- && handle->instanceh[inst].glyphs[TT_HASH(j)].ch == j) {
- mm_log((1,"i_tt_get_glyph: %lu in cache\n",j));
- return 1;
- }
-
- if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) ) {
- /* clean up the entry */
- TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
- USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
- handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
- }
-
- /* Ok - it wasn't cached - try to get it in */
- load_flags = TTLOAD_SCALE_GLYPH;
- if ( LTT_hinted ) load_flags |= TTLOAD_HINT_GLYPH;
-
- if ( !TT_VALID(handle->char_map) ) {
- code = (j - ' ' + 1) < 0 ? 0 : (j - ' ' + 1);
- if ( code >= handle->properties.num_Glyphs ) code = 0;
- } else code = TT_Char_Index( handle->char_map, j );
-
- if ( (error = TT_New_Glyph( handle->face, &handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)) ) {
- mm_log((1, "Cannot allocate and load glyph: error %#x.\n", (unsigned)error ));
- i_push_error(error, "TT_New_Glyph()");
- return 0;
- }
- if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, code, load_flags)) ) {
- mm_log((1, "Cannot allocate and load glyph: error %#x.\n", (unsigned)error ));
- /* Don't leak */
- TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
- USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
- i_push_error(error, "TT_Load_Glyph()");
- return 0;
- }
-
- /* At this point the glyph should be allocated and loaded */
- handle->instanceh[inst].glyphs[TT_HASH(j)].ch = j;
-
- /* Next get the glyph metrics */
- error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
- &handle->instanceh[inst].gmetrics[TT_HASH(j)] );
- if (error) {
- mm_log((1, "TT_Get_Glyph_Metrics: error %#x.\n", (unsigned)error ));
- TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
- USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
- handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
- i_push_error(error, "TT_Get_Glyph_Metrics()");
- return 0;
- }
-
- return 1;
-}
-
-/*
-=item i_tt_has_chars(handle, text, len, utf8, out)
-
-Check if the given characters are defined by the font. Note that len
-is the number of bytes, not the number of characters (when utf8 is
-non-zero).
-
-Returns the number of characters that were checked.
-
-=cut
-*/
-
-size_t
-i_tt_has_chars(TT_Fonthandle *handle, char const *text, size_t len, int utf8,
- char *out) {
- size_t count = 0;
- mm_log((1, "i_tt_has_chars(handle %p, text %p, len %ld, utf8 %d)\n",
- handle, text, (long)len, utf8));
-
- while (len) {
- unsigned long c;
- int index;
- if (utf8) {
- c = i_utf8_advance(&text, &len);
- if (c == ~0UL) {
- i_push_error(0, "invalid UTF8 character");
- return 0;
- }
- }
- else {
- c = (unsigned char)*text++;
- --len;
- }
-
- if (TT_VALID(handle->char_map)) {
- index = TT_Char_Index(handle->char_map, c);
- }
- else {
- index = (c - ' ' + 1) < 0 ? 0 : (c - ' ' + 1);
- if (index >= handle->properties.num_Glyphs)
- index = 0;
- }
- *out++ = index != 0;
- ++count;
- }
-
- return count;
-}
-
-/*
-=item i_tt_destroy(handle)
-
-Clears the data taken by a font including all cached data such as
-pixmaps and glyphs
-
- handle - pointer to font handle
-
-=cut
-*/
-
-void
-i_tt_destroy( TT_Fonthandle *handle) {
- TT_Close_Face( handle->face );
- myfree( handle );
-
- /* FIXME: Should these be freed automatically by the library?
-
- TT_Done_Instance( instance );
- void
- i_tt_done_glyphs( void ) {
- int i;
-
- if ( !glyphs ) return;
-
- for ( i = 0; i < 256; ++i ) TT_Done_Glyph( glyphs[i] );
- free( glyphs );
-
- glyphs = NULL;
- }
- */
-}
-
-
-/*
- * FreeType Rendering functions
- */
-
-
-/*
-=item i_tt_render_glyph(handle, gmetrics, bit, smallbit, x_off, y_off, smooth)
-
-Renders a single glyph into the bit rastermap (internal)
-
- handle - pointer to font handle
- gmetrics - the metrics for the glyph to be rendered
- bit - large bitmap that is the destination for the text
- smallbit - small bitmap that is used only if smooth is true
- x_off - x offset of glyph
- y_off - y offset of glyph
- smooth - boolean (True: antialias on, False: antialias is off)
-
-=cut
-*/
-
-static
-void
-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 ) {
-
- 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",
- USTRCT(glyph), gmetrics, bit, small_bit, i_DFc(x_off),
- i_DFc(y_off), smooth));
-
- if ( !smooth ) TT_Get_Glyph_Bitmap( glyph, bit, x_off * 64, y_off * 64);
- else {
- TT_F26Dot6 xmin, ymin, xmax, ymax;
-
- xmin = gmetrics->bbox.xMin & -64;
- ymin = gmetrics->bbox.yMin & -64;
- xmax = (gmetrics->bbox.xMax + 63) & -64;
- ymax = (gmetrics->bbox.yMax + 63) & -64;
-
- i_tt_clear_raster_map( small_bit );
- TT_Get_Glyph_Pixmap( glyph, small_bit, -xmin, -ymin );
- i_tt_blit_or( bit, small_bit, xmin/64 + x_off, -ymin/64 - y_off );
- }
-}
-
-
-/*
-=item i_tt_render_all_glyphs(handle, inst, bit, small_bit, cords, txt, len, smooth)
-
-calls i_tt_render_glyph to render each glyph into the bit rastermap (internal)
-
- handle - pointer to font handle
- inst - font instance
- bit - large bitmap that is the destination for the text
- smallbit - small bitmap that is used only if smooth is true
- txt - string to render
- len - length of the string to render
- smooth - boolean (True: antialias on, False: antialias is off)
-
-=cut
-*/
-
-static
-int
-i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
- TT_Raster_Map *small_bit, i_img_dim cords[6],
- char const* txt, size_t len, int smooth, int utf8 ) {
- unsigned long j;
- TT_F26Dot6 x,y;
-
- 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",
- handle, inst, bit, small_bit, (int)len, txt, (long)len, smooth, utf8));
-
- /*
- y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM);
- */
-
- 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 */
- y=-cords[4];
-
- while (len) {
- if (utf8) {
- j = i_utf8_advance(&txt, &len);
- if (j == ~0UL) {
- i_push_error(0, "invalid UTF8 character");
- return 0;
- }
- }
- else {
- j = (unsigned char)*txt++;
- --len;
- }
- if ( !i_tt_get_glyph(handle,inst,j) )
- continue;
- i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
- &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit,
- small_bit, x, y, smooth );
- x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64;
- }
-
- return 1;
-}
-
-
-/*
- * Functions to render rasters (single channel images) onto images
- */
-
-/*
-=item i_tt_dump_raster_map2(im, bit, xb, yb, cl, smooth)
-
-Function to dump a raster onto an image in color used by i_tt_text() (internal).
-
- im - image to dump raster on
- bit - bitmap that contains the text to be dumped to im
- xb, yb - coordinates, left edge and baseline
- cl - color to use for text
- smooth - boolean (True: antialias on, False: antialias is off)
-
-=cut
-*/
-
-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 ) {
- unsigned char *bmap;
- i_img_dim x, y;
- mm_log((1,"i_tt_dump_raster_map2(im %p, bit %p, xb %" i_DF ", yb %" i_DF ", cl %p)\n",
- im, bit, i_DFc(xb), i_DFc(yb), cl));
-
- bmap = bit->bitmap;
-
- if ( smooth ) {
-
- i_render r;
- i_render_init(&r, im, bit->cols);
- for(y=0;y<bit->rows;y++) {
- i_render_color(&r, xb, yb+y, bit->cols, bmap + y*bit->cols, cl);
- }
- i_render_done(&r);
- } else {
- for(y=0;y<bit->rows;y++) {
- unsigned mask = 0x80;
- unsigned char *p = bmap + y * bit->cols;
-
- for(x = 0; x < bit->width; x++) {
- if (*p & mask) {
- i_ppix(im, x+xb, y+yb, cl);
- }
- mask >>= 1;
- if (!mask) {
- mask = 0x80;
- ++p;
- }
- }
- }
-
- }
-}
-
-
-/*
-=item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth)
-
-Function to dump a raster onto a single channel image in color (internal)
-
- im - image to dump raster on
- bit - bitmap that contains the text to be dumped to im
- xb, yb - coordinates, left edge and baseline
- channel - channel to copy to
- smooth - boolean (True: antialias on, False: antialias is off)
-
-=cut
-*/
-
-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 ) {
- unsigned char *bmap;
- i_color val;
- int c;
- i_img_dim x,y;
- int old_mask = im->ch_mask;
- im->ch_mask = 1 << channel;
-
- mm_log((1,"i_tt_dump_raster_channel(im %p, bit %p, xb %" i_DF ", yb %" i_DF ", channel %d)\n",
- im, bit, i_DFc(xb), i_DFc(yb), channel));
-
- bmap = bit->bitmap;
-
- if ( smooth ) {
- for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
- c = bmap[y*(bit->cols)+x];
- val.channel[channel] = c;
- i_ppix(im,x+xb,y+yb,&val);
- }
- } else {
- for(y=0;y<bit->rows;y++) {
- unsigned mask = 0x80;
- unsigned char *p = bmap + y * bit->cols;
-
- for(x=0;x<bit->width;x++) {
- val.channel[channel] = (*p & mask) ? 255 : 0;
- i_ppix(im,x+xb,y+yb,&val);
-
- mask >>= 1;
- if (!mask) {
- ++p;
- mask = 0x80;
- }
- }
- }
- }
- im->ch_mask = old_mask;
-}
-
-
-/*
-=item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth)
-
-interface for generating single channel raster of text (internal)
-
- handle - pointer to font handle
- bit - the bitmap that is allocated, rendered into and NOT freed
- cords - the bounding box (modified in place)
- points - font size to use
- txt - string to render
- len - length of the string to render
- smooth - boolean (True: antialias on, False: antialias is off)
-
-=cut
-*/
-
-static
-int
-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 ) {
- int inst;
- i_img_dim width, height;
- TT_Raster_Map small_bit;
-
- /* find or install an instance */
- if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) {
- mm_log((1,"i_tt_rasterize: get instance failed\n"));
- return 0;
- }
-
- /* calculate bounding box */
- if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 ))
- return 0;
-
-
- width = cords[2]-cords[0];
- height = cords[5]-cords[4];
-
- mm_log((1,"i_tt_rasterize: width=%" i_DF ", height=%" i_DF "\n",
- i_DFc(width), i_DFc(height) ));
-
- i_tt_init_raster_map ( bit, width, height, smooth );
- i_tt_clear_raster_map( bit );
- if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth );
-
- if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len,
- smooth, utf8 )) {
- if ( smooth )
- i_tt_done_raster_map( &small_bit );
- return 0;
- }
-
- if ( smooth ) i_tt_done_raster_map( &small_bit );
- return 1;
-}
-
-
-
-/*
- * Exported text rendering interfaces
- */
-
-
-/*
-=item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth, utf8)
-
-Interface to text rendering into a single channel in an image
-
- handle - pointer to font handle
- im - image to render text on to
- xb, yb - coordinates, left edge and baseline
- channel - channel to render into
- points - font size to use
- txt - string to render
- len - length of the string to render
- smooth - boolean (True: antialias on, False: antialias is off)
-
-=cut
-*/
-
-undef_int
-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 ) {
-
- i_img_dim cords[BOUNDING_BOX_COUNT];
- i_img_dim ascent, st_offset, y;
- TT_Raster_Map bit;
-
- i_clear_error();
- if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
-
- ascent=cords[BBOX_ASCENT];
- st_offset=cords[BBOX_NEG_WIDTH];
- y = align ? yb-ascent : yb;
-
- i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , y, channel, smooth );
- i_tt_done_raster_map( &bit );
-
- return 1;
-}
-
-
-/*
-=item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8)
-
-Interface to text rendering in a single color onto an image
-
- handle - pointer to font handle
- im - image to render text on to
- xb, yb - coordinates, left edge and baseline
- cl - color to use for text
- points - font size to use
- txt - string to render
- len - length of the string to render
- smooth - boolean (True: antialias on, False: antialias is off)
-
-=cut
-*/
-
-undef_int
-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) {
- i_img_dim cords[BOUNDING_BOX_COUNT];
- i_img_dim ascent, st_offset, y;
- TT_Raster_Map bit;
-
- i_clear_error();
-
- if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
-
- ascent=cords[BBOX_ASCENT];
- st_offset=cords[BBOX_NEG_WIDTH];
- y = align ? yb-ascent : yb;
-
- i_tt_dump_raster_map2( im, &bit, xb+st_offset, y, cl, smooth );
- i_tt_done_raster_map( &bit );
-
- return 1;
-}
-
-
-/*
-=item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8)
-
-Function to get texts bounding boxes given the instance of the font (internal)
-
- handle - pointer to font handle
- inst - font instance
- txt - string to measure
- len - length of the string to render
- cords - the bounding box (modified in place)
-
-=cut
-*/
-
-static
-undef_int
-i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, size_t len, i_img_dim cords[BOUNDING_BOX_COUNT], int utf8 ) {
- int upm, casc, cdesc, first;
-
- int start = 0;
- i_img_dim width = 0;
- int gdescent = 0;
- int gascent = 0;
- int descent = 0;
- int ascent = 0;
- int rightb = 0;
-
- unsigned long j;
- unsigned char *ustr;
- ustr=(unsigned char*)txt;
-
- mm_log((1,"i_tt_box_inst(handle %p,inst %d,txt '%.*s', len %ld, utf8 %d)\n",
- handle, inst, (int)len, txt, (long)len, utf8));
-
- upm = handle->properties.header->Units_Per_EM;
- gascent = ( handle->properties.horizontal->Ascender * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm;
- gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem - upm + 1) / upm;
-
- width = 0;
- start = 0;
-
- mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent));
-
- first=1;
- while (len) {
- if (utf8) {
- j = i_utf8_advance(&txt, &len);
- if (j == ~0UL) {
- i_push_error(0, "invalid UTF8 character");
- return 0;
- }
- }
- else {
- j = (unsigned char)*txt++;
- --len;
- }
- if ( i_tt_get_glyph(handle,inst,j) ) {
- TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j);
- width += gm->advance / 64;
- casc = (gm->bbox.yMax+63) / 64;
- cdesc = (gm->bbox.yMin-63) / 64;
-
- mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n",
- (int)((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc));
-
- if (first) {
- start = gm->bbox.xMin / 64;
- ascent = (gm->bbox.yMax+63) / 64;
- descent = (gm->bbox.yMin-63) / 64;
- first = 0;
- }
- if (!len) { /* if at end of string */
- /* the right-side bearing - in case the right-side of a
- character goes past the right of the advance width,
- as is common for italic fonts
- */
- rightb = gm->advance - gm->bearingX
- - (gm->bbox.xMax - gm->bbox.xMin);
- /* fprintf(stderr, "font info last: %d %d %d %d\n",
- gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */
- }
-
- ascent = (ascent > casc ? ascent : casc );
- descent = (descent < cdesc ? descent : cdesc);
- }
- }
-
- cords[BBOX_NEG_WIDTH]=start;
- cords[BBOX_GLOBAL_DESCENT]=gdescent;
- cords[BBOX_POS_WIDTH]=width;
- if (rightb < 0)
- cords[BBOX_POS_WIDTH] -= rightb / 64;
- cords[BBOX_GLOBAL_ASCENT]=gascent;
- cords[BBOX_DESCENT]=descent;
- cords[BBOX_ASCENT]=ascent;
- cords[BBOX_ADVANCE_WIDTH] = width;
- cords[BBOX_RIGHT_BEARING] = rightb / 64;
-
- return BBOX_RIGHT_BEARING + 1;
-}
-
-
-/*
-=item i_tt_bbox(handle, points, txt, len, cords, utf8)
-
-Interface to get a strings bounding box
-
- handle - pointer to font handle
- points - font size to use
- txt - string to render
- len - length of the string to render
- cords - the bounding box (modified in place)
-
-=cut
-*/
-
-undef_int
-i_tt_bbox( TT_Fonthandle *handle, double points,const char *txt,size_t len,i_img_dim cords[6], int utf8) {
- int inst;
-
- i_clear_error();
- mm_log((1,"i_tt_box(handle %p,points %f,txt '%.*s', len %ld, utf8 %d)\n",
- handle, points, (int)len, txt, (long)len, utf8));
-
- if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
- i_push_errorf(0, "i_tt_get_instance(%g)", points);
- mm_log((1,"i_tt_text: get instance failed\n"));
- return 0;
- }
-
- return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8);
-}
-
-/*
-=item i_tt_face_name(handle, name_buf, name_buf_size)
-
-Retrieve's the font's postscript name.
-
-This is complicated by the need to handle encodings and so on.
-
-=cut
- */
-size_t
-i_tt_face_name(TT_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
- TT_Face_Properties props;
- int name_count;
- int i;
- TT_UShort platform_id, encoding_id, lang_id, name_id;
- TT_UShort name_len;
- TT_String *name;
- int want_index = -1; /* an acceptable but not perfect name */
- int score = 0;
-
- i_clear_error();
-
- TT_Get_Face_Properties(handle->face, &props);
- name_count = props.num_Names;
- for (i = 0; i < name_count; ++i) {
- TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
- &name_id);
-
- TT_Get_Name_String(handle->face, i, &name, &name_len);
-
- if (platform_id != TT_PLATFORM_APPLE_UNICODE && name_len
- && name_id == TT_NAME_ID_PS_NAME) {
- int might_want_index = -1;
- int might_score = 0;
- if ((platform_id == TT_PLATFORM_MACINTOSH && encoding_id == TT_MAC_ID_ROMAN)
- ||
- (platform_id == TT_PLATFORM_MICROSOFT && encoding_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)) {
- /* exactly what we want */
- want_index = i;
- break;
- }
-
- if (platform_id == TT_PLATFORM_MICROSOFT
- && (encoding_id & 0xFF) == TT_MS_LANGID_ENGLISH_GENERAL) {
- /* any english is good */
- might_want_index = i;
- might_score = 9;
- }
- /* there might be something in between */
- else {
- /* anything non-unicode is better than nothing */
- might_want_index = i;
- might_score = 1;
- }
- if (might_score > score) {
- score = might_score;
- want_index = might_want_index;
- }
- }
- }
-
- if (want_index != -1) {
- TT_Get_Name_String(handle->face, want_index, &name, &name_len);
-
- strncpy(name_buf, name, name_buf_size);
- name_buf[name_buf_size-1] = '\0';
-
- return strlen(name) + 1;
- }
- else {
- i_push_error(0, "no face name present");
- return 0;
- }
-}
-
-void i_tt_dump_names(TT_Fonthandle *handle) {
- TT_Face_Properties props;
- int name_count;
- int i;
- TT_UShort platform_id, encoding_id, lang_id, name_id;
- TT_UShort name_len;
- TT_String *name;
-
- TT_Get_Face_Properties(handle->face, &props);
- name_count = props.num_Names;
- for (i = 0; i < name_count; ++i) {
- TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
- &name_id);
- TT_Get_Name_String(handle->face, i, &name, &name_len);
-
- printf("# %d: plat %d enc %d lang %d name %d value ", i, platform_id,
- encoding_id, lang_id, name_id);
- if (platform_id == TT_PLATFORM_APPLE_UNICODE) {
- printf("(unicode)\n");
- }
- else {
- printf("'%s'\n", name);
- }
- }
- fflush(stdout);
-}
-
-size_t
-i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf,
- size_t name_buf_size) {
-#ifdef FTXPOST
- TT_Error rc;
- TT_String *psname;
- TT_UShort index;
-
- i_clear_error();
-
- if (!handle->loaded_names) {
- TT_Post post;
- mm_log((1, "Loading PS Names"));
- handle->load_cond = TT_Load_PS_Names(handle->face, &post);
- ++handle->loaded_names;
- }
-
- if (handle->load_cond) {
- i_push_errorf(handle->load_cond, "error loading names (%#x)",
- (unsigned)handle->load_cond);
- return 0;
- }
-
- index = TT_Char_Index(handle->char_map, ch);
- if (!index) {
- i_push_error(0, "no such character");
- return 0;
- }
-
- rc = TT_Get_PS_Name(handle->face, index, &psname);
-
- if (rc) {
- i_push_error(rc, "error getting name");
- return 0;
- }
-
- strncpy(name_buf, psname, name_buf_size);
- name_buf[name_buf_size-1] = '\0';
-
- return strlen(psname) + 1;
-#else
- mm_log((1, "FTXPOST extension not enabled\n"));
- i_clear_error();
- i_push_error(0, "Use of FTXPOST extension disabled");
-
- return 0;
-#endif
-}
-
-/*
-=item i_tt_push_error(code)
-
-Push an error message and code onto the Imager error stack.
-
-=cut
-*/
-static void
-i_tt_push_error(TT_Error rc) {
-#ifdef FTXERR18
- TT_String const *msg = TT_ErrToString18(rc);
-
- i_push_error(rc, msg);
-#else
- i_push_errorf(rc, "Error code 0x%04x", (unsigned)rc);
-#endif
-}
-
-#endif /* HAVE_LIBTT */
-
-
-/*
-=back
-
-=head1 AUTHOR
-
-Arnar M. Hrafnkelsson <addi@umich.edu>
-
-=head1 SEE ALSO
-
-Imager(3)
-
-=cut
-*/
--- /dev/null
+#include "imager.h"
+#include "imrender.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+
+/*
+=head1 NAME
+
+fontft1.c - Freetype 1.x font driver for Imager
+
+=head1 SYNOPSIS
+
+ handle = i_tt_new(path_to_ttf);
+ rc = i_tt_bbox(handle, points, "foo", 3, int cords[6], utf8);
+ i_tt_destroy(handle);
+
+ // and much more
+
+=head1 DESCRIPTION
+
+fontft1.c implements font creation, rendering, bounding box functions and
+more for Imager using Freetype 1.x.
+
+In general this driver should be ignored in favour of the FT2 driver.
+
+=head1 FUNCTION REFERENCE
+
+Some of these functions are internal.
+
+=over
+
+=cut
+
+*/
+
+
+/* Truetype font support */
+/* These are enabled by default when configuring Freetype 1.x
+ I haven't a clue how to reliably detect it at compile time.
+
+ We need a compilation probe in Makefile.PL
+*/
+#define FTXPOST 1
+#define FTXERR18 1
+
+#include <freetype.h>
+#define TT_CHC 5
+
+#ifdef FTXPOST
+#include <ftxpost.h>
+#endif
+
+#ifdef FTXERR18
+#include <ftxerr18.h>
+#endif
+
+/* some versions of FT1.x don't seem to define this - it's font defined
+ so it won't change */
+#ifndef TT_MS_LANGID_ENGLISH_GENERAL
+#define TT_MS_LANGID_ENGLISH_GENERAL 0x0409
+#endif
+
+/* convert a code point into an index in the glyph cache */
+#define TT_HASH(x) ((x) & 0xFF)
+
+typedef struct i_glyph_entry_ {
+ TT_Glyph glyph;
+ unsigned long ch;
+} i_tt_glyph_entry;
+
+#define TT_NOCHAR (~0UL)
+
+struct TT_Instancehandle_ {
+ TT_Instance instance;
+ TT_Instance_Metrics imetrics;
+ TT_Glyph_Metrics gmetrics[256];
+ i_tt_glyph_entry glyphs[256];
+ int smooth;
+ int order;
+ i_img_dim ptsize;
+};
+
+typedef struct TT_Instancehandle_ TT_Instancehandle;
+
+struct TT_Fonthandle_ {
+ TT_Face face;
+ TT_Face_Properties properties;
+ TT_Instancehandle instanceh[TT_CHC];
+ TT_CharMap char_map;
+#ifdef FTXPOST
+ int loaded_names;
+ TT_Error load_cond;
+#endif
+};
+
+/* Defines */
+
+#define USTRCT(x) ((x).z)
+#define TT_VALID( handle ) ( ( handle ).z != NULL )
+
+static void i_tt_push_error(TT_Error rc);
+
+/* Prototypes */
+
+static int i_tt_get_instance( TT_Fonthandle *handle, i_img_dim points, int smooth );
+static void i_tt_init_raster_map( TT_Raster_Map* bit, i_img_dim width, i_img_dim height, int smooth );
+static void i_tt_done_raster_map( TT_Raster_Map *bit );
+static void i_tt_clear_raster_map( TT_Raster_Map* bit );
+static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,i_img_dim x_off, i_img_dim y_off );
+static int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j );
+static void
+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 );
+static int
+i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
+ TT_Raster_Map *small_bit, i_img_dim cords[6],
+ char const* txt, size_t len, int smooth, int utf8 );
+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 );
+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 );
+static int
+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 );
+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 );
+
+
+/* static globals needed */
+
+static int TT_initialized = 0;
+static TT_Engine engine;
+static int LTT_dpi = 72; /* FIXME: this ought to be a part of the call interface */
+static int LTT_hinted = 1; /* FIXME: this too */
+
+
+/*
+ * FreeType interface
+ */
+
+
+/*
+=item init_tt()
+
+Initializes the freetype font rendering engine
+
+=cut
+*/
+
+static undef_int
+i_init_tt(void) {
+ TT_Error error;
+ TT_Byte palette[] = { 0, 64, 127, 191, 255 };
+
+ i_clear_error();
+
+ mm_log((1,"init_tt()\n"));
+ error = TT_Init_FreeType( &engine );
+ if ( error ){
+ mm_log((1,"Initialization of freetype failed, code = 0x%x\n",
+ (unsigned)error));
+ i_tt_push_error(error);
+ i_push_error(0, "Could not initialize freetype 1.x");
+ return(1);
+ }
+
+#ifdef FTXPOST
+ error = TT_Init_Post_Extension( engine );
+ if (error) {
+ mm_log((1, "Initialization of Post extension failed = 0x%x\n",
+ (unsigned)error));
+
+ i_tt_push_error(error);
+ i_push_error(0, "Could not initialize FT 1.x POST extension");
+ return 1;
+ }
+#endif
+
+ error = TT_Set_Raster_Gray_Palette(engine, palette);
+ if (error) {
+ mm_log((1, "Initialization of gray levels failed = 0x%x\n",
+ (unsigned)error));
+ i_tt_push_error(error);
+ i_push_error(0, "Could not initialize FT 1.x POST extension");
+ return 1;
+ }
+
+ TT_initialized = 1;
+
+ return(0);
+}
+
+
+/*
+=item i_tt_get_instance(handle, points, smooth)
+
+Finds a points+smooth instance or if one doesn't exist in the cache
+allocates room and returns its cache entry
+
+ fontname - path to the font to load
+ handle - handle to the font.
+ points - points of the requested font
+ smooth - boolean (True: antialias on, False: antialias is off)
+
+=cut
+*/
+
+static
+int
+i_tt_get_instance( TT_Fonthandle *handle, i_img_dim points, int smooth ) {
+ int i,idx;
+ TT_Error error;
+
+ mm_log((1,"i_tt_get_instance(handle %p, points %" i_DF ", smooth %d)\n",
+ handle, i_DFc(points), smooth));
+
+ if (smooth == -1) { /* Smooth doesn't matter for this search */
+ for(i=0;i<TT_CHC;i++) {
+ if (handle->instanceh[i].ptsize==points) {
+ mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
+ return i;
+ }
+ }
+ smooth=1; /* We will be adding a font - add it as smooth then */
+ } else { /* Smooth doesn't matter for this search */
+ for(i=0;i<TT_CHC;i++) {
+ if (handle->instanceh[i].ptsize == points
+ && handle->instanceh[i].smooth == smooth) {
+ mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
+ return i;
+ }
+ }
+ }
+
+ /* Found the instance in the cache - return the cache index */
+
+ for(idx=0;idx<TT_CHC;idx++) {
+ if (!(handle->instanceh[idx].order)) break; /* find the lru item */
+ }
+
+ mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
+ mm_log((1,"i_tt_get_instance: lru pointer %p\n",
+ USTRCT(handle->instanceh[idx].instance) ));
+
+ if ( USTRCT(handle->instanceh[idx].instance) ) {
+ mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
+
+ /* Free cached glyphs */
+ for(i=0;i<256;i++)
+ if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
+ TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
+
+ for(i=0;i<256;i++) {
+ handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
+ USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
+ }
+
+ /* Free instance if needed */
+ TT_Done_Instance( handle->instanceh[idx].instance );
+ }
+
+ /* create and initialize instance */
+ /* FIXME: probably a memory leak on fail */
+
+ (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) ||
+ ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
+ ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
+
+ if ( error ) {
+ mm_log((1, "Could not create and initialize instance: error %x.\n",
+ (unsigned)error ));
+ return -1;
+ }
+
+ /* Now that the instance should the inplace we need to lower all of the
+ ru counts and put `this' one with the highest entry */
+
+ for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
+
+ handle->instanceh[idx].order=TT_CHC-1;
+ handle->instanceh[idx].ptsize=points;
+ handle->instanceh[idx].smooth=smooth;
+ TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
+
+ /* Zero the memory for the glyph storage so they are not thought as
+ cached if they haven't been cached since this new font was loaded */
+
+ for(i=0;i<256;i++) {
+ handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
+ USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
+ }
+
+ return idx;
+}
+
+
+/*
+=item i_tt_new(fontname)
+
+Creates a new font handle object, finds a character map and initialise the
+the font handle's cache
+
+ fontname - path to the font to load
+
+=cut
+*/
+
+TT_Fonthandle*
+i_tt_new(const char *fontname) {
+ TT_Error error;
+ TT_Fonthandle *handle;
+ unsigned short i,n;
+ unsigned short platform,encoding;
+
+ if (!TT_initialized && i_init_tt()) {
+ i_push_error(0, "Could not initialize FT1 engine");
+ return NULL;
+ }
+
+ i_clear_error();
+
+ mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
+
+ /* allocate memory for the structure */
+
+ handle = mymalloc( sizeof(TT_Fonthandle) ); /* checked 5Nov05 tonyc */
+
+ /* load the typeface */
+ error = TT_Open_Face( engine, fontname, &handle->face );
+ if ( error ) {
+ if ( error == TT_Err_Could_Not_Open_File ) {
+ mm_log((1, "Could not find/open %s.\n", fontname ));
+ }
+ else {
+ mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname,
+ (unsigned)error ));
+ }
+ i_tt_push_error(error);
+ return NULL;
+ }
+
+ TT_Get_Face_Properties( handle->face, &(handle->properties) );
+
+ /* First, look for a Unicode charmap */
+ n = handle->properties.num_CharMaps;
+ USTRCT( handle->char_map )=NULL; /* Invalidate character map */
+
+ for ( i = 0; i < n; i++ ) {
+ TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
+ if ( (platform == 3 && encoding == 1 )
+ || (platform == 0 && encoding == 0 ) ) {
+ mm_log((2,"i_tt_new - found char map platform %u encoding %u\n",
+ platform, encoding));
+ TT_Get_CharMap( handle->face, i, &(handle->char_map) );
+ break;
+ }
+ }
+ if (!USTRCT(handle->char_map) && n != 0) {
+ /* just use the first one */
+ TT_Get_CharMap( handle->face, 0, &(handle->char_map));
+ }
+
+ /* Zero the pointsizes - and ordering */
+
+ for(i=0;i<TT_CHC;i++) {
+ USTRCT(handle->instanceh[i].instance)=NULL;
+ handle->instanceh[i].order=i;
+ handle->instanceh[i].ptsize=0;
+ handle->instanceh[i].smooth=-1;
+ }
+
+#ifdef FTXPOST
+ handle->loaded_names = 0;
+#endif
+
+ mm_log((1,"i_tt_new <- %p\n",handle));
+ return handle;
+}
+
+
+
+/*
+ *Â raster map management
+ */
+
+/*
+=item i_tt_init_raster_map(bit, width, height, smooth)
+
+Allocates internal memory for the bitmap as needed by the parameters (internal)
+
+ bit - bitmap to allocate into
+ width - width of the bitmap
+ height - height of the bitmap
+ smooth - boolean (True: antialias on, False: antialias is off)
+
+=cut
+*/
+
+static
+void
+i_tt_init_raster_map( TT_Raster_Map* bit, i_img_dim width, i_img_dim height, int smooth ) {
+
+ mm_log((1,"i_tt_init_raster_map( bit %p, width %" i_DF ", height %" i_DF
+ ", smooth %d)\n", bit, i_DFc(width), i_DFc(height), smooth));
+
+ bit->rows = height;
+ bit->width = ( width + 3 ) & -4;
+ bit->flow = TT_Flow_Down;
+
+ if ( smooth ) {
+ bit->cols = bit->width;
+ bit->size = bit->rows * bit->width;
+ } else {
+ bit->cols = ( bit->width + 7 ) / 8; /* convert to # of bytes */
+ bit->size = bit->rows * bit->cols; /* number of bytes in buffer */
+ }
+
+ /* rows can be 0 for some glyphs, for example ' ' */
+ if (bit->rows && bit->size / bit->rows != bit->cols) {
+ i_fatal(0, "Integer overflow calculating bitmap size (%d, %d)\n",
+ bit->width, bit->rows);
+ }
+
+ 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 ));
+
+ bit->bitmap = (void *) mymalloc( bit->size ); /* checked 6Nov05 tonyc */
+ if ( !bit->bitmap ) i_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",bit->size );
+}
+
+
+/*
+=item i_tt_clear_raster_map(bit)
+
+Frees the bitmap data and sets pointer to NULL (internal)
+
+ bit - bitmap to free
+
+=cut
+*/
+
+static
+void
+i_tt_done_raster_map( TT_Raster_Map *bit ) {
+ myfree( bit->bitmap );
+ bit->bitmap = NULL;
+}
+
+
+/*
+=item i_tt_clear_raster_map(bit)
+
+Clears the specified bitmap (internal)
+
+ bit - bitmap to zero
+
+=cut
+*/
+
+
+static
+void
+i_tt_clear_raster_map( TT_Raster_Map* bit ) {
+ memset( bit->bitmap, 0, bit->size );
+}
+
+
+/*
+=item i_tt_blit_or(dst, src, x_off, y_off)
+
+function that blits one raster map into another (internal)
+
+ dst - destination bitmap
+ src - source bitmap
+ x_off - x offset into the destination bitmap
+ y_off - y offset into the destination bitmap
+
+=cut
+*/
+
+static
+void
+i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,i_img_dim x_off, i_img_dim y_off ) {
+ i_img_dim x, y;
+ i_img_dim x1, x2, y1, y2;
+ unsigned char *s, *d;
+
+ x1 = x_off < 0 ? -x_off : 0;
+ y1 = y_off < 0 ? -y_off : 0;
+
+ x2 = (int)dst->cols - x_off;
+ if ( x2 > src->cols ) x2 = src->cols;
+
+ y2 = (int)dst->rows - y_off;
+ if ( y2 > src->rows ) y2 = src->rows;
+
+ if ( x1 >= x2 ) return;
+
+ /* do the real work now */
+
+ for ( y = y1; y < y2; ++y ) {
+ s = ( (unsigned char*)src->bitmap ) + y * src->cols + x1;
+ d = ( (unsigned char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
+
+ for ( x = x1; x < x2; ++x ) {
+ if (*s > *d)
+ *d = *s;
+ d++;
+ s++;
+ }
+ }
+}
+
+/* useful for debugging */
+#if 0
+
+static void dump_raster_map(FILE *out, TT_Raster_Map *bit ) {
+ int x, y;
+ fprintf(out, "cols %d rows %d flow %d\n", bit->cols, bit->rows, bit->flow);
+ for (y = 0; y < bit->rows; ++y) {
+ fprintf(out, "%2d:", y);
+ for (x = 0; x < bit->cols; ++x) {
+ if ((x & 7) == 0 && x) putc(' ', out);
+ fprintf(out, "%02x", ((unsigned char *)bit->bitmap)[y*bit->cols+x]);
+ }
+ putc('\n', out);
+ }
+}
+
+#endif
+
+/*
+=item i_tt_get_glyph(handle, inst, j)
+
+Function to see if a glyph exists and if so cache it (internal)
+
+ handle - pointer to font handle
+ inst - font instance
+ j - charcode of glyph
+
+=cut
+*/
+
+static
+int
+i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j) {
+ unsigned short load_flags, code;
+ TT_Error error;
+
+ mm_log((1, "i_tt_get_glyph(handle %p, inst %d, j %lu (%c))\n",
+ handle,inst,j, (int)((j >= ' ' && j <= '~') ? j : '.')));
+
+ /*mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] ));*/
+
+ if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)
+ && handle->instanceh[inst].glyphs[TT_HASH(j)].ch == j) {
+ mm_log((1,"i_tt_get_glyph: %lu in cache\n",j));
+ return 1;
+ }
+
+ if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) ) {
+ /* clean up the entry */
+ TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
+ USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
+ handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
+ }
+
+ /* Ok - it wasn't cached - try to get it in */
+ load_flags = TTLOAD_SCALE_GLYPH;
+ if ( LTT_hinted ) load_flags |= TTLOAD_HINT_GLYPH;
+
+ if ( !TT_VALID(handle->char_map) ) {
+ code = (j - ' ' + 1) < 0 ? 0 : (j - ' ' + 1);
+ if ( code >= handle->properties.num_Glyphs ) code = 0;
+ } else code = TT_Char_Index( handle->char_map, j );
+
+ if ( (error = TT_New_Glyph( handle->face, &handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)) ) {
+ mm_log((1, "Cannot allocate and load glyph: error %#x.\n", (unsigned)error ));
+ i_push_error(error, "TT_New_Glyph()");
+ return 0;
+ }
+ if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, code, load_flags)) ) {
+ mm_log((1, "Cannot allocate and load glyph: error %#x.\n", (unsigned)error ));
+ /* Don't leak */
+ TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
+ USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
+ i_push_error(error, "TT_Load_Glyph()");
+ return 0;
+ }
+
+ /* At this point the glyph should be allocated and loaded */
+ handle->instanceh[inst].glyphs[TT_HASH(j)].ch = j;
+
+ /* Next get the glyph metrics */
+ error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
+ &handle->instanceh[inst].gmetrics[TT_HASH(j)] );
+ if (error) {
+ mm_log((1, "TT_Get_Glyph_Metrics: error %#x.\n", (unsigned)error ));
+ TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
+ USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
+ handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
+ i_push_error(error, "TT_Get_Glyph_Metrics()");
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+=item i_tt_has_chars(handle, text, len, utf8, out)
+
+Check if the given characters are defined by the font. Note that len
+is the number of bytes, not the number of characters (when utf8 is
+non-zero).
+
+Returns the number of characters that were checked.
+
+=cut
+*/
+
+size_t
+i_tt_has_chars(TT_Fonthandle *handle, char const *text, size_t len, int utf8,
+ char *out) {
+ size_t count = 0;
+ mm_log((1, "i_tt_has_chars(handle %p, text %p, len %ld, utf8 %d)\n",
+ handle, text, (long)len, utf8));
+
+ while (len) {
+ unsigned long c;
+ int index;
+ if (utf8) {
+ c = i_utf8_advance(&text, &len);
+ if (c == ~0UL) {
+ i_push_error(0, "invalid UTF8 character");
+ return 0;
+ }
+ }
+ else {
+ c = (unsigned char)*text++;
+ --len;
+ }
+
+ if (TT_VALID(handle->char_map)) {
+ index = TT_Char_Index(handle->char_map, c);
+ }
+ else {
+ index = (c - ' ' + 1) < 0 ? 0 : (c - ' ' + 1);
+ if (index >= handle->properties.num_Glyphs)
+ index = 0;
+ }
+ *out++ = index != 0;
+ ++count;
+ }
+
+ return count;
+}
+
+/*
+=item i_tt_destroy(handle)
+
+Clears the data taken by a font including all cached data such as
+pixmaps and glyphs
+
+ handle - pointer to font handle
+
+=cut
+*/
+
+void
+i_tt_destroy( TT_Fonthandle *handle) {
+ TT_Close_Face( handle->face );
+ myfree( handle );
+
+ /* FIXME: Should these be freed automatically by the library?
+
+ TT_Done_Instance( instance );
+ void
+ i_tt_done_glyphs( void ) {
+ int i;
+
+ if ( !glyphs ) return;
+
+ for ( i = 0; i < 256; ++i ) TT_Done_Glyph( glyphs[i] );
+ free( glyphs );
+
+ glyphs = NULL;
+ }
+ */
+}
+
+
+/*
+ * FreeType Rendering functions
+ */
+
+
+/*
+=item i_tt_render_glyph(handle, gmetrics, bit, smallbit, x_off, y_off, smooth)
+
+Renders a single glyph into the bit rastermap (internal)
+
+ handle - pointer to font handle
+ gmetrics - the metrics for the glyph to be rendered
+ bit - large bitmap that is the destination for the text
+ smallbit - small bitmap that is used only if smooth is true
+ x_off - x offset of glyph
+ y_off - y offset of glyph
+ smooth - boolean (True: antialias on, False: antialias is off)
+
+=cut
+*/
+
+static
+void
+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 ) {
+
+ 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",
+ USTRCT(glyph), gmetrics, bit, small_bit, i_DFc(x_off),
+ i_DFc(y_off), smooth));
+
+ if ( !smooth ) TT_Get_Glyph_Bitmap( glyph, bit, x_off * 64, y_off * 64);
+ else {
+ TT_F26Dot6 xmin, ymin, xmax, ymax;
+
+ xmin = gmetrics->bbox.xMin & -64;
+ ymin = gmetrics->bbox.yMin & -64;
+ xmax = (gmetrics->bbox.xMax + 63) & -64;
+ ymax = (gmetrics->bbox.yMax + 63) & -64;
+
+ i_tt_clear_raster_map( small_bit );
+ TT_Get_Glyph_Pixmap( glyph, small_bit, -xmin, -ymin );
+ i_tt_blit_or( bit, small_bit, xmin/64 + x_off, -ymin/64 - y_off );
+ }
+}
+
+
+/*
+=item i_tt_render_all_glyphs(handle, inst, bit, small_bit, cords, txt, len, smooth)
+
+calls i_tt_render_glyph to render each glyph into the bit rastermap (internal)
+
+ handle - pointer to font handle
+ inst - font instance
+ bit - large bitmap that is the destination for the text
+ smallbit - small bitmap that is used only if smooth is true
+ txt - string to render
+ len - length of the string to render
+ smooth - boolean (True: antialias on, False: antialias is off)
+
+=cut
+*/
+
+static
+int
+i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
+ TT_Raster_Map *small_bit, i_img_dim cords[6],
+ char const* txt, size_t len, int smooth, int utf8 ) {
+ unsigned long j;
+ TT_F26Dot6 x,y;
+
+ 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",
+ handle, inst, bit, small_bit, (int)len, txt, (long)len, smooth, utf8));
+
+ /*
+ y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM);
+ */
+
+ 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 */
+ y=-cords[4];
+
+ while (len) {
+ if (utf8) {
+ j = i_utf8_advance(&txt, &len);
+ if (j == ~0UL) {
+ i_push_error(0, "invalid UTF8 character");
+ return 0;
+ }
+ }
+ else {
+ j = (unsigned char)*txt++;
+ --len;
+ }
+ if ( !i_tt_get_glyph(handle,inst,j) )
+ continue;
+ i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
+ &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit,
+ small_bit, x, y, smooth );
+ x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64;
+ }
+
+ return 1;
+}
+
+
+/*
+ * Functions to render rasters (single channel images) onto images
+ */
+
+/*
+=item i_tt_dump_raster_map2(im, bit, xb, yb, cl, smooth)
+
+Function to dump a raster onto an image in color used by i_tt_text() (internal).
+
+ im - image to dump raster on
+ bit - bitmap that contains the text to be dumped to im
+ xb, yb - coordinates, left edge and baseline
+ cl - color to use for text
+ smooth - boolean (True: antialias on, False: antialias is off)
+
+=cut
+*/
+
+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 ) {
+ unsigned char *bmap;
+ i_img_dim x, y;
+ mm_log((1,"i_tt_dump_raster_map2(im %p, bit %p, xb %" i_DF ", yb %" i_DF ", cl %p)\n",
+ im, bit, i_DFc(xb), i_DFc(yb), cl));
+
+ bmap = bit->bitmap;
+
+ if ( smooth ) {
+
+ i_render r;
+ i_render_init(&r, im, bit->cols);
+ for(y=0;y<bit->rows;y++) {
+ i_render_color(&r, xb, yb+y, bit->cols, bmap + y*bit->cols, cl);
+ }
+ i_render_done(&r);
+ } else {
+ for(y=0;y<bit->rows;y++) {
+ unsigned mask = 0x80;
+ unsigned char *p = bmap + y * bit->cols;
+
+ for(x = 0; x < bit->width; x++) {
+ if (*p & mask) {
+ i_ppix(im, x+xb, y+yb, cl);
+ }
+ mask >>= 1;
+ if (!mask) {
+ mask = 0x80;
+ ++p;
+ }
+ }
+ }
+
+ }
+}
+
+
+/*
+=item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth)
+
+Function to dump a raster onto a single channel image in color (internal)
+
+ im - image to dump raster on
+ bit - bitmap that contains the text to be dumped to im
+ xb, yb - coordinates, left edge and baseline
+ channel - channel to copy to
+ smooth - boolean (True: antialias on, False: antialias is off)
+
+=cut
+*/
+
+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 ) {
+ unsigned char *bmap;
+ i_color val;
+ int c;
+ i_img_dim x,y;
+ int old_mask = im->ch_mask;
+ im->ch_mask = 1 << channel;
+
+ mm_log((1,"i_tt_dump_raster_channel(im %p, bit %p, xb %" i_DF ", yb %" i_DF ", channel %d)\n",
+ im, bit, i_DFc(xb), i_DFc(yb), channel));
+
+ bmap = bit->bitmap;
+
+ if ( smooth ) {
+ for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
+ c = bmap[y*(bit->cols)+x];
+ val.channel[channel] = c;
+ i_ppix(im,x+xb,y+yb,&val);
+ }
+ } else {
+ for(y=0;y<bit->rows;y++) {
+ unsigned mask = 0x80;
+ unsigned char *p = bmap + y * bit->cols;
+
+ for(x=0;x<bit->width;x++) {
+ val.channel[channel] = (*p & mask) ? 255 : 0;
+ i_ppix(im,x+xb,y+yb,&val);
+
+ mask >>= 1;
+ if (!mask) {
+ ++p;
+ mask = 0x80;
+ }
+ }
+ }
+ }
+ im->ch_mask = old_mask;
+}
+
+
+/*
+=item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth)
+
+interface for generating single channel raster of text (internal)
+
+ handle - pointer to font handle
+ bit - the bitmap that is allocated, rendered into and NOT freed
+ cords - the bounding box (modified in place)
+ points - font size to use
+ txt - string to render
+ len - length of the string to render
+ smooth - boolean (True: antialias on, False: antialias is off)
+
+=cut
+*/
+
+static
+int
+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 ) {
+ int inst;
+ i_img_dim width, height;
+ TT_Raster_Map small_bit;
+
+ /* find or install an instance */
+ if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) {
+ mm_log((1,"i_tt_rasterize: get instance failed\n"));
+ return 0;
+ }
+
+ /* calculate bounding box */
+ if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 ))
+ return 0;
+
+
+ width = cords[2]-cords[0];
+ height = cords[5]-cords[4];
+
+ mm_log((1,"i_tt_rasterize: width=%" i_DF ", height=%" i_DF "\n",
+ i_DFc(width), i_DFc(height) ));
+
+ i_tt_init_raster_map ( bit, width, height, smooth );
+ i_tt_clear_raster_map( bit );
+ if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth );
+
+ if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len,
+ smooth, utf8 )) {
+ if ( smooth )
+ i_tt_done_raster_map( &small_bit );
+ return 0;
+ }
+
+ if ( smooth ) i_tt_done_raster_map( &small_bit );
+ return 1;
+}
+
+
+
+/*
+ * Exported text rendering interfaces
+ */
+
+
+/*
+=item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth, utf8)
+
+Interface to text rendering into a single channel in an image
+
+ handle - pointer to font handle
+ im - image to render text on to
+ xb, yb - coordinates, left edge and baseline
+ channel - channel to render into
+ points - font size to use
+ txt - string to render
+ len - length of the string to render
+ smooth - boolean (True: antialias on, False: antialias is off)
+
+=cut
+*/
+
+undef_int
+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 ) {
+
+ i_img_dim cords[BOUNDING_BOX_COUNT];
+ i_img_dim ascent, st_offset, y;
+ TT_Raster_Map bit;
+
+ i_clear_error();
+ if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
+
+ ascent=cords[BBOX_ASCENT];
+ st_offset=cords[BBOX_NEG_WIDTH];
+ y = align ? yb-ascent : yb;
+
+ i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , y, channel, smooth );
+ i_tt_done_raster_map( &bit );
+
+ return 1;
+}
+
+
+/*
+=item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8)
+
+Interface to text rendering in a single color onto an image
+
+ handle - pointer to font handle
+ im - image to render text on to
+ xb, yb - coordinates, left edge and baseline
+ cl - color to use for text
+ points - font size to use
+ txt - string to render
+ len - length of the string to render
+ smooth - boolean (True: antialias on, False: antialias is off)
+
+=cut
+*/
+
+undef_int
+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) {
+ i_img_dim cords[BOUNDING_BOX_COUNT];
+ i_img_dim ascent, st_offset, y;
+ TT_Raster_Map bit;
+
+ i_clear_error();
+
+ if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
+
+ ascent=cords[BBOX_ASCENT];
+ st_offset=cords[BBOX_NEG_WIDTH];
+ y = align ? yb-ascent : yb;
+
+ i_tt_dump_raster_map2( im, &bit, xb+st_offset, y, cl, smooth );
+ i_tt_done_raster_map( &bit );
+
+ return 1;
+}
+
+
+/*
+=item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8)
+
+Function to get texts bounding boxes given the instance of the font (internal)
+
+ handle - pointer to font handle
+ inst - font instance
+ txt - string to measure
+ len - length of the string to render
+ cords - the bounding box (modified in place)
+
+=cut
+*/
+
+static
+undef_int
+i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, size_t len, i_img_dim cords[BOUNDING_BOX_COUNT], int utf8 ) {
+ int upm, casc, cdesc, first;
+
+ int start = 0;
+ i_img_dim width = 0;
+ int gdescent = 0;
+ int gascent = 0;
+ int descent = 0;
+ int ascent = 0;
+ int rightb = 0;
+
+ unsigned long j;
+ unsigned char *ustr;
+ ustr=(unsigned char*)txt;
+
+ mm_log((1,"i_tt_box_inst(handle %p,inst %d,txt '%.*s', len %ld, utf8 %d)\n",
+ handle, inst, (int)len, txt, (long)len, utf8));
+
+ upm = handle->properties.header->Units_Per_EM;
+ gascent = ( handle->properties.horizontal->Ascender * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm;
+ gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem - upm + 1) / upm;
+
+ width = 0;
+ start = 0;
+
+ mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent));
+
+ first=1;
+ while (len) {
+ if (utf8) {
+ j = i_utf8_advance(&txt, &len);
+ if (j == ~0UL) {
+ i_push_error(0, "invalid UTF8 character");
+ return 0;
+ }
+ }
+ else {
+ j = (unsigned char)*txt++;
+ --len;
+ }
+ if ( i_tt_get_glyph(handle,inst,j) ) {
+ TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j);
+ width += gm->advance / 64;
+ casc = (gm->bbox.yMax+63) / 64;
+ cdesc = (gm->bbox.yMin-63) / 64;
+
+ mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n",
+ (int)((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc));
+
+ if (first) {
+ start = gm->bbox.xMin / 64;
+ ascent = (gm->bbox.yMax+63) / 64;
+ descent = (gm->bbox.yMin-63) / 64;
+ first = 0;
+ }
+ if (!len) { /* if at end of string */
+ /* the right-side bearing - in case the right-side of a
+ character goes past the right of the advance width,
+ as is common for italic fonts
+ */
+ rightb = gm->advance - gm->bearingX
+ - (gm->bbox.xMax - gm->bbox.xMin);
+ /* fprintf(stderr, "font info last: %d %d %d %d\n",
+ gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */
+ }
+
+ ascent = (ascent > casc ? ascent : casc );
+ descent = (descent < cdesc ? descent : cdesc);
+ }
+ }
+
+ cords[BBOX_NEG_WIDTH]=start;
+ cords[BBOX_GLOBAL_DESCENT]=gdescent;
+ cords[BBOX_POS_WIDTH]=width;
+ if (rightb < 0)
+ cords[BBOX_POS_WIDTH] -= rightb / 64;
+ cords[BBOX_GLOBAL_ASCENT]=gascent;
+ cords[BBOX_DESCENT]=descent;
+ cords[BBOX_ASCENT]=ascent;
+ cords[BBOX_ADVANCE_WIDTH] = width;
+ cords[BBOX_RIGHT_BEARING] = rightb / 64;
+
+ return BBOX_RIGHT_BEARING + 1;
+}
+
+
+/*
+=item i_tt_bbox(handle, points, txt, len, cords, utf8)
+
+Interface to get a strings bounding box
+
+ handle - pointer to font handle
+ points - font size to use
+ txt - string to render
+ len - length of the string to render
+ cords - the bounding box (modified in place)
+
+=cut
+*/
+
+undef_int
+i_tt_bbox( TT_Fonthandle *handle, double points,const char *txt,size_t len,i_img_dim cords[6], int utf8) {
+ int inst;
+
+ i_clear_error();
+ mm_log((1,"i_tt_box(handle %p,points %f,txt '%.*s', len %ld, utf8 %d)\n",
+ handle, points, (int)len, txt, (long)len, utf8));
+
+ if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
+ i_push_errorf(0, "i_tt_get_instance(%g)", points);
+ mm_log((1,"i_tt_text: get instance failed\n"));
+ return 0;
+ }
+
+ return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8);
+}
+
+/*
+=item i_tt_face_name(handle, name_buf, name_buf_size)
+
+Retrieve's the font's postscript name.
+
+This is complicated by the need to handle encodings and so on.
+
+=cut
+ */
+size_t
+i_tt_face_name(TT_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
+ TT_Face_Properties props;
+ int name_count;
+ int i;
+ TT_UShort platform_id, encoding_id, lang_id, name_id;
+ TT_UShort name_len;
+ TT_String *name;
+ int want_index = -1; /* an acceptable but not perfect name */
+ int score = 0;
+
+ i_clear_error();
+
+ TT_Get_Face_Properties(handle->face, &props);
+ name_count = props.num_Names;
+ for (i = 0; i < name_count; ++i) {
+ TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
+ &name_id);
+
+ TT_Get_Name_String(handle->face, i, &name, &name_len);
+
+ if (platform_id != TT_PLATFORM_APPLE_UNICODE && name_len
+ && name_id == TT_NAME_ID_PS_NAME) {
+ int might_want_index = -1;
+ int might_score = 0;
+ if ((platform_id == TT_PLATFORM_MACINTOSH && encoding_id == TT_MAC_ID_ROMAN)
+ ||
+ (platform_id == TT_PLATFORM_MICROSOFT && encoding_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)) {
+ /* exactly what we want */
+ want_index = i;
+ break;
+ }
+
+ if (platform_id == TT_PLATFORM_MICROSOFT
+ && (encoding_id & 0xFF) == TT_MS_LANGID_ENGLISH_GENERAL) {
+ /* any english is good */
+ might_want_index = i;
+ might_score = 9;
+ }
+ /* there might be something in between */
+ else {
+ /* anything non-unicode is better than nothing */
+ might_want_index = i;
+ might_score = 1;
+ }
+ if (might_score > score) {
+ score = might_score;
+ want_index = might_want_index;
+ }
+ }
+ }
+
+ if (want_index != -1) {
+ TT_Get_Name_String(handle->face, want_index, &name, &name_len);
+
+ strncpy(name_buf, name, name_buf_size);
+ name_buf[name_buf_size-1] = '\0';
+
+ return strlen(name) + 1;
+ }
+ else {
+ i_push_error(0, "no face name present");
+ return 0;
+ }
+}
+
+void i_tt_dump_names(TT_Fonthandle *handle) {
+ TT_Face_Properties props;
+ int name_count;
+ int i;
+ TT_UShort platform_id, encoding_id, lang_id, name_id;
+ TT_UShort name_len;
+ TT_String *name;
+
+ TT_Get_Face_Properties(handle->face, &props);
+ name_count = props.num_Names;
+ for (i = 0; i < name_count; ++i) {
+ TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
+ &name_id);
+ TT_Get_Name_String(handle->face, i, &name, &name_len);
+
+ printf("# %d: plat %d enc %d lang %d name %d value ", i, platform_id,
+ encoding_id, lang_id, name_id);
+ if (platform_id == TT_PLATFORM_APPLE_UNICODE) {
+ printf("(unicode)\n");
+ }
+ else {
+ printf("'%s'\n", name);
+ }
+ }
+ fflush(stdout);
+}
+
+size_t
+i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf,
+ size_t name_buf_size) {
+#ifdef FTXPOST
+ TT_Error rc;
+ TT_String *psname;
+ TT_UShort index;
+
+ i_clear_error();
+
+ if (!handle->loaded_names) {
+ TT_Post post;
+ mm_log((1, "Loading PS Names"));
+ handle->load_cond = TT_Load_PS_Names(handle->face, &post);
+ ++handle->loaded_names;
+ }
+
+ if (handle->load_cond) {
+ i_push_errorf(handle->load_cond, "error loading names (%#x)",
+ (unsigned)handle->load_cond);
+ return 0;
+ }
+
+ index = TT_Char_Index(handle->char_map, ch);
+ if (!index) {
+ i_push_error(0, "no such character");
+ return 0;
+ }
+
+ rc = TT_Get_PS_Name(handle->face, index, &psname);
+
+ if (rc) {
+ i_push_error(rc, "error getting name");
+ return 0;
+ }
+
+ strncpy(name_buf, psname, name_buf_size);
+ name_buf[name_buf_size-1] = '\0';
+
+ return strlen(psname) + 1;
+#else
+ mm_log((1, "FTXPOST extension not enabled\n"));
+ i_clear_error();
+ i_push_error(0, "Use of FTXPOST extension disabled");
+
+ return 0;
+#endif
+}
+
+/*
+=item i_tt_push_error(code)
+
+Push an error message and code onto the Imager error stack.
+
+=cut
+*/
+static void
+i_tt_push_error(TT_Error rc) {
+#ifdef FTXERR18
+ TT_String const *msg = TT_ErrToString18(rc);
+
+ i_push_error(rc, msg);
+#else
+ i_push_errorf(rc, "Error code 0x%04x", (unsigned)rc);
+#endif
+}
+
+
+/*
+=back
+
+=head1 AUTHOR
+
+Arnar M. Hrafnkelsson <addi@umich.edu>
+
+=head1 SEE ALSO
+
+Imager(3)
+
+=cut
+*/