Roxen.git / server / modules / graphics / graphic_text.pike

version» Context lines:

Roxen.git/server/modules/graphics/graphic_text.pike:1: - // This is a roxen module. Copyright © 1996 - 2000, Roxen IS. + // This is a roxen module. Copyright © 1996 - 2009, Roxen IS.   //    - constant cvs_version="$Id: graphic_text.pike,v 1.275 2001/08/23 21:20:59 nilsson Exp $"; + constant cvs_version="$Id$";      #include <module.h>   inherit "module";         // ------------------- Module registration ---------------------      constant module_type = MODULE_TAG;   constant module_name = "Graphics: Graphic text";   constant module_doc =
Roxen.git/server/modules/graphics/graphic_text.pike:30:    TYPE_INT|VAR_MORE|VAR_NOT_CFIF,    "Per default, it will not be possible to render texts longer than "    "this. This is a safeguard so that a simple RXML error doesn't "    "cause a huge image to be generated. It can be turned of at a per "    "tag bases, with the <i>maxlen</i> attribute.");       defvar("ext", 0, "Append format to generated images",    TYPE_FLAG|VAR_MORE|VAR_NOT_CFIF,    "Append the image format (.gif, .png, .jpg, etc) to the generated "    "images. This is not necessary, but might seem nicer."); +  defvar("honor_supports", 1, "Honor supports", +  TYPE_FLAG|VAR_MORE, +  "Normally javascript tags generated by the 'magic' attribute are not " +  "sent to clients that don't support javascript image objects. " +  "This check can be disabled to always output those javascript tags.");   }      TAGDOCUMENTATION;   #ifdef manual   constant gtextargs=#"   <attr name='verbatim'><p>    By default the gtext tag will try to make typographical enhancements to the    text to make the resulting image as eye pleasing as possible. If you want to    turn this feature off, add this attribute to the tag.</p>   </attr>
Roxen.git/server/modules/graphics/graphic_text.pike:62: Inside #if defined(manual)
   the actual image.</p>   </attr>      <attr name='mirrortile'><p>    Tiles the background and foreground images around x-axis and y-axis    for odd frames, creating seamless textures.</p>   </attr>      <attr name='bevel' value='width'><p>    Draws a bevel-box around the text.</p> -  <ex type=vert> +  <ex>   <gtext bevel=\"2\">Ok</gtext>    </ex>   </attr>      <attr name='bgcolor' value='color'><p>    Sets the background color. If the module \"HTML color wiretap\" is    loaded, they are taken from the normal HTML tags in your document,    like body, table, tr and td.</p>       <p>If you set the background color, it is probably best to add the    notrans attribute as well.</p> -  <ex type=vert> +  <ex>   <gtext notrans=\"\" bgcolor=\"pink\">Pink</gtext>   <gtext notrans=\"\" bgcolor=\"#ff0000\">Red</gtext>   <gtext notrans=\"\" bgcolor=\"%50,0,100,0\">%50,0,100,0</gtext>    </ex>   </attr>      <attr name='bgturbulence' value='frequency,color;frequency,color...'><p>    Apply a turbulence effect on the background.</p>   </attr>      <attr name='bold'><p>    Use a bold version of the font, if available. Can not be used    together with the black or light attributes.</p> - <ex type=hor> + <ex>   <gtext font='lucida'>Aa3</gtext><br />   <gtext font='lucida' bold=''>Aa3</gtext><br />   <gtext font='lucida' italic=''>Aa3</gtext><br />   <gtext font='lucida' bold='' italic=''>Aa3</gtext><br />   </ex>   </attr>      <attr name='black'><p>    Use a black, or heavy, version of the font, if available. Can    not be used together with the bold or light attributes.</p>
Roxen.git/server/modules/graphics/graphic_text.pike:115: Inside #if defined(manual)
     <attr name='italic'><p>    Use an italic version of the font, if available.</p>   </attr>      <attr name='bshadow' value='distance'><p>    Draw a blured black drop-shadow behind the text. Using 0 as distance    does not currently place the shadow directly below the text. Using    negative values for distance is possible, but you might have to add    'spacing'.</p> -  <ex type=vert> +  <ex>   <gtext scale=\"0.8\" fgcolor=\"#FF6600\"   bshadow=\"1\">&lt;gtext bshadow=1&gt;</gtext>   <br /><gtext scale=\"0.8\" fgcolor=\"#FF6600\"   bshadow=\"2\">&lt;gtext bshadow=2&gt;</gtext>    </ex>   </attr>      <attr name='chisel'><p>    Make the text look like it has been cut into the background.</p> -  <ex type=vert> +  <ex>   <gtext font=\"lucida\" bold=\"\" chisel=\"\" talign=\"center\" tile=\"\"   opaque=\"70\" fgcolor=\"gold\" bevel=\"2\"   background=\"/internal-roxen-squares\"> Chisel opaque=\"70\"</gtext>    </ex>   </attr>      <attr name='crop'><p>    Remove all white-space around the image.</p>   </attr>      <attr name='encoding' value='string'><p>    Choose with which charset the text is encoded with.</p>   </attr>      <attr name='fadein' value='blur,steps,delay,initialdelay'><p>    Generates an animated GIF file of a fade-in effect.</p>   </attr>      <attr name='fgcolor' value='color'><p>    Sets the text color.</p> -  <ex type=vert> +  <ex>   <gtext fgcolor=\"#0080FF\">#0080FF</gtext>    </ex>   </attr>      <attr name='font' value='string'><p>    Selects which font to use. You can get a list of all available fonts    by using the list fonts task in the administration interface, or by    using the <xref href='../output/emit_fonts.tag' /> plugin.</p>   </attr>   
Roxen.git/server/modules/graphics/graphic_text.pike:175: Inside #if defined(manual)
  <attr name='fs'><p>    Apply floyd-steinberg dithering to the resulting image. Most of the    time it is much better to increase the number of colors, instead of    dithering the image, but sometimes when using very complex background    images dithering is O.K.</p>   </attr>      <attr name='ghost' value='dist,blur,color'><p>    Apply a ghost effect. Cannot be used together with shadow or magic    coloring.</p> -  <ex type='vert'><gtext spacing=\"2\" crop=\"\" ghost=\"1,1,red\">ghost=1,1,red</gtext></ex> +  <ex><gtext spacing=\"2\" crop=\"\" ghost=\"1,1,red\">ghost=1,1,red</gtext></ex>   </attr>      <attr name='glow' value='color'><p>    Apply a 'glow' filter to the image. Quite a CPU eater. Looks much    better on a dark background, where a real 'glow' effect can be    achieved.</p> -  <ex type=vert> +  <ex>   <gtext glow=\"red\">&lt;gtext glow=red&gt;</gtext>    </ex>   </attr>      <attr name='maxlen' value='number'><p>    Sets the maximum length of the text that will be rendered into an    image, by default 300.</p>   </attr>      <attr name='move' value='x,y'><p>
Roxen.git/server/modules/graphics/graphic_text.pike:204: Inside #if defined(manual)
   image. This will not change the size of the image.</p>   </attr>      <attr name='narrow'><p>    Use a narroe version of the font, if available.</p>   </attr>      <attr name='notrans'><p>    Do not make the background transparent. Useful when making 'boxes' of    color around the text.</p> -  <ex type=vert> +  <ex>   <gtext bgcolor=\"red\">&lt;gtext bgcolor=red&gt;</gtext><br />   <gtext bgcolor=\"red\" notrans=\"\">&lt;gtext   bgcolor=red notrans&gt;</gtext>    </ex>   </attr>      <attr name='nowhitespace'><p>    Removes all whitespaces before and after the real text.</p>   </attr>      <attr name='opaque' value='percentage'><p>    Sets the 'opaque' value of the color used to draw the text. Default    is 100%. In the example below, notice how the text color mixes with    the two background colors.</p> -  <ex type=vert> +  <ex>   <gtext scale=\"0.6\" textbox=\"100,pink,-11\" bgcolor=\"lightblue\"   notrans=\"\" opaque=\"40\" fgcolor=\"black\"   >&lt;Demonstration of opaque text&gt;</gtext>    </ex>   </attr>      <attr name='outline' value='color,extra-radius'><p>    Draw an outline around the text. Quite useful when combined with    textscale.</p> -  <ex type=vert> +  <ex>   <gtext xspacing=\"4\" quant=\"128\" textscale=\"red,red,yellow,yellow\"   outline=\"black,1\"   >black, 2 pixels</gtext>    </ex>   </attr>      <attr name='pressed'><p>    Inverts the direction of the bevel box, to make it look like a button    that is pressed down. The magic modifier will do this automatically.</p>   </attr>      <attr name='quant' value='number'><p>    Quantifies the image with this number of colors. Using a lower number    will decrease the image (file)size, but make the text look more    'edgy', and if you use complex backgrounds or image textures, more    colors will be neded. At most 255 colors can be used, and less than 2    is quite useless. It is advisable to use powers of 2 to optimize the    palette allocation.</p>    -  <ex type=vert> +  <ex>   <gtext quant=\"2\">A</gtext>   <gtext quant=\"6\">A</gtext>   <gtext quant=\"20\">A</gtext>   <gtext quant=\"200\">A</gtext>    </ex>   </attr>      <attr name='rescale'><p>    Rescale the background to fill the whole image.</p>   </attr>      <attr name='rotate' value='angle'><p>    Rotates the image this number of degrees counter-clockwise.</p>   </attr>      <attr name='scale' value='number'><p>    Sets the scale of the image. Larger than 1.0 is enlargement.</p> -  <ex type=vert> +  <ex>   <gtext scale=\"1.0\">&lt;gtext scale=1.0&gt;</gtext>   <gtext scale=\"0.5\">&lt;gtext scale=0.5&gt;</gtext>    </ex>   </attr>      <attr name='scolor' value='color'><p>    Use this color for the shadow. Used with the shadow attribute.</p>   </attr>      <attr name='scroll' value='width,steps,delay'><p>    Generate an animated GIF image of the text scrolling.</p>   </attr>      <attr name='shadow' value='intensity,distance'><p>    Draw a blured black drop-shadow behind the text. Using 0 as distance    does not currently place the shadow directly below the text. Using negative    values for distance is possible,</p> -  <ex type=vert><gtext scale=\"0.8\" fgcolor=\"blue\" +  <ex><gtext scale=\"0.8\" fgcolor=\"blue\"   shadow=\"40,0\">&lt;gtext shadow=40,0&gt;</gtext>   <br /><gtext scale=\"0.8\" fgcolor=\"blue\"   shadow=\"40,2\">&lt;gtext shadow=40,2&gt;</gtext></ex>   </attr>      <attr name='size' value='width,height'><p>    Set the size of the image.</p>   </attr>      <attr name='spacing' value='number'><p>    Add space around the text.</p>   </attr>      <attr name='talign' value='left|right|center'><p>    Adjust the alignment of the text.</p>   </attr>      <attr name='textbelow' value='color'><p>    Place the text centered in a box of the given color below the image    area. Useful together with background to make captions for images.</p> -  <ex type=vert> +  <ex>   <img src=\"/internal-roxen-roxen\" /> &nbsp;   <gtext scale=\"0.5\" background=\"/internal-roxen-roxen\"   textbelow=\"#c0c0c0\">Roxen</gtext>    </ex>   </attr>      <attr name='textbox' value='opaque,color'><p>    Draw a box with an opaque value below the text of the specified color.</p>   </attr>      <attr name='textscale' value='color,color,color,color'><p>    Apply a color filter to the text. The colors are,    respectively, upper left, lower left, upper right and lower right.    It is probably a good idea to increase the 'quant' value when    using this argument.</p> -  <ex type=vert> +  <ex>   <gtext quant=\"128\" textscale=\"blue,white,red,darkgreen\"   >Blue, white,   red, darkgreen</gtext>    </ex>   </attr>      <attr name='texture' value='path'><p>    Uses the specified images as a field texture.</p> - <ex type=hor> - <gtext font=\"\" fontsize=\"100\" - texture=\"/internal-roxen-squares\">A</gtext> + <ex> + <gtext texture=\"/internal-roxen-squares\" +  tile=\"1\" fontsize=\"100\">A</gtext>   </ex>   </attr>    - <attr name=tile> -  Tiles the background and foreground images if they are smaller than -  the actual image. + <attr name='tile'> +  <p>Tiles the background and foreground images if they are smaller than +  the actual image.</p>   </attr>    - <attr name='xpad' value='percentage|integer'><p> + <attr name='xpad' value='percentage|integer ended with \"px\"'><p>    Sets the padding between characters. The value can either be an -  relative change, in percent, or an absolute value. Note that +  relative change, in percent, or an absolute number of pixels, ended +  with the string \"px\". Note that    different fonts reacts differently on these values and for some it    will not have any effect at all. This depends on the type of the font    and the font implementation.</p>    - <ex type='vert'> + <ex>   <gtext font=\"niquel\">HELLO ROXEN</gtext><br /> - <gtext xpad=\"4\" font=\"niquel\">HELLO ROXEN</gtext><br /> + <gtext xpad=\"4px\" font=\"niquel\">HELLO ROXEN</gtext><br />   <gtext xpad=\"50%\" font=\"niquel\">HELLO ROXEN</gtext><br />   </ex>   </attr>      <attr name='xsize' value='number'><p>    Sets the width.</p>   </attr>      <attr name='xspacing' value='number'><p>    Sets the horizontal spacing.</p>   </attr>    - <attr name='ypad' value='percentage'><p> -  Sets the padding beteen lines.</p> + <attr name='ypad' value='percentage|integer ended with \"px\"'><p> +  Sets the padding beteen lines. Works as xpad.</p>   </attr>         <attr name='ysize' value='number'><p>    Sets the height.</p>   </attr>      <attr name='yspacing' value='number'><p>    Sets the vertical spacing.</p> -  + </attr> +  + <h1>Timeout</h1> +  + <p>The generated image will by default never expire, but + in some circumstances it may be pertinent to limit the + time the image and its associated data is kept. Its + possible to set an (advisory) timeout on the image data + using the following attributes.</p> +  + <attr name='unix-time' value='number'><p> + Set the base expiry time to this absolute time.</p><p> + If left out, the other attributes are relative to current time.</p> + </attr> +  + <attr name='years' value='number'><p> + Add this number of years to the time this entry is valid.</p> + </attr> +  + <attr name='months' value='number'><p> + Add this number of months to the time this entry is valid.</p> + </attr> +  + <attr name='weeks' value='number'><p> + Add this number of weeks to the time this entry is valid.</p> + </attr> +  + <attr name='days' value='number'><p> + Add this number of days to the time this entry is valid.</p> + </attr> +  + <attr name='hours' value='number'><p> + Add this number of hours to the time this entry is valid.</p> + </attr> +  + <attr name='beats' value='number'><p> + Add this number of beats to the time this entry is valid.</p> + </attr> +  + <attr name='minutes' value='number'><p> + Add this number of minutes to the time this entry is valid.</p> + </attr> +  + <attr name='seconds' value='number'><p> + Add this number of seconds to the time this entry is valid.</p>   </attr>";         constant tagdoc=([ - "anfang":#"<desc cont='cont'><p><short> + "anfang":#"<desc type='cont'><p><short>    Creates an anfang in the beginning of a text.</short> This tag takes    the same attributes as <xref href='gtext.tag' />.</p>    -  <ex type=vert> +  <ex>    <anfang crop=\"\">This is a beginning<br />    of a very short text,<br />    and here it ends.    </anfang>    </ex>   </desc>   <noex>"+gtextargs+"</noex>",    - "gh":#"<desc cont='cont'><p><short> + "gh":#"<desc type='cont'><p><short>    Creates a graphical header.</short> <tag>gh</tag> takes the same    attributes as <xref href='gtext.tag' />. <tag>gh</tag> comes in six    flavors, from <tag>gh1</tag> through <tag>gh6</tag> and are the RXML    counterpart to the HTML tags <tag>h1</tag> through <tag>h6</tag>.</p>   </desc>   <noex>"+gtextargs+"</noex>",    - "gh1":#"<desc cont='cont'></desc> + "gh1":#"<desc type='cont'></desc>   <noex>"+gtextargs+"</noex>",    - "gh2":#"<desc cont='cont'></desc> + "gh2":#"<desc type='cont'></desc>   <noex>"+gtextargs+"</noex>",    - "gh3":#"<desc cont='cont'></desc> + "gh3":#"<desc type='cont'></desc>   <noex>"+gtextargs+"</noex>",    - "gh4":#"<desc cont='cont'></desc> + "gh4":#"<desc type='cont'></desc>   <noex>"+gtextargs+"</noex>",    - "gh5":#"<desc cont='cont'></desc> + "gh5":#"<desc type='cont'></desc>   <noex>"+gtextargs+"</noex>",    - "gh6":#"<desc cont='cont'></desc> + "gh6":#"<desc type='cont'></desc>   <noex>"+gtextargs+"</noex>",    - "gtext":#"<desc cont='cont'><p><short> + "gtext":#"<desc type='cont'><p><short>    Creates an image with the tag content texts.</short> It is possible to pass    attributes, such as the target attribute, to the resulting tags by including    them in the gtext tag.</p>   </desc>      <attr name='alt' value='string'><p>    Sets the alt attribute of the generated <tag>img</tag> tag. By    default the alt attribute will be set to the contents of the    <tag>gtext</tag> tag.</p>    -  <ex type=vert> +  <ex>    <gtext fgcolor=\"blue\" alt=\"Hello!\">Welcome!</gtext>    </ex>   </attr>      <attr name='border' value='width,color'><p>    Draws a border around the text of the specified width and color.</p>    -  <ex type=vert> +  <ex>   <gtext fgcolor=\"blue\" border=\"2,red\">Red border</gtext>    </ex>   </attr>      <attr name='href' value='URL'><p>    Link the image to the specified URL. The link color of the document    will be used as the default foreground rather than the foreground    color.</p>   </attr>      <attr name='magic' value='message'><p>    Used together with the href attribute to generate a JavaScript that    will highlight the image when the mouse is moved over it. The message    is shown in the browser's status bar.</p>    -  <ex type=vert> +  <ex>   <gtext href=\"http://www.roxen.com\" magic=\"Roxen\">www.roxen.com</gtext>    </ex>   </attr>      <attr name='magic-attribute' value='value'><p>    Same as for any <tag>gtext</tag> attribute, except for the    highlighted image.</p>    - <ex type=vert><gtext fgcolor=\"blue\" magic=\"\" - magic-glow=\"red\" magic-fg=\"white\">Mouse me!</gtext></ex> + <ex><gtext fgcolor=\"blue\" magic=\"\" + magic-glow=\"red\" magic-fgcolor=\"white\">Mouse me!</gtext></ex>   </attr>      <attr name='noxml'><p>    Do not terminate the image tag with \"/\".</p>   </attr>      <attr name='split'>    <p>Make each word into a separate gif image. Useful if you are    writing a large text, and word wrap at the edges of the display is    desired.</p>    - <ex type='vert'> + <ex>    <gtext scale='0.4' split='split'>    Useful if you are writing a large text, and word wrap at the edges    of the display is desired.    </gtext>   </ex>       <p>This will allow the browser to word-wrap the text, but will    disable certain attributes like <att>magic</att>. Note that the word    wraping functionality of this example cannot be shown as the size of    the browser window is determined by the largest example box. </p>    -  <ex type='vert'><gtext scale=\"0.4\" split=\"\">One image per character.</gtext></ex> +  <ex><gtext scale=\"0.4\" split=\"\">One image per character.</gtext></ex>   </attr>      <attr name='submit'><p>    Creates a submit-button for forms. Does not work together with    <att>split</att> or <att>magic</att> attributes.</p>   </attr>"+gtextargs,      "gtext-id":#"<desc tag='tag'><p><short>    Returns an internal URL to an image with the specified gtext attributes    applied.</short> Rendering a text image with the given arguments is
Roxen.git/server/modules/graphics/graphic_text.pike:511: Inside #if defined(manual)
   <p>Be aware that this tag could be abused for denial of service    attacks by malicious users swarming your server with requests for    images of great length that the server would happily try to render    for them. Hence this tag should only be used in environments where    you trust all your users, e.g. Intranets.</p>       <p>In most cases the tag <tag>gtext-url</tag> solves this problem,    but if you would like to generate new text images live without    reloading some RXML page, you need this tag. An example application:</p>    - <ex type='live'> - <define variable='var.id' preparse='' trimwhites=''> + <ex-box><define variable='var.id' preparse='' trimwhites=''>   <gtext-id font='FranklinGothicDemi' fgcolor='blue'/>   </define>      <img src='&var.id;Please type some text here:' -  alt='' name='banner' width='468' height='60'/> +  alt='' name='banner' width='468' height='60' />      <script language='javascript'>   var image = document.images.banner;   function alter_image(label)   {    image.src = '&var.id:js;' + escape(label.value);    label.focus();    return false;   }   </script>      <form onsubmit='return alter_image(this.label);'>   <input type='text' size='40' name='label' /> - </form></ex> - </desc> + </form></ex-box></desc>      <attr name='short'><p>    Returns a relative path to the image, i.e. a shorter one.   </p>   </attr>"+gtextargs,    - "gtext-url":#"<desc cont='cont'><p><short> + "gtext-js":#"<desc tag='tag'><p><short> +  Inserts javascript funcations that are used by gtext when using +  the magic attributes.</short> +  This is normally inserted at the first instance of a gtext, but it may +  be necessary to insert the gtext using this tag if the first gtext in a page +  may be hidden, for example if contained in an if tag. +  </p> + </desc>", +  + "gtext-url":#"<desc type='cont'><p><short>    Returns an internal URL to an image with the specified attributes    applied.</short></p>   </desc>      <attr name='short'><p>    Returns a relative path to the image, i.e. a shorter one.   </p>   </attr>   <noex>"+gtextargs+"</noex>",]);   #endif         // -------------------- Image cache functions --------------------      private roxen.ImageCache image_cache;   private string compat_level; -  + private int honor_supports;      string status() {    array s=image_cache->status();    return sprintf("<b>Images in cache:</b> %d images<br />\n"    "<b>Cache size:</b> %s",    s[0], Roxen.sizetostring(s[1]));   }      mapping(string:function) query_action_buttons() { -  return ([ "Clear cache":flush_cache ]); +  return ([ "Clear Cache":flush_cache ]);   }      void flush_cache() {    image_cache->flush();   }      void start(int num, Configuration conf)   {    image_cache = roxen.ImageCache( "gtext", draw_callback );    roxen.dump( "etc/modules/GText.pmod" );    if(query("colorparse")) module_dependencies(conf, ({ "wiretap" }) );    compat_level = conf->query("compat_level"); -  +  honor_supports = query("honor_supports");   }    -  + void stop() + { +  destruct(image_cache); + } +    private constant nbsp = Roxen.iso88591["&nbsp;"];      private Image.Image|mapping draw_callback(mapping args, string text, RequestID id)   {    array data;    Image.Font font;    Image.Image img;       if( objectp( text ) )    {
Roxen.git/server/modules/graphics/graphic_text.pike:656:    else    {    int bold=0, italic=0;    if(args->bold) bold=1;    if(args->light) bold=-1;    if(args->black) bold=2;    if(args->italic) italic=1;       int|float xpad=0.0;    if(args->xpad) -  if(args->xpad[-1]=='%') -  xpad = (float)args->xpad; -  else +  if(args->xpad[-1]=='x' && args->xpad[-2]=='p')    xpad = (int)args->xpad; -  +  else +  xpad = (float)args->xpad;       int|float ypad=0.0;    if(args->ypad) -  if(args->ypad[-1]=='%') -  ypad = (float)args->ypad; -  else +  if(args->ypad[-1]=='x' && args->ypad[-2]=='p')    ypad = (int)args->ypad; -  +  else +  ypad = (float)args->ypad;       font = get_font(args->font,    (int)args->fontsize||32,    bold,    italic,    lower_case(args->talign||"left"),    xpad,    ypad);    }       if (!font)    error("gtext: No font (tried "+    (args->afont||args->font||args->nfont)+ ")!\n");       // Fonts and such are now initialized. -  [img, Image.Image alpha] = GText.make_text_image(args, font, text, id); +  array|mapping make_res = GText.make_text_image(args, font, text, id); +  if (mappingp(make_res)) +  return make_res; +  [img, Image.Image alpha] = make_res;       // Now we have the image in 'img'.       if( !args->scroll && !args->fadein )    {    if(!args->notrans)    {    return ([ "img":img, "alpha":alpha ]);    }    return img;    }       if(args->fadein)    { -  int amount=2, steps=10, delay=10, initialdelay=0, ox; +  int amount=2, steps=10, delay=10, initialdelay=0;    string res = img->gif_begin();    sscanf(args->fadein,"%d,%d,%d,%d", amount, steps, delay, initialdelay);    if(initialdelay)    {    Image.Image foo=Image.Image(img->xsize(),img->ysize(),@parse_color(args->bgcolor));    res += foo->gif_add(0,0,initialdelay);    }    for(int i = 0; i<(steps-1); i++)    {    Image.Image foo=img->clone();
Roxen.git/server/modules/graphics/graphic_text.pike:752:   }      mapping find_internal(string f, RequestID id)   {    if( strlen(f)>4 && query("ext") && f[-4]=='.') // Remove .ext    f = f[..strlen(f)-5];    if( strlen(f) && f[0]=='$' )    {    array id_text = f/"/";    if( sizeof(id_text)==2 ) -  { // It's a gtext-id -  string second_key = roxen->argcache->store( (["":id_text[1]]) ); -  return image_cache->http_file_answer( id_text[0][1..] +"$"+ second_key, id ); +  { // It's a gtext-id, let it live for an hour. +  string second_key = roxen->argcache->store( (["":id_text[1]]), 3600 ); +  return image_cache->http_file_answer( id_text[0][1..] +"$"+ second_key, +  id, UNDEFINED, 3600 );    }    }    return image_cache->http_file_answer( f, id ); -  return 0; +    }         // -------------- helpfunctions to gtext tags and containers -----------------      private constant filearg = ({    "background",    "texture",    "alpha",    "magic-texture",    "magic-background",    "magic-alpha"   });      private constant textarg = ({    "afont",    "alpha", -  +  "baselineoffset",    "bevel",    "bgcolor",    "bgturbulence",    "black",    "bold",    "bshadow",    "chisel",    "encoding",    "fadein",    "fgcolor",    "font",    "fontsize",    "ghost",    "glow",    "italic",    "light",    "mirrortile",    "move",    "narrow", -  +  "no-auto-alpha",    "notrans",    "opaque",    "outline", -  +  "oversampling",    "pressed",    "rescale",    "rotate",    "scale",    "scolor",    "scroll",    "shadow",    "size",    "spacing",    "talign",
Roxen.git/server/modules/graphics/graphic_text.pike:835:    "dither",    "fs",    "*-*",    "gamma",   });      private constant theme = ({ "fgcolor", "bgcolor", "font" });      private constant hreffilter = ([ "split":1, "magic":1, "noxml":1, "alt":1 ]);    + string gtext_javascript(RequestID id) { +  string res=""; +  if(!id->root_id->misc->gtext_magic_java) { +  res += "function gtext_mo(ri,hi,txt)\n" +  "{\n" +  " document.images[ri].src = hi.src;\n" +  " if( txt != 0 )\n" +  " setTimeout(\"top.window.status = '\"+txt+\"'\", 100);\n" +  "}"; +  id->root_id->misc->gtext_magic_java="yes"; +  } +  return res; + } +  + private int get_file_stat( string f, RequestID id ) + { +  int res; +  mapping stat_cache; +  +  // -1 is used to cache negative results. When SiteBuilder crawler runs +  // we must let the stat_file() run unconditionally to register +  // dependencies properly. +  if (stat_cache = id->misc->gtext_statcache) { +  if (!id->misc->persistent_cache_crawler) +  if (res = stat_cache[f]) +  return (res > 0) && res; +  } else +  stat_cache = id->misc->gtext_statcache = ([ ]); +  +  int was_internal = id->misc->internal_get; +  id->misc->internal_get = 1; +  res = stat_cache[ f ] = (id->conf->stat_file( f,id ) || +  ({ 0,0,0,0 }) )[ST_MTIME] || -1; +  if (!was_internal) +  m_delete(id->misc, "internal_get"); +  return (res > 0) && res; + } +    private mapping mk_gtext_arg(mapping arg, RequestID id)   {    mapping p=([]); //Picture rendering arguments.      #if ROXEN_COMPAT < 2.2    if(compat_level < "2.2") {    p->compat = compat_level;    p->font = m_delete(arg, "nfont");    }   #endif
Roxen.git/server/modules/graphics/graphic_text.pike:866:    // Make format selection explicit since fallback may vary depending on    // encoders present in this installation.    if (!arg->format)   #if constant(Image.GIF) && constant(Image.GIF.encode)    arg->format = "gif";   #else    arg->format = "jpg";   #endif       foreach(filearg, string tmp) -  if(arg[tmp]) +  if(string path = arg[tmp])    { -  p[tmp]=Roxen.fix_relative(arg[tmp],id); -  m_delete(arg,tmp); +  // Reject empty file paths for sufficiently high compat_level +  if (path == "" && compat_level >= "5.2") +  RXML.parse_error("Empty " + tmp + " attribute not allowed."); +  +  if (!has_prefix(tmp, "magic-")) { +  p[tmp] = Roxen.fix_relative(path, id); +  p[tmp + "_stat"] = get_file_stat(path, id); + #if constant(Sitebuilder) +  // The file we called get_file_stat() on above may be a SiteBuilder +  // file. If so we need to extend the argument data with e.g. +  // current language fork. +  if (Sitebuilder.sb_prepare_imagecache) +  p = Sitebuilder.sb_prepare_imagecache(p, path, id); + #endif +  m_delete(arg, tmp); +  } else { +  // The magic-* attributes will be removed later. +  arg[tmp]=Roxen.fix_relative(arg[tmp],id);    } -  +  }      // if(arg->border && (search(arg->border,",")!=-1))   // {   // p->border=arg->border;   // m_delete(arg,"border");   // }       array i = indices( arg );    foreach(textarg, string tmp)    foreach( glob( tmp, i ), string a )
Roxen.git/server/modules/graphics/graphic_text.pike:913:    if(id->misc->defines->black && !p->black) p->black=id->misc->gtext_black;    if(id->misc->defines->narrow && !p->narrow) p->narrow=id->misc->gtext_narrow;       if(p->afont) {    p->font = roxen->fonts->verify_font( p->afont+" "+p->fontsize );    if(!p->font) RXML.parse_error("Font "+p->afont+" could not be loaded.\n");    }    else {    string font = p->font;    p->font = roxen->fonts->verify_font(p->font, p->fontsize||32); -  if(!p->font) RXML.parse_error("Font "+font+" could not be loaded.\n"); +  if(!p->font) { +  RXML.parse_error("Font " + (font || roxen->query("default_font")) + +  " could not be loaded.\n");    } -  +  }       return p;   }      private string fix_text(string c, mapping m, RequestID id) {       if(m->nowhitespace)    {    c=String.trim_all_whites(c);    m_delete(m, "nowhitespace");    }       m_delete(m, "noparse");    m_delete(m, "preparse");    -  c=replace(c, Roxen.replace_entities+({" "," ", "\n\n\n", "\n\n", "\r"}), -  Roxen.replace_values+({" ", " ", "\n", "\n", ""})); +  c=replace(c, Roxen.replace_entities + ({ +  " ", " ", +  "\n\n\n", "\n\n", +  "\r\n\r\n\r\n", "\r\n\r\n", "\r\n", +  "\r\r\r", "\r\r", "\r" +  }), +  Roxen.replace_values + ({ +  " ", " ", +  "\n", "\n", +  "\n", "\n", "\n", +  "\n", "\n", "\n", +  }));       if(m->maxlen)    c = c[..(( (int)m_delete(m,"maxlen") || query("deflen"))-1)];       return c;   }         // ----------------- gtext tags and containers -------------------      class TagGTextURL {    inherit RXML.Tag;    constant name = "gtext-url";    constant flags = RXML.FLAG_DONT_REPORT_ERRORS;       class Frame {    inherit RXML.Frame;    array do_return(RequestID id) { -  content=fix_text(content,args,id); +  int timeout = Roxen.timeout_dequantifier(args); +  content=fix_text(content||"",args,id);    mapping p=mk_gtext_arg(args,id);    if(args->href && !p->fgcolor) p->fgcolor=id->misc->gtext_link||"#0000ff";    string ext="";    if(query("ext")) ext="."+(p->format || "gif");    if(!args->short)    return ({ query_absolute_internal_location(id) + -  image_cache->store( ({p,content}), id )+ext }); -  return ({ "+"+image_cache->store( ({p,content}), id )+ext }); +  image_cache->store( ({p,content}), id, timeout )+ext }); +  return ({ "+"+image_cache->store( ({p,content}), id, timeout )+ext });    }    }   }    -  + class TagGTextJS { +  inherit RXML.Tag; +  constant name = "gtext-js"; +  constant flags = RXML.FLAG_EMPTY_ELEMENT; +  +  class Frame { +  inherit RXML.Frame; +  +  array do_return(RequestID id) { +  return ({ gtext_javascript(id) }); +  } +  } + } +    class TagGTextID {    inherit RXML.Tag;    constant name = "gtext-id";    constant flags = RXML.FLAG_EMPTY_ELEMENT;       class Frame {    inherit RXML.Frame;       array do_return(RequestID id) { -  +  int timeout = Roxen.timeout_dequantifier(args);    mapping p=mk_gtext_arg(args,id);    if(args->href && !p->fgcolor) p->fgcolor=id->misc->gtext_link||"#0000ff";    if(!args->short)    return ({ query_absolute_internal_location(id) + -  "$"+image_cache->store(p, id)+"/" }); -  return ({ "+"+image_cache->store(p, id )+"/foo" }); +  "$"+image_cache->store(p, id, timeout)+"/" }); +  return ({ "+"+image_cache->store(p, id, timeout)+"/foo" });    }    }   }      class TagGText {    inherit RXML.Tag;    constant name = "gtext";    constant flags = RXML.FLAG_DONT_REPORT_ERRORS;       class Frame {    inherit RXML.Frame;       array do_return(RequestID id) {    return ({ do_gtext(args, (string) content, id) });    }    }   }    -  + #ifdef GTEXT_RANDOM_PREFIX + constant random_chars = "abcdefghijklmnopqrstuvwxyz" / ""; +  + string random_pfx() { +  string s = ""; +  for(int i=0; i < 4; i++) +  s += random_chars[random(sizeof(random_chars))]; +  return s; + } +  + string get_gtext_pfx(RequestID id) { +  if(!id->root_id->misc->gtext_pfx) { +  id->root_id->misc->gtext_pfx = random_pfx(); +  RXML_CONTEXT->set_root_id_misc("gtext_pfx", id->root_id->misc->gtext_pfx); +  } +  return id->root_id->misc->gtext_pfx; + } + #endif +    private string do_gtext(mapping arg, string c, RequestID id)   {    if((c-" ")=="") return "";    -  id = id->root_id; -  +     c=fix_text(c,arg,id);    mapping p=mk_gtext_arg(arg,id);       string ext="";    if(query("ext")) ext="."+(p->format || "gif");       string lp="%s", url="", ea=" ";       int input=0;    if(arg->submit)
Roxen.git/server/modules/graphics/graphic_text.pike:1038:    if(arg[name]) {    ea+=name+"=";    if(!has_value(arg[name], "\"")) ea+="\""+arg[name]+"\" ";    else if(!has_value(arg[name], "'")) ea+="'"+arg[name]+"' ";    else ea+="\""+replace(arg[name], "'", "&#39;")+"\" ";    m_delete(arg, name);    }       int xml=!m_delete(arg, "noxml");    -  if(!arg->border) arg->border="0"; +  // No border should be used for <img>. Force CSS declaration to avoid +  // borders when gtext is clickable. +  m_delete(arg, "border"); +  arg->style = "border: none;" + (arg->style || "");    -  +  int timeout = Roxen.timeout_dequantifier(arg); +  int no_draw = !id->misc->generate_images;    if(arg->split)    {    string res="",split=arg->split;    if(lower_case(split)=="split") split=" ";    m_delete(arg,"split");    c=replace(c, "\n", " ");    int setalt=!arg->alt;    foreach(c/split-({""}), string word)    { -  string fn = image_cache->store( ({ p, word }),id ); -  mapping size = image_cache->metadata( fn, id, 1 ); +  string fn = image_cache->store( ({ p, word }), id, timeout ); +  mapping size = image_cache->metadata( fn, id, no_draw, timeout);    if(setalt) arg->alt=word;    arg->src=query_absolute_internal_location(id)+fn+ext;    if( size )    {    arg->width = (string)size->xsize;    arg->height = (string)size->ysize;    } -  +  else { +  m_delete (arg, "width"); +  m_delete (arg, "height"); +  }    res+=Roxen.make_tag( "img", arg, xml )+" ";    }    return sprintf(lp,res);    }    -  string num = image_cache->store( ({ p, c }), id ); -  mapping size = image_cache->metadata( num, id, 1 ); +  string num = image_cache->store( ({ p, c }), id, timeout ); +  mapping size = image_cache->metadata( num, id, no_draw, timeout );    if(!arg->alt) arg->alt=replace(c,"\"","'");       arg->src=query_absolute_internal_location(id)+num+ext;    if(size) {    arg->width=(string)size->xsize;    arg->height=(string)size->ysize;    }       if(arg->magic)    {    string magic=replace(m_delete(arg,"magic"), "'", "`");       if(p->bevel) p->pressed=1;       m_delete(p, "fgcolor");    foreach(glob("magic-*", indices(arg)), string q) -  p[q[6..]]=arg[q]; +  p[q[6..]]=m_delete(arg, q);       if(!p->fgcolor) p->fgcolor=id->misc->defines->theme_alink||    id->misc->defines->alink||"#ff0000";    -  string num2 = image_cache->store( ({ p, c }),id ); -  size = image_cache->metadata( num2, id ); +  string num2 = image_cache->store( ({ p, c }), id, timeout ); +  size = image_cache->metadata( num2, id, UNDEFINED, timeout );    if(size) {    arg->width=(string)max(arg->xsize,size->xsize);    arg->height=(string)max(arg->ysize,size->ysize);    }    -  if(!id->supports->images) return sprintf(lp,arg->alt); + #ifdef GTEXT_RANDOM_PREFIX +  string sn = "gtext" + get_gtext_pfx(id) + id->root_id->misc->gtext_mi++; + #else +  string sn = "gtext" + id->root_id->misc->gtext_mi++; + #endif +  RXML_CONTEXT->set_root_id_misc("gtext_mi", id->root_id->misc->gtext_mi);    -  string sn = "gtext" + id->misc->gtext_mi++; -  if(!id->supports->js_image_object) { +  if(honor_supports && !id->supports->js_image_object) {    return (!input)?    ("<a"+ea+"href=\""+url+"\">"+Roxen.make_tag("img",arg+(["name":sn]),xml)+"</a>"):    Roxen.make_tag("input",arg+(["type":"image"]),xml);    }       arg->name=sn; -  string res="<script>\n"; -  if(!id->misc->gtext_magic_java) { -  res += "function gtext_mo(ri,hi,txt)\n" -  "{\n" -  " document.images[ri].src = hi.src;\n" -  " if( txt != 0 )\n" -  " setTimeout(\"top.window.status = '\"+txt+\"'\", 100);\n" -  "}"; -  } -  id->misc->gtext_magic_java="yes"; +  string res="<script type='text/javascript'>\n"; +  res += gtext_javascript(id);       return    res+    " "+sn+"l = new Image("+arg->width+", "+arg->height+");"+sn+"l.src = \""+arg->src+"\";\n"    " "+sn+"h = new Image("+arg->width+", "+arg->height+");"+sn+"h.src = \"" +    query_absolute_internal_location(id)+num2+ext+"\";\n"    "</script>\n"+    "<a"+ea+"href=\""+url+"\" "+    (input?"onClick='document.forms[0].submit();' ":"")    +"onMouseover=\"gtext_mo('"+sn+"',"+sn+"h,"+((strlen(magic) && magic != "magic")?