/* 3 Nov 1997.   SRE-Filter utility and generic CGI-BIN script:
GIF_TEXT: create a gif file containing a message, using an
          "alphabyte" collection of character files.

This program will work as a:
 1)"native" SRE-Filter add-on,
 2) as a generic CGI-BIN script
 3) a stand/alone program
...it will automatically detect how it's being called.

NOTE: You MUST set the GIF_DIR_ROOT parameter below (other parameters are
      optional).

************************************************************************/

signal on error name wow1 ; signal on syntax name wow1 ;


/******************************************************
  ***********BEGIN USER CHANGABLE PARAMETERS ******************************
  ***********BEGIN USER CHANGABLE PARAMETERS ******************************
  ***********BEGIN USER CHANGABLE PARAMETERS ******************************/
 
/*-                   --------------------
                    User changeable parameters

The user changeable parameters are:

GIF_DIR_ROOT : The "root" directory of the "alphabytes".

         !!!!  YOU MUST SET GIF_DIR_ROOT WHEN YOU INSTALL GIF_TEXT  !!!!!
                All the other parameters can be left unchanged with
                minimal deteriment, but GIF_TEXT will not work
                if GIF_DIR_ROOT is not properly set.

SEND_PIECES: Try to send early versions of the image, as they become available
DEFAULT_FONT : The default "alphabyte font" (actually, it's directory)
FONT_NAME : Name (prefix) used to match characters to .GIF files
FONT_INDEX : Index file with "alphabyte specific" configuration information
CACHE_SIZE : The maximum number of images to "cache"
CACHE_DURATION : Maximum duration of cached images
HEIGHT : Default height of the created image
WIDTH : Default width of the created image
X_FRAME: Width of frame, in pixels (left and right)
Y_FRAME: Height of frame, in pixels (top and bottom)
TIME_FMT : Default format to use when display current time
DATE_FMT : Default format to use when displaying current date
DEF_BACKCOLOR= Default backcolor
DEF_TEXTCOLOR= Default textcolor (used when no character .GIF file is available)
DEF_TRANSPARENT = Default transparent color index
DEF_TEXTSIZE = Default size of default text characters

*  You MUST set the GIF_DIR_ROOT parameter
*  The SEND_PIECES parameter is useful if you are using SRE-Filter, and 
    large/complicated images are likely
    to be created (which may require the client to wait a minute or more). 
*  You should, but do not need to, set the TIME_FMT, DATE_FMT, and FONT_DIR
   parameters.
*  You should probably set HEIGHT=0 and WIDTH=0.
*  You should probably set FONT_NAME=' ' and FONT_INDEX=' '
*  The DEF_BACKCOLOR, and DEF_TEXTCOLOR are usually
   overridden by "alphabyte" specific values,so you probably
   don't need to worry about them.
*  The DEF_TEXTSIZE is  rarely used (only if there are NO matching characters)!
*  The DEF_TRANSPARENT should almost always equal 0

*  CACHE_SIZE=100 and CACHE_DURATION=1 are reasonable values; but if you
   want to avoid clutter, set CACHE_SIZE=0 (in which case, the 
   CACHE option is ignored)
 

                                -----------------
-*/
/*  !!!! You MUST set the GIF_DIR_ROOT parameter !!!! */
/* The "base directory" of the alphabytes (the collection of character gifs) */
/* if no drive is specified, the default drive (i.e.; the goserve working
directory) will be used */
gif_dir_root='\goserve\alphabyt'

/* Attempt to send "pieces" (actually, less detailed versions) of the image as it becomes
   available (only works with browsers that recognize connection:keep-alive). 
   1=yes, 0=no
   Send_pieces will ONLY work if GIF_TEXT is called as an SRE-Filter addon*/
send_pieces=1


/* Default font directory (relative to gif_dir_root) */
DEFAULT_FONT='enviro'

/* default font name.If ' ', use "font_dir own name". This should NOT
   include directory information */
font_name=' '

/* default "index file" (in font_dir) -- contains alphabyte specific
   configuration information. If ' ', usein font_name.ind.
   This should NOT contain subdirectory information. */
font_index=' '

/* the maximum number of images to cache. 0 means "disable caching of images"*/
cache_size=100

/* the maximum lifespan of an image cache file. 0 means "disable caching".
   (measured in days, no fractions allowed). */
cache_duration=1

/* default height in pixels (0= as big as needed) */
height=0
/* default width in pixels (0=as big as needed) */
width=0

/* default size of frame, left and right */
x_frame=0

/* default size of frame, top and bottom */
y_frame=0

/* time format (using REXX TIME('x') syntax); eg; N= 15:32:01*/
time_fmt='N'
/*date format (using REXX DATE('x') syntax ); eg; N=16 Jun 1997 */
date_fmt='N'

/* Set the default RGB intensities for the background (color table #0)
   Use a 6-hex-character (00 to ff); with 000000=black and ffffff=white.
   This may be overridden by the font-index file, or by an option  */
def_backcolor=b0b0b0


/* set values to use for characters when a .gif file can not be found 
   This may be overridden by the font-index file, or by an option */
def_textcolor=ffffff

/* size of text, in pixel, if NO gifs found */
def_text_size =15

/* set the "transparent color index" -- use a value between 0 and 255.
  If you do NOT want a transparent color index, use -1. */
def_transparent=0

/* verbose level (only used if called as cgi-bin script:
  0=none, 1=minimal, 2=more
 If called as sre-filter addon, then sre-filter's VERBOSE variable is used */
def_verbose=2

/* background scaling: 1 for yes, 0 for use tiles */
back_scale=0


/********** END of USER CHANGABLE PARAMETERS *********/
/********** END of USER CHANGABLE PARAMETERS *********/
/********** END of USER CHANGABLE PARAMETERS *********/

foo=rxfuncquery('rxgdloadfuncs')
if foo=1 then do
  Call RxFuncAdd 'RxgdLoadFuncs', 'RXGDUTIL', 'RxgdLoadFuncs'
  Call RxgdLoadFuncs
end
/* Load up advanced REXX functions */
foo=rxfuncquery('sysloadfuncs')
if foo=1 then do
  call RxFuncAdd 'SysLoadFuncs', 'RexxUtil', 'SysLoadFuncs'
  call SysLoadFuncs
end
if datatype(CACHE_SIZE)<>'NUM'  then cache_size=0
if datatype(CACHE_DURATION)<>'NUM'  then cache_size=0
if datatype(DEF_VERBOSE)<>'NUM'  then def_verbose=1
if datatype(back_scale)<>'NUM'  then back_scale=0

if cache_size<1  then cache_size=0
if cache_duration<1 then cache_size=0

send_delay=12           /* time to wait before SENDing a piece */

if filespec('D',gif_dir_root)=' ' then do
     oof=directory()
     arf=filespec('d',oof)
     gif_dir_root=arf||gif_dir_root
end  /* Do */
if gif_dir_root=' ' then
    gif_dir_root=strip(basedir,'t','\')||'\alphabyt'

gif_dir_root=strip(gif_dir_root,'t','\')||'\'

parse arg  ddir, tempfile, reqstrg,list,verb ,uri,user, ,
          basedir ,workdir,privset,enmadd,transaction,verbose, ,
         servername,host_nickname,homedir

a_box=d2c(254)

/* check for CGI-BIN call */
is_cgi=0
if verb="" then do    /* is it cgi-bin? */
   method = value("REQUEST_METHOD",,'os2environment')
   if method="" then do
       list=ask_values()
       if list="" then exit
       is_cgi=2         /* signals "stand alone */
   end /* do */
   else do
      is_cgi=1
      list=value("QUERY_STRING",,'os2environment')
      verbose=def_verbose
   end
end

if is_cgi=0 then parse var uri . '?' list   /* if srefilter addon, get purer version of request string */

aa=sysfiletree(gif_dir_root||'*.*','arf','b')
if arf.0=0 then do
   call gpmprintf(" GIF_TEXT: GIF_DIR_ROOT is empty or missing: "gif_dir_root)
   if is_cgi=1 then
        return
   return 0
end /* do */


/*  request options understood:
   FONT_DIR, SEND, FONT_NAME, FONT_INDEX, TIME_FMT, DATE_FMT, BACKCOLOR,
   TEXTCOLOR, TRANSPARENT, WIDTH, HEIGHT, LITERAL,X_FRAME,y_FRAME 
    X_SCALES Y_SCALES V_ALIGN 
    SLIDE SLIDE_VERT SLIDE_HORIZ SLIDE_THRESH SLIDE_PROB
    SLIDE_COORD SLIDE_SIZE SLIDE_RED SLIDE_GREEN SLIDE_BLUE 
 */

/* set to blank means "use font_index value if none specified in request */
send_bim=0
back_file=' ' ; text=' '
amessage=' ' ; cache_file=' ';  do_cache=0
backcolor=' ' ; textcolor=' ' ; transparent=""
fontdir=default_font; fontname=font_name ; fontindex=font_index
many_type=0 ; many_type_max=0
x_scales="" ;y_scales="" ; y_valign="" ; slide="" ;  slide_vert="" ; 
slide_thresh=' ' ; slide_horiz=''
slide_red="" ; slide_green="" ;slide_blue=""
slide_size=""
slide_coord=""
slide_xcoord="" ; slide_ycoord="" ; slide_prob=''

/* pull options from request */
literal=0 ;
 do until list=""                /* get user input */
   parse var list a1 '&' list
   parse var a1 a1a '=' a1b0
   a1a=translate(strip(a1a))
   a1b1=packur2(a1b0)
   a1b=strip(translate(a1b1))
   select
      when a1a="FONT_DIR" | a1a="FONT" then do
          if a1b<>' ' then fontdir=a1b
      end  /* Do */
      when a1a="FONT_NAME" | a1a="NAME" then do
           if a1b<>' ' then fontname=a1b
      end
      when a1a="FONT_INDEX" | a1a="INDEX" then do
            if a1b<>' ' then fontindex=a1b
      end  /* Do */
      when abbrev(a1a,'TIME')=1 then time_fmt=a1b
      when abbrev(a1a,'CACHE')=1 then do
                cache_file=strip(a1b)
                do_cache=1
                if a1a='CACHE2' then do_cache=2
      end  /* Do */
      when abbrev(a1a,'DATE')=1 then date_fmt=a1b
      when abbrev(a1a,"TEXTC")=1 then textcolor=a1b
      when abbrev(a1a,"BACKG")+abbrev(a1a,"BACKC")>0 then backcolor=a1b
      when abbrev(a1a,"TRANS")=1 then do
         if datatype(a1b)='NUM' then transparent=a1b
      end
      when a1a="WIDTH" | a1a="W" then width=a1b
      when a1a="SEND" then send_pieces=a1b
      when abbrev(a1a,'MANY_')=1 then do
          if datatype(a1b)='NUM' then
               many_type_max=a1b
          else
               many_type=wordpos(translate(a1b),'CYCLE FIT END RANDOM')
      end /* do */
      when abbrev(a1a,"X_F") then do
           if datatype(a1b)='NUM' then x_frame=a1b
      end
      when abbrev(a1a,"Y_F")=1 then 
          if datatype(a1b)='NUM' then y_Frame=a1b

      when a1a="HEIGHT" | a1a="H" then height=a1b
      when abbrev(a1a,"LIT") then literal=a1b
      when a1a="BACK" | a1a="BACK_FILE" then back_file=a1b
      when a1a="BACK_SCALE" | a1a="BKSC" then back_scale=wordpos(translate(a1b),'Y YES 1')
      when abbrev(a1a,'X_SC')+abbrev(a1a,'XSCA')>0 then x_scales=a1b
      when abbrev(a1a,'Y_SC')+abbrev(a1a,'YSCA')>0 then y_scales=a1b
      when abbrev(a1a,'VALI')+abbrev(a1a,'V_ALI')>0 then y_valign=a1b
      when abbrev(a1a,'SLIDE_H')  then slide_horiz=a1b
      when abbrev(a1a,'SLIDE_T')  then slide_thresh=a1b
      when abbrev(a1a,'SLIDE_V')  then slide_vert=a1b
      when abbrev(a1a,'SLIDE_F')| a1a='SLIDE'  then  do
          ee=translate(a1b,'\','/')
          ee=strip(a1b,'l','\')
          slide=gif_dir_root||ee
      end
      when abbrev(a1a,'SLIDE_S')  then do
          if datatype(a1b)='NUM'  then slide_size=a1b
      end  /* Do */
      when abbrev(a1a,'SLIDE_C')  then slide_coord=a1b
      when abbrev(a1a,'SLIDE_R')  then slide_red=a1b
      when abbrev(a1a,'SLIDE_G')  then slide_green=a1b
      when abbrev(a1a,'SLIDE_B')  then slide_blue=a1b
      when abbrev(a1a,'SLIDE_P')  then slide_prob=a1b

      when a1a="MESSAGE" | a1a="TEXT" then do
         a1b0=strip(a1b0,,'"')
         amessage=packur2(a1b0)
      end
      otherwise nop
   end  /* select */
end /* do */
if amessage="" then amessage=' '

if verbose>1 then call gpmprintf(' GIF_TEXT font= ' fontdir ', message: 'amessage)

/* if send_pieces, then see if the browser supports multi part documents (connection:keep-alive) */
if is_cgi=0 & wordPos(upper(send_pieces),'Y YES 1')>0 then do
    a=upper(strip(reqfield('Connection')))
    a2=upper(strip(reqfield('PROXY-Connection')))
    if a<>'KEEP-ALIVE' & a<>'MAINTAIN' , 
       & a2<>'KEEP-ALIVE' & a2<>'MAINTAIN' then do
         send_pieces=0            /* browser does NOT support connection:keep-alive */
         nsent=0
    end                
    else do
        send_pieces=1   /*  it does */
    end /* do */
end
else  do
   send_pieces=0      /* send_Pieces ONLY works as sre-filter addon */
end  /* Do */

call fix_defaults               /* set some default parameters */

call fix_options                /* using font_index and request stuff, set options */

call fix_message                /* fix up message (special code replacmenet */

/* check on default font info */
font_ind.!ndims=0
if font_ind.!defgifs<>' ' then do
   do iww=1 to words(font_ind.!defgifs)
 
      adefgif=strip(word(font_ind.!defgifs,iww))
      bdefgif=gif_dir||adefgif
      dim= RxgdImageCreateFromGIF(bdefgif)
      if dim=1 | dim=0 then do
           CALL gpmprintf(' GIF_TEXT: missing alphabyte specific default:'adefgif)
           iterate
      end
      ndims=ndims+1
      att=transparent ; if att<0 then att=0
      dim.ndims=dim
      dim.ndims.!name=adefgif
      if ndims=1 then do
         dim_r=rxgdimagered(dim,att)
         dim_g=rxgdimagegreen(dim,att)
         dim_b=rxgdimageblue(dim,att)
      end
  end  /* Do */
  font_ind.!ndims=ndims
end  /* Do */

/* DONE WITH INITIALIZATIONS  ----------------------- */

/* for each charater in message, get it's gif file (if avaiable), it's
   size, and it's scale factors */

len = Length(amessage)          /* amessage is message, after $t, etc modifications */
xmess=0; ymess=0 ; cfound=0
ysize_tot=0 ; xsize_tot=0
do l = 1 to len
   cls.l=' '              /* the l'th characters GIF file. ''=n.a. */
   cls.!xscale.l=get_user_scale(l,len,x_scales) /* char specific width scale factor */
   cls.!yscale.l=get_user_scale(l,len,y_scales) /* char specific height scale factor */
   cls.!xsize.l=0                       /* char width (0=n.a. */
   cls.!ysize.l=0                       /* char height (0=n.a. */

/* note: xscale and yscale are image independent (uses character position in
  the message, and the user_scale parameter) */

   achar = substr(amessage,l,1)

   ichar=c2d(achar)
   select
      when ichar=1 | ichar=2 then do    /* am or pm character */
          achar='PM' ; if ichar=1 then achar='AM'
          cl=get_gifname(achar,gif_dir,fontname)  /* may use UC for LC, etc. */
          if cl=' ' then iterate
      end  /* Do */
      when ichar=254 then do            /* filled box characher */
          cls.l=achar 
          iterate
      end /* do */
      when ichar>139 then do            /* special $x character */
         ichar0=ichar-140
         if symbol('FONT_IND.!chars.'||ichar0)<>'VAR' then iterate /* error, skip*/
         cl=gif_dir||font_ind.!chars.ichar0
         if stream(cl,'c','query exists')=' ' then iterate
      end  /* Do */
      otherwise do             /* normal character -- check for file */
         if achar=' ' then iterate
         cl=get_gifname(achar,gif_dir,fontname)
         if cl=' ' then iterate
      end
   end
/* double check -- is it a gif file? */
   im = RxgdImageCreateFromGIF(cl)
   IF (im = 1 | im=0) THEN do
      IF VERBOSE>0 then call gpmprintf("GIF_TEXT bad GIF file: " cl' 'im)
      iterate
   end

   cls.l=cl             /* use the CL gif file for this l'th character */

   cfound=cfound+1

   cls.!xsize.l=RxgdImageSX(im)
   cls.!ysize.l=RxgdImageSY(im)

   xsize_tot=xsize_tot+cls.!xsize.l
   ysize_tot=ysize_tot+cls.!ysize.l

   Call RxgdImageDestroy im
end

/* Now, use CLS. to determine HEIGHT AND WIDTH OF MESSAGE */
xmess=X_FRAME*2 ; ymess=Y_FRAME*2 ; yf2=ymess
do mm=1 to len          /* note: n.a. characters do not contribute to these calcluations */
    xmess=xmess+trunc(cls.!xsize.mm*cls.!xscale.mm)
    if trunc(yf2+(cls.!yscale.mm*cls.!ysize.mm))>ymess then 
          ymess=yf2+trunc(cls.!yscale.mm*cls.!ysize.mm)
end /* do */

/* adjust for spaces and missing chars */
select
  when cfound=0 then do                 /* no characters found */
     ysize0=def_text_size ; xsize0=def_text_size
     IF FONT_IND.!ndims>0 then DO               /* not generic default, use complete font info */
         xSIZE0=FONT_ind.!WCHAR-(font_ind.!leftoffset+font_ind.!rightoffset)  /*correct for discarded offsets */
         Ysize0=FONT_IND.!HCHAR-(font_ind.!topoffset+font_ind.!bottomoffset)
     END
     do mmm=1 to len            /* fill in CLS. (sort of a stupid approach) */
        cls.!xsize.mmm=xsize0
        cls.!ysize.mmm=ysize0
        xmess=xmess+(xsize0*cls.!xscale.mmm)
        ymess=max(ymess,yf2+trunc(ysize0*cls.!yscale.mmm))
     end /* do */
  end  /* Do */

  when len=found then nop               /* all characters found */

  otherwise do                          /* some characters found */
     xavgsize=trunc(xsize_tot/cfound)    /* average size of found characters */
     yavgsize=trunc(ysize_tot/cfound)
     do mmm=1 to len              /* set values for n.a. characters */
        if (cls.mmm<>'' & cls.mmm<>a_box)  then iterate   /* got values, so skip */
        xmess=xmess+(xavgsize*cls.!xscale.mmm)
        cls.!xsize.mmm=xavgsize
        cls.!ysize.mmm=yavgsize
     end /* do */
  end   /* otherwise */

end  /* adjusting size for spaces etc. */

/* we now know the total image size (xmess and ymess), and the
size/scale/file for each character in the message (cls.) */

/* determine whole  image scale factors, if any */
width_fact=1
height_fact=1
if datatype(width)='NUM' then do   
  if width>0 then do 
      width_fact=(width-(2*x_Frame))/(xmess-(2*X_FRAME))   /* will force xmess into frame corrected width */
      xmess=width
  end  /* Do */
end  /* Do */
if datatype(height)='NUM' then do
  if height>0 then do 
     height_fact=(height-yf2)/(ymess-yf2)
     ymess=height
  end  /* Do */
end 

y_useable=ymess-(2*y_frame)  /* height that can be written to */

/* xmess and ymess are the width/height of message (either determined
from message+FRAME, or preset. Width_fact and height_fact will force actual
characters to fit into this rectangle */

/* create a message buffer of required, or desired, size */
   messim=rxgdimagecreate(xmess,ymess)
   if messim=1 | messim=0 then do
      if verbose>0 then  call gpmprintf(" could not create new message buffer ")
      if is_cgi=0 then do
         'NODATA'
         return '400 0'
      end
      else do
           return 
      end /* do */
   end  /* Do */
   if slide<>' ' | slide_size>0 then do                /* if color slide, use seperate back file */
         messim_b=rxgdimagecreate(xmess,ymess)
         if messim_b=1 | messim_b=0 then slide=' '
   end

/* set background, default text, and transparent colors */
   oy=rxgdimagecolorallocate(messim,red_back,green_back,blue_back)
   if slide<>' ' | slide_size>0 then  oy=rxgdimagecolorallocate(messim_b,red_back,green_back,blue_back)
 
   if transparent >-1  then do
       call rxgdimagecolortransparent messim,transparent
       if slide_size>0 | slide<>' ' then call rxgdimagecolortransparent messim_b,transparent
   end

/* fill in the background ? */

back_file=strip(translate(back_file,'\','/'),,'\')
bf2=back_file
if back_file<>' ' then back_file=stream(gif_dir_root||back_file,'c','query exists')
if back_file="" & bf2<>"" & pos(".gif",bf2)=0 then
   back_file=stream(gif_dir_root||bF2||'.gif','c','query exists')

mmb=messim 
if slide_size>0 | slide<>' ' then mmb=messim_b   /* where to write background */

if back_file<>' ' then do
   imb = RxgdImageCreateFromGIF(back_file)
   IF (imb = 1 | imb=0) THEN do
      IF VERBOSE>0 then call gpmprintf("GIF_TEXT bad background GIF file: " back_file' 'imb)
      back_file=' '  
   end
   else do              /* write scaled, or tiled/portion */
       if back_scale>0 then do     /* scale image to fit into box */
          srcw=RxgdImageSX(imb)
          srch=RxgdImageSY(imb)
          foo=rxgdimagecopyresized(mmb,imb,0,0,0,0,xmess,ymess,srcw,srch)
       end  /* Do */
       else do
          new1=rxgdimagesettile(mmb,imb)
          h1=trunc(xmess/2)+1  ; w1=trunc(ymess/2)+1
          fpp=rxgdimagefill(mmb,h1,w1,new1)  /* fill with multi-pixel color */
       end  /* Do */
   end  /* Do */
   oy=rxgdimagecolorstotal(mmb) 
   if verbose>1 then call gpmprintf(' GIF_TEXT: # of colors in background = ' oy)
end

/* background is done; should it be sent as a preliminary version? */
if send_pieces=1 then do
   oof=img_to_var(mmb,tempfile,1)   /* copy image handle to var; signal errdone if problem */
   fexp=value(enmadd||'FIX_EXPIRE',,'os2environment')
   foo=sref_multi_send(oof,'image/gif','S',,verbose,fexp)
   if foo<0  then signal errdone
   send_bim=rxgdimagecreatefromgif(tempfile)
   foo=sysfiledelete(tempfile)
   nsent=1
  if verbose>1 then call gpmprintf(' GIF_TEXT: SENDing background ')
end /* do */

/* Now copy the appripriate alphabet gifs to the message buffer */

nowx=x_frame
do l=1 to len
  achar=substr(amessage,l,1) ; fromdef=0  /* fromdef: 0=own.gif, 1=complete font, 2= generic */

  if achar=' '  then do          /* a space: skip pixels in image  */
      nowx=nowx+trunc(width_fact*cls.!xscale.l*cls.!xsize.l)
      iterate
  end

  if achar=a_box then do                        /* filled box,  treat as a special "default" character */
      im=rxgdimagecreate(16,16)
      oy=rxgdimagecolorallocate(im,red_back,green_back,blue_back)
      text_color=rxgdimagecolorallocate(im,red_text,green_text,blue_text)
      foo=rxgdimagefilledrectangle(im,0,0,15,15,1)
      xsize=16 ; ysize=16
      fromdef=2
      xsc1=cls.!xsize.l/xsize
      ysc1=cls.!ysize.l/ysize
      cls.!xscale.l=cls.!xscale.l*xsc1
      cls.!yscale.l=cls.!yscale.l*ysc1
      cls.!xsize.l=xsize
      cls.!ysize.l=ysize
  end

  else do                       /* a character */
     cl=cls.l
     if cl=' ' then     /* n.a. character */
         im=1               /* signal "n.a." .gif file */
     else
        im = RxgdImageCreateFromGIF(cl)
  end

  ichar=c2d(achar)              /* might be speial character */

/*  if no such file, use generic or complete font */
  select

     when im<=1 & (ichar<10 | ichar>139) & (ichar<>254) then do   /* missing special charcter == use space character */
        nowx=trunc(width_fact*cls.!xscale.l*cls.!xsize.l)+nowx
        iterate
     end  /* Do */

     when im <= 1  THEN   do       /* missing, use generic or DEFAULT font */
        im=get_default_char(achar,l,len,many_type_max)   /* alphabyte specific default? */

        if im<>1 then do    /* got an image containing the font */
          if  verbose>1 then call gpmprintf(' GIF_TEXT: using alphabyte specific default for character ' achar)
          xsize=RxgdImageSX(im)      /* complete font (useable) size */
          ysize=RxgdImageSY(im)  
          fromdef=1
        end

        else do         /* no complete font -- use generic default */
          if  verbose>1 then call gpmprintf(' GIF_TEXT: using default for character ' achar)
          im=rxgdimagecreate(16,16)
          oy=rxgdimagecolorallocate(im,red_back,green_back,blue_back)
          text_color=rxgdimagecolorallocate(im,red_text,green_text,blue_text)
          foo=rxgdimagestring(im,'G',0,0,achar,text_color)
          xsize=16 ; ysize=16
          fromdef=2
        end

/* scale must scale xsize,ysize to presumed size (cls.!xsize,!ysize);
  and still include character specific scale */
        xsc1=cls.!xsize.l/xsize
        ysc1=cls.!ysize.l/ysize
        cls.!xscale.l=cls.!xscale.l*xsc1
        cls.!yscale.l=cls.!yscale.l*ysc1
        cls.!xsize.l=xsize
        cls.!ysize.l=ysize
     end

     otherwise  do               /* use matching .gif file */
        xsize=cls.!xsize.l
        ysize=cls.!ysize.l
        fromdef=0
     end

  end           /* select */

/* copy to message buffer. Rxgd will take care of color table matching, etc */

/* fix background & transparency */
  foo=0
  if back_File<>' '& fromdef>0  & transparent>-1 then do
    tt=transparent
    if font_ind.!isbw=0 then do
       tt=rxgdimagecolorclosest(im,dim_r,dim_g,dim_b)
    end
    call rxgdimagecolortransparent im,tt
    foo=tt
  end
  else do
   if back_file<>' ' then foo=rxgdimagegettransparent(im)
  end
  if foo=-1 & back_file<>" " & fromdef=0 then do   /* try to fix transparency */
     call rxgdimagecolortransparent im,font_ind.!transparent
  end


/* now, write possibly scaled image to messim.  There are two scales:
  character specific scale: a combo of the "generic/default to average"
                            and the "user-specified character specific scale"
  whole message scale: fit message to specified message width/height
*/

  wfact=width_fact*cls.!xscale.l
  hfact=height_fact*cls.!yscale.l
  xsize=cls.!xsize.l ; ysize=cls.!ysize.l
  if wfact=1 &hfact=1 then do
      select
         when   y_valign='T' then yff=y_frame
         when y_valign='B' then do
              yff=y_frame+(y_useable-ysize)
         end  /* Do */
         when y_valign='M' then do
              yff=y_frame+((y_useable-ysize)/2)
         end  /* Do */
         otherwise yff=y_frame
      end  /* select */

      foo=rxgdimagecopy(messim,im,nowx,yff,0,0,xsize,ysize)
      nowx=nowx+xsize
  end
  else do   /* scale it */
      dxsize=trunc(xsize*wfact)
      dysize=trunc(ysize*hfact)

      select
         when   y_valign='T' then yff=y_frame
         when y_valign='B' then do
              yff=y_frame+(y_useable-dysize)
         end  /* Do */
         when y_valign='M' then do
              yff=y_frame+((y_useable-dysize)/2)
         end  /* Do */
         otherwise yff=y_frame
      end  /* select */
      foo=rxgdimagecopyresized(messim,im,nowx,yff,0,0, ,
                               dxsize,dysize,xsize,ysize)
      nowx=nowx+dxsize
  end  /* Do */

  Call RxgdImageDestroy im

end             /* l'th character of message */

/* if slide used, slideify messim, and then copyit to messim_b */

/* message is done; should it be sent as a secondary version? */
if send_pieces=1 & (slide<>"" | slide_size>0) then do
   foo=rxgdimagecopy(send_bim,messim,0,0,0,0,xmess,ymess) 
   oof=img_to_var(send_bim,tempfile)
   foo=sref_multi_send(oof,'image/gif','M')
   if foo<0 then signal errdone
   nsent=2
   if verbose>1 then call gpmprintf(' GIF_TEXT: SENDing message text ')
end /* do */

/* get first row of slide, and fix up color table */
if slide<>' ' then do              /* read slide from file */
   slim=rxgdimagecreatefromgif(slide)
   if (slim=0 | slim=1 )  then do
          slide=''              /* no slide avaialble */
          if verbose>1 then 
             call gpmprintf(' No Slide file available ')
   end  /* Do */
end  /* Do */

if slide="" & slide_size>0 then do   /* make your own slide */
   slidect.0=slide_size ; slide.0=slide_size
   do mm=1 to slide_size
      mm0=mm-1
      slidect.!r.mm0=map255(get_user_scale(mm,slide_size,slide_red))
      slidect.!g.mm0=map255(get_user_scale(mm,slide_size,slide_green))
      slidect.!b.mm0=map255(get_user_scale(mm,slide_size,slide_blue))
      slide.mm=mm-1
   end /* do */
   slide_vert='N'       /* force it to be "one row" color slide */
   foo=grab_slide(0,slide_horiz,xmess,1,ymess,,slide_xcoord,slide_ycoord)
end

/* valid color slide .gif file, so get the slide */
if slide<>' ' | slide_size>0 then do

   if slide<>' ' & slide_vert='N' then do   /* get slide just once */
       foo=grab_slide(slim,slide_horiz,xmess,0,ymess,,slide_xcoord,slide_ycoord)
       foo=rxgdimagecolorstotal(slim)

/* read the color slide's color table,*/
       foo=rxgdimagegetcolortable(slim,'tt')
       r='R'; g='G'; b='B'
       slidect.0=tt.0
       do il=0 to slidect.0-1
          slidect.!r.il=tt.r.il
          slidect.!g.il=tt.g.il
          slidect.!b.il=tt.b.il
       end
    end

/* if slide_size>0, then we use slidect that was created above */

/* get color table of messim (if slide_thresh_type<>'P') */
   if slide_thresh_type<>'P' then do
      foo=rxgdimagegetcolortable(messim,'tt')
      r='R'; g='G'; b='B'
      messct.0=tt.0
      do il=0 to messct.0-1
        messct.!r.il=tt.r.il
        messct.!g.il=tt.g.il
        messct.!b.il=tt.b.il
      end
   end /* do */

   foo=add_slide_ct(messim)             /* add/remapslide colors to message image */
   if slide_xcoord<>'' & slide_Ycoord<>'' then do
       ixcoord=slide_xcoord*xmess ; iycoord=slide_ycoord*ymess
   end
   nchanges=0

/* Get each row of message image, check and (possibly) convert each pixel to slide colors */
   hey=time('r')                /* timer used for SEND */
   do ny=0 to ymess-1           /* for each row of message image */
     if verbose>1 & ny//25=1 &send_pieces<>1 then call gpmprintf(" GIF_TEXT: Transforming message row " ny ' of ' ymess)
     if slide_vert<>'N' then do
        foo=grab_slide(slim,slide_horiz,xmess,ny,ymess,slide_vert)      /* get slide for this rowl */
     end  /* Do */
     foo=rxgdimagegetrowpixels(messim,ny,pxels)
     do nx=1 to xmess
       apix=pxels.nx
       if slide_thresh="P1" & apix=0 then iterate  /* the most common case */
       doit=do_change(apix,slide_thresh_type,slide_thresh_val,nx,xmess)
       if doit=1 then do
           if slide_xcoord="" | slide_ycoord="" then do
               itmp=slide.nx
           end
           else do
               nnx=max(1,trunc(figdist(nx,ny,ixcoord,iycoord)))
               doit=do_change(1,'P',1,nnx,slide.0,slide_prob,1)  /* check probability */
               if doit=0 then iterate 
               itmp=slide.nnx
           end /* do */
           apix=slidect.!alt.itmp ; nchanges=nchanges+1
        end  /* Do */
        pxels.nx=apix
     end
     drop pxels.0

      styled  = RxgdImageSetStyle(messim, pxels, xmess)         /* write transformed row back to */
      rc = RxgdImageLine(messim, 0,ny,xmess-1,ny,styled)        /*  the message image */

     if send_pieces=1 then do            /* SEND what ya got? */
           hey2=time('e')
           if hey2>send_delay then do
                foo=rxgdimagecopy(send_bim,messim,0,0,0,0,xmess,ymess) 
                oof=img_to_var(send_bim,tempfile)
                foo=sref_multi_send(oof,'image/gif','M')
                if foo<0 then signal errdone
                nsent=nsent+1
                if verbose>1 then call gpmprintf(' GIF_TEXT: SENDing transformed message text ' ny ' of 'ymess)
                hey=time('r')
           end /* do */
     end /* do */

   end              /* transforming row ny */

   foo=rxgdimagecopy(messim_b,messim,0,0,0,0,xmess,ymess)  /* final copy */
   mmb=messim_b

end  /* Do */

else do
    mmb=messim
end

/* copy buffer to a file, and clean up */
if do_cache=0 then do
  gif_file=gif_dir_root||"MES?????.GIF"
  gfile=systempfilename(gif_file)
end

foo=rxgdimagegif(mmb,gfile)

foo= RxgdImageDestroy(messim)
if slide<>' ' then foo= RxgdImageDestroy(messim_b)
do mm1=1 to font_ind.!ndims
  jdim=dim.mm1
  foo= RxgdImageDestroy(jdim)
end /* do */

if send_bim<>0 then foo=rxgdimagedestroy(send_bim)
if back_file<>' ' then  foo=rxgdimagedestroy(imb)
if slide<>' ' then foo=rxgdimagedestroy(slim)
IF VERBOSE>1 then CALL GPMPRINTF(' GIF_TEXT: completed image of size ' xmess ' x ' ymess )

shipit: nop             /* jump here if cache entry found */

if is_cgi=0 then do             /* srefilte addon */
  signal on failure name nocon
  if send_pieces=1 then do            /* final send? */
      oof=charin(gfile,1,chars(gfile))
      foof=stream(gfile,'c','close')
      foo=sref_multi_send(oof,'image/gif','E')
      ieek=stream(gfile,'c','query size')
      if do_cache=0 then   foo=sysfiledelete(gfile)
      if foo<0  then signal errdone
      nsent=3
      return 200' 'ieek
  end /* do */
/* else, use 'FILE  */
  if do_cache=0 then do
     return 'FILE ERASE TYPE image/gif name ' gfile     /* let sre deal with reply */
   end
   else do
     return 'FILE  TYPE image/gif NOCACHE name ' gfile
   end
   oof=stream(gfile,'c','query size')
  return '200 '||oof
end
if is_cgi=1 then do             /* cgi-bin */
  Say "Content-type: image/gif"
  Say
  ki=chars(gfile); foo=stream(gfile,'c','close')
  foo=charin(gfile,1,ki) 
  foo2=stream(gfile,'c','close')
  call charout,foo
  if result<>0 then 
     call gpmprintf(" GIF_TEXT CGI-BIN error: not all of file written: "||foo3)
  if do_cache=0 then  foo=sysfiledelete(gfile)
  return 
end /* do */

if is_cgi=2  then do            /* stand alone */
  foo2=stream(gfile2,'c','close')
  ki=stream(gfile,'c','query size')
  foo=charin(gfile,1,ki) 
  aa=charout(gfile2,foo,1)
  IF AA>0 then 
      SAY " Problem writing to outfile: " gfile2
  else
     say gfile2 " created (length = " ||stream(gfile2,'c','query size')
  foo=stream(gfile2,'c','close')
  foo=sysfiledelete(gfile)

   exit
end  /* Do */


errdone:
if is_cgi=1 then do
  Say "Content-type: text/plain"
  Say
  say "GIF_TEXT error at line  " sigl " (RC=" rc
  return 
end /* do */

say "GIF_TEXT error at line  " sigl " (RC=" rc
if is_cgi=0 then do
   'NODATA'
   return  '400 0'
end
exit

/******************/
/* copy an image to a variable (copy of what would be in .gif file */
img_to_var:procedure expose tempfile
parse arg im,afile,keepit
if afile=""  then afile=tempfile
foo1=rxgdimagegif(im,afile)
oof=charin(afile,1,chars(afile))
if oof="" then signal errdone           /* empty -- must be aproblem */
foo=stream(afile,'c','close')
if keepit<>1 then foo=sysfiledelete(afile)
return oof


/**************************************************/
/* set/cleanup DEFAULT parametrs */
fix_defaults:

red_text=200 ;green_text=200 ; blue_text=200
red_back=255 ; green_back=255 ; blue_back=255

def_transparent=check_byte(def_transparent,-1)
def_text_size=check_byte(def_text_size,15)
if fontname=0 then fontname=' '
if fontindex=0 then fontindex=' '
if back_file=0 then back_File=' '

gif_dir=gif_dir_root||strip(fontdir,,'\')||'\'

/* check the cache? */
if do_cache=1 then
  if pos('$D',translate(amessage))+pos('$T',translate(amessage))>0 then do_cache=0
if cache_size=0 then do_cache=0

/* use a cached file? */
foo=do_from_cache(cache_file)
if foo=1 then signal shipit

oof=translate(fontdir,'  ','\/')
if fontname=' ' then fontname=strip(word(oof,words(oof)))
if fontindex=' ' then fontindex=fontname||'.IND'

fontindex=gif_dir||fontindex
dim=0 ; ndims=0;font_ind.!defgifs=' '
dim_r=0;  dim_g=0 ; dim_b=0
return 1



/***************************/
/* set options, using font_index and request stuff */
fix_options:
/* get font index, and possibly  text and back colors and default-font info */
inind=READ_FONT_INDEX(fontindex)  /* read in font index, and back and text color_index*/

if textcolor="" then textcolor=font_ind.!textcolor
if backcolor="" then backcolor=font_ind.!backcolor
vvs=get_from_hex(textcolor)
if vvs<>' ' then do
   parse var vvs red_text green_text blue_text
end  
vvs=get_from_hex(backcolor)
if vvs<>' ' then do
   parse var vvs red_back green_back blue_back
end  /* Do */
/* many_defaults from options ? */
if many_type>0 then font_ind.!manytype=many_type

/* if no x_scales or y_scales in request, use .IND file (if exists) */
if x_scales<>"" then font_ind.!x_user_scale=x_scales
if y_scales<>"" then font_ind.!y_user_scale=y_scales
x_SCALES=FIX_SCALE(FONT_IND.!X_USER_SCALE)
Y_SCALES=FIX_SCALE(FONT_IND.!Y_USER_SCALE)

if y_valign="" then y_valign=font_ind.!y_valign
y_valign=upper(y_valign)
select
  when abbrev(y_valign,'B')=1 then y_valign='B'
  when abbrev(y_valign,'T')=1 then y_valign='T'
  when abbrev(y_valign,'M')+abbrev(y_valign,'C')>0 then y_valign='M'
  otherwise y_valign='T'
end

if slide_vert="" then slide_vert=font_ind.!slide_vert
/* slide_vert= Tile, Fit, None */
slide_vert=left(strip(upper(slide_vert)),1)
if pos(slide_vert,'TFN')=0 then slide_vert='N'  /* use 1 slide is default */

if slide_horiz="" then slide_horiz=font_ind.!slide_horiz
slide_horiz=left(strip(upper(slide_horiz)),1)
/* slide_horiz types: Tile. Resize */

if slide="" then slide=font_ind.!slide
if slide<>'' then do
   stmp=slide
   slide=stream(slide,'c','query exists')
   if  slide=' ' & pos('.',stmp)=0 then do  /* try adding .gif to end */
       slide=stream(stmp||'.gif','c','query exists')
   end  /* Do */
end  /* Do */

if slide_thresh="" then slide_thresh=font_ind.!slide_thresh
slide_thresh=upper(strip(slide_thresh))
slide_thresh_type=left(slide_thresh,1)
slide_thresh_val=substr(slide_thresh,2)
slide_thresh_val=strip(translate(slide_thresh_val,' ','+:'))  /* might be list of values */

if slide_size="" then  slide_size=font_ind.!slide_size
if slide_size<>0 then do
  if slide_green="" then  slide_green=font_ind.!slide_gre en
  if slide_red="" then  slide_red=font_ind.!slide_red
  if slide_blue="" then  slide_blue=font_ind.!slide_blue
  slide_green=fix_scale(slide_green)
  slide_red=fix_scale(slide_red)
  slide_blue=fix_scale(slide_blue)
end

if slide_prob="" then slide_prob=font_ind.!slide_prob
slide_prob=fix_scale(slide_prob)

if slide_coord="" then slide_coord=fonT_ind.!slide_coord
slide_coord=fix_scale(slide_coord)  
parse var slide_coord tx ty
if datatype(tx)='NUM' & datatype(ty)='NUM' then do
        slide_xcoord=tx
        slide_ycoord=ty
end /* do */
if pos(slide_thresh_type,'PCB')=0  then do
     slide=''
     call gpmprintf(' Error specifying slide_thresh:'slide_thresh)
end  /* Do */
do ll=1 to words(slide_thresh_val)
  if datatype(strip(word(slide_thresh_val,ll)))<>'NUM' then do
     slide=''                                                        
     call gpmprintf(' Error specifying slide_thresh:'slide_thresh)   
  end
end

if verbose >1 & slide<>' ' then  do
  call gpmprintf(" GIF_TEXT: Using color slide " slide)
end
else do
  if verbose >1 & slide_size>0  then call gpmprintf(" GIF_TEXT: Using generated color slide, #colors=" slide_size)
end

/* what's the "transparent" color table entry */
if transparent='' then    /* not specified in request */
    transparent=font_ind.!transparent
if transparent>255 | transparent <-1 then transparent=def_transparent /* is it copecetic? */

return 1


/**************************************/
/* fix up message */
/* convert $x into time, date, etc. */
fix_message:
if literal<>1  & pos('$',amessage)<>0 then do
/* parse amessage, converting $x into appropriate stuff. Note that $$ (or $$$..)
   is interpreted at $ (or $$...) */
  newmess=""
  do until amessage=""
       parse var amessage m1 '$' m2
       newmess=newmess||m1
       if m2="" then leave
       if abbrev(m2,'$')=1 then do  /* strip out $ and display */
          amessage=strip(m2,'l','$')
          idls=length(m2)-length(amessage)
          newmess=newmess||copies('$',idls)
          iterate
       end  
       akey=translate(left(m2,1))
       select
          when  akey='T' then newmess=newmess||get_time(time_fmt)
          when  akey='D' then newmess=newmess||get_date(date_fmt)
          when  akey='S' then newmess=newmess||'SERVERNAME'
          when pos(akey,'1234567890')>0 then do
             rval=akey
             akey2=translate(substr(m2,2,1))
             if pos(akey2,'1234567890')>0 then do
                 rval=(rval*10)+akey2
                 amessage=substr(m2,2)
             end
             newmess=newmess||d2c(rval+140) /* if val>139, then it's a special */
          end
          when akey='B' then  newmess=newmess||d2c(254) /* 254 is "filled box " */
          otherwise nop
       end
       amessage=substr(m2,2)
  end /* do */
  amessage=newmess
end  /* interpret $x */
return 1

/***********************************/
/* map a 0.. 1 to 0..255 */
map255:procedure
parse arg a1
return trunc(max(min(a1*255,255),0))

/***********************************/
/* change this pixel ? */
do_change:procedure expose messct.
parse arg apix,atype,aval0,jjx,xlen,slide_prob,useaval
if useaval=1 then
 aval=aval0
else
  aval=get_user_scale(jjx,xlen,aval0) /* pixel specific threshold */

aprob=get_user_scale(jjx,xlen,slide_prob)   /* probability of using scale: 1- always use,0-use original value*/
if aprob<1 then do
   arf=random()/999
   if arf>aprob then return 0           /* retain with current value */
end /* do */

if atype='P' then do
   if apix >= aval then return 1
   return 0
end  /* Do */
r=messct.!r.apix
b=messct.!b.apix
g=messct.!g.apix
if atype='C' then do            /* if brightest color is over threshold */
    if max(r,b,g)>=aval then return 1
    return 0
end  /* Do */
if atype='B' then do            /* if average brightness over threshold */
     if (r+b+g)/3 >= aval then return 1
     return 0
end  /* Do */
return 0                /* shoud never get here */


/***************/
/* process from a cached file 
  return 1 if "used a cache file"; 0 if not. 
  Also, set do_cache=0 if a problem arises */

do_from_cache:procedure expose gif_dir_root verbose do_cache cache_duration
parse arg cache_file
if do_cache=0 then return 0

if do_cache>0 then do
   do_cache=1
   cache_file=gif_dir_root||cache_file
   gfile=cache_file

   eek=sysfiletree(cache_file,afile,'FT')
   if afile.0>0 then do         /* match -- check duration */
        parse var afile.1 dd .
        mkdate=space(translate(left(afile.1,8),' ','/'),0)
        nowdate=space(translate(date('o'),' ','/'),0)
        if abs(nowdate-mkdate) <= cache_duration then do
          if verbose>1 then call gpmprintf(' GIF_TEXT: using cached image file: ' cache_file)
          return 1
       end
       if verbose>1  then call gpmprintf('GIF_TEXT: Rewriting cached image file: ' cache_file)
       return 0
   end  /* Do */
   else do              /* no match -- is there room? */
      foo=sysfiletree(gif_dir_root||'*.*','eek','FO')
      if eek.0 > cache_size then do 
         do_cache=0     /* suppress cache! */
         if verbose>1 then call gpmprintf(' GIF_TEXT: cache_size exceeded, can not cache image file: ' cache_file)
      end  /* Do */
      else do
          if verbose>1 then call gpmprintf(' GIF_TEXT: creating cached image file: ' cache_file)
      end /* do */
   end
end
return 0



/***********************************/
/* get the slide file stuff 
ATYPE has 3 values:
  T= repeat slide
  F= fit (internally repeat)
*/
grab_slide:procedure expose slide. verbose slidect.  
parse upper arg sim,atype,mx,ajy,my,stype,sxc,syc
if sim<>0 then do
  foo=rxgdimagecolorstotal(sim)
  jx=rxgdimagesx(sim)
  jy=rxgdimagesy(sim)
end
else do
   jy=1
   jx=slide.0
end /* do */
jy0=jy
/* which row to read from ? */
if jy>1 & ( stype="F" | stype="T" ) then do  /* multi row style -- use my ajy row */
    select
     when ajy=0 then jy=0
     when ajy=my then jy=jy-1
     when jy>my | stype='F' then do
       tt=ajy/my
       jy=trunc(tt*(jy-1))
     end
     when stype='T' then do
        jy=trunc(ajy//(jy-1))
     end  /* Do */
     otherwise jy=1
   end
end  /* Do */
else do
   if jy>1 then 
       jy=trunc(1+(jy/3))
   else
     jy=0
end

/* if sxc and syc specified, then measrue distance from there (rather then just using
    column #. This means computing max distance from sxc,syc */
if datatype(sxc)='NUM' & datatype(syc)='NUM' then do    /* use distance, not colunm */
    ixc=1+((mx-1)*sxc);   ixc= max(min(ixc,mx),1)
    iyc=1+((my-1)*syc) ;  iyc= max(min(iyc,my),1)
    d1=figdist(ixc,iyc,1,1)
    d2=figdist(ixc,iyc,mx,1)
    d3=figdist(ixc,iyc,mx,my)
    d4=figdist(ixc,iyc,1,my)
    mx=trunc(max(d1,d2,d3,d4))   /* new "max distance from slide */
    if verbose>1 then call gpmprintf(' GIF_TEXT: Max distance from slide_coord='mx)
end

if verbose>1 & ajy//25=1 then 
   call gpmprintf(" GIF_TEXT: Getting color slide from row:" jy ' of ' jy0)

drop aslide.
if sim<>0 then do               /* using slide form file */
  foo=rxgdimagegetrowpixels(sim,jy,aslide)
end
else do                         /* using user set slide */
  do mm=0 to slide.0
     aslide.mm=slide.mm        
  end /* do */
end

/* we now have base slide (from file or from use set); now expand/shrink to fit mx */
slide.0=mx
/* if slide > mx, then pick from slide */
if jx>mx & atype<>'T' then do
   slide.1=aslide.1 
   slide.mx=aslide.jx 
 
   do ll=2 to mx-1
       tt=(ll-1)/(mx-1)
       itt=1+trunc(tt*(jx-1))
       slide.ll=aslide.itt 
   end /* do */
   return 1
end  /* Do */

/* slide < mx, need to expand it */
if atype='T' then do            /* tile it, both cases (jx> or < mx)  */
    ii=0
    do mm=1 to mx
       ii=ii+1
       if ii>jx then ii=1
       slide.mm=aslide.ii 
    end /* do */
    return 1
end  /* Do */

/* fit (internal repeat */
   slide.1=aslide.1 
   slide.mx=aslide.jx 
   do ll=2 to mx-1
       tt=(ll-1)/(mx-1)
       itt=1+trunc(tt*(jx-1))
       slide.ll=aslide.itt 
   end /* do */
   return 1

/***********/
/* squared distance */
figdist:procedure
parse arg ax,ay,cx,cy
dx=ax-cx ; dy=ay-cy 
return sqrt( (dx*dx)+(dy*dy))

/***********************************/
/* add slide's color table to messim */
add_slide_ct:procedure expose slidect. verbose
parse arg mim
ist=rxgdimagegettransparent(mim)
usepre=0
do jj=0 to slidect.0-1
   r=slidect.!r.jj ; g=slidect.!g.jj ; b=slidect.!b.jj
   oo=rxgdimagecolorexact(mim,r,g,b)  /* check if color already exists */
   if oo=-1 | oo=ist then do             /*no exact match, or match transparent  */
      aa=rxgdimagecolorallocate(mim,r,g,b)  /* add this color */
      if aa>-1  then do          /* success */
           slidect.!alt.jj=aa
      end  /* Do */
      else do           /* no more colors, use closest */
         slidect.!alt.jj=rxgdimagecolorclosest(mim,r,g,b)
         usepre=usepre+1
      end
   end  /* Do */
   else do
       slidect.!alt.jj=oo              /* use prexisting color */
   end
end /* do */
if usepre>0 & verbose>1  then call gpmprintf(' GIF_TEXT: too many colors, had to share for 'usepre)

return 1




/***********************************/

/* determine a user scale, given ith of Ilen position, and 
   list of "user_scales". We assume user_scales is a space delimited list
  of numbers, with 1="use current size", >1 means larger, <1 means smaller */

get_user_scale:procedure 
parse arg ith,ilen,user_scales
if user_scales="" then return 1

igoo=words(user_scales)

if ith=1 then return word(user_scales,1)

if ith=ilen then return word(user_scales,igoo)

/* middle characters*/
frac=(ith-1)/(ilen-1)    /* where in scale list is it */
spot=1+ ((igoo-1)*frac)
ifrac=trunc(spot)
afrac=spot-ifrac

if afrac=0 then return word(user_scales,ifrac)

ii=ifrac+1
a1=word(user_scales,ii)
a2=word(user_scales,ifrac)

diff=a1-a2
return (a2+(diff*afrac))


/***********************************/
/* get the gif name, using several naming tricks */
get_gifname:procedure expose font_ind.
parse arg achar,gif_dir,fontname
if length(achar)>1 then achar=translate(achar)
/* check index first */
do iu=1 to font_ind.0
   if font_ind.iu=achar then do 
        cl=gif_dir||font_ind.iu.!file
        if stream(cl,'c','query exists')<>' ' then  return cl
        leave
   end  /* Do */
end /* do */
if length(achar)>1 then return ' '  /* special character not found */


/* is it a valid file name (i.e.; don't look for *.gif*/
if pos(translate(achar),'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$%#&@!~-&^')=0 then RETURN ' '

/* it is lowerr case: look for xlc.gif? */

if translate(achar)<>achar then do
   cl=gif_dir||achar||'lc.gif'
   if stream(cl,'c','query exists')<>' ' then  return cl
   cl=gif_dir||fontname||achar||'lc.gif'
   if stream(cl,'c','query exists')<>' ' then  return cl
   cl=gif_dir||fontname||'-'||achar||'lc.gif'
   if stream(cl,'c','query exists')<>' ' then  return cl

end  /* Do */

/* try generic name: look for x.gif? */
cl=gif_dir||achar||'.gif'
if stream(cl,'c','query exists')<>' ' then  return cl
cl=gif_dir||fontname||achar||'.gif'
if stream(cl,'c','query exists')<>' ' then  return cl
cl=gif_dir||fontname||'-'||achar||'.gif'
if stream(cl,'c','query exists')<>' ' then  return cl
cl=gif_dir||achar||fontname||'.gif'
if stream(cl,'c','query exists')<>' ' then  return cl

return ' '



/******/
/* check for valid 0-255 value, set to def if not */
check_byte:procedure
parse arg aval,adef
if adef=' ' then adef=0
if datatype(aval)<>'NUM' then return adef
if aval<0 | aval>255  then return adef
return aval



/*******/
/* read a font index file into font_ind. */
read_font_index:procedure expose font_ind.  gif_dir  def_transparent def_textcolor def_backcolor
parse arg afile

defgifs=' '; xoffset=0 ; yoffset=0 ; inrow=16 ; hchar=47  ; wchar=35 ;isbw=1
charset=' !"'||"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
leftoffset=0;rightoffset=0;topoffset=0;bottomoffset=0
transparent="" ; manytype=1
x_user_scales='' ; y_user_scales="" ;y_valign=' ' ;slide='' ;  slide_horiz=''
slide_thresh='P1' ; slide_vert=''
slide_size=""  ; textcolor="" ; backcolor="" ; slide_prob=''

font_ind.0=0
font_ind.!defgifs=defgifs
font_ind.!xoffset=xoffset
font_ind.!yoffset=yoffset
font_ind.!topoffset=topoffset
font_ind.!bottomoffset=bottomoffset
font_ind.!rightoffset=rightoffset
font_ind.!leftoffset=leftoffset
font_ind.!inrow=inrow
font_ind.!hchar=hchar
font_ind.!wchar=wchar
font_ind.!isbw=isbw
font_ind.!charset=charset
font_ind.!manytype=1
font_ind.!x_user_scales=''
font_ind.!y_user_scales=''
font_ind.!y_valign=''
font_ind.!slide=''
font_ind.!slide_horiz=''
font_ind.!slide_vert=''
font_ind.!slide_thresh=''
font_ind.!transparent=def_transparent
font_ind.!textcolor=def_textcolor
font_ind.!backcolor=def_backcolor
font_ind.!slide_size=0
font_ind.!slide_coord=''
font_ind.!slide_blue=''
font_ind.!slide_red=''
font_ind.!slide_green=''
font_ind.!slide_prob=""

if afile=' ' then return 0
ii=0
if stream(afile,'c','query exists')=' ' then return 0
do until lines(afile)=0
  ii=ii+1
  tmp.ii=linein(afile)
end
tmp.0=ii
foo=stream(afile,'c','close')

iin=0
do mm=1 to tmp.0
   aline=strip(tmp.mm)
   if aline=' ' then iterate
   if abbrev(aline,'**')=1 then iterate  /* comment */
   athing=' '
   if pos('=',aline)<>0 then do
       parse var aline athing '=' stuff ; athing=strip(translate(athing))
   end  /* Do */
   select
     when abbrev(athing,'DEFAUL')>0 then  defgifs=defgifs||' '||strip(stuff)
     when athing='DEF_OFFSET' then do
             stuff=translate(stuff,' ',',')
             parse var stuff a1 a2
             if datatype(a1)='NUM'  then xoffset=a1
             if datatype(a2)='NUM'  then yoffset=a2
     end
     when abbrev(athing,'DEF_CHAR_OF')+abbrev(athing,'DEFCHAROF')>0 then do
             stuff=translate(stuff,' ',',')            
             parse var stuff a1 a2 a3 a4                    
             if datatype(a1)='NUM'  then leftoffset=a1    
             if datatype(a2)='NUM'  then topoffset=a2    
             if datatype(a3)='NUM'  then rightoffset=a3    
             if datatype(a4)='NUM'  then bottomoffset=a4    
     end  /* Do */
     when abbrev(athing,'DEF_TR')+abbrev(athing,'TRAN')>0 then do
           if datatype(stuff)='NUM'  then transparent=stuff
     end

     when abbrev(athing,'DEF_TEXTC')+abbrev(athing,'TEXT')>0 then do
        if verify(stuff,'0123456789ABCDEFabcdef#')=0 then  textcolor=stuff
     end

     when abbrev(athing,'DEF_BACKC')+abbrev(athing,'BACK')>0 then do
        if verify(stuff,'0123456789ABCDEFabcdef#')=0 then  backcolor=stuff
     end

     when athing='DEF_CHARSIZE' then do
             stuff=translate(stuff,' ',',')
             parse var stuff a1 a2
             if datatype(a1)='NUM'  then wchar=a1
             if datatype(a2)='NUM'  then hchar=a2
     end  /* Do */
     when athing='DEF_CHARS' then charset=stuff
     when athing='DEF_BW' then isbw=pos(strip(translate(stuff)),'Y YES 1')
     when abbrev(athing,"MANY_DEF")+abbrev(athing,'MANYDEF')>0 then do
       manytype=wordpos(translate(stuff),'CYCLE FIT END RANDOM ')
       if manytype=0 then manytype=1
     end
     when athing='DEF_INROW' then
              if datatype(strip(stuff))='NUM' then inrow=strip(stuff)
     when athing='CHAR' then do
      parse var stuff aval afile
      if datatype(aval)<>'NUM' then iterate /* error- ignoe */
      if aval<0  | aval>99 then iterate /* out of range, ignore */
      aval=strip(aval,'l','0')
      font_ind.!chars.aval=strip(afile)
      iterate
     end
     when abbrev(athing,"X_SC")+abbrev(athing,'XSC')>0 then
        x_user_scales=stuff
     when abbrev(athing,"Y_SC")+abbrev(athing,'YSC')>0 then
        y_user_scales=stuff
     when abbrev(athing,'VAL')+abbrev(athing,'Y_VAL')>0 then
        y_valign=stuff
     when abbrev(athing,'SLIDE_H')>0 then
        slide_horiz=packur2(stuff)
     when abbrev(athing,'SLIDE_V')>0 then
        slide_vert=packur2(stuff)
     when abbrev(athing,'SLIDE_T')>0 then
        slide_thresh=packur2(stuff)
     when abbrev(athing,'SLIDE_F')>0 | athing='SLIDE' then do
        slide=packur2(stuff)
        if pos(':',slide)+pos('\',slide)=0 then
           slide=gif_dir||slide
     end
     when abbrev(athing,'SLIDE_R')>0 then
        slide_red=packur2(stuff)
     when abbrev(athing,'SLIDE_G')>0 then
        slide_green=packur2(stuff)
     when abbrev(athing,'SLIDE_B')>0 then
        slide_blue=packur2(stuff)
     when abbrev(athing,'SLIDE_C')>0 then
        slide_coord=packur2(stuff)
     when abbrev(athing,'SLIDE_P')>0 then
        slide_prob=packur2(stuff)
     when abbrev(athing,'SLIDE_S')>0 then do
         tt=packur2(stuff)
         if datatype(tt)='NUM' then slide_size=tt
     end  /* Do */

     otherwise do               /* it's a charater to file map */
        parse var tmp.mm achar afile
        if length(achar)>1 then achar=translate(achar)
        iin=iin+1
        font_ind.iin=strip(achar)   ; font_ind.iin.!file=strip(afile)
     end
   end
end /* do */



if isbw>0 then isbw=1
font_ind.!defgifs=defgifs
font_ind.!xoffset=xoffset
font_ind.!yoffset=yoffset

font_ind.!topoffset=topoffset
font_ind.!bottomoffset=bottomoffset
font_ind.!rightoffset=rightoffset
font_ind.!leftoffset=leftoffset

font_ind.!inrow=inrow
font_ind.!hchar=hchar
font_ind.!wchar=wchar
font_ind.!isbw=isbw
font_ind.!charset=charset
font_ind.!transparent=transparent 
font_ind.!manytype=manytype
font_ind.!x_user_scale=x_user_scales
font_ind.!y_user_scale=y_user_scales
font_ind.!y_valign=y_valign
font_ind.!slide=slide
font_ind.!slide_horiz=slide_horiz
font_ind.!slide_thresh=slide_thresh
font_ind.!slide_vert=slide_vert
font_ind.!textcolor=textcolor
font_ind.!backcolor=backcolor

font_ind.!slide_size=slide_size
font_ind.!slide_red=slide_red
font_ind.!slide_green=slide_green
font_ind.!slide_blue=slide_blue
font_ind.!slide_coord=slide_coord
font_ind.!slide_prob=slide_prob

font_ind.0=iin

return iin

/**********/
/* fIX A user scale entry */
fix_scale:procedure
parse arg ascale
if ascale=0 then return 1
ascale=translate(ascale,' ','+')
tt=''
do mm=1 to words(ascale)
   av=strip(word(ascale,mm))
   if datatype(av)<>'NUM' then  iterate
   tt=tt' 'av
end  /* Do */

return tt



/**************************/
/* convert ff21b3 "hex" color code to decimal r g b values
  If bad value, return ' /' */
get_from_hex:procedure
parse arg hval

hval=strip(strip(hval),,'"')
hval=strip(hval,,'#')
select 
  when length(hval)<>6 then return ' '
  when verify(translate(hval),'0123456789ABCDEF')>0 then return ' '
  otherwise do
    a1=left(hval,2)
    a2=substr(hval,3,2)
    a3=substr(hval,5,2)
    r=x2d(a1)
    g=x2d(a2)
    b=x2d(a3)
  end
end /* do */
return r ' ' g ' ' b


/********************/
/* return time, using REXX time_fmt. Also, special code: 1 - C without am or pm */
get_time:procedure
parse arg tfmt

if pos(tfmt,'CHLMNS1')=0 then tfmt='N'
if tfmt='1' then do
  aa=time('C')
  a2=translate(right(aa,2))
  oof=2
  if a2="AM" then oof=1         /* reserved special character: 1=am, 2=pm */
  return left(aa,length(aa)-2)||d2c(oof)
end
return time(tfmt)

/********************/
/* return time, using REXX time_fmt */
get_date:procedure
parse arg tfmt

if pos(tfmt,'BDELMNOSUW')=0 then tfmt='N'

return date(tfmt)


/* procedure from TEST-CGI.CMD by  Frankie Fan <kfan@netcom.com>  7/11/94 */
DecodeKeyVal: procedure
  parse arg Code
  Text=''
  Code=translate(Code, ' ', '+')
  rest='%'
  do while (rest\='')
     Parse var Code T '%' rest
     Text=Text || T
     if (rest\='') then
      do
        ch = left( rest,2)
        c=X2C(ch)
        Text=Text || c
        Code=substr( rest, 3)
      end
  end
  return Text


/*********/
packur2:procedure expose is_cgi
parse arg a1b0

if is_cgi=0 then
   return packur(translate(a1b0,' ','+'))
else
   return decodekeyval(translate(a1b0,' ','+'))

/************/
wow1:
call gpmprintf(" GIF_TEXT error at line "sigl)
      if is_cgi=0 then do
         'NODATA'
         exit '400 0'
      end
      else do
           exit 
      end /* do */


/***********************/
/* see if an alphabyte specific default is available */
get_default_char:procedure expose font_ind. verbose dim. red_back green_back blue_back ,
                        red_text green_text blue_text
parse arg achar,ithchar,mlen,manymax
if font_ind.!ndims=0 then return 1
ikk=1
nfonts=font_ind.!ndims
if manymax>0 & manymax<nfonts then nfonts=manymax
if nfonts>1 then do
  select
     when font_ind.!manytype=1 then do  /* cycle */
       ikk=ithchar//nfonts
       if ikk=0 then ikk=nfonts
     end
     when font_ind.!manytype=3 then do   /* end */
        ikk=min(nfonts,ithchar)
     end  /* Do */
     when font_ind.!manytype=4 then do  /* random */
        ikk=random(1,nfonts)
     end
     otherwise    do      /* fit */
        ikk=1+trunc(nfonts*ithchar/(mlen+0.1))
     end
  end
end
ic=pos(achar,font_ind.!charset)
if ic=0 then do
  achar=translate(achar)
  ic=pos(achar,font_ind.!charset)
end
if ic=0 then return 1

/* for each character in the charset ... */
/* determine x offset: */
   irow=1+((ic-0.1)%font_ind.!inrow)
   icol=ic-((irow-1)*font_ind.!inrow)

/* upper left is 0,0 */
   xat=font_ind.!xoffset + ((icol-1)*font_ind.!wchar)+font_ind.!leftoffset
   yat=font_ind.!yoffset+ ((irow-1)*font_ind.!hchar)+font_ind.!topoffset
   jx=font_ind.!wchar-(font_ind.!leftoffset+font_ind.!rightoffset)
   jy=font_ind.!hchar-(font_ind.!bottomoffset+font_ind.!topoffset)
   cim=rxgdimagecreate(jx,jy)
   if font_ind.!isbw=0 then do  /* use colors as is, but include back text colors */
      oy1=rxgdimagecolorallocate(cim,red_back,green_back,blue_back)
      oy2=rxgdimagecolorallocate(cim,red_text,green_text,blue_text)
   end

   tdim=dim.ikk         /* use the ikk (of possible many_defaults) complete font */

   foo=rxgdimagecopy(cim,tdim,0,0,xat,yat,jx,jy)
   if font_ind.!isbw=1 then do   /* convert b/w to back/text colors */
ww=rxgdimagegettransparent(cim)
      foo=rxgdimagecolordeallocate(cim,0)
      oy1=rxgdimagecolorallocate(cim,red_back,green_back,blue_back)
      foo=rxgdimagecolordeallocate(cim,1)
      oy2=rxgdimagecolorallocate(cim,red_text,green_text,blue_text)
   end

   return cim


/*********************************/
/* return r g b of aim at ctable ival */
three_color:procedure
parse arg aim,ctable
r=rxgdimagered(aim,ctable)
g=rxgdimagegreen(aim,ctable)
b=rxgdimageblue(aim,ctable)
return r g b

/*********************************/
/* stand alone mode -- build the "list " */
ask_values:procedure expose gfile2 gif_dir_root

SIGNAL OFF  ERROR ; SIGNAL OFF SYNTAX
SIGNAL ON ERROR NAME ASKV 
 SIGNAL ON SYNTAX NAME ASKV 

ansion=checkansi()
if ansion=1 then do
  aesc='1B'x
  cy_ye=aesc||'[37;46;m'
  normal=aesc||'[0;m'
  bold=aesc||'[1;m'
  re_wh=aesc||'[31;47;m'
  reverse=aesc||'[7;m'
end
else do
  say " Warning: Could not detect ANSI....  output will look ugly ! "
  cy_ye="" ; normal="" ; bold="" ;re_wh="" ;
  reverse=""
end  /* Do */

cls
say  " " ; say

call lineout, bold cy_ye
call lineout, "This is the GIF_TEXT text-to-gif utility: stand alone mode "
call lineout, normal


say " Although designed primarily as a WWW script, you can use GIF_TEXT "
say " to create .GIF files in a stand-alone mode."
say
say "      "||cy_ye||"GIF_TEXT does not have graphics display capability    " normal
say "      "||cy_ye||" ... you'll have to read the files it creates with a  " normal
say "      "||cy_ye||"     graphics viewer (you can often use your browser)." normal
say "  "

if yesno(" Are you ready to continue ")=1 then
 nop
else do
 say " See you later?.. "
 exit
end

say
say " We recommend reading the documentation (GIF_TEXT.DOC) before running "
say " running this program.  On the other hand, you can always learn by "
say " making mistakes .... "
say
whatfont:
call charout , "What "reverse "alphabyte font "normal" do you want to use (?=list):"
pull font
if font=""  then signal whatfont

if font="?" then do
    oop='DIR /ad /p  '||gif_dir_root
    oop
    signal whatfont
end  /* Do */

getbACK:
call charout , bold"Background file (0=None, ?=list):" normal
pull backgrnd
if backgrnd="?" then do
    oop='DIR /p  '||gif_dir_root||'BACKS\*.gif'
    oop
   say
   say bold" Note: be sure you enter a file name relative to " gif_dir_root normal
 say
    signal getback
end  /* Do */


getht:
call charout , bold"Height (in pixels), ENTER=program determines: "normal
pull height
if height="" then height=0
if datatype(height)<>'NUM' then signal getht

getwt:
call charout , bold "Width (in pixels), ENTER=program determines: "normal
pull width
if width="" then width=0
if datatype(width)<>'NUM' then signal getwt


amess:
Say
Say reverse "Enter your message ($d=date, $t=time)" normal
call charout ,bold "The message:"normal
pull adesc
adesc=a_replacestrg(adesc,'&','%26;','ALL')
message=translate(adesc,'+',' ')


say
call charout,bold"Optional Additional options (space delimited):"normal
pull moreopts
moreopts=translate(moreopts,'&',' ')

/* now make a list */

list="font="||font||'&text='||message||'&height='||height||'&width='||width
list=list||'&back='||backgrnd
if moreopts<>'' then list=list||'&'||moreopts

getgfile2:
Say
call charout,bold"Enter output file name:"normal
pull gfile2
if gfile2="" then signal getgfile2

say
say " creating the image ..... "
return list  /* gfile2 is exposed */


ASKV:
SAY "Sorry, you made a goof.  Try again "
exit



 /* ------------------------------------------------------------------ */
 /* function: Check if ANSI is activated                               */
 /*                                                                    */
 /* call:     CheckAnsi                                                */
 /*                                                                    */
 /* where:    -                                                        */
 /*                                                                    */
 /* returns:  1 - ANSI support detected                                */
 /*           0 - no ANSI support available                            */
 /*          -1 - error detecting ansi                                 */
 /*                                                                    */
 /* note:     Tested with the German and the US version of OS/2 3.0    */
 /*                                                                    */
 /*                                                                    */
 CheckAnsi: PROCEDURE
   thisRC = -1

   trace off
                         /* install a local error handler              */
   SIGNAL ON ERROR Name InitAnsiEnd

   "@ANSI 2>NUL | rxqueue 2>NUL"

   thisRC = 0

   do while queued() <> 0
     queueLine = lineIN( "QUEUE:" )
     if pos( " on.", queueLine ) <> 0 | ,                       /* USA */
        pos( " (ON).", queueLine ) <> 0 then                    /* GER */
       thisRC = 1
   end /* do while queued() <> 0 */

 InitAnsiEnd:
 signal off error
 RETURN thisRC






a_replacestrg:

exactmatch=0
backward=0 ; doall=0

parse arg astring ,  target   , putme , type , exactmatch

type = translate(type)
if type="BACKWARD" then backward="YES"
if type="ALL" then doall="YES"

iat=1
joelen=length(target)
joelen2=length(putme)

doagain:                /* here if doall=yes */
 if exactmatch="YES" then do
    if   backward="YES" then
        joe= lastpos(target,astring)
    else
        joe= pos(target,astring,iat)
 end
 else do
   if   backward="YES" then
        joe= lastpos(translate(target),translate(astring))
    else
        joe= pos(translate(target),translate(astring),iat)
 end
 if joe=0 then
         return astring

 astring=delstr(astring,joe,joelen)
 if putme<>' ' then
    astring=insert(putme,astring,joe-1)

 if doall="YES" then do
     iat=joe+joelen2
     signal doagain
 end
/* else, all done */
 return astring




/* -------------------- */
/* get a yes or no , return 1 if yes */
yesno:procedure expose normal reverse bold
parse arg fooa , allopt,altans
if altans<>" " & words(altans)>1 then do
   w1=strip(word(altans,1))
   w2=strip(word(altans,2))
   a1=left(w1,1) ; a2=left(w2,1)
   a1a=substr(w1,2) ; a2a=substr(w2,2)
end
else do
    a1='Y' ; a1a='es'
    a2='N' ; a2a='o'
end  /* Do */
ayn='  '||bold||a1||normal||a1a||'\'||bold||a2||normal||a2a
if allopt=1 then  ayn=ayn||'\'||bold||'A'||normal||'ll'

do forever
 foo1=normal||reverse||fooa||normal||ayn
 call charout,  foo1 normal ':'
 pull anans
 if abbrev(anans,a1)=1 then return 1
 if abbrev(anans,a2)=1 then return 0
 if allopt=1 & abbrev(anans,'A')=1 then return 2
end

nocon:
if rc=-7 then return 0
exit 0

gpmprintf:procedure
parse arg a1
if rxfuncquery('pmprintf')=0 then
    call pmprintf(a1)
return 0


