Roxen.git
/
server
/
font_handlers
/
ttf.pike
version
»
Context lines:
10
20
40
80
file
none
3
Roxen.git/server/font_handlers/ttf.pike:1:
+
#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
Newline at end of file added.