c6fd2e2000-09-03Per Hedbor #if constant(has_Image_TTF) constant cvs_version = "$Id: ttf.pike,v 1.1 2000/09/03 02:33:01 per Exp $"; constant name = "TTF fonts"; constant doc = "True Type font loader."; constant scalable = 1; inherit FontHandler; static mapping ttf_font_names_cache; static string trimttfname( string n ) { n = lower_case(replace( n, "\t", " " )); return ((n/" ")*"")-"'"; } static string translate_ttf_style( string style ) { switch( lower_case( (style-"-")-" " ) ) { case "normal": case "regular": return "nn"; case "italic": return "ni"; case "oblique": return "ni"; case "bold": return "bn"; case "bolditalic":case "italicbold": return "bi"; case "black": return "Bn"; case "blackitalic":case "italicblack":return "Bi"; case "light": return "ln"; case "lightitalic":case "italiclight":return "li"; } if(search(lower_case(style), "oblique")) return "ni"; // for now. return "nn"; } static void build_font_names_cache( ) { mapping ttf_done = ([ ]); ttf_font_names_cache=([]); void traverse_font_dir( string dir ) { foreach(r_get_dir( dir )||({}), string fname) { string path=combine_path(dir+"/",fname); if(!ttf_done[path]++) { Stat a=file_stat(path); if(a && a[1]==-2) { traverse_font_dir( path ); continue; } // Here is a good place to impose the artificial restraint that // the file must match *.ttf Image.TTF ttf; if(catch(ttf = Image.TTF( combine_path(dir+"/",fname) ))) continue; if(ttf) { mapping n = ttf->names(); string f = lower_case(trimttfname(n->family)); if(!ttf_font_names_cache[f]) ttf_font_names_cache[f] = ([]); ttf_font_names_cache[f][ translate_ttf_style(n->style) ] = combine_path(dir+"/",fname); } } } }; map( roxen->query("font_dirs"), traverse_font_dir ); } class TTFWrapper { inherit Font; static int size, rsize; static object real; static object encoder; static function(string ...:Image.image) real_write; int height( ) { return rsize ? rsize : (rsize = text_extents("W")[1] ); } static string _sprintf() { return sprintf( "TTF(%O,%d)", real, size ); } static Image.image write_encoded(string ... what) { return real->write(@(encoder? Array.map(what, lambda(string s) { return encoder->clear()-> feed(s)->drain(); }):what)); } // FIXME: Handle x_spacing Image.Image write( string ... what ) { if( !sizeof( what ) ) return Image.Image( 1,height() ); // nbsp -> "" what = map( (array(string))what, replace, " ", "" ); // cannot write "" with Image.TTF. what = replace( what, "", " " ); array(Image.Image) res = map( what, real_write ); Image.Image rr = Image.Image( max(0,@res->xsize()), (int)abs(`+(0,@res->ysize())*y_spacing) ); float start; if( y_spacing < 0 ) start = (float)rr->ysize()-res[0]->ysize(); foreach( res, object r ) { if( j_right ) rr->paste( r, rr->xsize()-r->xsize(), (int)start ); else if( j_center ) rr->paste( r, (rr->xsize()-r->xsize())/2, (int)start ); else rr->paste( r, 0, (int)start ); start += r->ysize()*y_spacing; } return rr->scale(0.5); } array text_extents( string what ) { Image.Image o = real_write( what ); return ({ o->xsize()/2, o->ysize()/2 }); } static void create(object r, int s, string fn) { string encoding; real = r; size = s; real->set_height( (int)(size*64/34.5) ); // aproximate to pixels if(r_file_stat(fn+".properties")) parse_html(lopen(fn+".properties","r")->read(), ([]), (["encoding":lambda(string tag, mapping m, string enc) { encoding = enc; }])); if(encoding) encoder = Locale.Charset.encoder(encoding, ""); real_write = (encoder? write_encoded : real->write); } } array available_fonts() { if( !ttf_font_names_cache ) build_font_names_cache( ); return indices( ttf_font_names_cache ); } mapping font_information( string font ) { if( !has_font( font, 0 ) ) return 0; mapping res = ([ "name":font, "ttf":"yes", // compatibility "format":"ttf", ]); Image.TTF f; if( font[0] == '/' ) f = Image.TTF( font ); else f = Image.TTF( (font=ttf_font_names_cache[ font ]) ); res->path = font; res |= f->names(); return res; } array(string) has_font( string name, int size ) { if( !ttf_font_names_cache ) build_font_names_cache( ); if( ttf_font_names_cache[ name ] ) return indices(ttf_font_names_cache[ name ]); } void create() { roxen.getvar( "font_dirs" ) ->add_changed_callback( lambda(Variable.Variable v){ ttf_font_names_cache=0; } ); } Font open(string f, int size, int bold, int italic ) { string tmp; int|string style = font_style( f, size, bold, italic ); object fo; if( style == -1 ) // exact file { if( fo = Image.TTF( name ) ) return TTFWrapper( fo, size, f ); return 0; } if(ttf_font_names_cache[ lower_case(f) ]) { f = lower_case(f); if( tmp = ttf_font_names_cache[ f ][ style ] ) { fo = Image.TTF( tmp ); if( fo ) return TTFWrapper( fo(), size, tmp ); } if( fo = Image.TTF( roxen_path(f = values(ttf_font_names_cache[ f ])[0]))) return TTFWrapper( fo(), size, f ); } return 0; } #endif