8b6da62000-09-03Martin Nilsson // This file is part of Roxen WebServer.
b655bf2004-06-30Martin Stjernholm // Copyright © 1996 - 2004, Roxen IS.
8b6da62000-09-03Martin Nilsson 
2078852000-12-11Per Hedbor #if !constant(Image.FreeType.Face)
c6fd2e2000-09-03Per Hedbor #if constant(has_Image_TTF)
8b6da62000-09-03Martin Nilsson #include <config.h>
fc40392008-08-15Martin Stjernholm constant cvs_version = "$Id: ttf.pike,v 1.18 2008/08/15 12:33:54 mast Exp $";
c6fd2e2000-09-03Per Hedbor  constant name = "TTF fonts";
0bd4d92000-09-04Per Hedbor constant doc = "True Type font loader. Uses freetype to render text.";
c6fd2e2000-09-03Per Hedbor constant scalable = 1; inherit FontHandler;
fc40392008-08-15Martin Stjernholm protected mapping ttf_font_names_cache;
c6fd2e2000-09-03Per Hedbor 
fc40392008-08-15Martin Stjernholm protected string trimttfname( string n )
c6fd2e2000-09-03Per Hedbor { n = lower_case(replace( n, "\t", " " )); return ((n/" ")*"")-"'"; }
fc40392008-08-15Martin Stjernholm protected string translate_ttf_style( string style )
c6fd2e2000-09-03Per Hedbor {
326b922003-05-05Jonas Wallden  // Check for weight. Default is "n" for normal/regular/roman. style = lower_case((style - "-") - " "); string weight = "n"; if (has_value(style, "bold")) weight = "b"; else if (has_value(style, "black")) weight = "B"; else if (has_value(style, "light")) weight = "l"; // Check for slant. Default is "n" for regular. string slant = "n"; if (has_value(style, "italic") || has_value(style, "oblique")) slant = "i"; // Combine to full style return weight + slant;
c6fd2e2000-09-03Per Hedbor }
fc40392008-08-15Martin Stjernholm protected void build_font_names_cache( )
c6fd2e2000-09-03Per Hedbor { mapping ttf_done = ([ ]);
2963142001-11-14Henrik Grubbström (Grubba)  mapping new_ttf_font_names_cache=([]);
c6fd2e2000-09-03Per Hedbor  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) {
745fce2000-09-04Per Hedbor  if( !file_stat( path+"/fontname" ) ) // no, we do not want to try this dir. :-) traverse_font_dir( path );
c6fd2e2000-09-03Per Hedbor  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));
2963142001-11-14Henrik Grubbström (Grubba)  if(!new_ttf_font_names_cache[f]) new_ttf_font_names_cache[f] = ([]); new_ttf_font_names_cache[f][ translate_ttf_style(n->style) ] = combine_path(dir+"/",fname);
c6fd2e2000-09-03Per Hedbor  } } } }; map( roxen->query("font_dirs"), traverse_font_dir );
2963142001-11-14Henrik Grubbström (Grubba)  ttf_font_names_cache = new_ttf_font_names_cache;
c6fd2e2000-09-03Per Hedbor } class TTFWrapper { inherit Font;
fc40392008-08-15Martin Stjernholm  protected int size, rsize; protected object real; protected object encoder; protected function(string ...:Image.image) real_write; protected int fake_bold, fake_italic;
c6fd2e2000-09-03Per Hedbor  int height( ) { return rsize ? rsize : (rsize = text_extents("W")[1] ); }
fc40392008-08-15Martin Stjernholm  protected string _sprintf()
c6fd2e2000-09-03Per Hedbor  { return sprintf( "TTF(%O,%d)", real, size ); }
fc40392008-08-15Martin Stjernholm  protected Image.image write_encoded(string ... what)
c6fd2e2000-09-03Per Hedbor  { 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() );
3f123d2003-11-05Jonas Wallden  int oversample = roxen->query("font_oversampling");
c6fd2e2000-09-03Per Hedbor  // nbsp -> "" what = map( (array(string))what, replace, " ", "" ); // cannot write "" with Image.TTF. what = replace( what, "", " " ); array(Image.Image) res = map( what, real_write );
3f123d2003-11-05Jonas Wallden  int image_width = max(0, @res->xsize()); int image_height = (int)abs(`+(0, 0, @res[..sizeof(res) - 2]->ysize()) * y_spacing) + res[-1]->ysize(); int y_add = 0; if (oversample) { // Make sure image dimensions are a multiple of 2. If height is odd // we'll offset the text baseline one pixel to get the extra line at // the top of the image. image_width = (image_width + 1) & 0xFFFFFFFE; y_add = (image_height & 1); image_height = (image_height + 1) & 0xFFFFFFFE; } Image.Image rr = Image.Image(image_width, image_height);
c6fd2e2000-09-03Per Hedbor  float start; if( y_spacing < 0 ) start = (float)rr->ysize()-res[0]->ysize();
3f123d2003-11-05Jonas Wallden  start += (float) y_add;
c6fd2e2000-09-03Per Hedbor  foreach( res, object r ) { if( j_right )
4243242000-10-21Per Hedbor  rr->paste_alpha_color( r, 255,255,255, rr->xsize()-r->xsize(), (int)start );
c6fd2e2000-09-03Per Hedbor  else if( j_center )
4243242000-10-21Per Hedbor  rr->paste_alpha_color( r, 255,255,255,(rr->xsize()-r->xsize())/2, (int)start );
c6fd2e2000-09-03Per Hedbor  else
4243242000-10-21Per Hedbor  rr->paste_alpha_color( r, 255,255,255, 0, (int)start );
c6fd2e2000-09-03Per Hedbor  start += r->ysize()*y_spacing; }
b5a1a72001-08-21Per Hedbor  if( fake_bold ) { object r2 = Image.Image( rr->xsize()+2, rr->ysize() ); object r3 = rr*0.3; for( int i = 0; i<2; i++ ) for( int j = 0; j<2; j++ ) r2->paste_alpha_color( r3, 255, 255, 255, i, j ); rr = r2->paste_alpha_color( rr, 255,255,255, 1,1 ); }
7e06712001-09-27Per Hedbor  rr->setcolor( 0,0,0 );
b5a1a72001-08-21Per Hedbor  if( fake_italic )
7e06712001-09-27Per Hedbor  rr = rr->skewx( -(rr->ysize()/3) );
3f123d2003-11-05Jonas Wallden  if (oversample)
449d242002-08-16Mattias Andersson  return rr->scale(0.5); else return rr;
c6fd2e2000-09-03Per Hedbor  } array text_extents( string what ) {
745fce2000-09-04Per Hedbor  Image.Image o = write( what ); return ({ o->xsize(), o->ysize() });
c6fd2e2000-09-03Per Hedbor  }
b5a1a72001-08-21Per Hedbor  void create(object r, int s, string fn, int fb, int fi)
c6fd2e2000-09-03Per Hedbor  { string encoding;
b5a1a72001-08-21Per Hedbor  fake_bold = fb; fake_italic = fi;
c6fd2e2000-09-03Per Hedbor  real = r; size = s;
449d242002-08-16Mattias Andersson  if( roxen->query("font_oversampling") ) real->set_height( (int)(size*64/34.5) ); // aproximate to pixels else real->set_height( (int)(size*32/34.5) ); // aproximate to pixels
c6fd2e2000-09-03Per Hedbor  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); } }
e892262000-09-04Per Hedbor #ifdef THREADS Thread.Mutex lock = Thread.Mutex(); #endif
2963142001-11-14Henrik Grubbström (Grubba) array available_fonts(int(0..1)|void force_reload)
c6fd2e2000-09-03Per Hedbor {
e892262000-09-04Per Hedbor #ifdef THREADS object key = lock->lock(); #endif
2963142001-11-14Henrik Grubbström (Grubba)  if( !ttf_font_names_cache || force_reload ) build_font_names_cache( );
c6fd2e2000-09-03Per Hedbor  return indices( ttf_font_names_cache ); }
8b6da62000-09-03Martin Nilsson array(mapping) font_information( string font )
c6fd2e2000-09-03Per Hedbor { if( !has_font( font, 0 ) )
8b6da62000-09-03Martin Nilsson  return ({});
c6fd2e2000-09-03Per Hedbor  mapping res = ([ "name":font, "format":"ttf", ]); Image.TTF f; if( font[0] == '/' ) f = Image.TTF( font ); else
8b6da62000-09-03Martin Nilsson  f = Image.TTF( (font=values(ttf_font_names_cache[ font ])[0]) );
c6fd2e2000-09-03Per Hedbor  res->path = font; res |= f->names();
8b6da62000-09-03Martin Nilsson  return ({ res });
c6fd2e2000-09-03Per Hedbor }
7ff1c02001-11-15Anders Johansson array(string) has_font( string name, int size, int(0..1)|void force )
c6fd2e2000-09-03Per Hedbor {
e892262000-09-04Per Hedbor #ifdef THREADS object key = lock->lock(); #endif
c6fd2e2000-09-03Per Hedbor  if( !ttf_font_names_cache ) build_font_names_cache( ); if( ttf_font_names_cache[ name ] ) return indices(ttf_font_names_cache[ name ]);
2963142001-11-14Henrik Grubbström (Grubba)  if (force) { build_font_names_cache(); if( ttf_font_names_cache[ name ] ) return indices(ttf_font_names_cache[ name ]); }
c6fd2e2000-09-03Per Hedbor } 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 ) )
b5a1a72001-08-21Per Hedbor  return TTFWrapper( fo, size, f,0,0 );
c6fd2e2000-09-03Per Hedbor  return 0; }
2963142001-11-14Henrik Grubbström (Grubba)  if (!ttf_font_names_cache) { build_font_names_cache(); }
c6fd2e2000-09-03Per Hedbor  if(ttf_font_names_cache[ lower_case(f) ]) { f = lower_case(f); if( tmp = ttf_font_names_cache[ f ][ style ] ) { fo = Image.TTF( tmp );
b5a1a72001-08-21Per Hedbor  if( fo ) return TTFWrapper( fo(), size, tmp,0,0 );
c6fd2e2000-09-03Per Hedbor  } if( fo = Image.TTF( roxen_path(f = values(ttf_font_names_cache[ f ])[0])))
b5a1a72001-08-21Per Hedbor  return TTFWrapper( fo(), size, f, bold, italic );
c6fd2e2000-09-03Per Hedbor  } return 0; }
745fce2000-09-04Per Hedbor  void create() { roxen.getvar( "font_dirs" ) ->add_changed_callback( lambda(Variable.Variable v){ ttf_font_names_cache=0; } ); }
c6fd2e2000-09-03Per Hedbor #endif
2078852000-12-11Per Hedbor #endif