X-Git-Url: http://git.imager.perl.org/imager.git/blobdiff_plain/aa6e55ebcfabb884884c53c4a7b42eb22e419e2f..1a19d068476d9ed873426ad315ef9c530440f499:/font.c diff --git a/font.c b/font.c index 395d7da0..7b5d0931 100644 --- a/font.c +++ b/font.c @@ -7,11 +7,6 @@ #include #include - - - - - /* =head1 NAME @@ -29,7 +24,7 @@ font.c - implements font handling functions for t1 and truetype fonts #ifdef HAVE_LIBTT handle = i_tt_new(path_to_ttf); - rc = i_tt_bbox(handle, points, "foo", 3, int cords[6]); + rc = i_tt_bbox(handle, points, "foo", 3, int cords[6], utf8); i_tt_destroy(handle); // and much more @@ -43,20 +38,12 @@ more for Imager. Some of these functions are internal. -=over 4 +=over =cut */ - - - - - - - - /* =item i_init_fonts() @@ -66,15 +53,15 @@ Initialize font rendering libraries if they are avaliable. */ undef_int -i_init_fonts() { +i_init_fonts(int t1log) { mm_log((1,"Initializing fonts\n")); #ifdef HAVE_LIBT1 - init_t1(); + i_init_t1(t1log); #endif #ifdef HAVE_LIBTT - init_tt(); + i_init_tt(); #endif #ifdef HAVE_FT2 @@ -90,10 +77,13 @@ i_init_fonts() { #ifdef HAVE_LIBT1 +static int t1_get_flags(char const *flags); +static char *t1_from_utf8(char const *in, int len, int *outlen); +static void t1_push_error(void); /* -=item i_init_t1() +=item i_init_t1(t1log) Initializes the t1lib font rendering engine. @@ -101,9 +91,13 @@ Initializes the t1lib font rendering engine. */ undef_int -init_t1() { +i_init_t1(int t1log) { + int init_flags = IGNORE_CONFIGFILE|IGNORE_FONTDATABASE; mm_log((1,"init_t1()\n")); - if ((T1_InitLib(LOGFILE|IGNORE_CONFIGFILE|IGNORE_FONTDATABASE) == NULL)){ + + if (t1log) + init_flags |= LOGFILE; + if ((T1_InitLib(init_flags) == NULL)){ mm_log((1,"Initialization of t1lib failed\n")); return(1); } @@ -143,6 +137,7 @@ Loads the fonts with the given filenames, returns its font id int i_t1_new(char *pfb,char *afm) { int font_id; + mm_log((1,"i_t1_new(pfb %s,afm %s)\n",pfb,(afm?afm:"NULL"))); font_id = T1_AddFont(pfb); if (font_id<0) { @@ -154,6 +149,7 @@ i_t1_new(char *pfb,char *afm) { mm_log((1,"i_t1_new: requesting afm file '%s'.\n",afm)); if (T1_SetAfmFileName(font_id,afm)<0) mm_log((1,"i_t1_new: afm loading of '%s' failed.\n",afm)); } + return font_id; } @@ -230,16 +226,25 @@ Interface to text rendering into a single channel in an image */ undef_int -i_t1_cp(i_img *im,int xb,int yb,int channel,int fontnum,float points,char* str,int len,int align) { +i_t1_cp(i_img *im,int xb,int yb,int channel,int fontnum,float points,char* str,int len,int align, int utf8, char const *flags) { GLYPH *glyph; int xsize,ysize,x,y; i_color val; + int mod_flags = t1_get_flags(flags); unsigned int ch_mask_store; if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); } - glyph=T1_AASetString( fontnum, str, len, 0, T1_KERNING, points, NULL); + if (utf8) { + int worklen; + char *work = t1_from_utf8(str, len, &worklen); + glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL); + myfree(work); + } + else { + glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL); + } if (glyph == NULL) return 0; @@ -283,15 +288,26 @@ function to get a strings bounding box given the font id and sizes =cut */ -void -i_t1_bbox(int fontnum,float points,char *str,int len,int cords[6]) { +int +i_t1_bbox(int fontnum,float points,char *str,int len,int cords[6], int utf8,char const *flags) { BBox bbox; BBox gbbox; + int mod_flags = t1_get_flags(flags); + int advance; mm_log((1,"i_t1_bbox(fontnum %d,points %.2f,str '%.*s', len %d)\n",fontnum,points,len,str,len)); T1_LoadFont(fontnum); /* FIXME: Here a return code is ignored - haw haw haw */ - bbox = T1_GetStringBBox(fontnum,str,len,0,T1_KERNING); + if (utf8) { + int worklen; + char *work = t1_from_utf8(str, len, &worklen); + bbox = T1_GetStringBBox(fontnum,work,worklen,0,mod_flags); + myfree(work); + } + else { + bbox = T1_GetStringBBox(fontnum,str,len,0,mod_flags); + } gbbox = T1_GetFontBBox(fontnum); + advance = T1_GetStringWidth(fontnum, str, len, 0, mod_flags); mm_log((1,"bbox: (%d,%d,%d,%d)\n", (int)(bbox.llx*points/1000), @@ -302,14 +318,18 @@ i_t1_bbox(int fontnum,float points,char *str,int len,int cords[6]) { (int)(bbox.ury*points/1000) )); - cords[0]=((float)bbox.llx*points)/1000; - cords[2]=((float)bbox.urx*points)/1000; + cords[BBOX_NEG_WIDTH]=((float)bbox.llx*points)/1000; + cords[BBOX_POS_WIDTH]=((float)bbox.urx*points)/1000; - cords[1]=((float)gbbox.lly*points)/1000; - cords[3]=((float)gbbox.ury*points)/1000; + cords[BBOX_GLOBAL_DESCENT]=((float)gbbox.lly*points)/1000; + cords[BBOX_GLOBAL_ASCENT]=((float)gbbox.ury*points)/1000; - cords[4]=((float)bbox.lly*points)/1000; - cords[5]=((float)bbox.ury*points)/1000; + cords[BBOX_DESCENT]=((float)bbox.lly*points)/1000; + cords[BBOX_ASCENT]=((float)bbox.ury*points)/1000; + + cords[BBOX_ADVANCE_WIDTH] = ((float)advance * points)/1000; + + return BBOX_ADVANCE_WIDTH+1; } @@ -332,15 +352,24 @@ Interface to text rendering in a single color onto an image */ undef_int -i_t1_text(i_img *im,int xb,int yb,i_color *cl,int fontnum,float points,char* str,int len,int align) { +i_t1_text(i_img *im,int xb,int yb,i_color *cl,int fontnum,float points,char* str,int len,int align, int utf8, char const *flags) { GLYPH *glyph; int xsize,ysize,x,y,ch; i_color val; unsigned char c,i; + int mod_flags = t1_get_flags(flags); if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); } - glyph=T1_AASetString( fontnum, str, len, 0, T1_KERNING, points, NULL); + if (utf8) { + int worklen; + char *work = t1_from_utf8(str, len, &worklen); + glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL); + myfree(work); + } + else { + glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL); + } if (glyph == NULL) return 0; @@ -366,22 +395,370 @@ i_t1_text(i_img *im,int xb,int yb,i_color *cl,int fontnum,float points,char* str return 1; } +/* +=item t1_get_flags(flags) -#endif /* HAVE_LIBT1 */ +Processes the characters in I to create a mod_flags value used +by some T1Lib functions. +=cut + */ +static int +t1_get_flags(char const *flags) { + int mod_flags = T1_KERNING; + + while (*flags) { + switch (*flags++) { + case 'u': case 'U': mod_flags |= T1_UNDERLINE; break; + case 'o': case 'O': mod_flags |= T1_OVERLINE; break; + case 's': case 'S': mod_flags |= T1_OVERSTRIKE; break; + /* ignore anything we don't recognize */ + } + } + return mod_flags; +} +/* +=item t1_from_utf8(char const *in, int len, int *outlen) +Produces an unencoded version of I by dropping any Unicode +character over 255. +Returns a newly allocated buffer which should be freed with myfree(). +Sets *outlen to the number of bytes used in the output string. +=cut +*/ + +static char * +t1_from_utf8(char const *in, int len, int *outlen) { + char *out = mymalloc(len+1); + char *p = out; + unsigned long c; + + while (len) { + c = i_utf8_advance(&in, &len); + if (c == ~0UL) { + myfree(out); + i_push_error(0, "invalid UTF8 character"); + return 0; + } + /* yeah, just drop them */ + if (c < 0x100) { + *p++ = (char)c; + } + } + *p = '\0'; + *outlen = p - out; + return out; +} +/* +=item i_t1_has_chars(font_num, 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). -/* Truetype font support */ +out[char index] will be true if the character exists. + +Accepts UTF-8, but since T1 can only have 256 characters, any chars +with values over 255 will simply be returned as false. + +Returns the number of characters that were checked. + +=cut +*/ + +int +i_t1_has_chars(int font_num, const char *text, int len, int utf8, + char *out) { + int count = 0; + + mm_log((1, "i_t1_has_chars(font_num %d, text %p, len %d, utf8 %d)\n", + font_num, text, len, utf8)); + + i_clear_error(); + if (T1_LoadFont(font_num)) { + t1_push_error(); + return 0; + } + + while (len) { + unsigned long c; + 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 (c >= 0x100) { + /* limit of 256 characters for T1 */ + *out++ = 0; + } + else { + char const * name = T1_GetCharName(font_num, (unsigned char)c); + + if (name) { + *out++ = strcmp(name, ".notdef") != 0; + } + else { + mm_log((2, " No name found for character %lx\n", c)); + *out++ = 0; + } + } + ++count; + } + + return count; +} + +/* +=item i_t1_face_name(font_num, name_buf, name_buf_size) + +Copies the face name of the given C to C. Returns +the number of characters required to store the name (which can be +larger than C, including the space required to store +the terminating NUL). + +If name_buf is too small (as specified by name_buf_size) then the name +will be truncated. name_buf will always be NUL termintaed. + +=cut +*/ + +int +i_t1_face_name(int font_num, char *name_buf, size_t name_buf_size) { + char *name; + + T1_errno = 0; + if (T1_LoadFont(font_num)) { + t1_push_error(); + return 0; + } + name = T1_GetFontName(font_num); + + if (name) { + strncpy(name_buf, name, name_buf_size); + name_buf[name_buf_size-1] = '\0'; + return strlen(name) + 1; + } + else { + t1_push_error(); + return 0; + } +} + +int +i_t1_glyph_name(int font_num, unsigned long ch, char *name_buf, + size_t name_buf_size) { + char *name; + + i_clear_error(); + if (ch > 0xFF) { + return 0; + } + if (T1_LoadFont(font_num)) { + t1_push_error(); + return 0; + } + name = T1_GetCharName(font_num, (unsigned char)ch); + if (name) { + if (strcmp(name, ".notdef")) { + strncpy(name_buf, name, name_buf_size); + name_buf[name_buf_size-1] = '\0'; + return strlen(name) + 1; + } + else { + return 0; + } + } + else { + t1_push_error(); + return 0; + } +} + +static void +t1_push_error(void) { + switch (T1_errno) { + case 0: + i_push_error(0, "No error"); + break; + +#ifdef T1ERR_SCAN_FONT_FORMAT + case T1ERR_SCAN_FONT_FORMAT: + i_push_error(T1ERR_SCAN_FONT_FORMAT, "SCAN_FONT_FORMAT"); + break; +#endif + +#ifdef T1ERR_SCAN_FILE_OPEN_ERR + case T1ERR_SCAN_FILE_OPEN_ERR: + i_push_error(T1ERR_SCAN_FILE_OPEN_ERR, "SCAN_FILE_OPEN_ERR"); + break; +#endif + +#ifdef T1ERR_SCAN_OUT_OF_MEMORY + case T1ERR_SCAN_OUT_OF_MEMORY: + i_push_error(T1ERR_SCAN_OUT_OF_MEMORY, "SCAN_OUT_OF_MEMORY"); + break; +#endif + +#ifdef T1ERR_SCAN_ERROR + case T1ERR_SCAN_ERROR: + i_push_error(T1ERR_SCAN_ERROR, "SCAN_ERROR"); + break; +#endif + +#ifdef T1ERR_SCAN_FILE_EOF + case T1ERR_SCAN_FILE_EOF: + i_push_error(T1ERR_SCAN_FILE_EOF, "SCAN_FILE_EOF"); + break; +#endif + +#ifdef T1ERR_PATH_ERROR + case T1ERR_PATH_ERROR: + i_push_error(T1ERR_PATH_ERROR, "PATH_ERROR"); + break; +#endif + +#ifdef T1ERR_PARSE_ERROR + case T1ERR_PARSE_ERROR: + i_push_error(T1ERR_PARSE_ERROR, "PARSE_ERROR"); + break; +#endif + +#ifdef T1ERR_TYPE1_ABORT + case T1ERR_TYPE1_ABORT: + i_push_error(T1ERR_TYPE1_ABORT, "TYPE1_ABORT"); + break; +#endif + +#ifdef T1ERR_INVALID_FONTID + case T1ERR_INVALID_FONTID: + i_push_error(T1ERR_INVALID_FONTID, "INVALID_FONTID"); + break; +#endif + +#ifdef T1ERR_INVALID_PARAMETER + case T1ERR_INVALID_PARAMETER: + i_push_error(T1ERR_INVALID_PARAMETER, "INVALID_PARAMETER"); + break; +#endif + +#ifdef T1ERR_OP_NOT_PERMITTED + case T1ERR_OP_NOT_PERMITTED: + i_push_error(T1ERR_OP_NOT_PERMITTED, "OP_NOT_PERMITTED"); + break; +#endif + +#ifdef T1ERR_ALLOC_MEM + case T1ERR_ALLOC_MEM: + i_push_error(T1ERR_ALLOC_MEM, "ALLOC_MEM"); + break; +#endif + +#ifdef T1ERR_FILE_OPEN_ERR + case T1ERR_FILE_OPEN_ERR: + i_push_error(T1ERR_FILE_OPEN_ERR, "FILE_OPEN_ERR"); + break; +#endif + +#ifdef T1ERR_UNSPECIFIED + case T1ERR_UNSPECIFIED: + i_push_error(T1ERR_UNSPECIFIED, "UNSPECIFIED"); + break; +#endif + +#ifdef T1ERR_NO_AFM_DATA + case T1ERR_NO_AFM_DATA: + i_push_error(T1ERR_NO_AFM_DATA, "NO_AFM_DATA"); + break; +#endif + +#ifdef T1ERR_X11 + case T1ERR_X11: + i_push_error(T1ERR_X11, "X11"); + break; +#endif + +#ifdef T1ERR_COMPOSITE_CHAR + case T1ERR_COMPOSITE_CHAR: + i_push_error(T1ERR_COMPOSITE_CHAR, "COMPOSITE_CHAR"); + break; +#endif + + default: + i_push_errorf(T1_errno, "unknown error %d", (int)T1_errno); + } +} + +#endif /* HAVE_LIBT1 */ + +/* Truetype font support */ #ifdef HAVE_LIBTT +/* This is 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 + +#include +#define TT_CHC 5 + +#ifdef FTXPOST +#include +#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 ptsize; + int order; +}; + +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 */ @@ -396,13 +773,21 @@ static void i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int 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,int x_off, int y_off ); -static int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned char j ); -static void i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics, TT_Raster_Map *bit, TT_Raster_Map *small_bit, int x_off, int y_off, int smooth ); -static void i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit, TT_Raster_Map *small_bit, int cords[6], char* txt, int len, int smooth ); +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, + int x_off, int 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, int cords[6], + char const* txt, int len, int smooth, int utf8 ); static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, i_color *cl, int smooth ); static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth ); -static int i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char* txt, int len, int smooth ); -static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6] ); +static int +i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], + float points, char const* txt, int len, int smooth, int utf8 ); +static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6], int utf8 ); /* static globals needed */ @@ -426,7 +811,7 @@ Initializes the freetype font rendering engine */ undef_int -init_tt() { +i_init_tt() { TT_Error error; mm_log((1,"init_tt()\n")); error = TT_Init_FreeType( &engine ); @@ -434,6 +819,15 @@ init_tt() { mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error)); return(1); } + +#ifdef FTXPOST + error = TT_Init_Post_Extension( engine ); + if (error) { + mm_log((1, "Initialization of Post extension failed = 0x%x\n", error)); + return 1; + } +#endif + return(0); } @@ -458,31 +852,52 @@ i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) { int i,idx; TT_Error error; - mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n",handle,points,smooth)); + mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n", + handle,points,smooth)); if (smooth == -1) { /* Smooth doesn't matter for this search */ - for(i=0;iinstanceh[i].ptsize==points) { - mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i)); - return i; + for(i=0;iinstanceh[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;iinstanceh[i].ptsize==points && handle->instanceh[i].smooth==smooth) { - mm_log((1,"i_tt_get_instance: in cache returning %d\n",i)); - return i; + for(i=0;iinstanceh[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;idxinstanceh[idx].order)) break; /* find the lru item */ + for(idx=0;idxinstanceh[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 0x%X\n",USTRCT(handle->instanceh[idx].instance) )); + mm_log((1,"i_tt_get_instance: lru pointer 0x%X\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)); - TT_Done_Instance( handle->instanceh[idx].instance ); /* Free instance if needed */ + + /* 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 */ @@ -507,10 +922,13 @@ i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) { 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 */ + /* 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++) USTRCT(handle->instanceh[idx].glyphs[i])=NULL; + for(i=0;i<256;i++) { + handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR; + USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL; + } return idx; } @@ -538,29 +956,41 @@ i_tt_new(char *fontname) { /* allocate memory for the structure */ - handle=mymalloc( sizeof(TT_Fonthandle) ); + handle = mymalloc( sizeof(TT_Fonthandle) ); /* 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, 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, + 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 ) ) { + 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 */ @@ -571,6 +1001,10 @@ i_tt_new(char *fontname) { handle->instanceh[i].smooth=-1; } +#ifdef FTXPOST + handle->loaded_names = 0; +#endif + mm_log((1,"i_tt_new <- 0x%X\n",handle)); return handle; } @@ -733,17 +1167,27 @@ Function to see if a glyph exists and if so cache it (internal) static int -i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned char j) { /* FIXME: Check if unsigned char is enough */ +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 0x%X, inst %d, j %d (%c))\n",handle,inst,j,j)); - mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] )); + mm_log((1, "i_tt_get_glyph(handle 0x%X, inst %d, j %d (%c))\n", + handle,inst,j, ((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[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: %d 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; @@ -754,19 +1198,86 @@ i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned char j) { /* FIXME: Ch 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[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 0x%x.\n", error )); - if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[j], code, load_flags)) ) + 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 0x%x.\n", 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[j], &handle->instanceh[inst].gmetrics[j] ); - mm_log((1, "TT_Get_Glyph_Metrics: error 0x%x.\n", error )); + 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 0x%x.\n", 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 +*/ + +int +i_tt_has_chars(TT_Fonthandle *handle, char const *text, int len, int utf8, + char *out) { + int count = 0; + mm_log((1, "i_tt_has_chars(handle %p, text %p, len %d, utf8 %d)\n", + handle, text, 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) @@ -782,6 +1293,7 @@ pixmaps and glyphs void i_tt_destroy( TT_Fonthandle *handle) { TT_Close_Face( handle->face ); + myfree( handle ); /* FIXME: Should these be freed automatically by the library? @@ -862,28 +1374,44 @@ calls i_tt_render_glyph to render each glyph into the bit rastermap (internal) */ static -void -i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit, TT_Raster_Map *small_bit, int cords[6], char* txt, int len, int smooth ) { - unsigned char j; - int i; +int +i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit, + TT_Raster_Map *small_bit, int cords[6], + char const* txt, int len, int smooth, int utf8 ) { + unsigned long j; TT_F26Dot6 x,y; - mm_log((1,"i_tt_render_all_glyphs( handle 0x%X, inst %d, bit 0x%X, small_bit 0x%X, txt '%.*s', len %d, smooth %d)\n", - handle, inst, bit, small_bit, len, txt, len, smooth)); + mm_log((1,"i_tt_render_all_glyphs( handle 0x%X, inst %d, bit 0x%X, small_bit 0x%X, txt '%.*s', len %d, smooth %d, utf8 %d)\n", + handle, inst, bit, small_bit, len, txt, 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[1]; - - for ( i = 0; i < len; i++ ) { - j = txt[i]; - if ( !i_tt_get_glyph(handle,inst,j) ) continue; - i_tt_render_glyph( handle->instanceh[inst].glyphs[j], &handle->instanceh[inst].gmetrics[j], bit, small_bit, x, y, smooth ); - x += handle->instanceh[inst].gmetrics[j].advance / 64; + 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; } @@ -1000,7 +1528,7 @@ interface for generating single channel raster of text (internal) static int -i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char* txt, int len, int smooth ) { +i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char const* txt, int len, int smooth, int utf8 ) { int inst; int width, height; TT_Raster_Map small_bit; @@ -1012,10 +1540,12 @@ i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float p } /* calculate bounding box */ - i_tt_bbox_inst( handle, inst, txt, len, cords ); + if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 )) + return 0; + width = cords[2]-cords[0]; - height = cords[3]-cords[1]; + height = cords[5]-cords[4]; mm_log((1,"i_tt_rasterize: width=%d, height=%d\n",width, height )); @@ -1023,7 +1553,12 @@ i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float p i_tt_clear_raster_map( bit ); if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth ); - i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len, 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; + } /* ascent = ( handle->properties.horizontal->Ascender * handle->instanceh[inst].imetrics.y_ppem ) / handle->properties.header->Units_Per_EM; */ @@ -1039,7 +1574,7 @@ i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float p /* -=item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth) +=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 @@ -1056,15 +1591,16 @@ Interface to text rendering into a single channel in an image */ undef_int -i_tt_cp( TT_Fonthandle *handle, i_img *im, int xb, int yb, int channel, float points, char* txt, int len, int smooth ) { +i_tt_cp( TT_Fonthandle *handle, i_img *im, int xb, int yb, int channel, float points, char const* txt, int len, int smooth, int utf8 ) { - int cords[6]; + int cords[BOUNDING_BOX_COUNT]; int ascent, st_offset; TT_Raster_Map bit; - if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth ) ) return 0; + i_clear_error(); + if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0; - ascent=cords[3]; + ascent=cords[5]; st_offset=cords[0]; i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , yb-ascent, channel, smooth ); @@ -1075,7 +1611,7 @@ i_tt_cp( TT_Fonthandle *handle, i_img *im, int xb, int yb, int channel, float po /* -=item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth) +=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 @@ -1092,14 +1628,16 @@ Interface to text rendering in a single color onto an image */ undef_int -i_tt_text( TT_Fonthandle *handle, i_img *im, int xb, int yb, i_color *cl, float points, char* txt, int len, int smooth) { - int cords[6]; +i_tt_text( TT_Fonthandle *handle, i_img *im, int xb, int yb, i_color *cl, float points, char const* txt, int len, int smooth, int utf8) { + int cords[BOUNDING_BOX_COUNT]; int ascent, st_offset; TT_Raster_Map bit; + + i_clear_error(); - if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth ) ) return 0; + if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0; - ascent=cords[3]; + ascent=cords[5]; st_offset=cords[0]; i_tt_dump_raster_map2( im, &bit, xb+st_offset, yb-ascent, cl, smooth ); @@ -1110,7 +1648,7 @@ i_tt_text( TT_Fonthandle *handle, i_img *im, int xb, int yb, i_color *cl, float /* -=item i_tt_bbox_inst(handle, inst, txt, len, cords) +=item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8) Function to get texts bounding boxes given the instance of the font (internal) @@ -1125,38 +1663,58 @@ Function to get texts bounding boxes given the instance of the font (internal) static undef_int -i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6] ) { - int i, upm, ascent, descent, gascent, gdescent, width, casc, cdesc, first, start; - unsigned int j; +i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[BOUNDING_BOX_COUNT], int utf8 ) { + int i, upm, casc, cdesc, first; + + int start = 0; + int 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 0x%X,inst %d,txt '%.*s', len %d)\n",handle,inst,len,txt,len)); + + mm_log((1,"i_tt_box_inst(handle 0x%X,inst %d,txt '%.*s', len %d, utf8 %d)\n",handle,inst,len,txt,len, utf8)); upm = handle->properties.header->Units_Per_EM; - gascent = ( handle->properties.horizontal->Ascender * handle->instanceh[inst].imetrics.y_ppem ) / upm; - gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem ) / upm; + 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: glyph='%c' gascent=%d gdescent=%d\n", j, gascent, gdescent)); + mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent)); first=1; - for ( i = 0; i < len; ++i ) { - j = ustr[i]; + 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 + j; + TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j); width += gm->advance / 64; - casc = gm->bbox.yMax / 64; - cdesc = gm->bbox.yMin / 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", j, casc, cdesc)); + mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n", + ((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc)); if (first) { start = gm->bbox.xMin / 64; - ascent = gm->bbox.yMax / 64; - descent = gm->bbox.yMin / 64; + ascent = (gm->bbox.yMax+63) / 64; + descent = (gm->bbox.yMin-63) / 64; first = 0; } if (i == len-1) { @@ -1164,12 +1722,12 @@ i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int c character goes past the right of the advance width, as is common for italic fonts */ - int rightb = gm->advance - gm->bearingX + 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); */ - if (rightb < 0) - width -= rightb/64; + if (rightb > 0) + rightb = 0; } ascent = (ascent > casc ? ascent : casc ); @@ -1177,18 +1735,20 @@ i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int c } } - cords[0]=start; - cords[1]=gdescent; - cords[2]=width; - cords[3]=gascent; - cords[4]=descent; - cords[5]=ascent; - return 1; + cords[BBOX_NEG_WIDTH]=start; + cords[BBOX_GLOBAL_DESCENT]=gdescent; + cords[BBOX_POS_WIDTH]=width - rightb / 64; + cords[BBOX_GLOBAL_ASCENT]=gascent; + cords[BBOX_DESCENT]=descent; + cords[BBOX_ASCENT]=ascent; + cords[BBOX_ADVANCE_WIDTH] = width; + + return BBOX_ADVANCE_WIDTH + 1; } /* -=item i_tt_bbox(handle, points, txt, len, cords) +=item i_tt_bbox(handle, points, txt, len, cords, utf8) Interface to get a strings bounding box @@ -1202,19 +1762,183 @@ Interface to get a strings bounding box */ undef_int -i_tt_bbox( TT_Fonthandle *handle, float points,char *txt,int len,int cords[6]) { +i_tt_bbox( TT_Fonthandle *handle, float points,char *txt,int len,int cords[6], int utf8) { int inst; - - mm_log((1,"i_tt_box(handle 0x%X,points %f,txt '%.*s', len %d)\n",handle,points,len,txt,len)); + + i_clear_error(); + mm_log((1,"i_tt_box(handle 0x%X,points %f,txt '%.*s', len %d, utf8 %d)\n",handle,points,len,txt,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); + 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 + */ +int +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); + } + } +} + +int +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(rc, "error loading names (%d)", 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 +} #endif /* HAVE_LIBTT */ + + +/* +=back + +=head1 AUTHOR + +Arnar M. Hrafnkelsson + +=head1 SEE ALSO + +Imager(3) + +=cut +*/