X-Git-Url: http://git.imager.perl.org/imager.git/blobdiff_plain/a743c0a625a41e31c47e8bf52b88fc8949b879dd..1c988d82e6c2d6ea87693734b9adce534e5393cb:/font.c diff --git a/font.c b/font.c index 2119aac0..706f06d6 100644 --- a/font.c +++ b/font.c @@ -1,4 +1,5 @@ -#include "image.h" +#include "imager.h" +#include "imrender.h" #include #include @@ -7,9 +8,9 @@ #include #include - - - +#ifdef HAVE_LIBT1 +#include +#endif /* @@ -29,7 +30,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 +44,12 @@ more for Imager. Some of these functions are internal. -=over 4 +=over =cut */ - - - - - - - - /* =item i_init_fonts() @@ -66,17 +59,14 @@ 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(); + if (i_init_t1(t1log)) + return 0; #endif -#ifdef HAVE_LIBTT - init_tt(); -#endif - return(1); /* FIXME: Always true - check the return values of the init_t1 and init_tt functions */ } @@ -85,10 +75,16 @@ 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); +static int t1_active_fonts = 0; +static int t1_initialized = 0; /* -=item i_init_t1() +=item i_init_t1(t1log) Initializes the t1lib font rendering engine. @@ -96,14 +92,34 @@ 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)){ + + i_clear_error(); + + if (t1_active_fonts) { + mm_log((1, "Cannot re-initialize T1 - active fonts\n")); + i_push_error(0, "Cannot re-initialize T1 - active fonts"); + return 1; + } + + if (t1_initialized) { + T1_CloseLib(); + } + + if (t1log) + init_flags |= LOGFILE; + if ((T1_InitLib(init_flags) == NULL)){ mm_log((1,"Initialization of t1lib failed\n")); + i_push_error(0, "T1_InitLib failed"); return(1); } T1_SetLogLevel(T1LOG_DEBUG); i_t1_set_aa(1); /* Default Antialias value */ + + ++t1_initialized; + return(0); } @@ -119,8 +135,9 @@ Shuts the t1lib font rendering engine down. */ void -i_close_t1() { +i_close_t1(void) { T1_CloseLib(); + t1_initialized = 0; } @@ -138,6 +155,12 @@ Loads the fonts with the given filenames, returns its font id int i_t1_new(char *pfb,char *afm) { int font_id; + + i_clear_error(); + + if (!t1_initialized && i_init_t1(0)) + return -1; + mm_log((1,"i_t1_new(pfb %s,afm %s)\n",pfb,(afm?afm:"NULL"))); font_id = T1_AddFont(pfb); if (font_id<0) { @@ -149,6 +172,9 @@ 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)); } + + ++t1_active_fonts; + return font_id; } @@ -165,6 +191,9 @@ Frees resources for a t1 font with given font id. int i_t1_destroy(int font_id) { mm_log((1,"i_t1_destroy(font_id %d)\n",font_id)); + + --t1_active_fonts; + return T1_DeleteFont(font_id); } @@ -225,16 +254,27 @@ 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; mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent)); mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing)); @@ -260,6 +300,17 @@ i_t1_cp(i_img *im,int xb,int yb,int channel,int fontnum,float points,char* str,i return 1; } +static void +t1_fix_bbox(BBox *bbox, const char *str, int len, int advance, + int space_position) { + /* never called with len == 0 */ + if (str[0] == space_position && bbox->llx > 0) + bbox->llx = 0; + if (str[len-1] == space_position && bbox->urx < advance) + bbox->urx = advance; + if (bbox->lly > bbox->ury) + bbox->lly = bbox->ury = 0; +} /* =item i_t1_bbox(handle, fontnum, points, str, len, cords) @@ -276,14 +327,38 @@ 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,const 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; + int space_position = T1_GetEncodingIndex(fontnum, "space"); 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 (len == 0) { + /* len == 0 has special meaning to T1lib, but it means there's + nothing to draw, so return that */ + bbox.llx = bbox.lly = bbox.urx = bbox.ury = 0; + advance = 0; + } + else { + if (utf8) { + int worklen; + char *work = t1_from_utf8(str, len, &worklen); + advance = T1_GetStringWidth(fontnum, work, worklen, 0, mod_flags); + bbox = T1_GetStringBBox(fontnum,work,worklen,0,mod_flags); + t1_fix_bbox(&bbox, work, worklen, advance, space_position); + myfree(work); + } + else { + advance = T1_GetStringWidth(fontnum, (char *)str, len, 0, mod_flags); + bbox = T1_GetStringBBox(fontnum,(char *)str,len,0,mod_flags); + t1_fix_bbox(&bbox, str, len, advance, space_position); + } + } gbbox = T1_GetFontBBox(fontnum); mm_log((1,"bbox: (%d,%d,%d,%d)\n", @@ -295,14 +370,20 @@ 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[BBOX_GLOBAL_DESCENT]=((float)gbbox.lly*points)/1000; + cords[BBOX_GLOBAL_ASCENT]=((float)gbbox.ury*points)/1000; + + cords[BBOX_DESCENT]=((float)bbox.lly*points)/1000; + cords[BBOX_ASCENT]=((float)bbox.ury*points)/1000; - cords[1]=((float)gbbox.lly*points)/1000; - cords[3]=((float)gbbox.ury*points)/1000; + cords[BBOX_ADVANCE_WIDTH] = ((float)advance * points)/1000; + cords[BBOX_RIGHT_BEARING] = + cords[BBOX_ADVANCE_WIDTH] - cords[BBOX_POS_WIDTH]; - cords[4]=((float)bbox.lly*points)/1000; - cords[5]=((float)bbox.ury*points)/1000; + return BBOX_RIGHT_BEARING+1; } @@ -325,15 +406,26 @@ 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,const i_color *cl,int fontnum,float points,const 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 xsize,ysize,y; + int mod_flags = t1_get_flags(flags); + i_render r; 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 { + /* T1_AASetString() accepts a char * not a const char */ + glyph=T1_AASetString( fontnum, (char *)str, len, 0, mod_flags, points, NULL); + } + if (glyph == NULL) + return 0; mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent)); mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing)); @@ -346,39 +438,393 @@ i_t1_text(i_img *im,int xb,int yb,i_color *cl,int fontnum,float points,char* str mm_log((1,"width: %d height: %d\n",xsize,ysize)); if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; } - - for(y=0;ybits[y*xsize+x]; - i=255-c; - i_gpix(im,x+xb,y+yb,&val); - for(ch=0;chchannels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255; - i_ppix(im,x+xb,y+yb,&val); + + i_render_init(&r, im, xsize); + for(y=0;ybits+y*xsize, cl); } + i_render_done(&r); + 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) { + /* at this point len is from a perl SV, so can't approach MAXINT */ + char *out = mymalloc(len+1); /* checked 5Nov05 tonyc */ + 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 +/* 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 +#define TT_CHC 5 + +#ifdef FTXPOST +#include +#endif + +#ifdef FTXERR18 +#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 */ #define USTRCT(x) ((x).z) #define TT_VALID( handle ) ( ( handle ).z != NULL ) +static void i_tt_push_error(TT_Error rc); /* Prototypes */ @@ -387,17 +833,26 @@ 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 void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, i_color *cl, 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, const 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 */ +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 */ @@ -417,14 +872,42 @@ Initializes the freetype font rendering engine */ undef_int -init_tt() { +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",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", 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", 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); } @@ -449,31 +932,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 */ @@ -498,10 +1002,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; } @@ -519,39 +1026,59 @@ the font handle's cache */ TT_Fonthandle* -i_tt_new(char *fontname) { +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) ); + 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, 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 )); + } + 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 ) ) { + 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 */ @@ -562,6 +1089,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; } @@ -602,11 +1133,17 @@ i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth ) { 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 %d)\n", bit->width, bit->cols, bit->rows, bit->size )); - bit->bitmap = (void *) mymalloc( bit->size ); - if ( !bit->bitmap ) m_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",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 ); } @@ -664,7 +1201,7 @@ void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off ) { int x, y; int x1, x2, y1, y2; - char *s, *d; + unsigned char *s, *d; x1 = x_off < 0 ? -x_off : 0; y1 = y_off < 0 ? -y_off : 0; @@ -680,13 +1217,35 @@ i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off ) { /* do the real work now */ for ( y = y1; y < y2; ++y ) { - s = ( (char*)src->bitmap ) + y * src->cols + x1; - d = ( (char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off; + 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 ) *d++ |= *s++; + 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) @@ -702,17 +1261,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; @@ -723,19 +1292,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) @@ -751,6 +1387,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? @@ -801,7 +1438,7 @@ i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics, TT_Raster_Map *bi 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; @@ -831,28 +1468,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]; + 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]; - 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; + 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; } @@ -876,32 +1529,47 @@ Function to dump a raster onto an image in color used by i_tt_text() (internal). static void -i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, i_color *cl, int smooth ) { - char *bmap; - i_color val; - int c, i, ch, x, y; +i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, const i_color *cl, int smooth ) { + unsigned char *bmap; + int x, y; mm_log((1,"i_tt_dump_raster_map2(im 0x%x, bit 0x%X, xb %d, yb %d, cl 0x%X)\n",im,bit,xb,yb,cl)); - bmap = (char *)bit->bitmap; + bmap = bit->bitmap; if ( smooth ) { - for(y=0;yrows;y++) for(x=0;xwidth;x++) { - c=(255*bmap[y*(bit->cols)+x])/4; - i=255-c; - i_gpix(im,x+xb,y+yb,&val); - for(ch=0;chchannels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255; - i_ppix(im,x+xb,y+yb,&val); + i_render r; + i_render_init(&r, im, bit->cols); + for(y=0;yrows;y++) { +#if 0 + for(x=0;xwidth;x++) { + c = (unsigned char)bmap[y*(bit->cols)+x]; + i=255-c; + i_gpix(im,x+xb,y+yb,&val); + for(ch=0;chchannels;ch++) + val.channel[ch] = (c*cl->channel[ch]+i*val.channel[ch])/255; + i_ppix(im,x+xb,y+yb,&val); + } +#else + i_render_color(&r, xb, yb+y, bit->cols, bmap + y*bit->cols, cl); +#endif } - + i_render_done(&r); } else { - - for(y=0;yrows;y++) for(x=0;xwidth;x++) { - c=( bmap[y*bit->cols+x/8] & (128>>(x%8)) ) ? 255 : 0; - i=255-c; - i_gpix(im,x+xb,y+yb,&val); - for(ch=0;chchannels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255; - i_ppix(im,x+xb,y+yb,&val); + for(y=0;yrows;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; + } + } } } @@ -925,29 +1593,40 @@ Function to dump a raster onto a single channel image in color (internal) static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth ) { - char *bmap; + unsigned char *bmap; i_color val; int c,x,y; + int old_mask = im->ch_mask; + im->ch_mask = 1 << channel; mm_log((1,"i_tt_dump_raster_channel(im 0x%x, bit 0x%X, xb %d, yb %d, channel %d)\n",im,bit,xb,yb,channel)); - bmap = (char *)bit->bitmap; + bmap = bit->bitmap; if ( smooth ) { for(y=0;yrows;y++) for(x=0;xwidth;x++) { - c=(255*bmap[y*(bit->cols)+x])/4; - i_gpix(im,x+xb,y+yb,&val); - val.channel[channel]=c; + c = bmap[y*(bit->cols)+x]; + val.channel[channel] = c; i_ppix(im,x+xb,y+yb,&val); } } else { - for(y=0;yrows;y++) for(x=0;xwidth;x++) { - c=( bmap[y*bit->cols+x/8] & (128>>(x%8)) ) ? 255 : 0; - i_gpix(im,x+xb,y+yb,&val); - val.channel[channel]=c; - i_ppix(im,x+xb,y+yb,&val); + for(y=0;yrows;y++) { + unsigned mask = 0x80; + unsigned char *p = bmap + y * bit->cols; + + for(x=0;xwidth;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; } @@ -969,7 +1648,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; @@ -981,10 +1660,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 )); @@ -992,10 +1673,13 @@ 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; */ - if ( smooth ) i_tt_done_raster_map( &small_bit ); return 1; } @@ -1008,7 +1692,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 @@ -1025,18 +1709,20 @@ 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 align ) { - int cords[6]; - int ascent, st_offset; + int cords[BOUNDING_BOX_COUNT]; + int ascent, st_offset, y; 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]; - st_offset=cords[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 , yb-ascent, channel, smooth ); + i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , y, channel, smooth ); i_tt_done_raster_map( &bit ); return 1; @@ -1044,7 +1730,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 @@ -1061,17 +1747,20 @@ 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]; - int ascent, st_offset; +i_tt_text( TT_Fonthandle *handle, i_img *im, int xb, int yb, const i_color *cl, float points, char const* txt, int len, int smooth, int utf8, int align) { + int cords[BOUNDING_BOX_COUNT]; + int ascent, st_offset, y; 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]; - st_offset=cords[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, yb-ascent, cl, smooth ); + i_tt_dump_raster_map2( im, &bit, xb+st_offset, y, cl, smooth ); i_tt_done_raster_map( &bit ); return 1; @@ -1079,7 +1768,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) @@ -1094,57 +1783,93 @@ 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 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) ) { - width += handle->instanceh[inst].gmetrics[j].advance / 64; - casc = handle->instanceh[inst].gmetrics[j].bbox.yMax / 64; - cdesc = handle->instanceh[inst].gmetrics[j].bbox.yMin / 64; + 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", 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 = handle->instanceh[inst].gmetrics[j].bbox.xMin / 64; - ascent = handle->instanceh[inst].gmetrics[j].bbox.yMax / 64; - descent = handle->instanceh[inst].gmetrics[j].bbox.yMin / 64; + 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[0]=start; - cords[1]=gdescent; - cords[2]=width+start; - 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; + 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) +=item i_tt_bbox(handle, points, txt, len, cords, utf8) Interface to get a strings bounding box @@ -1158,19 +1883,202 @@ 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,const 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); + } + } + fflush(stdout); +} + +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(handle->load_cond, "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 +} + +/* +=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 + +=head1 SEE ALSO + +Imager(3) + +=cut +*/