string cvs_version="$Id: graphic_text.pike,v 1.5 1996/12/06 15:57:27 per Exp $"; |
#include <module.h> |
inherit "module"; |
inherit "roxenlib"; |
|
|
array register_module() |
{ |
return ({ MODULE_LOCATION | MODULE_PARSER, |
"Graphics text", |
"Makes a few new tags:<p>" |
"<b><gh1></b> to <b><gh6>:</b> Headers<br>\n" |
"<b><gh>:</b> Header<br>\n" |
"<b><gtext>:</b> Graphical text<br>\n" |
"<b><anfang>:</b> Make the first character to a graphical one. Not all that usefull, really.<br>\n" |
"\n" |
"Common arguments:\n <pre>" |
" bg=#rrggbb Use this background, default taken from the\n" |
" <body> tag, if any\n" |
" fg=#rrggbb Use this foreground, default taken from the\n" |
" <body> tag, if any\n" |
" font=fnt Use this font. The fonts can be found in the\n" |
" directory specified in the configuration\n" |
" interface.\n" |
" If no font is specified, the one from the\n" |
" define 'font' is used, or if there is no\n" |
" define, the default font will be used.\n" |
" scale=float Scale to this font, mostly useful in the <gtext>\n" |
" tag, will not work at all in the <gh[number]>\n" |
" tags.\n" |
" 2 3 4 5 6 Short for scale=1.0/([number]*0.6)\n" |
" notrans Do _not_ make the background color transparent\n" |
" split Make each word into a separate gif image\n" |
" href=url Link the image to the specified URL\n" |
" The 'link' color of the document will be\n" |
" used as the default foreground of the text\n" |
" quant=cols Use this number of colors\n" |
" magic[=message] Modifier to href, more flashy links\n" |
" Does <b>not</b> work with 'split'\n" |
" fs Use floyd-steinberg dithering\n" |
" border=int,#col Draw an border (width is the first argument\n" |
" in the specified color\n" |
" spacing=int Add this amount of spacing around the text\n" |
" bevel=int Draw a bevel box (width is the argument)\n" |
" textbox=al,#col Use 'al' as opaque value to draw a box below\n" |
" the text with the specified color.\n" |
" shadow=int,dist Draw a drop-shadow (variable distance/intensity)\n" |
" fuzz=#col The 'shine' effect used in the 'magic'\n" |
" highlightning\n" |
" opaque=0-100% Draw with more or less opaque text (100%\n" |
" is default)\n" |
" rotate=ang(deg.)Rotate the finished image\n" |
" background=file Use the specifed file as a background\n" |
" texture=file Use the specified file as text texture\n" |
"</pre>\n", |
0, |
1, |
}); |
} |
|
|
array (string) list_fonts() |
{ |
array fnts; |
catch(fnts = get_dir("fonts/32/") - ({".",".."})); |
if(!fnts) |
{ |
report_error("Failed to find any fonts in 'fonts/32/'. No default font.\n"); |
return ({}); |
} |
return fnts; |
} |
|
void create() |
{ |
defvar("location", "/gtext/", "Mountpoint", TYPE_LOCATION, |
"The URL-prefix for the anfang characters."); |
|
defvar("cols", 16, "Default number of colors per image", TYPE_INT_LIST, |
"The default number of colors to use. 16 seems to be enough. " |
"The size of the image depends on the number of colors", |
({ 1,2,3,4,5,6,7,8,10,16,32,64,128,256 })); |
|
defvar("default_size", 32, "Default font size", TYPE_INT_LIST, |
"The default size for the font. This is used for the 'base' size, " |
"and can be scaled up or down in the tags.", |
({ 16, 32, 64 })); |
|
defvar("default_font", "urw_itc_avant_garde-demi-r", "Default font", |
TYPE_STRING_LIST, |
"The default font. The 'font dir' will be prepended to the path", |
list_fonts()); |
} |
|
string query_location() { return query("location"); } |
|
mapping (string:object) fonts = ([]); |
|
object(Font) load_font(string name) |
{ |
object fnt = Font(); |
if(!name) name = QUERY(default_size)+"/"+QUERY(default_font); |
else if(sscanf(name, "%*s/%*s") != 2) name=QUERY(default_size)+"/"+name; |
|
name = "fonts/" + name; |
|
if(fnt->load( name )) return fnt; |
perror("Failed to load the font "+name+", using the default font.\n"); |
if(!fnt->load("fonts/"+QUERY(default_size) +"/"+ QUERY(default_font))) |
error("Failed to load the default font\n"); |
return fnt; |
} |
|
static private mapping (int:mapping(string:mixed)) cached_args = ([ ]); |
|
#define MAX(a,b) ((a)<(b)?(b):(a)) |
|
static private mapping (int:array(array(int))) matrixes = ([]); |
array (array(int)) make_matrix(int size) |
{ |
if(matrixes[size]) return matrixes[size]; |
array res; |
int i; |
int j; |
res = map_array(allocate(size), lambda(int s, int size){ |
return allocate(size); }, size); |
|
for(i=0; i<size; i++) |
for(j=0; j<size; j++) |
res[i][j] = (int)MAX((float)size/2.0-sqrt((size/2-i)*(size/2-i) + (size/2-j)*(size/2-j)),0); |
return matrixes[size] = res; |
} |
|
|
object (Image) load_image(string f) |
{ |
object file = File(); |
string data; |
object img = Image(); |
perror("Loading "+f+"\n"); |
if(!file->open(f,"r")) |
{ |
perror("Failed to open file ("+f+").\n"); |
return 0; |
} |
if(!(data=file->read(0x7fffffff))) return 0; |
if(img->frompnm(data)) return img; |
if(img->fromgif(data)) return img; |
perror("Failed to parse file.\n"); |
return 0; |
} |
|
object (Image) blur(object (Image) img, int amnt) |
{ |
for(int i=0; i<amnt; i++) |
img = img->apply_matrix( make_matrix((int)sqrt(img->ysize()+10))); |
return img; |
} |
|
constant white = ({ 255,255,255 }); |
constant lgrey = ({ 200,200,200 }); |
constant grey = ({ 128,128,128 }); |
constant black = ({ 0,0,0 }); |
|
constant wwwb = ({ lgrey,lgrey,grey,black }); |
object (Image) bevel(object (Image) in, int width) |
{ |
int h=in->ysize(); |
int w=in->xsize(); |
|
in->paste_alpha(Image(width,h-width*2,@white), 160, 0, width); |
in->paste_alpha(Image(width,h-width*2,@black), 128, in->xsize()-width, width); |
in->paste_alpha(Image(w-width,width,@white), 160, 0, 0); |
in->paste_alpha(Image(w-width,width,@black), 128, width, in->ysize()-width); |
|
object corner = Image(width+1,width+1); |
for(int i=-1; i<=width; i++) corner->line(i,width-i,i,-1, 200,200,200); |
in->paste_alpha(corner, 128, in->xsize()-width,0); |
in->paste_alpha(corner, 128, -1, in->ysize()-width); |
corner=0; |
|
return in; |
} |
|
|
object (Image) make_text_image(mapping args, object font, string text) |
{ |
object (Image) text_alpha=font->write(@(text/"\n")); |
int xoffset=0, yoffset=0; |
|
if(int op=((((int)args->opaque)*255)/100)) |
text_alpha=text_alpha->color(op,op,op); |
|
int txsize=text_alpha->xsize(); |
int tysize=text_alpha->ysize(); |
|
int xsize=txsize; |
int ysize=tysize; |
|
if(args->bevel) |
{ |
xoffset += (int)args->bevel; |
yoffset += (int)args->bevel; |
xsize += ((int)args->bevel)*2; |
ysize += ((int)args->bevel)*2; |
} |
|
if(args->spacing) |
{ |
xoffset += (int)args->spacing; |
yoffset += (int)args->spacing; |
xsize += ((int)args->spacing)*2; |
ysize += ((int)args->spacing)*2; |
} |
|
if(args->yspacing) |
{ |
yoffset += (int)args->yspacing; |
ysize += ((int)args->yspacing)*2; |
} |
|
if(args->shadow) |
{ |
xsize+=((int)(args->shadow/",")[-1])+2; |
ysize+=((int)(args->shadow/",")[-1])+2; |
} |
|
if(args->xspacing) |
{ |
xoffset += (int)args->xspacing; |
xsize += ((int)args->xspacing)*2; |
} |
|
if(args->border) |
{ |
xoffset += (int)args->border; |
yoffset += (int)args->border; |
xsize += ((int)args->border)*2; |
ysize += ((int)args->border)*2; |
} |
|
|
array (int) bgcolor = parse_color(args->bg); |
array (int) fgcolor = parse_color(args->fg); |
|
object background,foreground; |
|
|
if(args->texture) foreground = load_image(args->texture); |
|
if(args->background) |
{ |
background = load_image(args->background); |
xsize = background->xsize(); |
ysize = background->ysize(); |
} else |
background = Image(xsize, ysize, @bgcolor); |
|
if(args->bevel) background = bevel(background, (int)args->bevel); |
|
if(args->textbox) |
{ |
int alpha,border; |
string bg; |
sscanf(args->textbox, "%d,%s", alpha, bg); |
sscanf(bg,"%s,%d", bg,border); |
background->paste_alpha(Image(txsize+border,tysize+border,@parse_color(bg)), |
255-(alpha*255/100), xoffset, yoffset); |
} |
|
if(args->shadow) |
{ |
int sd = ((int)args->shadow+10)*2; |
int sdist = ((int)(args->shadow/",")[-1])+2; |
object ta = text_alpha->copy(); |
ta = ta->color(256-sd,256-sd,256-sd); |
background->paste_mask(Image(txsize,tysize),ta,xoffset+sdist, yoffset+sdist); |
} |
|
if(!foreground) foreground=Image(xsize, ysize, @fgcolor); |
|
background->paste_mask(foreground, text_alpha, xoffset, yoffset); |
|
foreground = text_alpha = 0; |
|
|
if(args->scale) |
if((float)args->scale <= 2.0) |
background = background->scale((float)args->scale); |
|
if(args->rotate) |
{ |
string c; |
if(sscanf(args->rotate, "%*d,%s", c)) |
background->setcolor(@parse_color(c)); |
else |
background->setcolor(@bgcolor); |
background = background->rotate((float)args->rotate); |
} |
|
if(args->crop) background = background->autocrop(); |
|
return background; |
} |
|
|
array(int)|string write_text(int _args, string text, int size, |
object id) |
{ |
object img; |
mapping args = cached_args[_args]; |
|
if(!args) return 0; |
|
text = replace(text, ({ "<", ">", "&" }), ({ "<", ">", "&" })); |
|
|
if(!id || (!id->pragma["no-cache"])) |
if(mixed data = cache_lookup("font:"+_args, text)) |
{ |
if(size) return data[1]; |
return data[0]; |
} |
|
|
|
data = cache_lookup("fonts:fonts", args->font); |
if(!data) |
{ |
data = load_font(args->font); |
cache_set("fonts:fonts", args->font, data); |
} |
|
|
|
img = make_text_image(args,data,text); |
|
|
|
if(!img) return 0; |
|
|
int q = (int)args->quant || (args->background?256:16); |
img = img->map_closest(img->select_colors(q-1)+({parse_color(args->bg)})); |
if(args->fs) |
data=({ img->togif_fs(@(args->notrans?({}):parse_color(args->bg))), |
({img->xsize(),img->ysize()})}); |
else |
data=({ img->togif(@(args->notrans?({}):parse_color(args->bg))), |
({img->xsize(),img->ysize()})}); |
img=0; |
|
cache_set("font:"+_args, text, data); |
if(size) return data[1]; |
return data[0]; |
} |
|
|
mapping find_file(string f, object rid) |
{ |
string id; |
sscanf(f,"%d/%s", id, f); |
return http_string_answer(write_text((int)id,f,0,rid), "image/gif"); |
} |
|
string quote(string in) |
{ |
string res=""; |
for(int i=0; i<strlen(in); i++) |
switch(in[i]) |
{ |
case 'a'..'z': |
case 'A'..'Z': |
case '0'..'9': |
case '.': |
case ',': |
case '!': |
res += in[i..i]; |
break; |
default: |
res += sprintf("%%%02x", in[i]); |
} |
return res; |
} |
|
int number=time(1); |
|
int find_or_insert(mapping find) |
{ |
array a = indices(cached_args); |
array b = values(cached_args); |
int i; |
for(i=0; i<sizeof(a); i++) |
if(equal(find, b[i])) return a[i]; |
cached_args[number]=find; |
return number++; |
} |
|
|
string magic_javascript_header(object id) |
{ |
if(!id->supports->javascript || !id->supports->images) return ""; |
return |
("<script>\n" |
"<!-- \n" |
"version = 1;\n" |
"browserName = navigator.appName;\n" |
"browserVer = parseInt(navigator.appVersion); \n" |
"if(browserName == \"Netscape\" && (browserVer == 3 || browserVer == 4 || browserVer == 5 || browserVer == 6)) \n" |
" version = \"3\";\n" |
"else\n" |
" version= \"1\";\n" |
"\n" |
"function stat(txt)\n" |
"{\n" |
" top.window.status = txt;\n" |
"}\n" |
"\n" |
"function img_act(imgName, txt)\n" |
"{\n" |
" if (version == \"3\") \n" |
" {\n" |
" imgOn = eval(imgName + \"2.src\");\n" |
" document [imgName].src = imgOn;\n" |
" }\n" |
" setTimeout(\"stat('\"+txt+\"')\", 100);\n" |
"}\n" |
"\n" |
"function img_inact(imgName)\n" |
"{\n" |
" if (version == \"3\") \n" |
" {\n" |
" imgOff = eval(imgName + \".src\");\n" |
" document [imgName].src = imgOff;\n" |
" }\n" |
"}\n" |
"// -->\n" |
"</script>\n"); |
} |
|
|
string magic_image(string url, int xs, int ys, string sn, |
string image_1, string image_2, string alt, |
string mess,object id) |
{ |
if(!id->supports->images) return alt; |
if(!id->supports->javascript) |
return |
("<a href=\""+url+"\"><img src="+image_1+" name="+ |
sn+" border=0 alt=\""+alt+"\" ></a>\n"); |
|
return |
("<script>\n" |
"<!-- \n" |
"if(version == \"3\")\n" |
"{\n" |
" "+sn+" = new Image("+xs+", "+ys+");\n" |
" "+sn+".src = \""+image_1+"\";\n" |
" "+sn+"2 = new Image("+xs+", "+ys+");\n" |
" "+sn+"2.src = \""+image_2+"\";\n" |
"}\n" |
"// -->\n" |
"</script>\n" |
"<a href=\""+url+"\" onMouseover=\"img_act('"+sn+"','" |
+(mess||url)+"');return true;\"\n" |
"\n" |
"onMouseout=\"img_inact('"+sn+"')\"><img \n" |
" src="+image_1+" name="+sn+" border=0 alt=\""+alt+"\" ></a>\n"); |
} |
|
string tag_graphicstext(string t, mapping arg, string contents, |
object id, object foo, mapping defines) |
{ |
if(!strlen(contents)) return ""; |
|
string pre, post, defalign, gt, rest, magic; |
int i, split; |
|
|
|
if(!id->supports->images || id->prestate->noimages) |
{ |
if(!arg->split) contents=replace(contents,"\n", "\n<br>\n"); |
switch(t) |
{ |
case "gtext": |
case "anfang": |
if(arg->href) |
return "<a href=\""+arg->href+"\">"+contents+"</a>"; |
return contents; |
default: |
if(sscanf(t, "%s%d", t, i)==2) |
rest="<h"+i+">"+contents+"</h"+i+">"; |
else |
rest="<h1>"+contents+"</h1>"; |
if(arg->href) |
return "<a href=\""+arg->href+"\">"+rest+"</a>"; |
return rest; |
|
} |
} |
|
if(arg->magic) |
{ |
magic=arg->magic; |
m_delete(arg,"magic"); |
} |
|
string lp, url; |
if(arg->href) |
{ |
url = arg->href; |
lp = "<a href=\""+arg->href+"\">"; |
if(!arg->fg) arg->fg=defines->link||"#0000ff"; |
m_delete(arg,"href"); |
} |
|
|
if(defines->fg && !arg->fg) arg->fg=defines->fg; |
if(defines->bg && !arg->bg) arg->bg=defines->bg; |
if(defines->font && !arg->font) arg->font=defines->font||QUERY(default_font); |
if(!arg->font) arg->font = QUERY(default_font); |
|
|
if(arg->split) |
{ |
split=1; |
m_delete(arg,"split"); |
} |
|
|
for(i=2; i<10; i++) |
if(arg[(string)i]) |
{ |
arg->scale = 1.0 / ((float)i*0.6); |
m_delete(arg, (string)i); |
} |
|
|
if(sscanf(t, "%s%d", t, i)==2) |
if(i > 1) arg->scale = 1.0 / ((float)i*0.6); |
|
|
string moreargs=""; |
if(arg->hspace) |
{ |
moreargs += "hspace="+arg->hspace+" "; |
m_delete(arg,"hspace"); |
} |
|
if(arg->vspace) |
{ |
moreargs += "vspace="+arg->vspace+" "; |
m_delete(arg,"vspace"); |
} |
|
|
|
|
int num = find_or_insert( arg ); |
|
|
gt=contents; |
rest=""; |
|
if(split) |
{ |
string word; |
array res = ({}); |
string pre = query_location()+num+"/"; |
|
if(lp) res+=({ lp }); |
|
gt=replace(gt, "\n", " "); |
|
foreach(gt/" "-({""}), word) |
{ |
array size = write_text(num,word,1,0); |
res += ({ "<img border=0 alt=\""+replace(word,"\"","'") |
+"\" src=\'"+pre+quote(word)+"\' width="+ |
size[0]+" height="+size[1]+" "+moreargs+">\n" |
}); |
} |
if(lp) res+=({ "</a>" }); |
return res*""; |
} |
|
switch(t) |
{ |
case "gh1": case "gh2": case "gh3": case "gh4": |
case "gh5": case "gh6": case "gh7": |
case "gh": pre="<p>"; post="<br>"; defalign="top"; break; |
case "gtext": |
pre=""; post=""; defalign="bottom"; |
break; |
case "anfang": |
gt=contents[0..0]; rest=contents[1..]; |
pre="<br clear=left>"; post=""; defalign="left"; |
break; |
} |
|
array size = write_text(num,gt,1,0); |
|
if(magic) |
{ |
string res = ""; |
arg = mkmapping(indices(arg), values(arg)); |
arg->fuzz = arg->fg; |
arg->fg = defines->alink||"#ff0000"; |
int num2 = find_or_insert(arg); |
array size = write_text(num2,gt,1,0); |
if(!defines->magic_java) |
res = magic_javascript_header(id); |
defines->magic_java="yes"; |
return res + magic_image(url||"", size[0], size[1], |
"i"+(num+""+hash(gt,0x7fffffff))+"g", |
query_location()+num+"/"+quote(gt), |
query_location()+num2+"/"+quote(gt), |
replace(gt, "\"","'"),(magic=="magic"?0:magic), |
id); |
} |
|
return (pre+(lp?lp:"")+ |
"<img border=0 alt=\""+replace(gt,"\"","'")+"\" src="+ |
query_location()+num+"/"+quote(gt) |
+" align="+(arg->align?arg->align:defalign)+ |
" width="+size[0]+" height="+size[1]+">"+rest+(lp?"</a>":"")+post); |
} |
|
string tag_body(string t, mapping args, object id, object file, |
mapping defines) |
{ |
if(args->bgcolor) defines->bg = args->bgcolor; |
if(args->text) defines->fg = args->text; |
if(args->link) defines->link = args->link; |
if(args->alink) defines->alink = args->alink; |
if(args->clink) defines->clink = args->clink; |
if(args->vlink) defines->vlink = args->vlink; |
} |
|
|
mapping query_tag_callers() |
{ |
return (["body":tag_body]); |
} |
|
|
mapping query_container_callers() |
{ |
return ([ "anfang":tag_graphicstext, |
"gh":tag_graphicstext, |
"gh1":tag_graphicstext, |
"gh2":tag_graphicstext, |
"gh3":tag_graphicstext, |
"gh4":tag_graphicstext, |
"gh5":tag_graphicstext, |
"gh6":tag_graphicstext, |
"gtext":tag_graphicstext, ]); |
} |
|
|
|
|
|
|