blank


® John Forkosh Associates, Inc. Welcome message 1d wave equation (under construction)

Copyright © 2002-2011, John Forkosh Associates, Inc.http://www.forkosh.com
All rights reserved.

Click for:   homepagehttp://www.forkosh.com,   resumethrough y2k,
email for more
recent info



           


  CCCCCCC    OOOOOO   NN      NN TTTTTTTTTT EEEEEEEEEE NN      NN TTTTTTTTTT   SSSSSSS 
 CCCCCCCCC  OOOOOOOO  NNN     NN TTTTTTTTTT EEEEEEEEEE NNN     NN TTTTTTTTTT  SSSSSSSSS
CC      CC OO      OO NNNN    NN TT  TT  TT  EE     EE NNNN    NN TT  TT  TT SS        
CC         OO      OO NN NN   NN     TT      EEEEEE    NN NN   NN     TT      SSSSSSS  
CC         OO      OO NN   NN NN     TT      EEEEEE    NN   NN NN     TT       SSSSSSS 
CC      CC OO      OO NN    NNNN     TT      EE     EE NN    NNNN     TT             SS
 CCCCCCCCC  OOOOOOOO  NN     NNN    TTTT    EEEEEEEEEE NN     NNN    TTTT    SSSSSSSSS 
  CCCCCCC    OOOOOO   NN      NN    TTTT    EEEEEEEEEE NN      NN    TTTT     SSSSSSS  
   1: mimetex.cClick for mimetex.c
   2: mimetex.hClick for mimetex.h
MM      MM   IIIIII   MM      MM EEEEEEEEEE TTTTTTTTTT EEEEEEEEEE XX      XX              CCCCCCC 
MMM    MMM   IIIIII   MMM    MMM EEEEEEEEEE TTTTTTTTTT EEEEEEEEEE  XX    XX              CCCCCCCCC
MMMM  MMMM     II     MMMM  MMMM  EE     EE TT  TT  TT  EE     EE   XX  XX              CC      CC
MM MMMM MM     II     MM MMMM MM  EEEEEE        TT      EEEEEE        XX       .        CC        
MM  MM  MM     II     MM  MM  MM  EEEEEE        TT      EEEEEE        XX      ...       CC        
MM      MM     II     MM      MM  EE     EE     TT      EE     EE   XX  XX   .....      CC      CC
MM      MM   IIIIII   MM      MM EEEEEEEEEE    TTTT    EEEEEEEEEE  XX    XX   ...        CCCCCCCCC
MM      MM   IIIIII   MM      MM EEEEEEEEEE    TTTT    EEEEEEEEEE XX      XX   .          CCCCCCC 
   1: /****************************************************************************
   2:  *
   3:  * Copyright(c) 2002-2017, John Forkosh Associates, Inc. All rights reserved.
   4:  *           http://www.forkosh.com   mailto: john@forkosh.com
   5:  * --------------------------------------------------------------------------
   6:  * This file is part of mimeTeX, which is free software. You may redistribute
   7:  * and/or modify it under the terms of the GNU General Public License,
   8:  * version 3 or later, as published by the Free Software Foundation.
   9:  *      MimeTeX is distributed in the hope that it will be useful, but
  10:  * WITHOUT ANY WARRANTY, not even the implied warranty of MERCHANTABILITY.
  11:  * See the GNU General Public License for specific details.
  12:  *      By using mimeTeX, you warrant that you have read, understood and
  13:  * agreed to these terms and conditions, and that you possess the legal
  14:  * right and ability to enter into this agreement and to use mimeTeX
  15:  * in accordance with it.
  16:  *      Your mimetex.zip distribution file should contain the file COPYING,
  17:  * an ascii text copy of the GNU General Public License, version 3.
  18:  * If not, point your browser to  http://www.gnu.org/licenses/
  19:  * or write to the Free Software Foundation, Inc.,
  20:  * 59 Temple Place, Suite 330,  Boston, MA 02111-1307 USA.
  21:  * --------------------------------------------------------------------------
  22:  *
  23:  * Purpose:   o MimeTeX, licensed under the gpl, lets you easily embed
  24:  *              LaTeX math in your html pages.  It parses a LaTeX math
  25:  *              expression and immediately emits the corresponding gif
  26:  *              image, rather than the usual TeX dvi.  And mimeTeX is an
  27:  *              entirely separate little program that doesn't use TeX or
  28:  *              its fonts in any way.  It's just one cgi that you put in
  29:  *              your site's cgi-bin/ directory, with no other dependencies.
  30:  *                   So mimeTeX is very easy to install.  And it's equally
  31:  *              easy to use.  Just place an html <img> tag in your document
  32:  *              wherever you want to see the corresponding LaTeX expression.
  33:  *              For example,
  34:  *               <img src="../cgi-bin/mimetex.cgi?\int_{-\infty}^xe^{-t^2}dt"
  35:  *                alt="" border=0 align=middle>
  36:  *              immediately generates the corresponding gif image on-the-fly,
  37:  *              displaying the rendered expression wherever you put that
  38:  *              <img> tag.
  39:  *                   MimeTeX doesn't need intermediate dvi-to-gif conversion,
  40:  *              and it doesn't clutter up your filesystem with separate
  41:  *              little gif files for each converted expression.
  42:  *              But image caching is available by using mimeTeX's
  43:  *              -DCACHEPATH=\"path/\" compile option (see below).
  44:  *              There's also no inherent need to repeatedly write the
  45:  *              cumbersome <img> tag illustrated above.  You can write
  46:  *              your own custom tags, or write a wrapper script around
  47:  *              mimeTeX to simplify the notation.
  48:  *                   Further discussion about mimeTeX's features and
  49:  *              usage is available on its homepage,
  50:  *                http://www.forkosh.com/mimetex.html
  51:  *              and similarly in mimetex.html included with your mimetex.zip
  52:  *              distribution file. (Note: http://www.forkosh.com/mimetex.html
  53:  *              is a "quickstart" version of the the full mimetex.html manual
  54:  *              included in your mimetex.zip distribution file.)
  55:  *
  56:  * Functions:   The following "table of contents" lists each function
  57:  *              comprising mimeTeX in the order it appears in this file.
  58:  *              See individual function entry points for specific comments
  59:  *              about its purpose, calling sequence, side effects, etc.
  60:  *              (All these functions eventually belong in several
  61:  *              different modules, possibly along the lines suggested
  62:  *              by the divisions below.  But until the best decomposition
  63:  *              becomes clear, it seems better to keep mimetex.c
  64:  *              neatly together, avoiding a bad decomposition that
  65:  *              becomes permanent by default.)
  66:  *              ===================== Raster Functions ======================
  67:  *      PART2   --- raster constructor functions ---
  68:  *              new_raster(width,height,pixsz)   allocation (and constructor)
  69:  *              new_subraster(width,height,pixsz)allocation (and constructor)
  70:  *              new_chardef()                         allocate chardef struct
  71:  *              delete_raster(rp)        deallocate raster (rp =  raster ptr)
  72:  *              delete_subraster(sp)  deallocate subraster (sp=subraster ptr)
  73:  *              delete_chardef(cp)      deallocate chardef (cp = chardef ptr)
  74:  *              --- primitive (sub)raster functions ---
  75:  *              rastcpy(rp)                           allocate new copy of rp
  76:  *              subrastcpy(sp)                        allocate new copy of sp
  77:  *              rastrot(rp)         new raster rotated right 90 degrees to rp
  78:  *              rastrot3d(rp,axis,theta)new rast rotated around axis by theta
  79:  *              rastmag(rp,magstep)   new raster magnified by "magstep" to rp
  80:  *              bytemapmag(bytemap,width,height,magstep)      magnify bytemap
  81:  *              rastref(rp,axis)    new raster reflected (axis 1=horz,2=vert)
  82:  *              rastput(target,source,top,left,isopaque)  overlay src on trgt
  83:  *              rastcompose(sp1,sp2,offset2,isalign,isfree) sp2 on top of sp1
  84:  *              rastcat(sp1,sp2,isfree)                  concatanate sp1||sp2
  85:  *              rastack(sp1,sp2,base,space,iscenter,isfree)stack sp2 atop sp1
  86:  *              rastile(tiles,ntiles)      create composite raster from tiles
  87:  *              rastsmash(sp1,sp2,xmin,ymin)      calc #smash pixels sp1||sp2
  88:  *              rastsmashcheck(term)         check if term is "safe" to smash
  89:  *              --- raster "drawing" functions ---
  90:  *              accent_subraster(accent,width,height,direction,pixsz)\hat\vec
  91:  *              arrow_subraster(width,height,drctn,isBig)    left/right arrow
  92:  *              uparrow_subraster(width,height,drctn,isBig)     up/down arrow
  93:  *              rule_raster(rp,top,left,width,height,type)    draw rule in rp
  94:  *              line_raster(rp,row0,col0,row1,col1,thickness) draw line in rp
  95:  *              line_recurse(rp,row0,col0,row1,col1,thickness)   recurse line
  96:  *              circle_raster(rp,row0,col0,row1,col1,thickness,quads) ellipse
  97:  *              circle_recurse(rp,row0,col0,row1,col1,thickness,theta0,theta1)
  98:  *              bezier_raster(rp,r0,c0,r1,c1,rt,ct)   draw bezier recursively
  99:  *              border_raster(rp,ntop,nbot,isline,isfree)put border around rp
 100:  *              backspace_raster(rp,nback,pback,minspace,isfree)    neg space
 101:  *              --- raster (and chardef) output (and one input) functions ---
 102:  *              type_raster(rp,fp)       emit ascii dump of rp on file ptr fp
 103:  *              type_bytemap(bp,grayscale,width,height,fp) dump bytemap on fp
 104:  *              xbitmap_raster(rp,fp)           emit mime xbitmap of rp on fp
 105:  *              type_pbmpgm(rp,ptype,file)     pbm or pgm image of rp to file
 106:  *              read_pbm(fp,sf)     read pbm from file (or pipe) opened on fp
 107:  *              cstruct_chardef(cp,fp,col1)         emit C struct of cp on fp
 108:  *              cstruct_raster(rp,fp,col1)          emit C struct of rp on fp
 109:  *              hex_bitmap(rp,fp,col1,isstr)emit hex dump of rp->pixmap on fp
 110:  *              --- ancillary output functions ---
 111:  *              emit_string(fp,col1,string,comment) emit string and C comment
 112:  *              gftobitmap(rp)        convert .gf-like pixmap to bitmap image
 113:  *              ====================== Font Functions =======================
 114:  *              --- font lookup functions ---
 115:  *              get_symdef(symbol)              return mathchardef for symbol
 116:  *              get_ligature(expr,family)  return symtable index for ligature
 117:  *              get_chardef(symdef,size)       return chardef for symdef,size
 118:  *              get_charsubraster(symdef,size)  wrap subraster around chardef
 119:  *              get_symsubraster(symbol,size)    returns subraster for symbol
 120:  *              --- ancillary font functions ---
 121:  *              get_baseline(gfdata)       determine baseline (in our coords)
 122:  *              get_delim(symbol,height,family) delim just larger than height
 123:  *              make_delim(symbol,height) construct delim exactly height size
 124:  *              ================= Tokenize/Parse Functions ==================
 125:  *              texchar(expression,chartoken)  returns next char or \sequence
 126:  *              texsubexpr(expr,subexpr,maxsubsz,left,right,isescape,isdelim)
 127:  *              texleft(expr,subexpr,maxsubsz,ldelim,rdelim)   \left...\right
 128:  *              texscripts(expression,subscript,superscript,which)get scripts
 129:  *              --- ancillary parse functions ---
 130:  *              isbrace(expression,braces,isescape)   check for leading brace
 131:  *              preamble(expression,size,subexpr)              parse preamble
 132:  *              mimeprep(expression) preprocessor converts \left( to \(, etc.
 133:  *              strchange(nfirst,from,to)   change nfirst chars of from to to
 134:  *              strreplace(string,from,to,nreplace)  change from to to in str
 135:  *              strwstr(string,substr,white,sublen)     find substr in string
 136:  *              strdetex(s,mode)    replace math chars like \^_{} for display
 137:  *              strtexchr(string,texchr)                find texchr in string
 138:  *              findbraces(expression,command)    find opening { or closing }
 139:  *              strpspn(s,reject,segment)     non-() chars of s not in reject
 140:  *              isstrstr(string,snippets,iscase)  are any snippets in string?
 141:  *              isnumeric(s)                     determine if s is an integer
 142:  *              evalterm(store,term)     evaluate numeric value of expression
 143:  *              getstore(store,identifier)return value corresponding to ident
 144:  *              getdirective(string,directive,iscase,isvalid,nargs,args) \dir
 145:  *              strpspn(s,reject,segment) non-{[()]} chars of s not in reject
 146:  *              strqspn(s,q,isunescape) find matching " or ' in quoted string
 147:  *              unescape_url(url,isescape), x2c(what)   xlate %xx url-encoded
 148:  *      PART3   =========== Rasterize an Expression (recursively) ===========
 149:  *              --- here's the primary entry point for all of mimeTeX ---
 150:  *              rasterize(expression,size)     parse and rasterize expression
 151:  *              --- explicitly called handlers that rasterize... ---
 152:  *              rastparen(subexpr,size,basesp)          parenthesized subexpr
 153:  *              rastlimits(expression,size,basesp)    dispatch super/sub call
 154:  *              rastscripts(expression,size,basesp) super/subscripted exprssn
 155:  *              rastdispmath(expression,size,sp)      scripts for displaymath
 156:  *              --- table-driven handlers that rasterize... ---
 157:  *              rastleft(expression,size,basesp,ildelim,arg2,arg3)\left\right
 158:  *              rastright(expression,size,basesp,ildelim,arg2,arg3) ...\right
 159:  *              rastmiddle(expression,size,basesp,arg1,arg2,arg3)     \middle
 160:  *              rastflags(expression,size,basesp,flag,value,arg3)    set flag
 161:  *              rastspace(expression,size,basesp,width,isfill,isheight)\,\:\;
 162:  *              rastnewline(expression,size,basesp,arg1,arg2,arg3)         \\
 163:  *              rastarrow(expression,size,basesp,width,height,drctn) \longarr
 164:  *              rastuparrow(expression,size,basesp,width,height,drctn)up/down
 165:  *              rastoverlay(expression,size,basesp,overlay,arg2,arg3)    \not
 166:  *              rastfrac(expression,size,basesp,isfrac,arg2,arg3) \frac \atop
 167:  *              rastackrel(expression,size,basesp,base,arg2,arg3)   \stackrel
 168:  *              rastmathfunc(expression,size,basesp,base,arg2,arg3) \lim,\etc
 169:  *              rastsqrt(expression,size,basesp,arg1,arg2,arg3)         \sqrt
 170:  *              rastaccent(expression,size,basesp,accent,isabove,isscript)
 171:  *              rastfont(expression,size,basesp,font,arg2,arg3) \cal{},\scr{}
 172:  *              rastbegin(expression,size,basesp,arg1,arg2,arg3)     \begin{}
 173:  *              rastarray(expression,size,basesp,arg1,arg2,arg3)       \array
 174:  *              rastpicture(expression,size,basesp,arg1,arg2,arg3)   \picture
 175:  *              rastline(expression,size,basesp,arg1,arg2,arg3)         \line
 176:  *              rastrule(expression,size,basesp,arg1,arg2,arg3)         \rule
 177:  *              rastcircle(expression,size,basesp,arg1,arg2,arg3)     \circle
 178:  *              rastbezier(expression,size,basesp,arg1,arg2,arg3)     \bezier
 179:  *              rastraise(expression,size,basesp,arg1,arg2,arg3)    \raisebox
 180:  *              rastrotate(expression,size,basesp,arg1,arg2,arg3)  \rotatebox
 181:  *              rastmagnify(expression,size,basesp,arg1,arg2,arg3)   \magnify
 182:  *              rastreflect(expression,size,basesp,arg1,arg2,arg3)\reflectbox
 183:  *              rastfbox(expression,size,basesp,arg1,arg2,arg3)         \fbox
 184:  *              rastovalbox(expression,size,basesp,arg1,arg2,arg3)   \ovalbox
 185:  *              rastinput(expression,size,basesp,arg1,arg2,arg3)       \input
 186:  *              rastcounter(expression,size,basesp,arg1,arg2,arg3)   \counter
 187:  *              rasteval(expression,size,basesp,arg1,arg2,arg3)         \eval
 188:  *              rastmathtex(expression,size,basesp,arg1,arg2,arg3)   \mathtex
 189:  *              rasttoday(expression,size,basesp,arg1,arg2,arg3)       \today
 190:  *              rastcalendar(expression,size,basesp,arg1,arg2,arg3) \calendar
 191:  *              rastenviron(expression,size,basesp,arg1,arg2,arg3)   \environ
 192:  *              rastmessage(expression,size,basesp,arg1,arg2,arg3)   \message
 193:  *              rastnoop(expression,size,basesp,arg1,arg2,arg3) flush \escape
 194:  *              --- helper functions for handlers ---
 195:  *              rastopenfile(filename,mode)      opens filename[.tex] in mode
 196:  *              rasteditfilename(filename)       edit filename (for security)
 197:  *              rastreadfile(filename,islock,tag,value)   read <tag>...</tag>
 198:  *              rastwritefile(filename,tag,value,isstrict)write<tag>...</tag>
 199:  *              calendar(year,month,day)    formats one-month calendar string
 200:  *              timestamp(tzdelta,ifmt)              formats timestamp string
 201:  *              tzadjust(tzdelta,year,month,day,hour)        adjust date/time
 202:  *              daynumber(year,month,day)     #days since Monday, Jan 1, 1973
 203:  *              strwrap(s,linelen,tablen)insert \n's and spaces to wrap lines
 204:  *              strnlower(s,n)        lowercase the first n chars of string s
 205:  *              urlprune(url,n)  http://abc.def.ghi.com/etc-->abc.def.ghi.com
 206:  *              urlncmp(url1,url2,n)   compares topmost n levels of two url's
 207:  *              dbltoa(d,npts)                double to comma-separated ascii
 208:  *              rotmatrix(axis,theta) rotation matrix, theta degs around axis
 209:  *              matmult(mat,vec)        returns result of mat(rix) x vec(tor)
 210:  *              === Anti-alias completed raster (lowpass) or symbols (ss) ===
 211:  *              aalowpass(rp,bytemap,grayscale)     lowpass grayscale bytemap
 212:  *              aapnm(rp,bytemap,grayscale)       lowpass based on pnmalias.c
 213:  *              aapnmlookup(rp,bytemap,grayscale)  aapnm based on aagridnum()
 214:  *              aapatterns(rp,irow,icol,gridnum,patternum,grayscale) call 19,
 215:  *              aapattern1124(rp,irow,icol,gridnum,grayscale)antialias pattrn
 216:  *              aapattern19(rp,irow,icol,gridnum,grayscale) antialias pattern
 217:  *              aapattern20(rp,irow,icol,gridnum,grayscale) antialias pattern
 218:  *              aapattern39(rp,irow,icol,gridnum,grayscale) antialias pattern
 219:  *              aafollowline(rp,irow,icol,direction)       looks for a "turn"
 220:  *              aagridnum(rp,irow,icol)             calculates gridnum, 0-511
 221:  *              aapatternnum(gridnum)    looks up pattern#, 1-51, for gridnum
 222:  *              aalookup(gridnum)     table lookup for all possible 3x3 grids
 223:  *              aalowpasslookup(rp,bytemap,grayscale)   driver for aalookup()
 224:  *              aasupsamp(rp,aa,sf,grayscale)             or by supersampling
 225:  *              imgsupsamp(rp,sf,grayscale)supersampling to shrink rp->pixmap
 226:  *              ssweights(width,height,maxwt,wts)build canonical ss wt matrix
 227:  *              aacolormap(bytemap,nbytes,colors,colormap)make colors,colormap
 228:  *              aaweights(width,height)      builds "canonical" weight matrix
 229:  *              aawtpixel(image,ipixel,weights,rotate) weight image at ipixel
 230:  *              === miscellaneous ===
 231:  *              mimetexgetbytemap(expression,width,height) bytemap expression
 232:  *              mimetextypebytemap(bp,grayscale,width,height,fp) type bytemap
 233:  *              mimetexsetmsg(newmsglevel,newmsgfp)    set msglevel and msgfp
 234:  *      PART1   ========================== Driver ===========================
 235:  *              main(argc,argv) parses math expression and emits mime xbitmap
 236:  *              CreateGifFromEq(expression,gifFileName)  entry pt for win dll
 237:  *              ismonth(month)          is month current month ("jan"-"dec")?
 238:  *              logger(fp,msglevel,logvars)        logs environment variables
 239:  *              emitcache(cachefile,maxage,valign,isbuffer)    emit cachefile
 240:  *              readcachefile(cachefile,buffer)    read cachefile into buffer
 241:  *              advertisement(expression,mode)  wrap expression in ad message
 242:  *              crc16(s)                               16-bit crc of string s
 243:  *              md5str(instr)                      md5 hash library functions
 244:  *              GetPixel(x,y)           callback function for gifsave library
 245:  *
 246:  * Source:      mimetex.c  (needs mimetex.h and texfonts.h to compile,
 247:  *              and also needs gifsave.c when compiled with -DAA or -DGIF)
 248:  *
 249:  * --------------------------------------------------------------------------
 250:  * Notes      o See individual function entry points for specific comments
 251:  *              about the purpose, calling sequence, side effects, etc
 252:  *              of each mimeTeX function listed above.
 253:  *            o See bottom of file for main() driver (and "friends"),
 254:  *              and compile as
 255:  *                 cc -DAA mimetex.c gifsave.c -lm -o mimetex.cgi
 256:  *              to produce an executable that emits gif images with
 257:  *              anti-aliasing (see Notes below).  You may also compile
 258:  *                 cc -DGIF mimetex.c gifsave.c -lm -o mimetex.cgi
 259:  *              to produce an executable that emits gif images without
 260:  *              anti-aliasing.  Alternatively, compile mimeTeX as
 261:  *                 cc -DXBITMAP mimetex.c -lm -o mimetex.cgi
 262:  *              to produce an executable that just emits mime xbitmaps.
 263:  *              In either case you'll need mimetex.h and texfonts.h,
 264:  *              and with -DAA or -DGIF you'll also need gifsave.c
 265:  *            o The font information in texfonts.h was produced by multiple
 266:  *              runs of gfuntype, one run per struct (i.e., one run per font
 267:  *              family at a particular size).  Compile gfuntype as
 268:  *                 cc gfuntype.c mimetex.c -lm -o gfuntype
 269:  *              See gfuntype.c, and also mimetex.html#fonts, for details.
 270:  *            o For gif images, the gifsave.c library by Sverre H. Huseby
 271:  *              <http://shh.thathost.com> slightly modified by me to allow
 272:  *              (a)sending output to stdout or returning it in memory,
 273:  *              and (b)specifying a transparent background color index,
 274:  *              is included with mimeTeX, and it's documented in
 275:  *              mimetex.html#gifsave
 276:  *            o MimeTeX's principal reusable function is rasterize(),
 277:  *              which takes a string like "f(x)=\int_{-\infty}^xe^{-t^2}dt"
 278:  *              and returns a (sub)raster representing it as a bit or bytemap.
 279:  *              Your application can do anything it likes with this pixel map.
 280:  *              MimeTeX just outputs it, either as a mime xbitmap or as a gif.
 281:  *              See  mimetex.html#makeraster  for further discussion
 282:  *              and examples.
 283:  *            o File mimetex.c also contains library functions implementing
 284:  *              a raster datatype, functions to manipulate rasterized .mf
 285:  *              fonts (see gfuntype.c which rasterizes .mf fonts), functions
 286:  *              to parse LaTeX expressions, etc.  As already mentioned,
 287:  *              a complete list of mimetex.c functions is above.  See their
 288:  *              individual entry points below for further comments.
 289:  *                 As also mentioned, these functions eventually belong in
 290:  *              several different modules, possibly along the lines suggested
 291:  *              by the divisions above.  But until the best decomposition
 292:  *              becomes clear, it seems better to keep mimetex.c
 293:  *              neatly together, avoiding a bad decomposition that
 294:  *              becomes permanent by default.
 295:  *            o Optional compile-line -D defined symbols are documented
 296:  *              in mimetex.html#options .  They include (additional -D
 297:  *              switches are discussed at mimetex.html#options)...
 298:  *              -DAA
 299:  *                  Turns on gif anti-aliasing with default values
 300:  *                  (CENTERWT=32, ADJACENTWT=3, CORNERWT=1)
 301:  *                  for the following anti-aliasing parameters...
 302:  *              -DCENTERWT=n
 303:  *              -DADJACENTWT=j
 304:  *              -DCORNERWT=k
 305:  *                      *** Note: Ignore these three switches because
 306:  *                      *** mimeTeX's current anti-aliasing algorithm
 307:  *                      *** no longer uses them (as of version 1.60).
 308:  *                  MimeTeX currently provides a lowpass filtering
 309:  *                  algorithm for anti-aliasing, which is applied to the
 310:  *                  existing set of bitmap fonts.  This lowpass filter
 311:  *                  applies default weights
 312:  *                              1   2   1
 313:  *                              2   8   2
 314:  *                              1   2   1
 315:  *                  to neighboring pixels. The defaults weights are
 316:  *                  CENTERWT=8, ADJACENTWT=2 and CORNERWT=1,
 317:  *                  which you can adjust to control anti-aliasing.
 318:  *                  Lower CENTERWT values will blur/spread out lines
 319:  *                  while higher values will tend to sharpen lines.
 320:  *                  Experimentation is recommended to determine
 321:  *                  what value works best for you.
 322:  *              -DCACHEPATH=\"path/\"
 323:  *                  This option saves each rendered image to a file
 324:  *                  in directory  path/  which mimeTeX reads rather than
 325:  *                  re-rendering the same image every time it's given
 326:  *                  the same LaTeX expression.  Sometimes mimeTeX disables
 327:  *                  caching, e.g., expressions containing \input{ } are
 328:  *                  re-rendered since the contents of the inputted file
 329:  *                  may have changed.  If compiled without -DCACHEPATH
 330:  *                  mimeTeX always re-renders expressions.  This usually
 331:  *                  isn't too cpu intensive, but if you have unusually
 332:  *                  high hit rates then image caching may be helpful.
 333:  *                      The  path/  is relative to mimetex.cgi, and must
 334:  *                  be writable by it.  Files created under  path/  are
 335:  *                  named filename.gif, where filename is the 32-character
 336:  *                  MD5 hash of the LaTeX expression.
 337:  *              -DDEFAULTSIZE=n
 338:  *                  MimeTeX currently has eight font sizes numbered 0-7,
 339:  *                  and always starts in DEFAULTSIZE whose default value
 340:  *                  is 3 (corresponding to \large). Specify -DDEFAULTSIZE=4
 341:  *                  on the compile line if you prefer mimeTeX to start in
 342:  *                  larger default size 4 (corresponding to \Large), etc.
 343:  *              -DDISPLAYSIZE=n
 344:  *                  By default, operator limits like \int_a^b are rendered
 345:  *                  \textstyle at font sizes \normalsize and smaller,
 346:  *                  and rendered \displaystyle at font sizes \large and
 347:  *                  larger.  This default corresponds to -DDISPLAYSIZE=3,
 348:  *                  which you can adjust; e.g., -DDISPLAYSIZE=0 always
 349:  *                  defaults to \displaystyle, and 99 (or any large number)
 350:  *                  always defaults to \textstyle.  Note that explicit
 351:  *                  \textstyle, \displaystyle, \limits or \nolimits
 352:  *                  directives in an expression always override
 353:  *                  the DISPLAYSIZE default.
 354:  *              -DERRORSTATUS=n
 355:  *                  The default, 0, means mimeTeX always exits with status 0,
 356:  *                  regardless of whether or not it detects error(s) while
 357:  *                  trying to render your expression.  Specify any non-zero
 358:  *                  value (typically -1) if you write a script/plugin for
 359:  *                  mimeTeX that traps non-zero exit statuses.  MimeTeX then
 360:  *                  exits with its own non-zero status when it detects an
 361:  *                  error it can identify, or with your ERRORSTATUS value
 362:  *                  for errors it can't specifically identify.
 363:  *              -DREFERER=\"domain\"   -or-
 364:  *              -DREFERER=\"domain1,domain2,etc\"
 365:  *                  Blocks mimeTeX requests from unauthorized domains that
 366:  *                  may be using your server's mimetex.cgi without permission.
 367:  *                  If REFERER is defined, mimeTeX checks for the environment
 368:  *                  variable HTTP_REFERER and, if it exists, performs a
 369:  *                  case-insensitive test to make sure it contains 'domain'
 370:  *                  as a substring.  If given several 'domain's (second form)
 371:  *                  then HTTP_REFERER must contain either 'domain1' or
 372:  *                  'domain2', etc, as a (case-insensitive) substring.
 373:  *                  If HTTP_REFERER fails to contain a substring matching
 374:  *                  any of these domain(s), mimeTeX emits an error message
 375:  *                  image corresponding to the expression specified by
 376:  *                  the  invalid_referer_msg  string defined in main().
 377:  *                  Note: if HTTP_REFERER is not an environment variable,
 378:  *                  mimeTeX correctly generates the requested expression
 379:  *                  (i.e., no referer error).
 380:  *              -DWARNINGS=n  -or-
 381:  *              -DNOWARNINGS
 382:  *                  If an expression submitted to mimeTeX contains an
 383:  *                  unrecognzied escape sequence, e.g., "y=x+\abc+1", then
 384:  *                  mimeTeX generates a gif image containing an embedded
 385:  *                  warning in the form "y=x+[\abc?]+1".  If you want these
 386:  *                  warnings suppressed, -DWARNINGS=0 or -DNOWARNINGS tells
 387:  *                  mimeTeX to ignore unrecognized symbols, and the rendered
 388:  *                  image is "y=x++1" instead.
 389:  *              -DWHITE
 390:  *                  MimeTeX usually renders black symbols on a white
 391:  *                  background.  This option renders white symbols on
 392:  *                  a black background instead.
 393:  * --------------------------------------------------------------------------
 394:  * Revision History:
 395:  * 09/18/02     J.Forkosh       Installation.
 396:  * 12/11/02     J.Forkosh       Version 1.00 released.
 397:  * 07/04/03     J.Forkosh       Version 1.01 released.
 398:  * 10/17/03     J.Forkosh       Version 1.20 released.
 399:  * 12/21/03     J.Forkosh       Version 1.30 released.
 400:  * 02/01/04     J.Forkosh       Version 1.40 released.
 401:  * 10/02/04     J.Forkosh       Version 1.50 released.
 402:  * 11/30/04     J.Forkosh       Version 1.60 released.
 403:  * 10/11/05     J.Forkosh       Version 1.64 released.
 404:  * 11/30/06     J.Forkosh       Version 1.65 released.
 405:  * 09/06/08     J.Forkosh       Version 1.70 released.
 406:  * 03/23/09     J.Forkosh       Version 1.71 released.
 407:  * 11/18/09     J.Forkosh       Version 1.72 released.
 408:  * 11/15/11     J.Forkosh       Version 1.73 released.
 409:  * 02/15/12     J.Forkosh       Version 1.74 released.
 410:  * 12/28/16     J.Forkosh       Version 1.75 released.
 411:  * 07/11/17     J.Forkosh       Version 1.76 released.
 412:  * 07/11/17     J.Forkosh       Most recent revision (also see REVISIONDATE)
 413:  * See  http://www.forkosh.com/mimetexchangelog.html  for further details.
 414:  *
 415:  ****************************************************************************/
 416: 
 417: /* -------------------------------------------------------------------------
 418: Program id
 419: -------------------------------------------------------------------------- */
 420: #define VERSION "1.76"                  /* mimeTeX version number */
 421: #define REVISIONDATE "11 July 2017"     /* date of most recent revision */
 422: #define COPYRIGHTTEXT "Copyright(c) 2002-2017, John Forkosh Associates, Inc"
 423: 
 424: /* -------------------------------------------------------------------------
 425: header files and macros
 426: -------------------------------------------------------------------------- */
 427: /* --- standard headers --- */
 428: #include <stdio.h>
 429: #include <stdlib.h>
 430: /*#include <unistd.h>*/
 431: #define _GNU_SOURCE                     /* for strcasestr() in string.h */
 432: #include <string.h>
 433: char    *strcasestr();                  /* non-standard extension */
 434: #include <ctype.h>
 435: #include <math.h>
 436: #include <time.h>
 437: extern  char **environ;         /* for \environment directive */
 438: 
 439: /* -------------------------------------------------------------------------
 440: messages (used mostly by main() and also by rastmessage())
 441: -------------------------------------------------------------------------- */
 442: static  char *copyright1 =              /* copyright, gnu/gpl notice */
 443:  "+-----------------------------------------------------------------------+\n"
 444:  "|mimeTeX vers " VERSION ", " COPYRIGHTTEXT                             "|\n"
 445:  "+-----------------------------------------------------------------------+\n"
 446:  "| mimeTeX is free software, licensed to you under terms of the GNU/GPL, |\n"
 447:  "|           and comes with absolutely no warranty whatsoever.           |",
 448: *copyright2 =
 449:  "|          See http://www.forkosh.com/mimetex.html for details.         |\n"
 450:  "+-----------------------------------------------------------------------+";
 451: static  int maxmsgnum = 3,              /* maximum msgtable[] index */
 452:         /* --- keep these message numbers updated if table changes --- */
 453:         invmsgnum = 0,                  /* general invalid message */
 454:         refmsgnum = 3;                  /* urlncmp() failed to validate */
 455: static  char *msgtable[] = {            /* messages referenced by [index] */
 456:  "\\red\\small\\rm\\fbox{\\array{"      /* [0] is invalid_referer_msg */
 457:    "Please~read~www.forkosh.com/mimetex.html\\\\and~install~mimetex.cgi~"
 458:    "on~your~own~server.\\\\Thank~you,~John~Forkosh}}",
 459:  "\\red\\small\\rm\\fbox{\\array{"      /* [1] */
 460:    "Please~provide~your~{\\tiny~HTTP-REFERER}~to~access~the~public\\\\"
 461:    "mimetex~server.~~Or~please~read~~www.forkosh.com/mimetex.html\\\\"
 462:    "and~install~mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}",
 463:  "\\red\\small\\rm\\fbox{\\array{"      /* [2] */
 464:    "The~public~mimetex~server~is~for~testing.~~For~production,\\\\"
 465:    "please~read~~www.forkosh.com/mimetex.html~~and~install\\\\"
 466:    "mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}",
 467:  "\\red\\small\\rm\\fbox{\\array{"      /* [3] */
 468:    "Only~SERVER_NAME~may~use~mimetex~on~this~server.\\\\"
 469:    "Please~read~~www.forkosh.com/mimetex.html~~and~install\\\\"
 470:    "mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}",
 471:  NULL } ;                               /* trailer */
 472: 
 473: /* -------------------------------------------------------------------------
 474: additional symbols
 475: -------------------------------------------------------------------------- */
 476: /* ---
 477:  * windows-specific header info
 478:  * ---------------------------- */
 479: #if !defined(WINDOWS)           /* -DWINDOWS not supplied by user */
 480:   #if defined(_WINDOWS) || defined(_WIN32) || defined(WIN32) \
 481:   ||  defined(DJGPP)            /* try to recognize windows compilers */ \
 482:   ||  defined(_USRDLL)          /* must be WINDOWS if compiling for DLL */
 483:     #define WINDOWS             /* signal windows */
 484:   #endif
 485: #endif
 486: #if defined(WINDOWS)            /* Windows opens stdout in char mode, and */
 487:   #include <fcntl.h>            /* precedes every 0x0A with spurious 0x0D.*/
 488:   #include <io.h>               /* So emitcache() issues a Win _setmode() */
 489:                                 /* call to put stdout in binary mode. */
 490:   #if defined(_O_BINARY) && !defined(O_BINARY)  /* only have _O_BINARY */
 491:     #define O_BINARY _O_BINARY  /* make O_BINARY available, etc... */
 492:     #define setmode  _setmode
 493:     #define fileno   _fileno
 494:   #endif
 495:   #if defined(_O_BINARY) || defined(O_BINARY)  /* setmode() now available */
 496:     #define HAVE_SETMODE        /* so we'll use setmode() */
 497:   #endif
 498:   #if defined(_MSC_VER) && defined(_DEBUG) /* MS VC++ in debug mode */
 499:     /* to show source file and line numbers where memory leaks occur... */
 500:     #define _CRTDBG_MAP_ALLOC   /* ...include this debug macro */
 501:     #include <crtdbg.h>         /* and this debug library */
 502:   #endif
 503:   #define ISWINDOWS 1
 504: #else
 505:   #define ISWINDOWS 0
 506: #endif
 507: 
 508: /* ---
 509:  * check for supersampling or low-pass anti-aliasing
 510:  * ------------------------------------------------- */
 511: #if defined(SS)
 512:   #define ISSUPERSAMPLING 1
 513:   #if !defined(AAALGORITHM)
 514:     #define AAALGORITHM 1               /* default supersampling algorithm */
 515:   #endif
 516:   #if !defined(AA)                      /* anti-aliasing not explicitly set */
 517:     #define AA                          /* so define it ourselves */
 518:   #endif
 519:   #if !defined(SSFONTS)                 /* need supersampling fonts */
 520:     #define SSFONTS
 521:   #endif
 522: #else
 523:   #define ISSUPERSAMPLING 0
 524:   #if !defined(AAALGORITHM)
 525:     #define AAALGORITHM 3 /*2*/         /* default lowpass algorithm */
 526:   #endif
 527: #endif
 528: #if !defined(MAXFOLLOW)
 529:   #define MAXFOLLOW 8                   /* aafollowline() maxturn default */
 530: #endif
 531: 
 532: /* ---
 533:  * set aa (and default gif) if any anti-aliasing options specified
 534:  * --------------------------------------------------------------- */
 535: #if defined(AA) || defined(GIF) || defined(PNG) \
 536: ||  defined(CENTERWT) || defined(ADJACENTWT) || defined(CORNERWT) \
 537: ||  defined(MINADJACENT) || defined(MAXADJACENT)
 538:   #if !defined(GIF) && !defined(AA)     /* aa not explicitly specified */
 539:     #define AA                          /* so define it ourselves */
 540:   #endif
 541:   #if !defined(GIF) && !defined(PNG)    /* neither gif nor png specified */
 542:     #define GIF                         /* so default to gif */
 543:   #endif
 544: #endif
 545: /* --- resolve output option inconsistencies --- */
 546: #if defined(XBITMAP)                    /* xbitmap supercedes gif and png */
 547:   #if defined(AA)
 548:     #undef AA
 549:   #endif
 550:   #if defined(GIF)
 551:     #undef GIF
 552:   #endif
 553:   #if defined(PNG)
 554:     #undef PNG
 555:   #endif
 556: #endif
 557: 
 558: /* ---
 559:  * decide whether or not to compile main()
 560:  * ------------------------------------------ */
 561: #if defined(XBITMAP) || defined(GIF) || defined(PNG)
 562:   /* --- yes, main() >>will<< be compiled --- */
 563:   #define DRIVER                        /* main() driver will be compiled */
 564: #else /* --- no, main() >>won't<< be compiled (e.g., for gfuntype.c) --- */
 565:   /* ---
 566:    * e.g., for gfuntype.c you should compile it as
 567:    *   cc -DNOTEXFONTS gfuntype.c mimetex.c -lm -o gfuntype
 568:    * alternatively, gifscroll.c >>needs<< texfonts.h info
 569:    * and should be compiled >>without<< that -DNO... switch.
 570:    * Most programs should likely be compiled >>without<< it.
 571:    * ---------------------------------------------------------- */
 572:   #if defined(NOTEXFONTS) && defined(TEXFONTS)   /*do we want texfonts.h???*/
 573:     #undef TEXFONTS      /*assume texfonts.h >>not<< required if both given*/
 574:   #endif
 575: #endif
 576: 
 577: /* ---
 578:  * application headers
 579:  * ---------------------- */
 580: /* --- TeX font info --- */
 581: #if !defined(NOTEXFONTS) && !defined(TEXFONTS)
 582:   #define TEXFONTS                      /* default #include's texfonts.h */
 583: #endif
 584: /* --- "demo" programs only #include a few structs from mimetex.h --- */
 585: #define _MIMETEXMAIN (1)                /* to include "everything" */
 586: #include "mimetex.h"
 587: 
 588: /* ---
 589:  * info needed when gif image returned in memory buffer
 590:  * ---------------------------------------------------- */
 591: #if defined(GIF)                        /* compiling along with gifsave.c */
 592:   extern int gifSize;
 593:   extern int maxgifSize;
 594: #else                                   /* or just set dummy values */
 595:   static int gifSize=0, maxgifSize=0;
 596: #endif
 597: /* --- gamma correction --- */
 598: #if !defined(GAMMA)
 599:   #define GAMMA 1.25 /*1.75*/ /*2.2*/
 600: #endif
 601: #if !defined(REVERSEGAMMA)
 602:   #define REVERSEGAMMA 0.5              /* for \reverse white-on-black */
 603: #endif
 604: /* --- opaque background (default to transparent) --- */
 605: #if !defined(OPAQUE)
 606:   #define ISTRANSPARENT 1
 607: #else
 608:   #define ISTRANSPARENT 0
 609: #endif
 610: 
 611: /* ---
 612:  * internal buffer sizes
 613:  * --------------------- */
 614: #if !defined(MAXEXPRSZ)
 615:   #define MAXEXPRSZ (32768-1)           /*max #bytes in input tex expression*/
 616: #endif
 617: #if !defined(MAXSUBXSZ)
 618:   #define MAXSUBXSZ (((MAXEXPRSZ+1)/2)-1)/*max #bytes in input subexpression*/
 619: #endif
 620: #if !defined(MAXTOKNSZ)
 621:   #define MAXTOKNSZ (((MAXSUBXSZ+1)/4)-1) /* max #bytes in input token */
 622: #endif
 623: #if !defined(MAXFILESZ)
 624:   #define MAXFILESZ (65536-1)           /*max #bytes in input (output) file*/
 625: #endif
 626: #if !defined(MAXLINESZ)
 627:   #define MAXLINESZ (4096-1)            /* max #chars in line from file */
 628: #endif
 629: #if !defined(MAXGIFSZ)
 630:   #define MAXGIFSZ 131072               /* max #bytes in output GIF image */
 631: #endif
 632: 
 633: /* ---
 634:  * note: point3d.x,y,z are relative to the origin 0.,0.,0. of an abstract
 635:  * coordinate system, calculated to be at the center pixel of a raster.
 636:  * point3d and matrix3d are used for rotations, as per
 637:  *   https://en.wikipedia.org/wiki/Rotation_matrix#In_three_dimensions
 638:  * in function rotrast3d()
 639:  * ------------------------------------------------------------------------ */
 640: #if 0
 641:   /* ---
 642:    * >> Note: point3d and matrix3d now defined in mimetex.h <<
 643:    *      (they're temporarily left here only as comments)
 644:    * ------------------------------------------------------------ */
 645:   /* --- 3d-point --- */
 646:   #define point3d struct point3d_struct   /* "typedef" for point3d_struct */
 647:   point3d {
 648:     /*---x,y,z-coords relative to 0.,0.,0. origin of abstract coord axes---*/
 649:     double x;                             /* x-coord */
 650:     double y;                             /* y-coord */
 651:     double z;                             /* z-coord */
 652:     } ; /* --- end-of-point3d_struct --- */
 653:   /* --- 3d-matrix (rotation matrix) stored row-wise --- */
 654:   #define matrix3d struct matrix3d_struct /* "typedef" for matrix3d_struct */
 655:   matrix3d {
 656:     /* --- 3x3 matrix stored row-wise --- */
 657:     point3d xrow;                         /* x-row */
 658:     point3d yrow;                         /* y-row */
 659:     point3d zrow;                         /* z-row */
 660:     } ; /* --- end-of-matrix3d_struct --- */
 661: #endif
 662: /* --- 3d-rotation parameters --- */
 663: #define YAXIS {0.,1.,0.}                /* x-component=0, y=1, z=0 */
 664: #define UTHETA 0.0                      /* no rotation (in degrees) */
 665: GLOBAL(point3d,uaxis,YAXIS);            /* rotate around y-axis */
 666: GLOBAL(double,utheta,UTHETA);           /* rotation amount in degrees */
 667: 
 668: /* -------------------------------------------------------------------------
 669: adjustable default values
 670: -------------------------------------------------------------------------- */
 671: /* ---
 672:  * anti-aliasing parameters
 673:  * ------------------------ */
 674: #if !defined(CENTERWT)
 675:   /*#define CENTERWT 32*/               /* anti-aliasing centerwt default */
 676:   /*#define CENTERWT 10*/               /* anti-aliasing centerwt default */
 677:   #define CENTERWT 8                    /* anti-aliasing centerwt default */
 678: #endif
 679: #if !defined(ADJACENTWT)
 680:   /*#define ADJACENTWT 3*/              /* anti-aliasing adjacentwt default*/
 681:   #define ADJACENTWT 2                  /* anti-aliasing adjacentwt default*/
 682: #endif
 683: #if !defined(CORNERWT)
 684:   #define CORNERWT 1                    /* anti-aliasing cornerwt default*/
 685: #endif
 686: #if !defined(MINADJACENT)
 687:   #define MINADJACENT 6                 /*anti-aliasing minadjacent default*/
 688: #endif
 689: #if !defined(MAXADJACENT)
 690:   #define MAXADJACENT 8                 /*anti-aliasing maxadjacent default*/
 691: #endif
 692: /* --- variables for anti-aliasing parameters --- */
 693: GLOBAL(int,centerwt,CENTERWT);          /*lowpass matrix center pixel wt */
 694: GLOBAL(int,adjacentwt,ADJACENTWT);      /*lowpass matrix adjacent pixel wt*/
 695: GLOBAL(int,cornerwt,CORNERWT);          /*lowpass matrix corner pixel wt */
 696: GLOBAL(int,minadjacent,MINADJACENT);    /* darken if>=adjacent pts black*/
 697: GLOBAL(int,maxadjacent,MAXADJACENT);    /* darken if<=adjacent pts black */
 698: GLOBAL(int,weightnum,1);                /* font wt, */
 699: GLOBAL(int,maxaaparams,4);              /* #entries in table */
 700: /* --- anti-aliasing parameter values by font weight --- */
 701: #define aaparameters struct aaparameters_struct /* typedef */
 702: aaparameters
 703:   { int centerwt;                       /* lowpass matrix center   pixel wt*/
 704:     int adjacentwt;                     /* lowpass matrix adjacent pixel wt*/
 705:     int cornerwt;                       /* lowpass matrix corner   pixel wt*/
 706:     int minadjacent;                    /* darken if >= adjacent pts black */
 707:     int maxadjacent;                    /* darken if <= adjacent pts black */
 708:     int fgalias,fgonly,bgalias,bgonly; } ; /* aapnm() params */
 709: STATIC aaparameters aaparams[]          /* set params by weight */
 710:   #ifdef INITVALS
 711:   =
 712:   { /* ----------------------------------------------------
 713:     centerwt adj corner minadj max  fgalias,only,bgalias,only
 714:     ------------------------------------------------------- */
 715:         { 64,  1,  1,    6,  8,     1,0,0,0 },  /* 0 = light */
 716:         { CENTERWT,ADJACENTWT,CORNERWT,MINADJACENT,MAXADJACENT,1,0,0,0 },
 717:         { 8,   1,  1,    5,  8,     1,0,0,0 },  /* 2 = semibold */
 718:         { 8,   2,  1,    4,  9,     1,0,0,0 }   /* 3 = bold */
 719:   } /* --- end-of-aaparams[] --- */
 720:   #endif
 721:   ;
 722: /* --- anti-aliasing diagnostics (to help improve algorithm) --- */
 723: STATIC int patternnumcount0[99], patternnumcount1[99], /*aalookup() counts*/
 724:         ispatternnumcount = 1;          /* true to accumulate counts */
 725: 
 726: /* -------------------------------------------------------------------------
 727: other variables
 728: -------------------------------------------------------------------------- */
 729: /* --- black on white background (default), or white on black --- */
 730: #if defined(WHITE)
 731:   #define ISBLACKONWHITE 0              /* white on black background */
 732: #else
 733:   #define ISBLACKONWHITE 1              /* black on white background */
 734: #endif
 735: /* --- colors --- */
 736: #define BGRED   (ISBLACKONWHITE?255:0)
 737: #define BGGREEN (ISBLACKONWHITE?255:0)
 738: #define BGBLUE  (ISBLACKONWHITE?255:0)
 739: #if !defined(FGRED)
 740:   #define FGRED   (ISBLACKONWHITE?0:255)
 741: #endif
 742: #if !defined(FGGREEN)
 743:   #define FGGREEN (ISBLACKONWHITE?0:255)
 744: #endif
 745: #if !defined(FGBLUE)
 746:   #define FGBLUE  (ISBLACKONWHITE?0:255)
 747: #endif
 748: /* --- advertisement
 749:    one image in every ADFREQUENCY is wrapped in "advertisement" --- */
 750: #if !defined(ADFREQUENCY)
 751:   #define ADFREQUENCY 0                 /* never show advertisement if 0 */
 752: #endif
 753: #ifndef HOST_SHOWAD
 754:   #define HOST_SHOWAD "\000"            /* show ads on all hosts */
 755: #endif
 756: /* --- mathTeX info for \mathtex{} directive --- */
 757: #if !defined(MATHTEX)                   /* localhost:// or http://url/ */
 758:   #define MATHTEX "localhost://mathtex.cgi"
 759: #endif
 760: #if !defined(MATHTEXPWD)                /* mathTeX \password{} */
 761:   #define MATHTEXPWD "\000"
 762: #endif
 763: #if !defined(WGET)                      /* local /path/to/wget */
 764:   #define WGET "wget"
 765: #endif
 766: /* --- "smash" margin (0 means no smashing) --- */
 767: #if !defined(SMASHMARGIN)
 768:   #if defined(NOSMASH)
 769:     #define SMASHMARGIN 0
 770:   #else
 771:     #define SMASHMARGIN 3
 772:   #endif
 773: #endif
 774: #if !defined(SMASHCHECK)
 775:   #define SMASHCHECK 0
 776: #endif
 777: /* --- textwidth --- */
 778: #if !defined(TEXTWIDTH)
 779:   #define TEXTWIDTH (400)
 780: #endif
 781: /* --- font "combinations" --- */
 782: #define CMSYEX (109)                    /*select CMSY10, CMEX10 or STMARY10*/
 783: /* --- prefix prepended to all expressions --- */
 784: #if !defined(PREFIX)
 785:   #define PREFIX "\000"                 /* default no prepended prefix */
 786: #endif
 787: /* --- skip argv[]'s preceding ARGSIGNAL when parsing command-line args --- */
 788: #if defined(NOARGSIGNAL)
 789:   #define ARGSIGNAL NULL
 790: #endif
 791: #if !defined(ARGSIGNAL)
 792:   #define ARGSIGNAL "++"
 793: #endif
 794: /* --- security and logging (inhibit message logging, etc) --- */
 795: #if !defined(SECURITY)
 796:   #define SECURITY 999                  /* default highest security level */
 797: #endif
 798: #if !defined(LOGFILE)
 799:   #define LOGFILE "mimetex.log"         /* default log file */
 800: #endif
 801: #if !defined(CACHELOG)
 802:   #define CACHELOG "mimetex.log"        /* default caching log file */
 803: #endif
 804: #if !defined(NODUMPENVP) && !defined(DUMPENVP)
 805:   #define DUMPENVP                      /* assume char *envp[] available */
 806: #endif
 807: /* --- max query_string length if no http_referer supplied --- */
 808: #if !defined(NOREFMAXLEN)
 809:   #define NOREFMAXLEN 9999              /* default to any length query */
 810: #endif
 811: #if !defined(NOREFSAFELEN)
 812:   #define NOREFSAFELEN 24               /* too small for hack exploit */
 813: #endif
 814: /* --- check whether or not to perform http_referer check --- */
 815: #if defined(REFERER)                    /* only specified referers allowed */
 816:   #undef NOREFMAXLEN
 817:   #define NOREFMAXLEN NOREFSAFELEN
 818: #else                                   /* all http_referer's allowed */
 819:   #define REFERER NULL
 820: #endif
 821: /* --- check top levels of http_referer against server_name --- */
 822: #if defined(REFLEVELS)                  /* #topmost levels to check */
 823:   #undef NOREFMAXLEN
 824:   #define NOREFMAXLEN NOREFSAFELEN
 825: #else
 826:   #if defined(NOREFCHECK)
 827:     #define REFLEVELS 0                 /* don't match host and referer */
 828:   #else
 829:     #define REFLEVELS 3                 /* default matches abc.def.com */
 830:   #endif
 831: #endif
 832: /* --- password to bypass preceding REFERER checks --- */
 833: #if !defined(PASSWORD)
 834:   #define PASSWORD "\000"               /* \password{PASSWORD} in expression */
 835: #endif
 836: static  char password[128] = "\000";    /* user's \password{password} */
 837: static  int  ispassword = 0;            /* true if password==PASSWORD */
 838: /* --- check whether or not \input, \counter, \environment permitted --- */
 839: #if defined(DEFAULTSECURITY)            /* default security specified */
 840:   #define EXPLICITDEFSECURITY           /* don't override explicit default */
 841: #else                                   /* defualt security not specified */
 842:   #define DEFAULTSECURITY (8)           /* so set default security level */
 843: #endif
 844: #if defined(INPUTREFERER)               /*http_referer's permitted to \input*/
 845:   #if !defined(INPUTSECURITY)           /* so we need to permit \input{} */
 846:     #define INPUTSECURITY (99999)       /* make sure SECURITY<INPUTSECURITY */
 847:   #endif
 848: #else                                   /* no INPUTREFERER list supplied */
 849:   #define INPUTREFERER NULL             /* so init it as NULL pointer */
 850: #endif
 851: #if !defined(INPUTPATH)                 /* \input{} paths permitted for... */
 852:   #define INPUTPATH NULL                /* ...any referer */
 853: #endif
 854: #if !defined(INPUTSECURITY)             /* \input{} security not specified */
 855:   #if defined(INPUTOK)                  /* but INPUTOK flag specified */
 856:     #define INPUTSECURITY (99999)       /* so enable \input{} */
 857:     #if !defined(EXPLICITDEFSECURITY)   /* don't override explicit default */
 858:       #undef  DEFAULTSECURITY           /* but we'll override our default */
 859:       #define DEFAULTSECURITY (99999)   /*let -DINPUTOK enable \counter,etc*/
 860:     #endif
 861:   #else                                 /* else no \input{} specified */
 862:     #define INPUTSECURITY DEFAULTSECURITY /* set default \input security */
 863:   #endif
 864: #endif
 865: #if !defined(COUNTERSECURITY)           /*\counter{} security not specified*/
 866:   #if defined(COUNTEROK)                /* but COUNTEROK flag specified */
 867:     #define COUNTERSECURITY (99999)     /* so enable \counter{} */
 868:   #else                                 /* else no \counter{} specified */
 869:     #define COUNTERSECURITY DEFAULTSECURITY /*set default \counter security*/
 870:   #endif
 871: #endif
 872: #if !defined(ENVIRONSECURITY)           /* \environ security not specified */
 873:   #if defined(ENVIRONOK)                /* but ENVIRONOK flag specified */
 874:     #define ENVIRONSECURITY (99999)     /* so enable \environ */
 875:   #else                                 /* else no \environ specified */
 876:     #define ENVIRONSECURITY DEFAULTSECURITY /*set default \environ security*/
 877:   #endif
 878: #endif
 879: /* --- image caching (cache images if given -DCACHEPATH=\"path\") --- */
 880: #if !defined(CACHEPATH)
 881:   #define ISCACHING 0                   /* no caching */
 882:   #define CACHEPATH "\000"              /* same directory as mimetex.cgi */
 883: #else
 884:   #define ISCACHING 1                   /* caching if -DCACHEPATH="path" */
 885: #endif
 886: /* --- \input paths (prepend prefix if given -DPATHPREFIX=\"prefix\") --- */
 887: #if !defined(PATHPREFIX)
 888:   #define PATHPREFIX "\000"             /* paths relative mimetex.cgi */
 889: #endif
 890: /* --- time zone delta t (in hours) --- */
 891: #if !defined(TZDELTA)
 892:   #define TZDELTA 0
 893: #endif
 894: /* --- treat +'s in query string as blanks? --- */
 895: #if defined(PLUSBLANK)                  /* + always interpreted as blank */
 896:   #define ISPLUSBLANK 1
 897: #else
 898:   #if defined(PLUSNOTBLANK)             /* + never interpreted as blank */
 899:     #define ISPLUSBLANK 0
 900:   #else                                 /* program tries to determine */
 901:     #define ISPLUSBLANK (-1)
 902:   #endif
 903: #endif
 904: 
 905: /* -------------------------------------------------------------------------
 906: debugging and logging / error reporting
 907: -------------------------------------------------------------------------- */
 908: /* --- debugging and error reporting --- */
 909: #if !defined(MSGLEVEL)
 910:   #define MSGLEVEL 1
 911: #endif
 912: #define DBGLEVEL 9                      /* debugging if msglevel>=DBGLEVEL */
 913: #define LOGLEVEL 3                      /* logging if msglevel>=LOGLEVEL */
 914: #if !defined(FORMLEVEL)
 915:   #define FORMLEVEL LOGLEVEL            /*msglevel if called from html form*/
 916: #endif
 917: #if !defined(ERRORSTATUS)               /* exit(ERRORSTATUS) for any error */
 918:   #define ERRORSTATUS 0                 /* default doesn't signal errors */
 919: #endif
 920: GLOBAL(int,seclevel,SECURITY);          /* security level */
 921: GLOBAL(int,inputseclevel,INPUTSECURITY); /* \input{} security level */
 922: GLOBAL(int,counterseclevel,COUNTERSECURITY); /* \counter{} security level */
 923: GLOBAL(int,environseclevel,ENVIRONSECURITY); /* \environ{} security level */
 924: GLOBAL(int,msglevel,MSGLEVEL);          /* message level for verbose/debug */
 925: GLOBAL(int,errorstatus,ERRORSTATUS);    /* exit status if error encountered*/
 926: GLOBAL(int,exitstatus,0);               /* exit status (0=success) */
 927: STATIC  FILE *msgfp;                    /* output in command-line mode */
 928: /* --- logging macros --- */
 929: #define logdump(lvl,msg)  if(msglevel>=(lvl)) {logmsg(msg);}else
 930: #define logmsg(msg) if(1) {logger(msgfp,msglevel,(msg),NULL);}else
 931: /* --- embed warnings in rendered expressions, [\xxx?] if \xxx unknown --- */
 932: #if defined(WARNINGS)
 933:   #define WARNINGLEVEL WARNINGS
 934: #else
 935:   #if defined(NOWARNINGS)
 936:     #define WARNINGLEVEL 0
 937:   #else
 938:     #define WARNINGLEVEL 1
 939:   #endif
 940: #endif
 941: GLOBAL(int,warninglevel,WARNINGLEVEL);  /* warning level */
 942: 
 943: /* -------------------------------------------------------------------------
 944: control flags and values
 945: -------------------------------------------------------------------------- */
 946: GLOBAL(int,isquery,0);                  /* true=cgi?query; false=commandline*/
 947: GLOBAL(int,daemonlevel,0);              /* incremented in main() */
 948: GLOBAL(int,recurlevel,0);               /* inc/decremented in rasterize() */
 949: GLOBAL(int,scriptlevel,0);              /* inc/decremented in rastlimits() */
 950: GLOBAL(int,isstring,0);                 /*pixmap is ascii string, not raster*/
 951: GLOBAL(int,isligature,0);               /* true if ligature found */
 952: GLOBAL(char,*subexprptr,(char *)NULL);  /* ptr within expression to subexpr*/
 953: /*SHARED(int,imageformat,1);*/          /* image is 1=bitmap, 2=.gf-like */
 954: GLOBAL(int,isdisplaystyle,1);           /* displaystyle mode (forced if 2) */
 955: GLOBAL(int,ispreambledollars,0);        /* displaystyle mode set by $$...$$ */
 956: GLOBAL(int,ninputcmds,0);               /* # of \input commands processed */
 957: GLOBAL(int,fontnum,0);                  /* cal=1,scr=2,rm=3,it=4,bb=5,bf=6 */
 958: GLOBAL(int,fontsize,NORMALSIZE);        /* current size */
 959: GLOBAL(int,magstep,1);                  /* magstep (1=no change) */
 960: GLOBAL(int,displaysize,DISPLAYSIZE);    /* use \displaystyle when fontsize>=*/
 961: GLOBAL(int,shrinkfactor,3);             /* shrinkfactors[fontsize] */
 962: GLOBAL(int,rastlift,0);                 /* rastraise() lift parameter */
 963: GLOBAL(int,rastlift1,0);                /* rastraise() lift for base exprssn*/
 964: GLOBAL(double,unitlength,1.0);          /* #pixels per unit (may be <1.0) */
 965: GLOBAL(int,iunitlength,1);              /* #pixels per unit as int for store*/
 966: /*GLOBAL(int,textwidth,TEXTWIDTH);*/    /* #pixels across line */
 967: GLOBAL(int,adfrequency,ADFREQUENCY);    /* advertisement frequency */
 968: GLOBAL(int,isnocatspace,0);             /* >0 to not add space in rastcat()*/
 969: GLOBAL(int,smashmargin,SMASHMARGIN);    /* minimum "smash" margin */
 970: GLOBAL(int,mathsmashmargin,SMASHMARGIN); /* needed for \text{if $n-m$ even}*/
 971: GLOBAL(int,issmashdelta,1);             /* true if smashmargin is a delta */
 972: GLOBAL(int,isexplicitsmash,0);          /* true if \smash explicitly given */
 973: GLOBAL(int,smashcheck,SMASHCHECK);      /* check if terms safe to smash */
 974: GLOBAL(int,isnomath,0);                 /* true to inhibit math mode */
 975: GLOBAL(int,isscripted,0);               /* is (lefthand) term text-scripted*/
 976: GLOBAL(int,isdelimscript,0);            /* is \right delim text-scripted */
 977: GLOBAL(int,issmashokay,0);              /*is leading char okay for smashing*/
 978: #define BLANKSIGNAL (-991234)           /*rastsmash signal right-hand blank*/
 979: GLOBAL(int,blanksignal,BLANKSIGNAL);    /*rastsmash signal right-hand blank*/
 980: GLOBAL(int,blanksymspace,0);            /* extra (or too much) space wanted*/
 981: GLOBAL(int,istransparent,ISTRANSPARENT);/* true sets background transparent*/
 982: GLOBAL(int,fgred,FGRED);
 983:   GLOBAL(int,fggreen,FGGREEN);
 984:   GLOBAL(int,fgblue,FGBLUE);            /* fg r,g,b */
 985: GLOBAL(int,bgred,BGRED);
 986:   GLOBAL(int,bggreen,BGGREEN);
 987:   GLOBAL(int,bgblue,BGBLUE);            /* bg r,g,b */
 988: GLOBAL(double,gammacorrection,GAMMA);   /* gamma correction */
 989: GLOBAL(int,isplusblank,ISPLUSBLANK);    /*interpret +'s in query as blanks?*/
 990: GLOBAL(int,isblackonwhite,ISBLACKONWHITE); /*1=black on white,0=reverse*/
 991: GLOBAL(char,exprprefix[256],PREFIX);    /* prefix prepended to expressions */
 992: GLOBAL(int,aaalgorithm,AAALGORITHM);    /* for lp, 1=aalowpass, 2 =aapnm */
 993: GLOBAL(int,maxfollow,MAXFOLLOW);        /* aafollowline() maxturn parameter*/
 994: GLOBAL(int,fgalias,1);
 995:   GLOBAL(int,fgonly,0);
 996:   GLOBAL(int,bgalias,0);
 997:   GLOBAL(int,bgonly,0);                 /* aapnm() params */
 998: GLOBAL(int,issupersampling,ISSUPERSAMPLING); /*1=supersampling 0=lowpass*/
 999: GLOBAL(int,isss,ISSUPERSAMPLING);       /* supersampling flag for main() */
1000: GLOBAL(int,ispbmpgm,0);                 /* true for pbm/pgm instead of gif */
1001: GLOBAL(int,pbmpgmtype,0);               /* 1=pbm / 2=pgm (else error) */
1002: GLOBAL(int,*workingparam,(int *)NULL);  /* working parameter */
1003: GLOBAL(subraster,*workingbox,(subraster *)NULL); /*working subraster box*/
1004: GLOBAL(int,isreplaceleft,0);            /* true to replace leftexpression */
1005: GLOBAL(subraster,*leftexpression,(subraster *)NULL); /*rasterized so far*/
1006: GLOBAL(mathchardef,*leftsymdef,NULL);   /* mathchardef for preceding symbol*/
1007: GLOBAL(int,fraccenterline,NOVALUE);     /* baseline for punct. after \frac */
1008: /*GLOBAL(int,currentcharclass,NOVALUE);*/ /*primarily to check for PUNCTION*/
1009: GLOBAL(int,iscaching,ISCACHING);        /* true if caching images */
1010: GLOBAL(char,cachepath[256],CACHEPATH);  /* relative path to cached files */
1011: GLOBAL(int,isemitcontenttype,1);        /* true to emit mime content-type */
1012: int     iscachecontenttype = 0;         /* true to cache mime content-type */
1013: char    contenttype[2048] = "\000";     /* content-type:, etc buffer */
1014: GLOBAL(char,pathprefix[256],PATHPREFIX); /*prefix for \input,\counter paths*/
1015: /*GLOBAL(int,iswindows,ISWINDOWS);*/    /* true if compiled for ms windows */
1016: 
1017: /* -------------------------------------------------------------------------
1018: store for evalterm() [n.b., these are stripped-down funcs from nutshell]
1019: -------------------------------------------------------------------------- */
1020: #if 0
1021:   /* ---
1022:    * >> Note: STORE and MAXSTORE now defined in mimetex.h <<
1023:    *     (they're temporarily left here only as comments)
1024:    * ----------------------------------------------------------- */
1025:   #define STORE struct store_struct     /* "typedef" for store struct */
1026:   #define MAXSTORE 100                  /* max 100 identifiers */
1027:   STORE {
1028:     char *identifier;                   /* identifier */
1029:     int  *value;                        /* address of corresponding value */
1030:     } ; /* --- end-of-store_struct --- */
1031: #endif
1032: static STORE mimestore[MAXSTORE] = {
1033:     { "fontsize", &fontsize },  { "fs", &fontsize },    /* font size */
1034:     { "fontnum", &fontnum },    { "fn", &fontnum },     /* font number */
1035:     { "unitlength", &iunitlength },                     /* unitlength */
1036:     /*{ "mytestvar", &mytestvar },*/
1037:     { NULL, NULL }                                      /* end-of-store */
1038:   } ; /* --- end-of-mimestore[] --- */
1039: 
1040: /* -------------------------------------------------------------------------
1041: other application global data
1042: -------------------------------------------------------------------------- */
1043: /* --- getdirective() global data --- */
1044: static  int  argformat   = 0;           /* 111... if arg not {}-enclosed */
1045: static  int  optionalpos = 0;           /* # {args} before optional [args] */
1046: static  int  noptional   = 0;           /* # optional [args] found */
1047: static  char optionalargs[10][128] =    /* buffer for optional args */
1048:   { "\000", "\000", "\000", "\000", "\000", "\000", "\000", "\000" };
1049: /* --- **dump/debug** data --- */
1050: int     isdumpgif = 0;                  /* **debug** dump gif image */
1051: #if !defined(DUMPGIF)
1052:   #define DUMPGIF "dumpgif"             /* use .gif_dumpgif extension */
1053: #endif
1054: 
1055: /* -------------------------------------------------------------------------
1056: miscellaneous macros
1057: -------------------------------------------------------------------------- */
1058: #if 0   /* --- these are now #define'd in mimetex.h --- */
1059: #define max2(x,y)  ((x)>(y)? (x):(y))   /* larger of 2 arguments */
1060: #define min2(x,y)  ((x)<(y)? (x):(y))   /* smaller of 2 arguments */
1061: #define max3(x,y,z) max2(max2(x,y),(z)) /* largest of 3 arguments */
1062: #define min3(x,y,z) min2(min2(x,y),(z)) /* smallest of 3 arguments */
1063: #define absval(x)  ((x)>=0?(x):(-(x)))  /* absolute value */
1064: #define iround(x)  ((int)((x)>=0?(x)+0.5:(x)-0.5)) /* round double to int */
1065: #define dmod(x,y)  ((x)-((y)*((double)((int)((x)/(y)))))) /*x%y for doubles*/
1066: #endif
1067: #define compress(s,c) if((s)!=NULL)     /* remove embedded c's from s */ \
1068:         { char *p; while((p=strchr((s),(c)))!=NULL) {strsqueeze(p,1);} } else
1069: #define slower(s)  if ((s)!=NULL)       /* lowercase all chars in s */ \
1070:         { char *p=(s); while(*p!='\000'){*p=tolower(*p); p++;} } else
1071: /*subraster *subrastcpy();*/            /* need global module declaration */
1072: /*#define spnosmash(sp) if (sp->type==CHARASTER) sp=subrastcpy(sp); \ */
1073: /*      sp->type=blanksignal */
1074: /* ---evaluate \directive[arg] or \directive{arg} scaled by unitlength--- */
1075: #define eround(arg) (iround(unitlength*((double)evalterm(mimestore,(arg)))))
1076: /* --- check if a string is empty --- */
1077: #define isempty(s)  ((s)==NULL?1:(*(s)=='\000'?1:0))
1078: /* --- last char of a string --- */
1079: #define lastchar(s) (isempty(s)?'\000':*((s)+(strlen(s)-1)))
1080: /* --- lowercase a string --- */
1081: #define strlower(s) strnlower((s),0)    /* lowercase an entire string */
1082: /* --- strip leading and trailing whitespace (including ~) --- */
1083: #define trimwhite(thisstr) if ( (thisstr) != NULL ) { \
1084:         int thislen = strlen(thisstr); \
1085:         while ( --thislen >= 0 ) \
1086:           if ( isthischar((thisstr)[thislen]," \t\n\r\f\v") ) \
1087:             (thisstr)[thislen] = '\000'; \
1088:           else break; \
1089:         if ( (thislen = strspn((thisstr)," \t\n\r\f\v")) > 0 ) \
1090:           {strsqueeze((thisstr),thislen);} } else
1091: /* --- strncpy() n bytes and make sure it's null-terminated --- */
1092: #define strninit(target,source,n) if( (target)!=NULL && (n)>=0 ) { \
1093:           char *thissource = (source); \
1094:           (target)[0] = '\000'; \
1095:           if ( (n)>0 && thissource!=NULL ) { \
1096:             strncpy((target),thissource,(n)); \
1097:             (target)[(n)] = '\000'; } }
1098: /* --- strcpy(s,s+n) using memmove() (also works for negative n) --- */
1099: #define strsqueeze(s,n) if((n)!=0) { if(!isempty((s))) { \
1100:         int thislen3=strlen(s); \
1101:         if ((n) >= thislen3) *(s) = '\000'; \
1102:         else memmove(s,s+(n),1+thislen3-(n)); }} else/*user supplies final;*/
1103: /* --- strsqueeze(s,t) with two pointers --- */
1104: #define strsqueezep(s,t) if(!isempty((s))&&!isempty((t))) { \
1105:         int sqlen=strlen((s))-strlen((t)); \
1106:         if (sqlen>0 && sqlen<=999) {strsqueeze((s),sqlen);} } else
1107: /* --- skip/find whitespace (from mathtex.c for getdirective()) --- */
1108: #define findwhite(thisstr)  while ( !isempty(thisstr) ) \
1109:         if ( isthischar(*(thisstr),WHITESPACE) ) break; else (thisstr)++
1110:         /* thisstr += strcspn(thisstr,WHITESPACE) */
1111: /* --- skip \command (i.e., find char past last char of \command) --- */
1112: #define skipcommand(thisstr)  while ( !isempty(thisstr) ) \
1113:         if ( !isalpha(*(thisstr)) ) break; else (thisstr)++
1114: 
1115: /* ---
1116:  * PART2
1117:  * ------ */
1118: #if !defined(PARTS) || defined(PART2)
1119: /* ==========================================================================
1120:  * Function:    new_raster ( width, height, pixsz )
1121:  * Purpose:     Allocation and constructor for raster.
1122:  *              mallocs and initializes memory for width*height pixels,
1123:  *              and returns raster struct ptr to caller.
1124:  * --------------------------------------------------------------------------
1125:  * Arguments:   width (I)       int containing width, in bits,
1126:  *                              of raster pixmap to be allocated
1127:  *              height (I)      int containing height, in bits/scans,
1128:  *                              of raster pixmap to be allocated
1129:  *              pixsz (I)       int containing #bits per pixel, 1 or 8
1130:  * --------------------------------------------------------------------------
1131:  * Returns:     ( raster * )    ptr to allocated and initialized
1132:  *                              raster struct, or NULL for any error.
1133:  * --------------------------------------------------------------------------
1134:  * Notes:
1135:  * ======================================================================= */
1136: /* --- entry point --- */
1137: FUNCSCOPE raster *new_raster ( int width, int height, int pixsz )
1138: {
1139: /* -------------------------------------------------------------------------
1140: Allocations and Declarations
1141: -------------------------------------------------------------------------- */
1142: raster  *rp = (raster *)NULL;           /* raster ptr returned to caller */
1143: pixbyte *pixmap = NULL;                 /* raster pixel map to be malloced */
1144: int     nbytes = pixsz*bitmapsz(width,height); /* #bytes needed for pixmap */
1145: int     filler = (isstring?' ':0);      /* pixmap filler */
1146: /*int   delete_raster();*/              /* in case pixmap malloc() fails */
1147: int     npadding = (0&&issupersampling?8+256:0); /* padding bytes */
1148: /* -------------------------------------------------------------------------
1149: allocate and initialize raster struct and embedded bitmap
1150: -------------------------------------------------------------------------- */
1151: if ( msgfp!=NULL && msglevel>=9999 )
1152:   { fprintf(msgfp,"new_raster(%d,%d,%d)> entry point\n",
1153:     width,height,pixsz); fflush(msgfp); }
1154: /* --- allocate and initialize raster struct --- */
1155: rp = (raster *)malloc(sizeof(raster));  /* malloc raster struct */
1156: if ( msgfp!=NULL && msglevel>=9999 )
1157:   { fprintf(msgfp,"new_raster> rp=malloc(%d) returned (%s)\n",
1158:     (int)sizeof(raster),(rp==NULL?"null ptr":"success")); fflush(msgfp); }
1159: if ( rp == (raster *)NULL )             /* malloc failed */
1160:   goto end_of_job;                      /* return error to caller */
1161: rp->width = width;                      /* store width in raster struct */
1162: rp->height = height;                    /* and store height */
1163: rp->format = 1;                         /* initialize as bitmap format */
1164: rp->pixsz = pixsz;                      /* store #bits per pixel */
1165: rp->pixmap = (pixbyte *)NULL;           /* init bitmap as null ptr */
1166: /* --- allocate and initialize bitmap array --- */
1167: if ( msgfp!=NULL && msglevel>=9999 )
1168:   { fprintf(msgfp,"new_raster> calling pixmap=malloc(%d)\n",
1169:     nbytes); fflush(msgfp); }
1170: if ( nbytes>0 && nbytes<=pixsz*maxraster )  /* fail if width*height too big*/
1171:   pixmap = (pixbyte *)malloc(nbytes+npadding); /*bytes for width*height bits*/
1172: if ( msgfp!=NULL && msglevel>=9999 )
1173:   { fprintf(msgfp,"new_raster> pixmap=malloc(%d) returned (%s)\n",
1174:     nbytes,(pixmap==NULL?"null ptr":"success")); fflush(msgfp); }
1175: if ( pixmap == (pixbyte *)NULL )        /* malloc failed */
1176:   { delete_raster(rp);                  /* so free everything */
1177:     rp = (raster *)NULL;                /* reset pointer */
1178:     goto end_of_job; }                  /* and return error to caller */
1179: memset((void *)pixmap,filler,nbytes);   /* init bytes to binary 0's or ' 's*/
1180: *pixmap = (pixbyte)0;                   /* and first byte always 0 */
1181: rp->pixmap = pixmap;                    /* store ptr to malloced memory */
1182: /* -------------------------------------------------------------------------
1183: Back to caller with address of raster struct, or NULL ptr for any error.
1184: -------------------------------------------------------------------------- */
1185: end_of_job:
1186:   if ( msgfp!=NULL && msglevel>=9999 )
1187:     { fprintf(msgfp,"new_raster(%d,%d,%d)> returning (%s)\n",
1188:       width,height,pixsz,(rp==NULL?"null ptr":"success")); fflush(msgfp); }
1189:   return ( rp );                        /* back to caller with raster */
1190: } /* --- end-of-function new_raster() --- */
1191: 
1192: 
1193: /* ==========================================================================
1194:  * Function:    new_subraster ( width, height, pixsz )
1195:  * Purpose:     Allocate a new subraster along with
1196:  *              an embedded raster of width x height.
1197:  * --------------------------------------------------------------------------
1198:  * Arguments:   width (I)       int containing width of embedded raster
1199:  *              height (I)      int containing height of embedded raster
1200:  *              pixsz (I)       int containing #bits per pixel, 1 or 8
1201:  * --------------------------------------------------------------------------
1202:  * Returns:     ( subraster * ) ptr to newly-allocated subraster,
1203:  *                              or NULL for any error.
1204:  * --------------------------------------------------------------------------
1205:  * Notes:     o if width or height <=0, embedded raster not allocated
1206:  * ======================================================================= */
1207: /* --- entry point --- */
1208: FUNCSCOPE subraster *new_subraster ( int width, int height, int pixsz )
1209: {
1210: /* -------------------------------------------------------------------------
1211: Allocations and Declarations
1212: -------------------------------------------------------------------------- */
1213: subraster *sp=NULL;                     /* subraster returned to caller */
1214: raster  /**new_raster(),*/ *rp=NULL;    /* image raster embedded in sp */
1215: /*int   delete_subraster();*/           /* in case new_raster() fails */
1216: int     size = NORMALSIZE,              /* default size */
1217:         baseline = height-1;            /* and baseline */
1218: /* -------------------------------------------------------------------------
1219: allocate and initialize subraster struct
1220: -------------------------------------------------------------------------- */
1221: if ( msgfp!=NULL && msglevel>=9999 )
1222:   { fprintf(msgfp,"new_subraster(%d,%d,%d)> entry point\n",
1223:     width,height,pixsz); fflush(msgfp); }
1224: /* --- allocate subraster struct --- */
1225: sp = (subraster *)malloc(sizeof(subraster));  /* malloc subraster struct */
1226: if ( sp == (subraster *)NULL )          /* malloc failed */
1227:   goto end_of_job;                      /* return error to caller */
1228: /* --- initialize subraster struct --- */
1229: sp->type = NOVALUE;                     /* character or image raster */
1230: sp->symdef =  (mathchardef *)NULL;      /* mathchardef identifying image */
1231: sp->baseline = baseline;                /*0 if image is entirely descending*/
1232: sp->size = size;                        /* font size 0-4 */
1233: sp->toprow = sp->leftcol = (-1);        /* upper-left corner of subraster */
1234: sp->image = (raster *)NULL;             /*ptr to bitmap image of subraster*/
1235: /* -------------------------------------------------------------------------
1236: allocate raster and embed it in subraster, and return to caller
1237: -------------------------------------------------------------------------- */
1238: /* --- allocate raster struct if desired --- */
1239: if ( width>0 && height>0 && pixsz>0 )   /* caller wants raster */
1240:   { if ( (rp=new_raster(width,height,pixsz)) /* allocate embedded raster */
1241:     !=   NULL )                         /* if allocate succeeded */
1242:         sp->image = rp;                 /* embed raster in subraster */
1243:     else                                /* or if allocate failed */
1244:       { delete_subraster(sp);           /* free non-unneeded subraster */
1245:         sp = NULL; } }                  /* signal error */
1246: /* --- back to caller with new subraster or NULL --- */
1247: end_of_job:
1248:   if ( msgfp!=NULL && msglevel>=9999 )
1249:     { fprintf(msgfp,"new_subraster(%d,%d,%d)> returning (%s)\n",
1250:       width,height,pixsz,(sp==NULL?"null ptr":"success")); fflush(msgfp); }
1251:   return ( sp );
1252: } /* --- end-of-function new_subraster() --- */
1253: 
1254: 
1255: /* ==========================================================================
1256:  * Function:    new_chardef (  )
1257:  * Purpose:     Allocates and initializes a chardef struct,
1258:  *              but _not_ the embedded raster struct.
1259:  * --------------------------------------------------------------------------
1260:  * Arguments:   none
1261:  * --------------------------------------------------------------------------
1262:  * Returns:     ( chardef * )   ptr to allocated and initialized
1263:  *                              chardef struct, or NULL for any error.
1264:  * --------------------------------------------------------------------------
1265:  * Notes:
1266:  * ======================================================================= */
1267: /* --- entry point --- */
1268: FUNCSCOPE chardef *new_chardef (  )
1269: {
1270: /* -------------------------------------------------------------------------
1271: Allocations and Declarations
1272: -------------------------------------------------------------------------- */
1273: chardef *cp = (chardef *)NULL;          /* chardef ptr returned to caller */
1274: /* -------------------------------------------------------------------------
1275: allocate and initialize chardef struct
1276: -------------------------------------------------------------------------- */
1277: cp = (chardef *)malloc(sizeof(chardef)); /* malloc chardef struct */
1278: if ( cp == (chardef *)NULL )            /* malloc failed */
1279:   goto end_of_job;                      /* return error to caller */
1280: cp->charnum = cp->location = 0;         /* init character description */
1281: cp->toprow = cp->topleftcol = 0;        /* init upper-left corner */
1282: cp->botrow = cp->botleftcol = 0;        /* init lower-left corner */
1283: cp->image.width = cp->image.height = 0; /* init raster dimensions */
1284: cp->image.format = 0;                   /* init raster format */
1285: cp->image.pixsz = 0;                    /* and #bits per pixel */
1286: cp->image.pixmap = NULL;                /* init raster pixmap as null */
1287: /* -------------------------------------------------------------------------
1288: Back to caller with address of chardef struct, or NULL ptr for any error.
1289: -------------------------------------------------------------------------- */
1290: end_of_job:
1291:   return ( cp );
1292: } /* --- end-of-function new_chardef() --- */
1293: 
1294: 
1295: /* ==========================================================================
1296:  * Function:    delete_raster ( rp )
1297:  * Purpose:     Destructor for raster.
1298:  *              Frees memory for raster bitmap and struct.
1299:  * --------------------------------------------------------------------------
1300:  * Arguments:   rp (I)          ptr to raster struct to be deleted.
1301:  * --------------------------------------------------------------------------
1302:  * Returns:     ( int )         1 if completed successfully,
1303:  *                              or 0 otherwise (for any error).
1304:  * --------------------------------------------------------------------------
1305:  * Notes:
1306:  * ======================================================================= */
1307: /* --- entry point --- */
1308: FUNCSCOPE int delete_raster ( raster *rp )
1309: {
1310: /* -------------------------------------------------------------------------
1311: free raster bitmap and struct
1312: -------------------------------------------------------------------------- */
1313: if ( rp != (raster *)NULL )             /* can't free null ptr */
1314:   {
1315:   if ( rp->pixmap != (pixbyte *)NULL )  /* can't free null ptr */
1316:     free((void *)rp->pixmap);           /* free pixmap within raster */
1317:   free((void *)rp);                     /* lastly, free raster struct */
1318:   } /* --- end-of-if(rp!=NULL) --- */
1319: return ( 1 );                           /* back to caller, 1=okay 0=failed */
1320: } /* --- end-of-function delete_raster() --- */
1321: 
1322: 
1323: /* ==========================================================================
1324:  * Function:    delete_subraster ( sp )
1325:  * Purpose:     Deallocates a subraster (and embedded raster)
1326:  * --------------------------------------------------------------------------
1327:  * Arguments:   sp (I)          ptr to subraster struct to be deleted.
1328:  * --------------------------------------------------------------------------
1329:  * Returns:     ( int )         1 if completed successfully,
1330:  *                              or 0 otherwise (for any error).
1331:  * --------------------------------------------------------------------------
1332:  * Notes:
1333:  * ======================================================================= */
1334: /* --- entry point --- */
1335: FUNCSCOPE int delete_subraster ( subraster *sp )
1336: {
1337: /* -------------------------------------------------------------------------
1338: free subraster struct
1339: -------------------------------------------------------------------------- */
1340: /*int   delete_raster();*/              /* to delete embedded raster */
1341: if ( sp != (subraster *)NULL )          /* can't free null ptr */
1342:   {
1343:   if ( sp->type != CHARASTER )          /* not static character data */
1344:     if ( sp->image != NULL )            /*raster allocated within subraster*/
1345:       delete_raster(sp->image);         /* so free embedded raster */
1346:   free((void *)sp);                     /* and free subraster struct itself*/
1347:   } /* --- end-of-if(sp!=NULL) --- */
1348: return ( 1 );                           /* back to caller, 1=okay 0=failed */
1349: } /* --- end-of-function delete_subraster() --- */
1350: 
1351: 
1352: /* ==========================================================================
1353:  * Function:    delete_chardef ( cp )
1354:  * Purpose:     Deallocates a chardef (and bitmap of embedded raster)
1355:  * --------------------------------------------------------------------------
1356:  * Arguments:   cp (I)          ptr to chardef struct to be deleted.
1357:  * --------------------------------------------------------------------------
1358:  * Returns:     ( int )         1 if completed successfully,
1359:  *                              or 0 otherwise (for any error).
1360:  * --------------------------------------------------------------------------
1361:  * Notes:
1362:  * ======================================================================= */
1363: /* --- entry point --- */
1364: FUNCSCOPE int delete_chardef ( chardef *cp )
1365: {
1366: /* -------------------------------------------------------------------------
1367: free chardef struct
1368: -------------------------------------------------------------------------- */
1369: if ( cp != (chardef *)NULL )            /* can't free null ptr */
1370:   {
1371:   if ( cp->image.pixmap != NULL )       /* pixmap allocated within raster */
1372:     free((void *)cp->image.pixmap);     /* so free embedded pixmap */
1373:   free((void *)cp);                     /* and free chardef struct itself */
1374:   } /* --- end-of-if(cp!=NULL) --- */
1375: /* -------------------------------------------------------------------------
1376: Back to caller with 1=okay, 0=failed.
1377: -------------------------------------------------------------------------- */
1378: return ( 1 );
1379: } /* --- end-of-function delete_chardef() --- */
1380: 
1381: 
1382: /* ==========================================================================
1383:  * Function:    rastcpy ( rp )
1384:  * Purpose:     makes duplicate copy of rp
1385:  * --------------------------------------------------------------------------
1386:  * Arguments:   rp (I)          ptr to raster struct to be copied
1387:  * --------------------------------------------------------------------------
1388:  * Returns:     ( raster * )    ptr to new copy rp,
1389:  *                              or NULL for any error.
1390:  * --------------------------------------------------------------------------
1391:  * Notes:     o
1392:  * ======================================================================= */
1393: /* --- entry point --- */
1394: FUNCSCOPE raster *rastcpy ( raster *rp )
1395: {
1396: /* -------------------------------------------------------------------------
1397: Allocations and Declarations
1398: -------------------------------------------------------------------------- */
1399: raster  /**new_raster(),*/ *newrp=NULL; /*copied raster returned to caller*/
1400: int     height= (rp==NULL?0:rp->height), /* original and copied height */
1401:         width = (rp==NULL?0:rp->width), /* original and copied width */
1402:         pixsz = (rp==NULL?0:rp->pixsz), /* #bits per pixel */
1403:         nbytes= (rp==NULL?0:(pixmapsz(rp))); /* #bytes in rp's pixmap */
1404: /* -------------------------------------------------------------------------
1405: allocate copied raster and fill it
1406: -------------------------------------------------------------------------- */
1407: /* --- allocate copied raster with same width,height, and copy bitmap --- */
1408: if ( rp != NULL )                       /* nothing to copy if ptr null */
1409:   if ( (newrp = new_raster(width,height,pixsz)) /*same width,height in copy*/
1410:   !=   NULL )                           /* check that allocate succeeded */
1411:     memcpy(newrp->pixmap,rp->pixmap,nbytes); /* fill copied raster pixmap */
1412: return ( newrp );                       /* return copied raster to caller */
1413: } /* --- end-of-function rastcpy() --- */
1414: 
1415: 
1416: /* ==========================================================================
1417:  * Function:    subrastcpy ( sp )
1418:  * Purpose:     makes duplicate copy of sp
1419:  * --------------------------------------------------------------------------
1420:  * Arguments:   sp (I)          ptr to subraster struct to be copied
1421:  * --------------------------------------------------------------------------
1422:  * Returns:     ( subraster * ) ptr to new copy sp,
1423:  *                              or NULL for any error.
1424:  * --------------------------------------------------------------------------
1425:  * Notes:     o
1426:  * ======================================================================= */
1427: /* --- entry point --- */
1428: FUNCSCOPE subraster *subrastcpy ( subraster *sp )
1429: {
1430: /* -------------------------------------------------------------------------
1431: Allocations and Declarations
1432: -------------------------------------------------------------------------- */
1433: subraster /**new_subraster(),*/ *newsp=NULL; /* allocate new subraster */
1434: raster  /**rastcpy(),*/ *newrp=NULL;    /* and new raster image within it */
1435: /*int   delete_subraster();*/           /* dealloc newsp if rastcpy() fails*/
1436: /* -------------------------------------------------------------------------
1437: make copy, and return it to caller
1438: -------------------------------------------------------------------------- */
1439: if ( sp == NULL ) goto end_of_job;      /* nothing to copy */
1440: /* --- allocate new subraster "envelope" for copy --- */
1441: if ( (newsp=new_subraster(0,0,0))       /* allocate subraster "envelope" */
1442: ==   NULL ) goto end_of_job;            /* and quit if we fail to allocate */
1443: /* --- transparently copy original envelope to new one --- */
1444: memcpy((void *)newsp,(void *)sp,sizeof(subraster)); /* copy envelope */
1445: /* --- make a copy of the rasterized image itself, if there is one --- */
1446: if ( sp->image != NULL )                /* there's an image embedded in sp */
1447:   if ( (newrp = rastcpy(sp->image))     /* so copy rasterized image in sp */
1448:   ==   NULL )                           /* failed to copy successfully */
1449:     { delete_subraster(newsp);          /* won't need newsp any more */
1450:       newsp = NULL;                     /* because we're returning error */
1451:       goto end_of_job; }                /* back to caller with error signal*/
1452: /* --- set new params in new envelope --- */
1453: newsp->image = newrp;                   /* new raster image we just copied */
1454: switch ( sp->type )                     /* set new raster image type */
1455:   { case STRINGRASTER: case CHARASTER: newsp->type = STRINGRASTER; break;
1456:     case ASCIISTRING:                  newsp->type = ASCIISTRING;  break;
1457:     case FRACRASTER:                   newsp->type = FRACRASTER;   break;
1458:     case BLANKSIGNAL:                  newsp->type = blanksignal;  break;
1459:     case IMAGERASTER:  default:        newsp->type = IMAGERASTER;  break; }
1460: /* --- return copy of sp to caller --- */
1461: end_of_job:
1462:   return ( newsp );                     /* copy back to caller */
1463: } /* --- end-of-function subrastcpy() --- */
1464: 
1465: 
1466: /* ==========================================================================
1467:  * Function:    rastrot ( rp )
1468:  * Purpose:     rotates rp image 90 degrees right/clockwise
1469:  * --------------------------------------------------------------------------
1470:  * Arguments:   rp (I)          ptr to raster struct to be rotated
1471:  * --------------------------------------------------------------------------
1472:  * Returns:     ( raster * )    ptr to new raster rotated relative to rp,
1473:  *                              or NULL for any error.
1474:  * --------------------------------------------------------------------------
1475:  * Notes:     o An underbrace is } rotated 90 degrees clockwise,
1476:  *              a hat is <, etc.
1477:  * ======================================================================= */
1478: /* --- entry point --- */
1479: FUNCSCOPE raster *rastrot ( raster *rp )
1480: {
1481: /* -------------------------------------------------------------------------
1482: Allocations and Declarations
1483: -------------------------------------------------------------------------- */
1484: raster  /**new_raster(),*/ *rotated=NULL;/*rotated raster returned to caller*/
1485: int     height = rp->height, irow,      /* original height, row index */
1486:         width = rp->width, icol,        /* original width, column index */
1487:         pixsz = rp->pixsz;              /* #bits per pixel */
1488: /* -------------------------------------------------------------------------
1489: allocate rotated raster and fill it
1490: -------------------------------------------------------------------------- */
1491: /* --- allocate rotated raster with flipped width<-->height --- */
1492: if ( (rotated = new_raster(height,width,pixsz)) /* flip width,height */
1493: !=   NULL )                             /* check that allocation succeeded */
1494:   /* --- fill rotated raster --- */
1495:   for ( irow=0; irow<height; irow++ )   /* for each row of rp */
1496:     for ( icol=0; icol<width; icol++ )  /* and each column of rp */
1497:       { int value = getpixel(rp,irow,icol);
1498:         /* setpixel(rotated,icol,irow,value); } */
1499:         setpixel(rotated,icol,(height-1-irow),value); }
1500: return ( rotated );                     /* return rotated raster to caller */
1501: } /* --- end-of-function rastrot() --- */
1502: 
1503: 
1504: /* ==========================================================================
1505:  * Function:    rastrot3d ( rp, axis, theta )
1506:  * Purpose:     rotates rp around 3d-axis by theta, projecting back onto
1507:  *              x,y-plane
1508:  * --------------------------------------------------------------------------
1509:  * Arguments:   rp (I)          ptr to raster struct with image to be rotated
1510:  *              axis (I)        point3d * specifying rotation axis
1511:  *              theta (I)       double specifying rotation in degrees
1512:  * --------------------------------------------------------------------------
1513:  * Returns:     ( raster * )    ptr to new raster struct with rotated image,
1514:  *                              or NULL for any error.
1515:  * --------------------------------------------------------------------------
1516:  * Notes:     o
1517:  * ======================================================================= */
1518: /* --- entry point --- */
1519: FUNCSCOPE raster *rastrot3d ( raster *rp, point3d *axis, double theta )
1520: {
1521: /* -------------------------------------------------------------------------
1522: Allocations and Declarations
1523: -------------------------------------------------------------------------- */
1524: raster  /**new_raster(),*/ *rotp = NULL; /* rotated raster back to caller */
1525: int     width = rp->width,  icol = 0,   /* raster width,  column index */
1526:         height= rp->height, irow = 0,   /* raster height, row index */
1527:         pixsz = rp->pixsz;              /* #bits per pixel */
1528: double  midcol = ((double)(width))/2.0, /* x-origin index coord */
1529:         midrow = ((double)(height))/2.0; /* y-origin index coord */
1530: int     bgpixel = 0;                    /* background pixel value */
1531: matrix3d /**rotmatrix(),*/ *rotmat=NULL; /* rotation matrix */
1532: /*point3d *matmult();*/                 /* matrix multiplication */
1533: /* -------------------------------------------------------------------------
1534: Initialization
1535: -------------------------------------------------------------------------- */
1536: /* --- check input --- */
1537: if ( rp == NULL ) goto end_of_job;      /* error if no input raster supplied */
1538: /* --- get rotation matrix --- */
1539: if ( (rotmat = rotmatrix(axis,theta))   /*rotate around axis by theta degrees*/
1540: ==   NULL ) goto end_of_job;            /*some problem, likely 0-length axis*/
1541: /* --- allocate rotated raster (with same width,height) --- */
1542: if ( (rotp = new_raster(width,height,pixsz)) /* (same width,height) */
1543: ==   NULL ) goto end_of_job;            /* quit if allocation failed */
1544: /* -------------------------------------------------------------------------
1545: rotate pixel-by-pixel
1546: -------------------------------------------------------------------------- */
1547: for ( irow=0; irow<height; irow++ ) {
1548:   double y = ((double)irow) - midrow;   /* y-coord relative to grid center */
1549:   for ( icol=0; icol<width; icol++ ) {
1550:     int pixval = getpixel(rp,irow,icol); /* pixel value */
1551:     double x = ((double)icol) - midcol; /* x-coord relative to grid center */
1552:     point3d u={x,y,0.}, *urot=NULL;     /* original, rotated coords */
1553:     /* ---
1554:      * rotate pixel
1555:      * --------------- */
1556:     if ( pixval != bgpixel )            /* if not a background pixel */
1557:       if ( (urot = matmult(rotmat,&u)) /* apply rotation matrix */
1558:       !=   NULL ) {                     /* succeeded */
1559:         double dy=(+0.00), dx=(-0.25);  /*ad hoc rounding determined by tests*/
1560:         int jrow = (int)(urot->y + midrow + dy); /* rotated row */
1561:         int jcol = (int)(urot->x + midcol + dx); /* rotated col */
1562:         if ( jrow>=0 && jrow<height     /* check rotated pixel coords */
1563:         &&   jcol>=0 && jcol<width ) {  /* rotated pixel in bounds */
1564:           setpixel(rotp,jrow,jcol,pixval); } /* rotated pixel in place */
1565:         } /* --- end-of-if(urot!=NULL) --- */
1566:     } /* --- end-of-for(icol) --- */
1567:   } /* --- end-of-for(irow) --- */
1568: /* ---
1569:  * end-of-job
1570:  * ------------- */
1571: end_of_job:
1572:   return ( rotp );                      /* return rotated raster to caller */
1573: } /* --- end-of-function rastrot3d() --- */
1574: 
1575: 
1576: /* ==========================================================================
1577:  * Function:    rastmag ( rp, magstep )
1578:  * Purpose:     magnifies rp by integer magstep,
1579:  *              e.g., double-height and double-width if magstep=2
1580:  * --------------------------------------------------------------------------
1581:  * Arguments:   rp (I)          ptr to raster struct to be "magnified"
1582:  *              magstep (I)     int containing magnification scale,
1583:  *                              e.g., 2 to double the width and height of rp
1584:  * --------------------------------------------------------------------------
1585:  * Returns:     ( raster * )    ptr to new raster magnified relative to rp,
1586:  *                              or NULL for any error.
1587:  * --------------------------------------------------------------------------
1588:  * Notes:     o
1589:  * ======================================================================= */
1590: /* --- entry point --- */
1591: FUNCSCOPE raster *rastmag ( raster *rp, int magstep )
1592: {
1593: /* -------------------------------------------------------------------------
1594: Allocations and Declarations
1595: -------------------------------------------------------------------------- */
1596: raster  /**new_raster(),*/ *magnified=NULL; /*magnified raster back to caller*/
1597: int     height = rp->height, irow,      /* height, row index */
1598:         width = rp->width, icol,        /* width, column index */
1599:         mrow = 0, mcol = 0,             /* dup pixels magstep*magstep times*/
1600:         pixsz = rp->pixsz;              /* #bits per pixel */
1601: /* -------------------------------------------------------------------------
1602: check args
1603: -------------------------------------------------------------------------- */
1604: if ( rp == NULL ) goto end_of_job;      /* no input raster supplied */
1605: if ( magstep<1 || magstep>10 ) goto end_of_job; /* sanity check */
1606: /* -------------------------------------------------------------------------
1607: allocate magnified raster and fill it
1608: -------------------------------------------------------------------------- */
1609: /* --- allocate magnified raster with magstep*width, magstep*height --- */
1610: if ( (magnified = new_raster(magstep*width,magstep*height,pixsz))/*allocate*/
1611: !=   NULL )                             /* check that allocation succeeded */
1612:   /* --- fill reflected raster --- */
1613:   for ( irow=0; irow<height; irow++ )   /* for each row of rp */
1614:     for ( mrow=0; mrow<magstep; mrow++ ) /* dup row magstep times */
1615:       for ( icol=0; icol<width; icol++ ) /* and for each column of rp */
1616:         for ( mcol=0; mcol<magstep; mcol++ ) { /* dup col magstep times */
1617:          int value = getpixel(rp,irow,icol);
1618:          int row1 = irow*magstep, col1 = icol*magstep;
1619:          setpixel(magnified,(row1+mrow),(col1+mcol),value); }
1620: end_of_job:
1621:   return ( magnified );                 /*return magnified raster to caller*/
1622: } /* --- end-of-function rastmag() --- */
1623: 
1624: 
1625: /* ==========================================================================
1626:  * Function:    bytemapmag ( bytemap, width, height, magstep )
1627:  * Purpose:     magnifies a bytemap by integer magstep,
1628:  *              e.g., double-height and double-width if magstep=2
1629:  * --------------------------------------------------------------------------
1630:  * Arguments:   bytemap (I)     intbyte * ptr to byte map to be "magnified"
1631:  *              width (I)       int containing #cols in original bytemap
1632:  *              height (I)      int containing #rows in original bytemap
1633:  *              magstep (I)     int containing magnification scale,
1634:  *                              e.g., 2 to double the width and height of rp
1635:  * --------------------------------------------------------------------------
1636:  * Returns:     ( intbyte * )   ptr to new bytemap magnified relative to
1637:  *                              original bytemap, or NULL for any error.
1638:  * --------------------------------------------------------------------------
1639:  * Notes:     o Apply EPX/Scale2x/AdvMAME2x  for magstep 2,
1640:  *              and Scale3x/AdvMAME3x  for magstep 3,
1641:  *              as described by http://en.wikipedia.org/wiki/2xSaI
1642:  * ======================================================================= */
1643: /* --- entry point --- */
1644: FUNCSCOPE intbyte *bytemapmag ( intbyte *bytemap,
1645:                                 int width, int height, int magstep )
1646: {
1647: /* -------------------------------------------------------------------------
1648: Allocations and Declarations
1649: -------------------------------------------------------------------------- */
1650: intbyte *magnified=NULL;                /* magnified bytemap back to caller*/
1651: int     irow, icol,                     /* original height, width indexes */
1652:         mrow=0, mcol=0;                 /* dup bytes magstep*magstep times */
1653: int     imap = (-1),                    /* original bytemap[] index */
1654:         byteval = 0;                    /* byteval=bytemap[imap] */
1655: int     isAdvMAME = 1;                  /* true to apply AdvMAME2x and 3x */
1656: int     icell[10],                      /* bytemap[] nearest neighbors */
1657:         bmmdiff = 64;                   /* nearest neighbor diff allowed */
1658: #define bmmeq(i,j) ((absval((icell[i]-icell[j]))<=bmmdiff)) /*approx equal*/
1659: /* -------------------------------------------------------------------------
1660: check args
1661: -------------------------------------------------------------------------- */
1662: if ( bytemap == NULL ) goto end_of_job; /* no input bytemap supplied */
1663: if ( width<1 || height<1 ) goto end_of_job; /* invalid bytemap dimensions */
1664: if ( width*height>100000 ) goto end_of_job; /* sanity check */
1665: if ( magstep<1 || magstep>10 ) goto end_of_job; /* sanity check */
1666: /* -------------------------------------------------------------------------
1667: allocate magnified bytemap and fill it
1668: -------------------------------------------------------------------------- */
1669: /* --- allocate bytemap for magstep*width, magstep*height --- */
1670: if ( (magnified = (intbyte *)(malloc(magstep*width*magstep*height)))/*alloc*/
1671: !=   NULL )                             /* check that allocation succeeded */
1672:   /* --- fill reflected raster --- */
1673:   for ( irow=0; irow<height; irow++ )   /* for each row of bytemap */
1674:    for ( icol=0; icol<width; icol++ ) { /* and for each column of bytemap */
1675:     int imag1 = (icol + irow*(width*magstep))*magstep; /*upper-left corner*/
1676:     imap++;                             /* bump bytemap[] index */
1677:     byteval = (int)(bytemap[imap]);     /* grayscale value at this pixel */
1678:     for ( mrow=0; mrow<magstep; mrow++ ) /* dup row magstep times */
1679:      for ( mcol=0; mcol<magstep; mcol++ ) { /* dup col magstep times */
1680:       int idup = mcol + mrow*(width*magstep); /* offset from imag1 */
1681:       int imag = imag1+idup;            /* adjust magnified[imag] */
1682:       magnified[imag] = (intbyte)(byteval);
1683:       /* --- apply AdvMAME2x and 3x (if desired) --- */
1684:       if ( isAdvMAME ) {                /* AdvMAME2x and 3x wanted */
1685:        int mcell = 1 + mcol + magstep*mrow; /*1,2,3,4 or 1,2,3,4,5,6,7,8,9*/
1686:        icell[5]= byteval,               /* center cell of 3x3 bytemap[] */
1687:        icell[4]= (icol>0?(int)(bytemap[imap-1]):byteval), /*left of center*/
1688:        icell[6]= (icol<width?(int)(bytemap[imap+1]):byteval), /*right*/
1689:        icell[2]= (irow>0?(int)(bytemap[imap-width]):byteval),/*above center*/
1690:        icell[8]= (irow<height?(int)(bytemap[imap+width]):byteval), /*below*/
1691:        icell[1]= (irow>0&&icol>0?(int)(bytemap[imap-width-1]):byteval),
1692:        icell[3]= (irow>0&&icol<width?(int)(bytemap[imap-width+1]):byteval),
1693:        icell[7]= (irow<height&&icol>0?(int)(bytemap[imap+width-1]):byteval),
1694:       icell[9]=(irow<height&&icol<width?(int)(bytemap[imap+width+1]):byteval);
1695:        switch ( magstep ) {             /* 2x magstep=2, 3x magstep=3 */
1696:         default: break;                 /* no AdvMAME at other magsteps */
1697:         case 2:                         /* AdvMAME2x */
1698:          if ( mcell == 1 )
1699:            if ( bmmeq(4,2) && !bmmeq(4,8) && !bmmeq(2,6) )
1700:              magnified[imag] = icell[2];
1701:          if ( mcell == 2 )
1702:            if ( bmmeq(2,6) && !bmmeq(2,4) && !bmmeq(6,8) )
1703:              magnified[imag] = icell[6];
1704:          if ( mcell == 4 )
1705:            if ( bmmeq(6,8) && !bmmeq(6,2) && !bmmeq(8,4) )
1706:              magnified[imag] = icell[8];
1707:          if ( mcell == 3 )
1708:            if ( bmmeq(8,4) && !bmmeq(8,6) && !bmmeq(4,2) )
1709:              magnified[imag] = icell[4];
1710:          break;
1711:         case 3:                         /* AdvMAME3x */
1712:          if ( mcell == 1 )
1713:            if ( bmmeq(4,2) && !bmmeq(4,8) && !bmmeq(2,6) )
1714:              magnified[imag] = icell[4];
1715:          if ( mcell == 2 )
1716:            if ( (bmmeq(4,2) && !bmmeq(4,8) && !bmmeq(2,6) && !bmmeq(5,3))
1717:              || (bmmeq(2,6) && !bmmeq(2,4) && !bmmeq(6,8) && !bmmeq(5,1)) )
1718:              magnified[imag] = icell[2];
1719:          if ( mcell == 3 )
1720:            if ( bmmeq(2,6) && !bmmeq(2,4) && !bmmeq(6,8) )
1721:              magnified[imag] = icell[6];
1722:          if ( mcell == 4 )
1723:            if ( (bmmeq(8,4) && !bmmeq(8,6) && !bmmeq(4,2) && !bmmeq(5,1))
1724:              || (bmmeq(4,2) && !bmmeq(4,8) && !bmmeq(2,6) && !bmmeq(5,7)) )
1725:              magnified[imag] = icell[4];
1726:          if ( mcell == 6 )
1727:            if ( (bmmeq(2,6) && !bmmeq(2,4) && !bmmeq(6,8) && !bmmeq(5,9))
1728:              || (bmmeq(6,8) && !bmmeq(6,2) && !bmmeq(8,4) && !bmmeq(5,3)) )
1729:              magnified[imag] = icell[6];
1730:          if ( mcell == 7 )
1731:            if ( bmmeq(8,4) && !bmmeq(8,6) && !bmmeq(4,2) )
1732:              magnified[imag] = icell[4];
1733:          if ( mcell == 8 )
1734:            if ( (bmmeq(6,8) && !bmmeq(6,2) && !bmmeq(8,4) && !bmmeq(5,7))
1735:              || (bmmeq(8,4) && !bmmeq(8,6) && !bmmeq(4,2) && !bmmeq(5,9)) )
1736:              magnified[imag] = icell[8];
1737:          if ( mcell == 9 )
1738:            if ( bmmeq(6,8) && !bmmeq(6,2) && !bmmeq(8,4) )
1739:              magnified[imag] = icell[6];
1740:          break;
1741:         } } /* --- end-of-switch(magstep) --- */
1742:       } /* --- end-of-for(mrow,mcol) --- */
1743:     } /* --- end-of-for(irow,icol) --- */
1744: end_of_job:
1745:   return ( magnified );                 /*return magnified raster to caller*/
1746: } /* --- end-of-function bytemapmag() --- */
1747: 
1748: 
1749: /* ==========================================================================
1750:  * Function:    rastref ( rp, axis )
1751:  * Purpose:     reflects rp, horizontally about y-axis |_ becomes _| if axis=1
1752:  *              or vertically about x-axis M becomes W if axis=2.
1753:  * --------------------------------------------------------------------------
1754:  * Arguments:   rp (I)          ptr to raster struct to be reflected
1755:  *              axis (I)        int containing 1 for horizontal reflection,
1756:  *                              or 2 for vertical
1757:  * --------------------------------------------------------------------------
1758:  * Returns:     ( raster * )    ptr to new raster reflected relative to rp,
1759:  *                              or NULL for any error.
1760:  * --------------------------------------------------------------------------
1761:  * Notes:     o
1762:  * ======================================================================= */
1763: /* --- entry point --- */
1764: FUNCSCOPE raster *rastref ( raster *rp, int axis )
1765: {
1766: /* -------------------------------------------------------------------------
1767: Allocations and Declarations
1768: -------------------------------------------------------------------------- */
1769: raster  /**new_raster(),*/ *reflected=NULL;/*reflected raster back to caller*/
1770: int     height = rp->height, irow,      /* height, row index */
1771:         width = rp->width, icol,        /* width, column index */
1772:         pixsz = rp->pixsz;              /* #bits per pixel */
1773: /* -------------------------------------------------------------------------
1774: allocate reflected raster and fill it
1775: -------------------------------------------------------------------------- */
1776: /* --- allocate reflected raster with same width, height --- */
1777: if ( axis==1 || axis==2 )               /* first validate axis arg */
1778:  if ( (reflected = new_raster(width,height,pixsz)) /* same width, height */
1779:  !=   NULL )                            /* check that allocation succeeded */
1780:   /* --- fill reflected raster --- */
1781:   for ( irow=0; irow<height; irow++ )   /* for each row of rp */
1782:     for ( icol=0; icol<width; icol++ ) { /* and each column of rp */
1783:       int value = getpixel(rp,irow,icol);
1784:       if ( axis == 1 ) { setpixel(reflected,irow,width-1-icol,value); }
1785:       if ( axis == 2 ) { setpixel(reflected,height-1-irow,icol,value); } }
1786: return ( reflected );                   /*return reflected raster to caller*/
1787: } /* --- end-of-function rastref() --- */
1788: 
1789: 
1790: /* ==========================================================================
1791:  * Function:    rastput ( target, source, top, left, isopaque )
1792:  * Purpose:     Overlays source onto target,
1793:  *              with the 0,0-bit of source onto the top,left-bit of target.
1794:  * --------------------------------------------------------------------------
1795:  * Arguments:   target (I)      ptr to target raster struct
1796:  *              source (I)      ptr to source raster struct
1797:  *              top (I)         int containing 0 ... target->height - 1
1798:  *              left (I)        int containing 0 ... target->width - 1
1799:  *              isopaque (I)    int containing false (zero) to allow
1800:  *                              original 1-bits of target to "show through"
1801:  *                              0-bits of source.
1802:  * --------------------------------------------------------------------------
1803:  * Returns:     ( int )         1 if completed successfully,
1804:  *                              or 0 otherwise (for any error).
1805:  * --------------------------------------------------------------------------
1806:  * Notes:
1807:  * ======================================================================= */
1808: /* --- entry point --- */
1809: FUNCSCOPE int rastput ( raster *target, raster *source,
1810:                         int top, int left, int isopaque )
1811: {
1812: /* -------------------------------------------------------------------------
1813: Allocations and Declarations
1814: -------------------------------------------------------------------------- */
1815: int     irow, icol,             /* indexes over source raster */
1816:         twidth=target->width, theight=target->height, /*target width,height*/
1817:         tpix, ntpix = twidth*theight; /* #pixels in target */
1818: int     isfatal = 0,            /* true to abend on out-of-bounds error */
1819:         isstrict = 0/*1*/,      /* true for strict bounds check - no "wrap"*/
1820:         isokay = 1;             /* true if no pixels out-of-bounds */
1821: /* -------------------------------------------------------------------------
1822: superimpose source onto target, one bit at a time
1823: -------------------------------------------------------------------------- */
1824: if ( isstrict && (top<0 || left<0) )            /* args fail strict test */
1825:  isokay = 0;                                    /* so just return error */
1826: else
1827:  for ( irow=0; irow<source->height; irow++ )    /* for each scan line */
1828:   {
1829:   tpix = (top+irow)*target->width + left - 1;   /*first target pixel (-1)*/
1830:   for ( icol=0; icol<source->width; icol++ )    /* each pixel in scan line */
1831:     {
1832:     int svalue = getpixel(source,irow,icol);    /* source pixel value */
1833:     ++tpix;                                     /* bump target pixel */
1834:     if ( msgfp!=NULL && msglevel>=9999 )        /* debugging output */
1835:       { fprintf(msgfp,"rastput> tpix,ntpix=%d,%d top,irow,theight=%d,%d,%d "
1836:         "left,icol,twidth=%d,%d,%d\n", tpix,ntpix, top,irow,theight,
1837:         left,icol,twidth);  fflush(msgfp); }
1838:     if ( tpix >= ntpix                          /* bounds check failed */
1839:     ||   (isstrict && (irow+top>=theight || icol+left>=twidth)) )
1840:       { isokay = 0;                             /* reset okay flag */
1841:         if ( isfatal ) goto end_of_job;         /* abort if error is fatal */
1842:         else break; }                           /*or just go on to next row*/
1843:     if ( tpix >= 0 )                            /* bounds check okay */
1844:      if ( svalue!=0 || isopaque ) {             /*got dark or opaque source*/
1845:       setpixel(target,irow+top,icol+left,svalue); }/*overlay source on targ*/
1846:     } /* --- end-of-for(icol) --- */
1847:   } /* --- end-of-for(irow) --- */
1848: /* -------------------------------------------------------------------------
1849: Back to caller with 1=okay, 0=failed.
1850: -------------------------------------------------------------------------- */
1851: end_of_job:
1852:   return ( isokay /*isfatal? (tpix<ntpix? 1:0) : 1*/ );
1853: } /* --- end-of-function rastput() --- */
1854: 
1855: 
1856: /* ==========================================================================
1857:  * Function:    rastcompose ( sp1, sp2, offset2, isalign, isfree )
1858:  * Purpose:     Overlays sp2 on top of sp1, leaving both unchanged
1859:  *              and returning a newly-allocated composite subraster.
1860:  *              Frees/deletes input sp1 and/or sp2 depending on value
1861:  *              of isfree (0=none, 1=sp1, 2=sp2, 3=both).
1862:  * --------------------------------------------------------------------------
1863:  * Arguments:   sp1 (I)         subraster *  to "underneath" subraster,
1864:  *                              whose baseline is preserved
1865:  *              sp2 (I)         subraster *  to "overlaid" subraster
1866:  *              offset2 (I)     int containing 0 or number of pixels
1867:  *                              to horizontally shift sp2 relative to sp1,
1868:  *                              either positive (right) or negative
1869:  *              isalign (I)     int containing 1 to align baselines,
1870:  *                              or 0 to vertically center sp2 over sp1.
1871:  *                              For isalign=2, images are vertically
1872:  *                              centered, but then adjusted by \raisebox
1873:  *                              lifts, using global variables rastlift1
1874:  *                              for sp1 and rastlift for sp2.
1875:  *              isfree (I)      int containing 1=free sp1 before return,
1876:  *                              2=free sp2, 3=free both, 0=free none.
1877:  * --------------------------------------------------------------------------
1878:  * Returns:     ( subraster * ) pointer to constructed subraster
1879:  *                              or  NULL for any error
1880:  * --------------------------------------------------------------------------
1881:  * Notes:     o The top-left corner of each raster box has coords (0,0),
1882:  *              down to (h-1,w-1) for a box of height h and width w.
1883:  *            o A raster's baseline, b, is typically 0 <= b < h.
1884:  *              But b can actually go out-of-bounds, b>=h or b<0, for
1885:  *              an image additionally lifted (b>=h) or lowered (b<0)
1886:  *              with respect to the surrounding expression.
1887:  *            o Note that b=h-1 means no descenders and the bottom
1888:  *              of the symbol rests exactly on the baseline,
1889:  *              whereas b=0 means the top pixel of the symbol rests
1890:  *              on the baseline, and all other pixels are descenders.
1891:  *            o The composite raster is constructed as follows...
1892:  *              The base image is labelled height h1 and baseline b1,
1893:  *              the overlay h2 and b2, and the composite H and B.
1894:  *                   base       overlay
1895:  *          --- +------------------------+ ---   For the overlay to be
1896:  *           ^  |   ^        +----------+|  ^    vertically centered with
1897:  *           |  |   |        |          ||  |    respect to the base,
1898:  *           |  |   |B-b1    |          ||  |      B - b1 = H-B -(h1-b1), so
1899:  *           |  |   v        |          ||  |      2*B = H-h1 + 2*b1
1900:  *           |  |+----------+|          ||  |      B = b1 + (H-h1)/2
1901:  *           B  ||  ^    ^  ||          ||  |    And when the base image is
1902:  *           |  ||  |    |  ||          ||  |    bigger, H=h1 and B=b1 is
1903:  *           |  ||  b1   |  ||          ||  |    the obvious correct answer.
1904:  *           |  ||  |    h1 ||          || H=h2
1905:  *           v  ||  v    |  ||          ||  |
1906:  *    ----------||-------|--||          ||--|--------
1907:  *    baseline  || h1-b1 v  || overlay  ||  |
1908:  *    for base  |+----------+| baseline ||  |
1909:  *    and com-  |   ^        | ignored  ||  |
1910:  *    posite    |   |H-B-    |----------||  |
1911:  *              |   | (h1-b1)|          ||  |
1912:  *              |   v        +----------+|  v
1913:  *              +------------------------+ ---
1914:  * ======================================================================= */
1915: /* --- entry point --- */
1916: FUNCSCOPE subraster *rastcompose ( subraster *sp1, subraster *sp2,
1917:                                    int offset2, int isalign, int isfree )
1918: {
1919: /* -------------------------------------------------------------------------
1920: Allocations and Declarations
1921: -------------------------------------------------------------------------- */
1922: subraster /**new_subraster(),*/ *sp=(subraster *)NULL; /*returned subraster*/
1923: raster  *rp=(raster *)NULL;             /* new composite raster in sp */
1924: /*int   delete_subraster();*/           /* in case isfree non-zero */
1925: /*int   rastput();*/                    /*place sp1,sp2 in composite raster*/
1926: int     base1   = sp1->baseline,        /*baseline for underlying subraster*/
1927:         height1 = (sp1->image)->height, /* height for underlying subraster */
1928:         width1  = (sp1->image)->width,  /* width for underlying subraster */
1929:         pixsz1  = (sp1->image)->pixsz,  /* pixsz for underlying subraster */
1930:         base2   = sp2->baseline,        /*baseline for overlaid subraster */
1931:         height2 = (sp2->image)->height, /* height for overlaid subraster */
1932:         width2  = (sp2->image)->width,  /* width for overlaid subraster */
1933:         pixsz2  = (sp2->image)->pixsz;  /* pixsz for overlaid subraster */
1934: int     height  = max2(height1,height2), /*composite height if sp2 centered*/
1935:         base    = base1 + (height-height1)/2, /* and composite baseline */
1936:         tlc2    = (height-height2)/2,   /* top-left corner for overlay */
1937:         width=0, pixsz=0;               /* other params for composite */
1938: int     lift1   = rastlift1,            /* vertical \raisebox lift for sp1 */
1939:         lift2   = rastlift;             /* vertical \raisebox lift for sp2 */
1940: /* -------------------------------------------------------------------------
1941: Initialization
1942: -------------------------------------------------------------------------- */
1943: /* --- determine height, width and baseline of composite raster --- */
1944: switch ( isalign ) {
1945:   default:
1946:   case 0:                               /* centered, baselines not aligned */
1947:     height = max2(height1,height2);     /* max height */
1948:     base   = base1 + (height-height1)/2; /* baseline for sp1 */
1949:     break;
1950:   case 1:                               /* baselines of sp1,sp2 aligned */
1951:     height = max2(base1+1,base2+1)      /* max height above baseline */
1952:            + max2(height1-base1-1,height2-base2-1); /*+max descending below*/
1953:     base   = max2(base1,base2);         /* max space above baseline */
1954:     break;
1955:   case 2:                               /* centered +/- \raisebox lifts */
1956:     base1 -= lift1;  base2 -= lift2;    /* reset to unlifted images */
1957:     /* --- start with default for centered, unlifted images --- */
1958:     height2 += 2*absval(lift2);         /* "virtual" height of overlay */
1959:     height = max2(height1,height2);     /* max height */
1960:     base   = base1 + (height-height1)/2; /* baseline for sp1 */
1961:     tlc2   = (height-height2)/2         /* top-left corner for overlay */
1962:            + (lift2>=0?0:2*absval(lift2)); /* "reflect" overlay below base */
1963:     break;
1964:   } /* --- end-of-switch(isalign) --- */
1965: width = max2(width1,width2+abs(offset2)); /* max width */
1966: pixsz = max2(pixsz1,pixsz2);            /* bitmap,bytemap becomes bytemap */
1967: /* -------------------------------------------------------------------------
1968: allocate concatted composite subraster
1969: -------------------------------------------------------------------------- */
1970: /* --- allocate returned subraster (and then initialize it) --- */
1971: if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
1972: ==   (subraster *)NULL ) goto end_of_job; /* failed, so quit */
1973: /* --- initialize subraster parameters --- */
1974: sp->type = IMAGERASTER;                 /* image */
1975: sp->baseline = base;                    /* composite baseline */
1976: sp->size = sp1->size;                   /* underlying char is sp1 */
1977: if ( isalign == 2 ) sp->baseline += lift1; /* adjust baseline */
1978: /* --- extract raster from subraster --- */
1979: rp = sp->image;                         /* raster allocated in subraster */
1980: /* -------------------------------------------------------------------------
1981: overlay sp1 and sp2 in new composite raster
1982: -------------------------------------------------------------------------- */
1983: switch ( isalign ) {
1984:   default:
1985:   case 0:                               /* centered, baselines not aligned */
1986:     rastput (rp, sp1->image, base-base1, (width-width1)/2, 1);  /*underlying*/
1987:     rastput (rp, sp2->image, (height-height2)/2,                /*overlaid*/
1988:                 (width-width2)/2+offset2, 0);
1989:     break;
1990:   case 1:                               /* baselines of sp1,sp2 aligned */
1991:     rastput (rp, sp1->image, base-base1, (width-width1)/2, 1);  /*underlying*/
1992:     rastput (rp, sp2->image, base-base2,                        /*overlaid*/
1993:                 (width-width2)/2+offset2, 0);
1994:     break;
1995:   case 2: if(1){                        /* centered +/- \raisebox lifts */
1996:     rastput (rp, sp1->image, base-base1, (width-width1)/2, 1);
1997:     rastput (rp, sp2->image, tlc2, (width-width2)/2+offset2, 0); }
1998:     break;
1999:   } /* --- end-of-switch(isalign) --- */
2000: /* -------------------------------------------------------------------------
2001: free input if requested
2002: -------------------------------------------------------------------------- */
2003: if ( isfree > 0 )                       /* caller wants input freed */
2004:   { if ( isfree==1 || isfree>2 ) delete_subraster(sp1); /* free sp1 */
2005:     if ( isfree >= 2 ) delete_subraster(sp2); }         /* and/or sp2 */
2006: /* -------------------------------------------------------------------------
2007: Back to caller with pointer to concatted subraster or with null for error
2008: -------------------------------------------------------------------------- */
2009: end_of_job:
2010:   return ( sp );                        /* back with subraster or null ptr */
2011: } /* --- end-of-function rastcompose() --- */
2012: 
2013: 
2014: /* ==========================================================================
2015:  * Function:    rastcat ( sp1, sp2, isfree )
2016:  * Purpose:     "Concatanates" subrasters sp1||sp2, leaving both unchanged
2017:  *              and returning a newly-allocated subraster.
2018:  *              Frees/deletes input sp1 and/or sp2 depending on value
2019:  *              of isfree (0=none, 1=sp1, 2=sp2, 3=both).
2020:  * --------------------------------------------------------------------------
2021:  * Arguments:   sp1 (I)         subraster *  to left-hand subraster
2022:  *              sp2 (I)         subraster *  to right-hand subraster
2023:  *              isfree (I)      int containing 1=free sp1 before return,
2024:  *                              2=free sp2, 3=free both, 0=free none.
2025:  * --------------------------------------------------------------------------
2026:  * Returns:     ( subraster * ) pointer to constructed subraster sp1||sp2
2027:  *                              or  NULL for any error
2028:  * --------------------------------------------------------------------------
2029:  * Notes:
2030:  * ======================================================================= */
2031: /* --- entry point --- */
2032: FUNCSCOPE subraster *rastcat ( subraster *sp1, subraster *sp2, int isfree )
2033: {
2034: /* -------------------------------------------------------------------------
2035: Allocations and Declarations
2036: -------------------------------------------------------------------------- */
2037: subraster /**new_subraster(),*/ *sp=(subraster *)NULL; /*returned subraster*/
2038: raster  *rp=(raster *)NULL;             /* new concatted raster */
2039: /*int   delete_subraster();*/           /* in case isfree non-zero */
2040: /*int   rastput();*/                    /*place sp1,sp2 in concatted raster*/
2041: /*int   type_raster();*/                /* debugging display */
2042: int     base1   = sp1->baseline,        /*baseline for left-hand subraster*/
2043:         height1 = (sp1->image)->height, /* height for left-hand subraster */
2044:         width1  = (sp1->image)->width,  /* width for left-hand subraster */
2045:         pixsz1  = (sp1->image)->pixsz,  /* pixsz for left-hand subraster */
2046:         type1   = sp1->type,            /* image type for left-hand */
2047:         base2   = sp2->baseline,        /*baseline for right-hand subraster*/
2048:         height2 = (sp2->image)->height, /* height for right-hand subraster */
2049:         width2  = (sp2->image)->width,  /* width for right-hand subraster */
2050:         pixsz2  = (sp2->image)->pixsz,  /* pixsz for right-hand subraster */
2051:         type2   = sp2->type;            /* image type for right-hand */
2052: int     height=0, width=0, pixsz=0, base=0; /*concatted sp1||sp2 composite*/
2053: int     issmash = (smashmargin!=0?1:0), /* true to "squash" sp1||sp2 */
2054:         isopaque = (issmash?0:1),       /* not oppaque if smashing */
2055:         rastsmash(), isblank=0, nsmash=0, /* #cols to smash */
2056:         oldsmashmargin = smashmargin,   /* save original smashmargin */
2057:         oldblanksymspace = blanksymspace, /* save original blanksymspace */
2058:         oldnocatspace = isnocatspace;   /* save original isnocatspace */
2059: mathchardef *symdef1 = sp1->symdef,     /*mathchardef of last left-hand char*/
2060:         *symdef2 = sp2->symdef;         /* mathchardef of right-hand char */
2061: int     class1 = (symdef1==NULL?ORDINARY:symdef1->class), /* symdef->class */
2062:         class2 = (symdef2==NULL?ORDINARY:symdef2->class), /* or default */
2063:         smash1 = (symdef1!=NULL)&&(class1==ORDINARY||class1==VARIABLE||
2064:                   class1==OPENING||class1==CLOSING||class1==PUNCTION),
2065:         smash2 = (symdef2!=NULL)&&(class2==ORDINARY||class2==VARIABLE||
2066:                   class2==OPENING||class2==CLOSING||class2==PUNCTION),
2067:         space = fontsize/2+1;           /* #cols between sp1 and sp2 */
2068: int     isfrac = (type1 == FRACRASTER   /* sp1 is a \frac */
2069:                   && class2 == PUNCTION); /* and sp2 is punctuation */
2070: /* -------------------------------------------------------------------------
2071: Initialization
2072: -------------------------------------------------------------------------- */
2073: /* --- determine inter-character space from character class --- */
2074: if ( !isstring )
2075:   space = max2(2,(symspace[class1][class2] + fontsize-3)); /* space */
2076: else space = 1;                         /* space for ascii string */
2077: if ( isnocatspace > 0 ) {               /* spacing explicitly turned off */
2078:   space = 0;                            /* reset space */
2079:   isnocatspace--; }                     /* and decrement isnocatspace flag */
2080: if ( 0 && sp1->type == BLANKSIGNAL ) space=0; /*implicitly turn off spacing*/
2081: if ( sp1->type==BLANKSIGNAL && sp2->type==BLANKSIGNAL ) /* both blank */
2082:   space = 0;                            /* no extra space between spaces */
2083: if ( sp2->type != BLANKSIGNAL )         /* not a blank space signal */
2084:   if ( blanksymspace != 0 ) {           /* and we have a space adjustment */
2085:     space = max2(0,space+blanksymspace); /* adjust as much as possible */
2086:     blanksymspace = 0; }                /* and reset adjustment */
2087: if ( msgfp!=NULL && msglevel>=999 )     /* display space results */
2088:   { fprintf(msgfp,"rastcat> space=%d, blanksymspace=%d, isnocatspace=%d\n",
2089:     space,oldblanksymspace,oldnocatspace);  fflush(msgfp); }
2090: /* --- determine smash --- */
2091: if ( !isstring && !isfrac )             /* don't smash strings or \frac's */
2092:  if ( issmash ) {                       /* raster smash wanted */
2093:    int  maxsmash = rastsmash(sp1,sp2),  /* calculate max smash space */
2094:         margin = smashmargin;           /* init margin without delta */
2095:    if ( (1 && smash1 && smash2)         /* concatanating two chars */
2096:    ||   (1 && type1!=IMAGERASTER && type2!=IMAGERASTER
2097:            && type1!=FRACRASTER  && type2!=FRACRASTER ) )
2098:      /*maxsmash = 0;*/                  /* turn off smash */
2099:      margin = max2(space-1,0);          /* force small smashmargin */
2100:    else                                 /* adjust for delta if images */
2101:      if ( issmashdelta )                /* smashmargin is a delta value */
2102:        margin += fontsize;              /* add displaystyle base to margin */
2103:    if ( maxsmash == blanksignal )       /* sp2 is intentional blank */
2104:      isblank = 1;                       /* set blank flag signal */
2105:    else                                 /* see how much extra space we have*/
2106:      if ( maxsmash > margin )           /* enough space for adjustment */
2107:        nsmash = maxsmash-margin;        /* make adjustment */
2108:    if ( msgfp!=NULL && msglevel>=99 )   /* display smash results */
2109:      { fprintf(msgfp,"rastcat> maxsmash=%d, margin=%d, nsmash=%d\n",
2110:        maxsmash,margin,nsmash);
2111:        fprintf(msgfp,"rastcat> type1=%d,2=%d, class1=%d,2=%d\n", type1,type2,
2112:        (symdef1==NULL?-999:class1),(symdef2==NULL?-999:class2));
2113:        fflush(msgfp); }
2114:    } /* --- end-of-if(issmash) --- */
2115: /* --- determine height, width and baseline of composite raster --- */
2116: if ( !isstring )
2117:  { height = max2(base1+1,base2+1)       /* max height above baseline */
2118:           + max2(height1-base1-1,height2-base2-1); /*+ max descending below*/
2119:    width  = width1+width2 + space-nsmash; /*add widths and space-smash*/
2120:    width  = max3(width,width1,width2); } /* don't "over-smash" composite */
2121: else                                    /* ascii string */
2122:  { height = 1;                          /* default */
2123:    width  = width1 + width2 + space - 1; } /* no need for two nulls */
2124: pixsz  = max2(pixsz1,pixsz2);           /* bitmap||bytemap becomes bytemap */
2125: base   = max2(base1,base2);             /* max space above baseline */
2126: if ( msgfp!=NULL && msglevel>=9999 )    /* display components */
2127:   { fprintf(msgfp,"rastcat> Left-hand ht,width,pixsz,base = %d,%d,%d,%d\n",
2128:     height1,width1,pixsz1,base1);
2129:     type_raster(sp1->image,msgfp);      /* display left-hand raster */
2130:     fprintf(msgfp,"rastcat> Right-hand ht,width,pixsz,base = %d,%d,%d,%d\n",
2131:     height2,width2,pixsz2,base2);
2132:     type_raster(sp2->image,msgfp);      /* display right-hand raster */
2133:     fprintf(msgfp,
2134:     "rastcat> Composite ht,width,smash,pixsz,base = %d,%d,%d,%d,%d\n",
2135:     height,width,nsmash,pixsz,base);
2136:     fflush(msgfp); }                    /* flush msgfp buffer */
2137: /* -------------------------------------------------------------------------
2138: allocate concatted composite subraster
2139: -------------------------------------------------------------------------- */
2140: /* --- allocate returned subraster (and then initialize it) --- */
2141: if ( msgfp!=NULL && msglevel>=9999 )
2142:   { fprintf(msgfp,"rastcat> calling new_subraster(%d,%d,%d)\n",
2143:     width,height,pixsz); fflush(msgfp); }
2144: if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
2145: ==   (subraster *)NULL )                /* failed */
2146:   { if ( msgfp!=NULL && msglevel>=1 )   /* report failure */
2147:       { fprintf(msgfp,"rastcat> new_subraster(%d,%d,%d) failed\n",
2148:         width,height,pixsz); fflush(msgfp); }
2149:     goto end_of_job; }                  /* failed, so quit */
2150: /* --- initialize subraster parameters --- */
2151: /* sp->type = (!isstring?STRINGRASTER:ASCIISTRING); */  /*concatted string*/
2152: if ( !isstring )
2153:   sp->type = /*type2;*//*(type1==type2?type2:IMAGERASTER);*/
2154:         (type2!=CHARASTER? type2 :
2155:         (type1!=CHARASTER&&type1!=BLANKSIGNAL
2156:          &&type1!=FRACRASTER?type1:IMAGERASTER));
2157: else
2158:   sp->type = ASCIISTRING;               /* concatted ascii string */
2159: sp->symdef = symdef2;                   /* rightmost char is sp2 */
2160: sp->baseline = base;                    /* composite baseline */
2161: sp->size = sp2->size;                   /* rightmost char is sp2 */
2162: if ( isblank )                          /* need to propagate blanksignal */
2163:   sp->type = blanksignal;               /* may not be completely safe??? */
2164: /* --- extract raster from subraster --- */
2165: rp = sp->image;                         /* raster allocated in subraster */
2166: /* -------------------------------------------------------------------------
2167: overlay sp1 and sp2 in new composite raster
2168: -------------------------------------------------------------------------- */
2169: if ( msgfp!=NULL && msglevel>=9999 )
2170:   { fprintf(msgfp,"rastcat> calling rastput() to concatanate left||right\n");
2171:     fflush(msgfp); }                    /* flush msgfp buffer */
2172: if ( !isstring )
2173:  rastput (rp, sp1->image, base-base1,   /* overlay left-hand */
2174:  max2(0,nsmash-width1), 1);             /* plus any residual smash space */
2175: else
2176:  memcpy(rp->pixmap,(sp1->image)->pixmap,width1-1);  /*init left string*/
2177: if ( msgfp!=NULL && msglevel>=9999 )
2178:   { type_raster(sp->image,msgfp);       /* display composite raster */
2179:     fflush(msgfp); }                    /* flush msgfp buffer */
2180: if ( !isstring )
2181:  { int  fracbase = ( isfrac?            /* baseline for punc after \frac */
2182:         max2(fraccenterline,base2):base ); /*adjust baseline or use original*/
2183:    rastput (rp, sp2->image, fracbase-base2, /* overlay right-hand */
2184:    max2(0,width1+space-nsmash), isopaque); /* minus any smashed space */
2185:    if ( 1 && type1 == FRACRASTER        /* we're done with \frac image */
2186:    &&   type2 != FRACRASTER )           /* unless we have \frac\frac */
2187:      fraccenterline = NOVALUE;          /* so reset centerline signal */
2188:    if ( fraccenterline != NOVALUE )     /* sp2 is a fraction */
2189:      fraccenterline += (base-base2); }  /* so adjust its centerline */
2190: else
2191:  { strcpy((char *)(rp->pixmap)+width1-1+space,(char *)((sp2->image)->pixmap));
2192:    ((char *)(rp->pixmap))[width1+width2+space-2] = '\000'; } /*null-term*/
2193: if ( msgfp!=NULL && msglevel>=9999 )
2194:   { type_raster(sp->image,msgfp);       /* display composite raster */
2195:     fflush(msgfp); }                    /* flush msgfp buffer */
2196: /* -------------------------------------------------------------------------
2197: free input if requested
2198: -------------------------------------------------------------------------- */
2199: if ( isfree > 0 )                       /* caller wants input freed */
2200:   { if ( isfree==1 || isfree>2 ) delete_subraster(sp1); /* free sp1 */
2201:     if ( isfree >= 2 ) delete_subraster(sp2); }         /* and/or sp2 */
2202: /* -------------------------------------------------------------------------
2203: Back to caller with pointer to concatted subraster or with null for error
2204: -------------------------------------------------------------------------- */
2205: end_of_job:
2206:   smashmargin = oldsmashmargin;         /* reset original smashmargin */
2207:   return ( sp );                        /* back with subraster or null ptr */
2208: } /* --- end-of-function rastcat() --- */
2209: 
2210: 
2211: /* ==========================================================================
2212:  * Function:    rastack ( sp1, sp2, base, space, iscenter, isfree )
2213:  * Purpose:     Stack subrasters sp2 atop sp1, leaving both unchanged
2214:  *              and returning a newly-allocated subraster,
2215:  *              whose baseline is sp1's if base=1, or sp2's if base=2.
2216:  *              Frees/deletes input sp1 and/or sp2 depending on value
2217:  *              of isfree (0=none, 1=sp1, 2=sp2, 3=both).
2218:  * --------------------------------------------------------------------------
2219:  * Arguments:   sp1 (I)         subraster *  to lower subraster
2220:  *              sp2 (I)         subraster *  to upper subraster
2221:  *              base (I)        int containing 1 if sp1 is baseline,
2222:  *                              or 2 if sp2 is baseline.
2223:  *              space (I)       int containing #rows blank space inserted
2224:  *                              between sp1's image and sp2's image.
2225:  *              iscenter (I)    int containing 1 to center both sp1 and sp2
2226:  *                              in stacked array, 0 to left-justify both
2227:  *              isfree (I)      int containing 1=free sp1 before return,
2228:  *                              2=free sp2, 3=free both, 0=free none.
2229:  * --------------------------------------------------------------------------
2230:  * Returns:     ( subraster * ) pointer to constructed subraster sp2 atop sp1
2231:  *                              or  NULL for any error
2232:  * --------------------------------------------------------------------------
2233:  * Notes:
2234:  * ======================================================================= */
2235: /* --- entry point --- */
2236: FUNCSCOPE subraster *rastack ( subraster *sp1, subraster *sp2,
2237:                                int base, int space, int iscenter, int isfree )
2238: {
2239: /* -------------------------------------------------------------------------
2240: Allocations and Declarations
2241: -------------------------------------------------------------------------- */
2242: subraster /**new_subraster(),*/ *sp=(subraster *)NULL; /*returned subraster*/
2243: raster  *rp=(raster *)NULL;             /* new stacked raster in sp */
2244: /*int   delete_subraster();*/           /* in case isfree non-zero */
2245: /*int   rastput();*/                    /* place sp1,sp2 in stacked raster */
2246: int     base1   = sp1->baseline,        /* baseline for lower subraster */
2247:         height1 = (sp1->image)->height, /* height for lower subraster */
2248:         width1  = (sp1->image)->width,  /* width for lower subraster */
2249:         pixsz1  = (sp1->image)->pixsz,  /* pixsz for lower subraster */
2250:         base2   = sp2->baseline,        /* baseline for upper subraster */
2251:         height2 = (sp2->image)->height, /* height for upper subraster */
2252:         width2  = (sp2->image)->width,  /* width for upper subraster */
2253:         pixsz2  = (sp2->image)->pixsz;  /* pixsz for upper subraster */
2254: int     height=0, width=0, pixsz=0, baseline=0; /*for stacked sp2 atop sp1*/
2255: mathchardef *symdef1 = sp1->symdef,     /* mathchardef of right lower char */
2256:         *symdef2 = sp2->symdef;         /* mathchardef of right upper char */
2257: /* -------------------------------------------------------------------------
2258: Initialization
2259: -------------------------------------------------------------------------- */
2260: /* --- determine height, width and baseline of composite raster --- */
2261: height   = height1 + space + height2;   /* sum of heights plus space */
2262: width    = max2(width1,width2);         /* max width is overall width */
2263: pixsz    = max2(pixsz1,pixsz2);         /* bitmap||bytemap becomes bytemap */
2264: baseline = (base==1? height2+space+base1 : (base==2? base2 : 0));
2265: /* -------------------------------------------------------------------------
2266: allocate stacked composite subraster (with embedded raster)
2267: -------------------------------------------------------------------------- */
2268: /* --- allocate returned subraster (and then initialize it) --- */
2269: if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
2270: ==   (subraster *)NULL ) goto end_of_job; /* failed, so quit */
2271: /* --- initialize subraster parameters --- */
2272: sp->type = IMAGERASTER;                 /* stacked rasters */
2273: sp->symdef = (base==1? symdef1 : (base==2? symdef2 : NULL)); /* symdef */
2274: sp->baseline = baseline;                /* composite baseline */
2275: sp->size = (base==1? sp1->size : (base==2? sp2->size : NORMALSIZE)); /*size*/
2276: /* --- extract raster from subraster --- */
2277: rp = sp->image;                         /* raster embedded in subraster */
2278: /* -------------------------------------------------------------------------
2279: overlay sp1 and sp2 in new composite raster
2280: -------------------------------------------------------------------------- */
2281: if ( iscenter == 1 )                    /* center both sp1 and sp2 */
2282:   { rastput (rp, sp2->image, 0, (width-width2)/2, 1);  /* overlay upper */
2283:     rastput (rp, sp1->image, height2+space, (width-width1)/2, 1); } /*lower*/
2284: else                                    /* left-justify both sp1 and sp2 */
2285:   { rastput (rp, sp2->image, 0, 0, 1);  /* overlay upper */
2286:     rastput (rp, sp1->image, height2+space, 0, 1); } /*lower*/
2287: /* -------------------------------------------------------------------------
2288: free input if requested
2289: -------------------------------------------------------------------------- */
2290: if ( isfree > 0 )                       /* caller wants input freed */
2291:   { if ( isfree==1 || isfree>2 ) delete_subraster(sp1); /* free sp1 */
2292:     if ( isfree>=2 ) delete_subraster(sp2); } /* and/or sp2 */
2293: /* -------------------------------------------------------------------------
2294: Back to caller with pointer to stacked subraster or with null for error
2295: -------------------------------------------------------------------------- */
2296: end_of_job:
2297:   return ( sp );                        /* back with subraster or null ptr */
2298: } /* --- end-of-function rastack() --- */
2299: 
2300: 
2301: /* ==========================================================================
2302:  * Function:    rastile ( tiles, ntiles )
2303:  * Purpose:     Allocate and build up a composite raster
2304:  *              from the ntiles components/characters supplied in tiles.
2305:  * --------------------------------------------------------------------------
2306:  * Arguments:   tiles (I)       subraster *  to array of subraster structs
2307:  *                              describing the components and their locations
2308:  *              ntiles (I)      int containing number of subrasters in tiles[]
2309:  * --------------------------------------------------------------------------
2310:  * Returns:     ( raster * )    ptr to composite raster,
2311:  *                              or NULL for any error.
2312:  * --------------------------------------------------------------------------
2313:  * Notes:     o The top,left corner of a raster is row=0,col=0
2314:  *              with row# increasing as you move down,
2315:  *              and col# increasing as you move right.
2316:  *              Metafont numbers rows with the baseline=0,
2317:  *              so the top row is a positive number that
2318:  *              decreases as you move down.
2319:  *            o rastile() is no longer used.
2320:  *              It was used by an earlier rasterize() algorithm,
2321:  *              and I've left it in place should it be needed again.
2322:  *              But recent changes haven't been tested/exercised.
2323:  * ======================================================================= */
2324: /* --- entry point --- */
2325: FUNCSCOPE raster *rastile ( subraster *tiles, int ntiles )
2326: {
2327: /* -------------------------------------------------------------------------
2328: Allocations and Declarations
2329: -------------------------------------------------------------------------- */
2330: raster  /**new_raster(),*/ *composite=(raster *)NULL;/*raster back to caller*/
2331: int     width=0, height=0, pixsz=0, /*width,height,pixsz of composite raster*/
2332:         toprow=9999, rightcol=-999, /* extreme upper-right corner of tiles */
2333:         botrow=-999, leftcol=9999;  /* extreme lower-left corner of tiles */
2334: int     itile;                  /* tiles[] index */
2335: /*int   rastput();*/            /* overlay each tile in composite raster */
2336: /* -------------------------------------------------------------------------
2337: run through tiles[] to determine dimensions for composite raster
2338: -------------------------------------------------------------------------- */
2339: /* --- determine row and column bounds of composite raster --- */
2340: for ( itile=0; itile<ntiles; itile++ ) {
2341:   subraster *tile = &(tiles[itile]);            /* ptr to current tile */
2342:   /* --- upper-left corner of composite --- */
2343:   toprow = min2(toprow, tile->toprow);
2344:   leftcol = min2(leftcol, tile->leftcol);
2345:   /* --- lower-right corner of composite --- */
2346:   botrow = max2(botrow, tile->toprow + (tile->image)->height - 1);
2347:   rightcol = max2(rightcol, tile->leftcol + (tile->image)->width  - 1);
2348:   /* --- pixsz of composite --- */
2349:   pixsz = max2(pixsz,(tile->image)->pixsz);
2350:   } /* --- end-of-for(itile) --- */
2351: /* --- calculate width and height from bounds --- */
2352: width  = rightcol - leftcol + 1;
2353: height = botrow - toprow + 1;
2354: /* --- sanity check (quit if bad dimensions) --- */
2355: if ( width<1 || height<1 ) goto end_of_job;
2356: /* -------------------------------------------------------------------------
2357: allocate composite raster, and embed tiles[] within it
2358: -------------------------------------------------------------------------- */
2359: /* --- allocate composite raster --- */
2360: if ( (composite=new_raster(width,height,pixsz)) /*allocate composite raster*/
2361: ==   (raster *)NULL ) goto end_of_job;          /* and quit if failed */
2362: /* --- embed tiles[] in composite --- */
2363: for ( itile=0; itile<ntiles; itile++ )
2364:   { subraster *tile = &(tiles[itile]);          /* ptr to current tile */
2365:     rastput (composite, tile->image,            /* overlay tile image at...*/
2366:       tile->toprow-toprow, tile->leftcol-leftcol, 1); } /*upper-left corner*/
2367: /* -------------------------------------------------------------------------
2368: Back to caller with composite raster (or null for any error)
2369: -------------------------------------------------------------------------- */
2370: end_of_job:
2371:   return ( composite );                 /* back with composite or null ptr */
2372: } /* --- end-of-function rastile() --- */
2373: 
2374: 
2375: /* ==========================================================================
2376:  * Function:    rastsmash ( sp1, sp2 )
2377:  * Purpose:     When concatanating sp1||sp2, calculate #pixels
2378:  *              we can "smash sp2 left"
2379:  * --------------------------------------------------------------------------
2380:  * Arguments:   sp1 (I)         subraster *  to left-hand raster
2381:  *              sp2 (I)         subraster *  to right-hand raster
2382:  * --------------------------------------------------------------------------
2383:  * Returns:     ( int )         max #pixels we can smash sp1||sp2,
2384:  *                              or "blanksignal" if sp2 intentionally blank,
2385:  *                              or 0 for any error.
2386:  * --------------------------------------------------------------------------
2387:  * Notes:     o
2388:  * ======================================================================= */
2389: /* --- entry point --- */
2390: FUNCSCOPE int rastsmash ( subraster *sp1, subraster *sp2 )
2391: {
2392: /* -------------------------------------------------------------------------
2393: Allocations and Declarations
2394: -------------------------------------------------------------------------- */
2395: int     nsmash = 0;                     /* #pixels to smash sp1||sp2 */
2396: int     base1   = sp1->baseline,        /*baseline for left-hand subraster*/
2397:         height1 = (sp1->image)->height, /* height for left-hand subraster */
2398:         width1  = (sp1->image)->width,  /* width for left-hand subraster */
2399:         base2   = sp2->baseline,        /*baseline for right-hand subraster*/
2400:         height2 = (sp2->image)->height, /* height for right-hand subraster */
2401:         width2  = (sp2->image)->width;  /* width for right-hand subraster */
2402: int     base = max2(base1,base2),       /* max ascenders - 1 above baseline*/
2403:         top1=base-base1, top2=base-base2, /* top irow indexes for sp1, sp2 */
2404:         bot1=top1+height1-1, bot2=top2+height2-1, /* bot irow indexes */
2405:         height = max2(bot1,bot2)+1;     /* total height */
2406: int     irow1=0,irow2=0, icol=0;        /* row,col indexes */
2407: int     firstcol1[1025], nfirst1=0,     /* 1st sp1 col containing set pixel*/
2408:         firstcol2[1025], nfirst2=0;     /* 1st sp2 col containing set pixel*/
2409: int     smin=9999, xmin=9999,ymin=9999; /* min separation (s=x+y) */
2410: /*int   type_raster();*/                /* display debugging output */
2411: /* -------------------------------------------------------------------------
2412: find right edge of sp1 and left edge of sp2 (these will be abutting edges)
2413: -------------------------------------------------------------------------- */
2414: /* --- check args --- */
2415: if ( isstring ) goto end_of_job;        /* ignore string rasters */
2416: if ( 0 && istextmode ) goto end_of_job; /* don't smash in text mode */
2417: if ( height > 1023 ) goto end_of_job;   /* don't try to smash huge image */
2418: if ( sp2->type == blanksignal )         /*blanksignal was propagated to us*/
2419:   goto end_of_job;                      /* don't smash intentional blank */
2420: /* --- init firstcol1[], firstcol2[] --- */
2421: for ( irow1=0; irow1<height; irow1++ )  /* for each row */
2422:   firstcol1[irow1] = firstcol2[irow1] = blanksignal; /* signal empty rows */
2423: /* --- set firstcol2[] indicating left edge of sp2 --- */
2424: for ( irow2=top2; irow2<=bot2; irow2++ ) /* for each row inside sp2 */
2425:   for ( icol=0; icol<width2; icol++ )   /* find first non-empty col in row */
2426:     if ( getpixel(sp2->image,irow2-top2,icol) != 0 ) /* found a set pixel */
2427:       { firstcol2[irow2] = icol;        /* icol is #cols from left edge */
2428:         nfirst2++;                      /* bump #rows containing set pixels*/
2429:         break; }                        /* and go on to next row */
2430: if ( nfirst2 < 1 )                      /*right-hand sp2 is completely blank*/
2431:   { nsmash = blanksignal;               /* signal intentional blanks */
2432:     goto end_of_job; }                  /* don't smash intentional blanks */
2433: /* --- now check if preceding image in sp1 was an intentional blank --- */
2434: if ( sp1->type == blanksignal )         /*blanksignal was propagated to us*/
2435:   goto end_of_job;                      /* don't smash intentional blank */
2436: /* --- set firstcol1[] indicating right edge of sp1 --- */
2437: for ( irow1=top1; irow1<=bot1; irow1++ ) /* for each row inside sp1 */
2438:   for ( icol=width1-1; icol>=0; icol-- ) /* find last non-empty col in row */
2439:     if ( getpixel(sp1->image,irow1-top1,icol) != 0 ) /* found a set pixel */
2440:       { firstcol1[irow1] = (width1-1)-icol; /* save #cols from right edge */
2441:         nfirst1++;                      /* bump #rows containing set pixels*/
2442:         break; }                        /* and go on to next row */
2443: if ( nfirst1 < 1 )                      /*left-hand sp1 is completely blank*/
2444:   goto end_of_job;                      /* don't smash intentional blanks */
2445: /* -------------------------------------------------------------------------
2446: find minimum separation
2447: -------------------------------------------------------------------------- */
2448: for ( irow2=top2; irow2<=bot2; irow2++ ) { /* check each row inside sp2 */
2449:  int margin1, margin2=firstcol2[irow2]; /* #cols to first set pixel */
2450:  if ( margin2 != blanksignal ) {        /* irow2 not an empty/blank row */
2451:   for ( irow1=max2(irow2-smin,top1); ; irow1++ )
2452:    if ( irow1 > min2(irow2+smin,bot1) ) break; /* upper bound check */
2453:    else
2454:     if ( (margin1=firstcol1[irow1]) != blanksignal ) { /*have non-blank row*/
2455:      int dx=(margin1+margin2), dy=absval(irow2-irow1), ds=dx+dy; /* deltas */
2456:      if ( ds >= smin ) continue;        /* min unchanged */
2457:      if ( dy>smashmargin && dx<xmin && smin<9999 ) continue; /* dy alone */
2458:      smin=ds; xmin=dx; ymin=dy;         /* set new min */
2459:      } /* --- end-of-if(margin1!=blanksignal) --- */
2460:   } /* --- end-of-if(margin2!=blanksignal) --- */
2461:  if ( smin<2 ) goto end_of_job;         /* can't smash */
2462:  } /* --- end-of-for(irow2) --- */
2463: /*nsmash = min2(xmin,width2);*/         /* permissible smash */
2464: nsmash = xmin;                          /* permissible smash */
2465: /* -------------------------------------------------------------------------
2466: Back to caller with #pixels to smash sp1||sp2
2467: -------------------------------------------------------------------------- */
2468: end_of_job:
2469:   /* --- debugging output --- */
2470:   if ( msgfp!=NULL && msglevel >= 99 )  /* display for debugging */
2471:     { fprintf(msgfp,"rastsmash> nsmash=%d, smashmargin=%d\n",
2472:       nsmash,smashmargin);
2473:       if ( msglevel >= 999 )            /* also display rasters */
2474:         { fprintf(msgfp,"rastsmash>left-hand image...\n");
2475:           if(sp1!=NULL) type_raster(sp1->image,msgfp); /* left image */
2476:           fprintf(msgfp,"rastsmash>right-hand image...\n");
2477:           if(sp2!=NULL) type_raster(sp2->image,msgfp); } /* right image */
2478:       fflush(msgfp); }
2479:   return ( nsmash );                    /* back with #smash pixels */
2480: } /* --- end-of-function rastsmash() --- */
2481: 
2482: 
2483: /* ==========================================================================
2484:  * Function:    rastsmashcheck ( term )
2485:  * Purpose:     Check an exponent term to see if its leading symbol
2486:  *              would make smashing dangerous
2487:  * --------------------------------------------------------------------------
2488:  * Arguments:   term (I)        char *  to null-terminated string
2489:  *                              containing right-hand exponent term about to
2490:  *                              be smashed against existing left-hand.
2491:  * --------------------------------------------------------------------------
2492:  * Returns:     ( int )         1 if it's okay to smash term, or
2493:  *                              0 if smash is dangerous.
2494:  * --------------------------------------------------------------------------
2495:  * Notes:     o
2496:  * ======================================================================= */
2497: /* --- entry point --- */
2498: FUNCSCOPE int rastsmashcheck ( char *term )
2499: {
2500: /* -------------------------------------------------------------------------
2501: Allocations and Declarations
2502: -------------------------------------------------------------------------- */
2503: int     isokay = 0;             /* 1 to signal okay to caller */
2504: static  char nosmashchars[64] = "-.,="; /* don't smash these leading chars */
2505: static  char *nosmashstrs[64] = { "\\frac", NULL }; /* or leading strings */
2506: static  char *grayspace[64] = { "\\tiny", "\\small", "\\normalsize",
2507:         "\\large", "\\Large", "\\LARGE", "\\huge", "\\Huge", NULL };
2508: char    *expression = term;     /* local ptr to beginning of expression */
2509: char    *token = NULL;  int i;  /* token = nosmashstrs[i] or grayspace[i] */
2510: /* -------------------------------------------------------------------------
2511: see if smash check enabled
2512: -------------------------------------------------------------------------- */
2513: if ( smashcheck < 1 ) {         /* no smash checking wanted */
2514:   if ( smashcheck >= 0 )        /* -1 means check should always fail */
2515:     isokay = 1;                 /* otherwise (if 0), signal okay to smash */
2516:   goto end_of_job; }            /* return to caller */
2517: /* -------------------------------------------------------------------------
2518: skip leading white and gray space
2519: -------------------------------------------------------------------------- */
2520: /* --- first check input --- */
2521: if ( isempty(term) ) goto end_of_job; /* no input so return 0 to caller */
2522: /* --- skip leading white space --- */
2523: skipwhite(term);                /* skip leading white space */
2524: if ( *term == '\000' ) goto end_of_job; /* nothing but white space */
2525: /* --- skip leading gray space --- */
2526: skipgray:
2527:  for ( i=0; (token=grayspace[i]) != NULL; i++ ) /* check each grayspace */
2528:   if ( strncmp(term,token,strlen(token)) == 0 ) { /* found grayspace */
2529:    term += strlen(token);       /* skip past this grayspace token */
2530:    skipwhite(term);             /* and skip any subsequent white space */
2531:    if ( *term == '\000' ) {     /* nothing left so quit */
2532:      if ( msgfp!=NULL && msglevel >= 99 ) /* display for debugging */
2533:        fprintf(msgfp,"rastsmashcheck> only grayspace in %.32s\n",expression);
2534:      goto end_of_job; }
2535:    goto skipgray; }             /* restart grayspace check from beginning */
2536: /* -------------------------------------------------------------------------
2537: check for leading no-smash single char
2538: -------------------------------------------------------------------------- */
2539: /* --- don't smash if term begins with a "nosmash" char --- */
2540: if ( (token=strchr(nosmashchars,*term)) != NULL ) {
2541:   if ( msgfp!=NULL && msglevel >= 99 )  /* display for debugging */
2542:     fprintf(msgfp,"rastsmashcheck> char %.1s found in %.32s\n",token,term);
2543:   goto end_of_job; }
2544: /* -------------------------------------------------------------------------
2545: check for leading no-smash token
2546: -------------------------------------------------------------------------- */
2547: for ( i=0; (token=nosmashstrs[i]) != NULL; i++ ) /* check each nosmashstr */
2548:  if ( strncmp(term,token,strlen(token)) == 0 ) { /* found a nosmashstr */
2549:   if ( msgfp!=NULL && msglevel >= 99 )  /* display for debugging */
2550:     fprintf(msgfp,"rastsmashcheck> token %s found in %.32s\n",token,term);
2551:   goto end_of_job; }            /* so don't smash term */
2552: /* -------------------------------------------------------------------------
2553: back to caller
2554: -------------------------------------------------------------------------- */
2555: isokay = 1;                     /* no problem, so signal okay to smash */
2556: end_of_job:
2557:   if ( msgfp!=NULL && msglevel >= 999 ) /* display for debugging */
2558:     fprintf(msgfp,"rastsmashcheck> returning isokay=%d for \"%.32s\"\n",
2559:     isokay,(expression==NULL?"<no input>":expression));
2560:   return ( isokay );            /* back to caller with 1 if okay to smash */
2561: } /* --- end-of-function rastsmashcheck() --- */
2562: 
2563: 
2564: /* ==========================================================================
2565:  * Function:    accent_subraster ( accent, width, height, direction, pixsz )
2566:  * Purpose:     Allocate a new subraster of width x height
2567:  *              (or maybe different dimensions, depending on accent),
2568:  *              and draw an accent (\hat or \vec or \etc) that fills it
2569:  * --------------------------------------------------------------------------
2570:  * Arguments:   accent (I)      int containing either HATACCENT or VECACCENT,
2571:  *                              etc, indicating the type of accent desired
2572:  *              width (I)       int containing desired width of accent (#cols)
2573:  *              height (I)      int containing desired height of accent(#rows)
2574:  *              direction (I)   int containing desired direction of accent,
2575:  *                              +1=right, -1=left, 0=left/right
2576:  *              pixsz (I)       int containing 1 for bitmap, 8 for bytemap
2577:  * --------------------------------------------------------------------------
2578:  * Returns:     ( subraster * ) ptr to newly-allocated subraster with accent,
2579:  *                              or NULL for any error.
2580:  * --------------------------------------------------------------------------
2581:  * Notes:     o Some accents have internally-determined dimensions,
2582:  *              and caller should check dimensions in returned subraster
2583:  * ======================================================================= */
2584: /* --- entry point --- */
2585: FUNCSCOPE subraster *accent_subraster ( int accent, int width, int height,
2586:                                         int direction, int pixsz )
2587: {
2588: /* -------------------------------------------------------------------------
2589: Allocations and Declarations
2590: -------------------------------------------------------------------------- */
2591: /* --- general info --- */
2592: raster  /**new_raster(),*/ *rp=NULL;    /*raster containing desired accent*/
2593: subraster /**new_subraster(),*/ *sp=NULL; /* subraster returning accent */
2594: /*int   delete_raster(), delete_subraster();*/ /*free allocated raster on err*/
2595: int     /*line_raster(),*/              /* draws lines */
2596:         /*rule_raster(),*/              /* draw solid boxes */
2597:         thickness = 1;                  /* line thickness */
2598: /*int   pixval = (pixsz==1? 1 : (pixsz==8?255:(-1)));*/ /*black pixel value*/
2599: /* --- other working info --- */
2600: int     col0, col1,                     /* cols for line */
2601:         row0, row1;                     /* rows for line */
2602: subraster /**get_delim(),*/ *accsp=NULL; /*find suitable cmex10 symbol/accent*/
2603: /* --- info for under/overbraces, tildes, etc --- */
2604: char    brace[16];                      /*"{" for over, "}" for under, etc*/
2605: /*raster *rastrot(),*/                  /* rotate { for overbrace, etc */
2606: /*      *rastcpy();*/                   /* may need copy of original */
2607: /*subraster *arrow_subraster();*/       /* rightarrow for vec */
2608: /*subraster *rastack();*/               /* stack accent atop extra space */
2609: int     iswidthneg = 0;                 /* set true if width<0 arg passed */
2610: int     serifwidth=0;                   /* serif for surd */
2611: int     isBig=0;                        /* true for ==>arrow, false for -->*/
2612: /* -------------------------------------------------------------------------
2613: initialization
2614: -------------------------------------------------------------------------- */
2615: if ( width < 0 ) { width=(-width); iswidthneg=1; } /* set neg width flag */
2616: /* -------------------------------------------------------------------------
2617: outer switch() traps accents that may change caller's height,width
2618: -------------------------------------------------------------------------- */
2619: switch ( accent )
2620:  {
2621:  default:
2622:   /* -----------------------------------------------------------------------
2623:   inner switch() first allocates fixed-size raster for accents that don't
2624:   ------------------------------------------------------------------------ */
2625:   if ( (rp = new_raster(width,height,pixsz)) /* allocate fixed-size raster */
2626:   !=   NULL )                           /* and if we succeeded... */
2627:    switch ( accent )                    /* ...draw requested accent in it */
2628:     {
2629:     /* --- unrecognized request --- */
2630:     default: delete_raster(rp);         /* unrecognized accent requested */
2631:         rp = NULL;  break;              /* so free raster and signal error */
2632:     /* --- bar request --- */
2633:     case UNDERBARACCENT:
2634:     case BARACCENT:
2635:         thickness = 1; /*height-1;*/    /* adjust thickness */
2636:         if ( accent == BARACCENT )      /* bar is above expression */
2637:          { row0 = row1 = max2(height-3,0); /* row numbers for overbar */
2638:            line_raster(rp,row0,0,row1,width-1,thickness); } /*blanks at bot*/
2639:         else                            /* underbar is below expression */
2640:          { row0 = row1 = min2(2,height-1); /* row numbers for underbar */
2641:            line_raster(rp,row0,0,row1,width-1,thickness); } /*blanks at top*/
2642:         break;
2643:     /* --- dot request --- */
2644:     case DOTACCENT:
2645:         thickness = height-1;           /* adjust thickness */
2646:         /*line_raster(rp,0,width/2,1,(width/2)+1,thickness);*//*centered dot*/
2647:         rule_raster(rp,0,(width+1-thickness)/2,thickness,thickness,3); /*box*/
2648:         break;
2649:     /* --- ddot request --- */
2650:     case DDOTACCENT:
2651:         thickness = height-1;           /* adjust thickness */
2652:         col0 = max2((width+1)/3-(thickness/2)-1,0); /* one-third of width */
2653:         col1 = min2((2*width+1)/3-(thickness/2)+1,width-thickness); /*2/3rds*/
2654:         if ( col0+thickness >= col1 )   /* dots overlap */
2655:           { col0 = max2(col0-1,0);      /* try moving left dot more left */
2656:             col1 = min2(col1+1,width-thickness); } /* and right dot right */
2657:         if ( col0+thickness >= col1 )   /* dots _still_ overlap */
2658:           thickness = max2(thickness-1,1); /* so try reducing thickness */
2659:         /*line_raster(rp,0,col0,1,col0+1,thickness);*//*set dot at 1st third*/
2660:         /*line_raster(rp,0,col1,1,col1+1,thickness);*//*and another at 2nd*/
2661:         rule_raster(rp,0,col0,thickness,thickness,3); /*box at 1st third*/
2662:         rule_raster(rp,0,col1,thickness,thickness,3); /*box at 2nd third*/
2663:         break;
2664:     /* --- hat request --- */
2665:     case HATACCENT:
2666:         thickness = 1; /*(width<=12? 2 : 3);*/  /* adjust thickness */
2667:         line_raster(rp,height-1,0,0,width/2,thickness);    /* / part of hat*/
2668:         line_raster(rp,0,(width-1)/2,height-1,width-1,thickness); /* \ part*/
2669:         break;
2670:     /* --- sqrt request --- */
2671:     case SQRTACCENT:
2672:         serifwidth = SURDSERIFWIDTH(height); /* leading serif on surd */
2673:         col1 = SQRTWIDTH(height,(iswidthneg?1:2)) - 1; /*right col of sqrt*/
2674:         /*col0 = (col1-serifwidth+2)/3;*/ /* midpoint col of sqrt */
2675:         col0 = (col1-serifwidth+1)/2;   /* midpoint col of sqrt */
2676:         row0 = max2(1,((height+1)/2)-2); /* midpoint row of sqrt */
2677:         row1 = height-1;                /* bottom row of sqrt */
2678:         /*line_raster(rp,row0,0,row1,col0,thickness);*/ /*descending portion*/
2679:         line_raster(rp,row0+serifwidth,0,row0,serifwidth,thickness);
2680:         line_raster(rp,row0,serifwidth,row1,col0,thickness); /* descending */
2681:         line_raster(rp,row1,col0,0,col1,thickness); /* ascending portion */
2682:         line_raster(rp,0,col1,0,width-1,thickness); /*overbar of thickness 1*/
2683:         break;
2684:     } /* --- end-of-inner-switch(accent) --- */
2685:     break;                              /* break from outer accent switch */
2686:  /* --- underbrace, overbrace request --- */
2687:  case UNDERBRACE:
2688:  case OVERBRACE:
2689:     if ( accent == UNDERBRACE ) strcpy(brace,"}"); /* start with } brace */
2690:     if ( accent ==  OVERBRACE ) strcpy(brace,"{"); /* start with { brace */
2691:     if ( (accsp=get_delim(brace,width,CMEX10)) /* use width for height */
2692:     !=  NULL )                          /* found desired brace */
2693:       { rp = rastrot(accsp->image);     /* rotate 90 degrees clockwise */
2694:         delete_subraster(accsp); }      /* and free subraster "envelope" */
2695:     break;
2696:  /* --- hat request --- */
2697:  case HATACCENT:
2698:     if ( accent == HATACCENT ) strcpy(brace,"<"); /* start with < */
2699:     if ( (accsp=get_delim(brace,width,CMEX10)) /* use width for height */
2700:     !=  NULL )                          /* found desired brace */
2701:       { rp = rastrot(accsp->image);     /* rotate 90 degrees clockwise */
2702:         delete_subraster(accsp); }      /* and free subraster "envelope" */
2703:     break;
2704:  /* --- vec request --- */
2705:  case VECACCENT:
2706:     height = 2*(height/2) + 1;          /* force height odd */
2707:     if ( absval(direction) >= 9 ) {     /* want ==> arrow rather than --> */
2708:       isBig = 1;                        /* signal "Big" arrow */
2709:       direction -= 10; }                /* reset direction = +1, -1, or 0 */
2710:     if ((accsp=arrow_subraster(width,height,pixsz,direction,isBig)) /*arrow*/
2711:     !=  NULL )                          /* succeeded */
2712:         { rp = accsp->image;            /* "extract" raster with bitmap */
2713:           free((void *)accsp); }        /* and free subraster "envelope" */
2714:     break;
2715:  /* --- tilde request --- */
2716:  case TILDEACCENT:
2717:     accsp=(width<25? get_delim("\\sim",-width,CMSY10) :
2718:                      get_delim("~",-width,CMEX10)); /*width search for tilde*/
2719:     if ( accsp !=  NULL )               /* found desired tilde */
2720:       if ( (sp=rastack(new_subraster(1,1,pixsz),accsp,1,0,1,3))/*space below*/
2721:       !=  NULL )                        /* have tilde with space below it */
2722:         { rp = sp->image;               /* "extract" raster with bitmap */
2723:           free((void *)sp);             /* and free subraster "envelope" */
2724:           leftsymdef = NULL; }          /* so \tilde{x}^2 works properly */
2725:     break;
2726:  } /* --- end-of-outer-switch(accent) --- */
2727: /* -------------------------------------------------------------------------
2728: if we constructed accent raster okay, embed it in a subraster and return it
2729: -------------------------------------------------------------------------- */
2730: /* --- if all okay, allocate subraster to contain constructed raster --- */
2731: if ( rp != NULL ) {                     /* accent raster constructed okay */
2732:   if ( (sp=new_subraster(0,0,0))        /* allocate subraster "envelope" */
2733:   ==   NULL )                           /* and if we fail to allocate */
2734:     delete_raster(rp);                  /* free now-unneeded raster */
2735:   else                                  /* subraster allocated okay */
2736:     { /* --- init subraster parameters, embedding raster in it --- */
2737:       sp->type = IMAGERASTER;           /* constructed image */
2738:       sp->image = rp;                   /* raster we just constructed */
2739:       sp->size = (-1);                  /* can't set font size here */
2740:       sp->baseline = 0; }               /* can't set baseline here */
2741:   } /* --- end-of-if(rp!=NULL) --- */
2742: /* --- return subraster containing desired accent to caller --- */
2743: return ( sp );                          /* return accent or NULL to caller */
2744: } /* --- end-of-function accent_subraster() --- */
2745: 
2746: 
2747: /* ==========================================================================
2748:  * Function:    arrow_subraster ( width, height, pixsz, drctn, isBig )
2749:  * Purpose:     Allocate a raster/subraster and draw left/right arrow in it
2750:  * --------------------------------------------------------------------------
2751:  * Arguments:   width (I)       int containing number of cols for arrow
2752:  *              height (I)      int containing number of rows for arrow
2753:  *              pixsz (I)       int containing 1 for bitmap, 8 for bytemap
2754:  *              drctn (I)       int containing +1 for right arrow,
2755:  *                              or -1 for left, 0 for leftright
2756:  *              isBig (I)       int containing 1/true for \Long arrows,
2757:  *                              or false for \long arrows, i.e.,
2758:  *                              true for ===> or false for --->.
2759:  * --------------------------------------------------------------------------
2760:  * Returns:     ( subraster * ) ptr to constructed left/right arrow
2761:  *                              or NULL for any error.
2762:  * --------------------------------------------------------------------------
2763:  * Notes:     o
2764:  * ======================================================================= */
2765: /* --- entry point --- */
2766: FUNCSCOPE subraster *arrow_subraster ( int width, int height, int pixsz,
2767:                                        int drctn, int isBig )
2768: {
2769: /* -------------------------------------------------------------------------
2770: Allocations and Declarations
2771: -------------------------------------------------------------------------- */
2772: subraster /**new_subraster(),*/ *arrowsp=NULL; /* allocate arrow subraster */
2773: /*int   rule_raster();*/                /* draw arrow line */
2774: int     irow, midrow=height/2;          /* index, midrow is arrowhead apex */
2775: int     icol, thickness=(height>15?2:2); /* arrowhead thickness and index */
2776: int     pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */
2777: int     ipix,                           /* raster pixmap[] index */
2778:         npix = width*height;            /* #pixels malloced in pixmap[] */
2779: /* -------------------------------------------------------------------------
2780: allocate raster/subraster and draw arrow line
2781: -------------------------------------------------------------------------- */
2782: if ( height < 3 ) { height=3; midrow=1; }       /* set minimum height */
2783: if ( (arrowsp=new_subraster(width,height,pixsz)) /* allocate empty raster */
2784: ==   NULL ) goto end_of_job;                    /* and quit if failed */
2785: if ( !isBig )                                   /* single line */
2786:   rule_raster(arrowsp->image,midrow,0,width,1,0); /*draw line across midrow*/
2787: else
2788:   { int delta = (width>6? (height>15? 3: (height>7? 2 : 1)) : 1);
2789:     rule_raster(arrowsp->image,midrow-delta,delta,width-2*delta,1,0);
2790:     rule_raster(arrowsp->image,midrow+delta,delta,width-2*delta,1,0); }
2791: /* -------------------------------------------------------------------------
2792: construct arrowhead(s)
2793: -------------------------------------------------------------------------- */
2794: for ( irow=0; irow<height; irow++ )             /* for each row of arrow */
2795:   {
2796:   int   delta = abs(irow-midrow);               /*arrowhead offset for irow*/
2797:   /* --- right arrowhead --- */
2798:   if ( drctn >= 0 )                             /* right arrowhead wanted */
2799:     for ( icol=0; icol<thickness; icol++ )      /* for arrowhead thickness */
2800:      { ipix = ((irow+1)*width - 1) - delta - icol; /* rightmost-delta-icol */
2801:        if ( ipix >= 0 ) {                               /* bounds check */
2802:         if ( pixsz == 1 )                       /* have a bitmap */
2803:           setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
2804:         else                                    /* should have a bytemap */
2805:          if ( pixsz == 8 )                      /* check pixsz for bytemap */
2806:           ((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
2807:   /* --- left arrowhead (same as right except for ipix calculation) --- */
2808:   if ( drctn <= 0 )                             /* left arrowhead wanted */
2809:     for ( icol=0; icol<thickness; icol++ )      /* for arrowhead thickness */
2810:      { ipix = irow*width + delta + icol;        /* leftmost bit+delta+icol */
2811:        if ( ipix < npix ) {                     /* bounds check */
2812:         if ( pixsz == 1 )                       /* have a bitmap */
2813:           setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
2814:         else                                    /* should have a bytemap */
2815:          if ( pixsz == 8 )                      /* check pixsz for bytemap */
2816:           ((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
2817:   } /* --- end-of-for(irow) --- */
2818: end_of_job:
2819:   return ( arrowsp );                   /*back to caller with arrow or NULL*/
2820: } /* --- end-of-function arrow_subraster() --- */
2821: 
2822: 
2823: /* ==========================================================================
2824:  * Function:    uparrow_subraster ( width, height, pixsz, drctn, isBig )
2825:  * Purpose:     Allocate a raster/subraster and draw up/down arrow in it
2826:  * --------------------------------------------------------------------------
2827:  * Arguments:   width (I)       int containing number of cols for arrow
2828:  *              height (I)      int containing number of rows for arrow
2829:  *              pixsz (I)       int containing 1 for bitmap, 8 for bytemap
2830:  *              drctn (I)       int containing +1 for up arrow,
2831:  *                              or -1 for down, or 0 for updown
2832:  *              isBig (I)       int containing 1/true for \Long arrows,
2833:  *                              or false for \long arrows, i.e.,
2834:  *                              true for ===> or false for --->.
2835:  * --------------------------------------------------------------------------
2836:  * Returns:     ( subraster * ) ptr to constructed up/down arrow
2837:  *                              or NULL for any error.
2838:  * --------------------------------------------------------------------------
2839:  * Notes:     o
2840:  * ======================================================================= */
2841: /* --- entry point --- */
2842: FUNCSCOPE subraster *uparrow_subraster ( int width, int height, int pixsz,
2843:                                          int drctn, int isBig )
2844: {
2845: /* -------------------------------------------------------------------------
2846: Allocations and Declarations
2847: -------------------------------------------------------------------------- */
2848: subraster /**new_subraster(),*/ *arrowsp=NULL; /* allocate arrow subraster */
2849: /*int   rule_raster();*/                /* draw arrow line */
2850: int     icol, midcol=width/2;           /* index, midcol is arrowhead apex */
2851: int     irow, thickness=(width>15?2:2); /* arrowhead thickness and index */
2852: int     pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */
2853: int     ipix,                           /* raster pixmap[] index */
2854:         npix = width*height;            /* #pixels malloced in pixmap[] */
2855: /* -------------------------------------------------------------------------
2856: allocate raster/subraster and draw arrow line
2857: -------------------------------------------------------------------------- */
2858: if ( width < 3 ) { width=3; midcol=1; }         /* set minimum width */
2859: if ( (arrowsp=new_subraster(width,height,pixsz)) /* allocate empty raster */
2860: ==   NULL ) goto end_of_job;                    /* and quit if failed */
2861: if ( !isBig )                                   /* single line */
2862:   rule_raster(arrowsp->image,0,midcol,1,height,0); /*draw line down midcol*/
2863: else
2864:   { int delta = (height>6? (width>15? 3: (width>7? 2 : 1)) : 1);
2865:     rule_raster(arrowsp->image,delta,midcol-delta,1,height-2*delta,0);
2866:     rule_raster(arrowsp->image,delta,midcol+delta,1,height-2*delta,0); }
2867: /* -------------------------------------------------------------------------
2868: construct arrowhead(s)
2869: -------------------------------------------------------------------------- */
2870: for ( icol=0; icol<width; icol++ )              /* for each col of arrow */
2871:   {
2872:   int   delta = abs(icol-midcol);               /*arrowhead offset for icol*/
2873:   /* --- up arrowhead --- */
2874:   if ( drctn >= 0 )                             /* up arrowhead wanted */
2875:     for ( irow=0; irow<thickness; irow++ )      /* for arrowhead thickness */
2876:      { ipix = (irow+delta)*width + icol;        /* leftmost+icol */
2877:        if ( ipix < npix ) {                     /* bounds check */
2878:         if ( pixsz == 1 )                       /* have a bitmap */
2879:           setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
2880:         else                                    /* should have a bytemap */
2881:          if ( pixsz == 8 )                      /* check pixsz for bytemap */
2882:           ((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
2883:   /* --- down arrowhead (same as up except for ipix calculation) --- */
2884:   if ( drctn <= 0 )                             /* down arrowhead wanted */
2885:     for ( irow=0; irow<thickness; irow++ )      /* for arrowhead thickness */
2886:      { ipix = (height-1-delta-irow)*width + icol; /* leftmost + icol */
2887:        if ( ipix > 0 ) {                        /* bounds check */
2888:         if ( pixsz == 1 )                       /* have a bitmap */
2889:           setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
2890:         else                                    /* should have a bytemap */
2891:          if ( pixsz == 8 )                      /* check pixsz for bytemap */
2892:           ((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
2893:   } /* --- end-of-for(icol) --- */
2894: end_of_job:
2895:   return ( arrowsp );                   /*back to caller with arrow or NULL*/
2896: } /* --- end-of-function uparrow_subraster() --- */
2897: 
2898: 
2899: /* ==========================================================================
2900:  * Function:    rule_raster ( rp, top, left, width, height, type )
2901:  * Purpose:     Draw a solid or dashed line (or box) in existing raster rp,
2902:  *              starting at top,left with dimensions width,height.
2903:  * --------------------------------------------------------------------------
2904:  * Arguments:   rp (I)          raster *  to raster in which rule
2905:  *                              will be drawn
2906:  *              top (I)         int containing row at which top-left corner
2907:  *                              of rule starts (0 is topmost)
2908:  *              left (I)        int containing col at which top-left corner
2909:  *                              of rule starts (0 is leftmost)
2910:  *              width (I)       int containing number of cols for rule
2911:  *              height (I)      int containing number of rows for rule
2912:  *              type (I)        int containing 0 for solid rule,
2913:  *                              1 for horizontal dashes, 2 for vertical
2914:  *                              3 for solid rule with corners removed (bevel)
2915:  *                              4 for strut (nothing drawn)
2916:  * --------------------------------------------------------------------------
2917:  * Returns:     ( int )         1 if rule drawn okay,
2918:  *                              or 0 for any error.
2919:  * --------------------------------------------------------------------------
2920:  * Notes:     o Rule line is implicitly "horizontal" or "vertical" depending
2921:  *              on relative width,height dimensions.  It's a box if they're
2922:  *              more or less comparable.
2923:  * ======================================================================= */
2924: /* --- entry point --- */
2925: FUNCSCOPE int rule_raster ( raster *rp, int top, int left,
2926:                             int width, int height, int type )
2927: {
2928: /* -------------------------------------------------------------------------
2929: Allocations and Declarations
2930: -------------------------------------------------------------------------- */
2931: int     irow=0, icol=0;         /* indexes over rp raster */
2932: int     ipix = 0,               /* raster pixmap[] index */
2933:         npix = rp->width * rp->height; /* #pixels malloced in rp->pixmap[] */
2934: int     isfatal = 0;            /* true to abend on out-of-bounds error */
2935: int     hdash=1, vdash=2,       /* type for horizontal, vertical dashes */
2936:         bevel=99/*3*/, strut=4; /* type for bevel (turned off), strut */
2937: int     dashlen=3, spacelen=2,  /* #pixels for dash followed by space */
2938:         isdraw=1;               /* true when drawing dash (init for solid) */
2939: /* -------------------------------------------------------------------------
2940: Check args
2941: -------------------------------------------------------------------------- */
2942: if ( rp == (raster *)NULL ) {   /* no raster arg supplied */
2943:   if ( workingbox != (subraster *)NULL )  /* see if we have a workingbox */
2944:     rp = workingbox->image;     /* use workingbox if possible */
2945:   else return ( 0 ); }          /* otherwise signal error to caller */
2946: if ( type == bevel )            /* remove corners of solid box */
2947:   if ( width<3 || height<3 ) type=0; /* too small to remove corners */
2948: /* -------------------------------------------------------------------------
2949: Fill line/box
2950: -------------------------------------------------------------------------- */
2951: if ( width > 0 )                                /* zero width implies strut*/
2952:  for ( irow=top; irow<top+height; irow++ )      /* for each scan line */
2953:   {
2954:   if ( type == strut ) isdraw = 0;              /* draw nothing for strut */
2955:   if ( type == vdash )                          /*set isdraw for vert dash*/
2956:     isdraw = (((irow-top)%(dashlen+spacelen)) < dashlen);
2957:   ipix = irow*rp->width + left - 1;             /*first pixel preceding icol*/
2958:   for ( icol=left; icol<left+width; icol++ )    /* each pixel in scan line */
2959:     {
2960:     if ( type == bevel ) {                      /* remove corners of box */
2961:       if ( (irow==top && icol==left)            /* top-left corner */
2962:       ||   (irow==top && icol>=left+width-1)    /* top-right corner */
2963:       ||   (irow>=top+height-1 && icol==left)   /* bottom-left corner */
2964:       ||   (irow>=top+height-1 && icol>=left+width-1) ) /* bottom-right */
2965:         isdraw = 0;  else isdraw = 1; }         /*set isdraw to skip corner*/
2966:     if ( type == hdash )                        /*set isdraw for horiz dash*/
2967:       isdraw = (((icol-left)%(dashlen+spacelen)) < dashlen);
2968:     if ( ++ipix >= npix )                       /* bounds check failed */
2969:          if ( isfatal ) goto end_of_job;        /* abort if error is fatal */
2970:          else break;                            /*or just go on to next row*/
2971:     else                                        /*ibit is within rp bounds*/
2972:       if ( isdraw ) {                           /*and we're drawing this bit*/
2973:         if ( rp->pixsz == 1 )                   /* have a bitmap */
2974:           setlongbit(rp->pixmap,ipix);          /* so turn on bit in line */
2975:         else                                    /* should have a bytemap */
2976:          if ( rp->pixsz == 8 )                  /* check pixsz for bytemap */
2977:           ((unsigned char *)(rp->pixmap))[ipix] = 255; } /* set black byte */
2978:     } /* --- end-of-for(icol) --- */
2979:   } /* --- end-of-for(irow) --- */
2980: end_of_job:
2981:   return ( isfatal? (ipix<npix? 1:0) : 1 );
2982: } /* --- end-of-function rule_raster() --- */
2983: 
2984: 
2985: /* ==========================================================================
2986:  * Function:    line_raster ( rp,  row0, col0,  row1, col1,  thickness )
2987:  * Purpose:     Draw a line from row0,col0 to row1,col1 of thickness
2988:  *              in existing raster rp.
2989:  * --------------------------------------------------------------------------
2990:  * Arguments:   rp (I)          raster *  to raster in which a line
2991:  *                              will be drawn
2992:  *              row0 (I)        int containing row at which
2993:  *                              line will start (0 is topmost)
2994:  *              col0 (I)        int containing col at which
2995:  *                              line will start (0 is leftmost)
2996:  *              row1 (I)        int containing row at which
2997:  *                              line will end (rp->height-1 is bottom-most)
2998:  *              col1 (I)        int containing col at which
2999:  *                              line will end (rp->width-1 is rightmost)
3000:  *              thickness (I)   int containing number of pixels/bits
3001:  *                              thick the line will be
3002:  * --------------------------------------------------------------------------
3003:  * Returns:     ( int )         1 if line drawn okay,
3004:  *                              or 0 for any error.
3005:  * --------------------------------------------------------------------------
3006:  * Notes:     o if row0==row1, a horizontal line is drawn
3007:  *              between col0 and col1, with row0(==row1) the top row
3008:  *              and row0+(thickness-1) the bottom row
3009:  *            o if col0==col1, a vertical bar is drawn
3010:  *              between row0 and row1, with col0(==col1) the left col
3011:  *              and col0+(thickness-1) the right col
3012:  *            o if both the above, you get a square thickness x thickness
3013:  *              whose top-left corner is row0,col0.
3014:  * ======================================================================= */
3015: /* --- entry point --- */
3016: FUNCSCOPE int line_raster ( raster *rp, int row0, int col0,
3017:                             int row1, int col1, int thickness )
3018: {
3019: /* -------------------------------------------------------------------------
3020: Allocations and Declarations
3021: -------------------------------------------------------------------------- */
3022: int     irow=0, icol=0,         /* indexes over rp raster */
3023:         locol=col0, hicol=col1, /* col limits at irow */
3024:         lorow=row0, hirow=row1; /* row limits at icol */
3025: int     width=rp->width, height=rp->height; /* dimensions of input raster */
3026: int     ipix = 0,               /* raster pixmap[] index */
3027:         npix = width*height;    /* #pixels malloced in rp->pixmap[] */
3028: int     isfatal = 0;            /* true to abend on out-of-bounds error */
3029: int     isline=(row1==row0), isbar=(col1==col0); /*true if slope a=0,\infty*/
3030: double  dy = row1-row0 /* + (row1>=row0? +1.0 : -1.0) */, /* delta-x */
3031:         dx = col1-col0 /* + (col1>=col0? +1.0 : -1.0) */, /* delta-y */
3032:         a= (isbar||isline? 0.0 : dy/dx), /* slope = tan(theta) = dy/dx */
3033:         xcol=0, xrow=0;         /* calculated col at irow, or row at icol */
3034: double  ar = ASPECTRATIO,       /* aspect ratio width/height of one pixel */
3035:         xwidth= (isline? 0.0 :  /*#pixels per row to get sloped line thcknss*/
3036:                 ((double)thickness)*sqrt((dx*dx)+(dy*dy*ar*ar))/fabs(dy*ar)),
3037:         xheight = 1.0;
3038: int     /*line_recurse(),*/ isrecurse=1; /* true to draw line recursively */
3039: /* -------------------------------------------------------------------------
3040: Check args
3041: -------------------------------------------------------------------------- */
3042: if ( rp == (raster *)NULL ) {   /* no raster arg supplied */
3043:   if ( workingbox != (subraster *)NULL )  /* see if we have a workingbox */
3044:     rp = workingbox->image;     /* use workingbox if possible */
3045:   else return ( 0 ); }          /* otherwise signal error to caller */
3046: /* -------------------------------------------------------------------------
3047: Initialization
3048: -------------------------------------------------------------------------- */
3049: if ( msgfp!=NULL && msglevel>=29 ) {            /* debugging */
3050:    fprintf(msgfp,"line_raster> row,col0=%d,%d row,col1=%d,%d, thickness=%d\n"
3051:    "\t dy,dx=%3.1f,%3.1f, a=%4.3f, xwidth=%4.3f\n",
3052:    row0,col0, row1,col1, thickness,  dy,dx, a, xwidth); fflush(msgfp); }
3053: /* --- check for recursive line drawing --- */
3054: if ( isrecurse ) {              /* drawing lines recursively */
3055:  for ( irow=0; irow<thickness; irow++ )         /* each line 1 pixel thick */
3056:   { double xrow0=(double)row0, xcol0=(double)col0,
3057:         xrow1=(double)row1, xcol1=(double)col1;
3058:     if ( isline ) xrow0 = xrow1 = (double)(row0+irow);
3059:     else if ( isbar ) xcol0 = xcol1 = (double)(col0+irow);
3060:     if( xrow0>(-0.001) && xcol0>(-0.001)        /*check line inside raster*/
3061:     &&  xrow1<((double)(height-1)+0.001) && xcol1<((double)(width-1)+0.001) )
3062:       line_recurse(rp,xrow0,xcol0,xrow1,xcol1,thickness); }
3063:  return ( 1 ); }
3064: /* --- set params for horizontal line or vertical bar --- */
3065: if ( isline )                                   /*interpret row as top row*/
3066:   row1 = row0 + (thickness-1);                  /* set bottom row for line */
3067: if ( 0&&isbar )                                 /*interpret col as left col*/
3068:   hicol = col0 + (thickness-1);                 /* set right col for bar */
3069: /* -------------------------------------------------------------------------
3070: draw line one row at a time
3071: -------------------------------------------------------------------------- */
3072: for ( irow=min2(row0,row1); irow<=max2(row0,row1); irow++ ) /*each scan line*/
3073:   {
3074:   if ( !isbar && !isline )                      /* neither vert nor horiz */
3075:     { xcol  = col0 + ((double)(irow-row0))/a;   /* "middle" col in irow */
3076:       locol = max2((int)(xcol-0.5*(xwidth-1.0)),0); /* leftmost col */
3077:       hicol = min2((int)(xcol+0.5*(xwidth-0.0)),max2(col0,col1)); } /*right*/
3078:   if ( msgfp!=NULL && msglevel>=29 )            /* debugging */
3079:     fprintf(msgfp,"\t irow=%d, xcol=%4.2f, lo,hicol=%d,%d\n",
3080:     irow,xcol,locol,hicol);
3081:   ipix = irow*rp->width + min2(locol,hicol) - 1; /*first pix preceding icol*/
3082:   for ( icol=min2(locol,hicol); icol<=max2(locol,hicol); icol++ ) /*each pix*/
3083:     if ( ++ipix >= npix )                       /* bounds check failed */
3084:         if ( isfatal ) goto end_of_job; /* abort if error is fatal */
3085:         else break;                             /*or just go on to next row*/
3086:     else                                        /* turn on pixel in line */
3087:         if ( rp->pixsz == 1 )                   /* have a pixel bitmap */
3088:           setlongbit(rp->pixmap,ipix);          /* so turn on bit in line */
3089:         else                                    /* should have a bytemap */
3090:          if ( rp->pixsz == 8 )                  /* check pixsz for bytemap */
3091:           ((unsigned char *)(rp->pixmap))[ipix] = 255; /* set black byte */
3092:   } /* --- end-of-for(irow) --- */
3093: /* -------------------------------------------------------------------------
3094: now _redraw_ line one col at a time to avoid "gaps"
3095: -------------------------------------------------------------------------- */
3096: if ( 1 )
3097:  for ( icol=min2(col0,col1); icol<=max2(col0,col1); icol++ )/*each scan line*/
3098:   {
3099:   if ( !isbar && !isline )                      /* neither vert nor horiz */
3100:     { xrow  = row0 + ((double)(icol-col0))*a;   /* "middle" row in icol */
3101:       lorow = max2((int)(xrow-0.5*(xheight-1.0)),0); /* topmost row */
3102:       hirow = min2((int)(xrow+0.5*(xheight-0.0)),max2(row0,row1)); } /*bot*/
3103:   if ( msgfp!=NULL && msglevel>=29 )            /* debugging */
3104:     fprintf(msgfp,"\t icol=%d, xrow=%4.2f, lo,hirow=%d,%d\n",
3105:     icol,xrow,lorow,hirow);
3106:   ipix = irow*rp->width + min2(locol,hicol) - 1; /*first pix preceding icol*/
3107:   for ( irow=min2(lorow,hirow); irow<=max2(lorow,hirow); irow++ ) /*each pix*/
3108:     if ( irow<0 || irow>=rp->height
3109:     ||   icol<0 || icol>=rp->width )            /* bounds check */
3110:       if ( isfatal ) goto end_of_job;           /* abort if error is fatal */
3111:       else continue;                            /*or just go on to next row*/
3112:     else
3113:       setpixel(rp,irow,icol,255);               /* set pixel at irow,icol */
3114:   } /* --- end-of-for(irow) --- */
3115: /* -------------------------------------------------------------------------
3116: Back to caller with 1=okay, 0=failed.
3117: -------------------------------------------------------------------------- */
3118: end_of_job:
3119:   return ( isfatal? (ipix<npix? 1:0) : 1 );
3120: } /* --- end-of-function line_raster() --- */
3121: 
3122: 
3123: /* ==========================================================================
3124:  * Function:    line_recurse ( rp,  row0, col0,  row1, col1,  thickness )
3125:  * Purpose:     Draw a line from row0,col0 to row1,col1 of thickness
3126:  *              in existing raster rp.
3127:  * --------------------------------------------------------------------------
3128:  * Arguments:   rp (I)          raster *  to raster in which a line
3129:  *                              will be drawn
3130:  *              row0 (I)        double containing row at which
3131:  *                              line will start (0 is topmost)
3132:  *              col0 (I)        double containing col at which
3133:  *                              line will start (0 is leftmost)
3134:  *              row1 (I)        double containing row at which
3135:  *                              line will end (rp->height-1 is bottom-most)
3136:  *              col1 (I)        double containing col at which
3137:  *                              line will end (rp->width-1 is rightmost)
3138:  *              thickness (I)   int containing number of pixels/bits
3139:  *                              thick the line will be
3140:  * --------------------------------------------------------------------------
3141:  * Returns:     ( int )         1 if line drawn okay,
3142:  *                              or 0 for any error.
3143:  * --------------------------------------------------------------------------
3144:  * Notes:     o Recurses, drawing left- and right-halves of line
3145:  *              until a horizontal or vertical segment is found
3146:  * ======================================================================= */
3147: /* --- entry point --- */
3148: FUNCSCOPE int line_recurse ( raster *rp, double row0, double col0,
3149:                              double row1, double col1, int thickness )
3150: {
3151: /* -------------------------------------------------------------------------
3152: Allocations and Declarations
3153: -------------------------------------------------------------------------- */
3154: double  delrow = fabs(row1-row0),       /* 0 if line horizontal */
3155:         delcol = fabs(col1-col0),       /* 0 if line vertical */
3156:         tolerance = 0.5;                /* draw line when it goes to point */
3157: double  midrow = 0.5*(row0+row1),       /* midpoint row */
3158:         midcol = 0.5*(col0+col1);       /* midpoint col */
3159: /* -------------------------------------------------------------------------
3160: recurse if either delta > tolerance
3161: -------------------------------------------------------------------------- */
3162: if ( delrow > tolerance                 /* row hasn't converged */
3163: ||   delcol > tolerance )               /* col hasn't converged */
3164:   { line_recurse(rp,row0,col0,midrow,midcol,thickness); /* left half */
3165:     line_recurse(rp,midrow,midcol,row1,col1,thickness); /* right half */
3166:     return ( 1 ); }
3167: /* -------------------------------------------------------------------------
3168: draw converged point
3169: -------------------------------------------------------------------------- */
3170: setpixel(rp,iround(midrow),iround(midcol),255); /*set pixel at midrow,midcol*/
3171: return ( 1 );
3172: } /* --- end-of-function line_recurse() --- */
3173: 
3174: 
3175: /* ==========================================================================
3176:  * Function:    circle_raster ( rp,  row0, col0,  row1, col1,
3177:  *              thickness, quads )
3178:  * Purpose:     Draw quad(rant)s of an ellipse in box determined by
3179:  *              diagonally opposite corner points (row0,col0) and
3180:  *              (row1,col1), of thickness pixels in existing raster rp.
3181:  * --------------------------------------------------------------------------
3182:  * Arguments:   rp (I)          raster *  to raster in which an ellipse
3183:  *                              will be drawn
3184:  *              row0 (I)        int containing 1st corner row bounding ellipse
3185:  *                              (0 is topmost)
3186:  *              col0 (I)        int containing 1st corner col bounding ellipse
3187:  *                              (0 is leftmost)
3188:  *              row1 (I)        int containing 2nd corner row bounding ellipse
3189:  *                              (rp->height-1 is bottom-most)
3190:  *              col1 (I)        int containing 2nd corner col bounding ellipse
3191:  *                              (rp->width-1 is rightmost)
3192:  *              thickness (I)   int containing number of pixels/bits
3193:  *                              thick the ellipse arc line will be
3194:  *              quads (I)       char * to null-terminated string containing
3195:  *                              any subset/combination of "1234" specifying
3196:  *                              which quadrant(s) of ellipse to draw.
3197:  *                              NULL ptr draws all four quadrants;
3198:  *                              otherwise 1=upper-right quadrant,
3199:  *                              2=uper-left, 3=lower-left, 4=lower-right,
3200:  *                              i.e., counterclockwise from 1=positive quad.
3201:  * --------------------------------------------------------------------------
3202:  * Returns:     ( int )         1 if ellipse drawn okay,
3203:  *                              or 0 for any error.
3204:  * --------------------------------------------------------------------------
3205:  * Notes:     o row0==row1 or col0==col1 are errors
3206:  *            o using ellipse equation x^2/a^2 + y^2/b^2 = 1
3207:  * ======================================================================= */
3208: /* --- entry point --- */
3209: FUNCSCOPE int circle_raster ( raster *rp, int row0, int col0,
3210:                               int row1, int col1, int thickness, char *quads )
3211: {
3212: /* -------------------------------------------------------------------------
3213: Allocations and Declarations
3214: -------------------------------------------------------------------------- */
3215: /* --- lower-left and upper-right bounding points (in our coords) --- */
3216: int     lorow = min2(row0,row1),        /* lower bounding row (top of box) */
3217:         locol = min2(col0,col1),        /* lower bounding col (left of box)*/
3218:         hirow = max2(row0,row1),        /* upper bounding row (bot of box) */
3219:         hicol = max2(col0,col1);        /* upper bounding col (right of box)*/
3220: /* --- a and b ellipse params --- */
3221: int     width = hicol-locol+1,          /* width of bounding box */
3222:         height= hirow-lorow+1,          /* height of bounding box */
3223:         islandscape = (width>=height? 1:0); /*true if ellipse lying on side*/
3224: double  a = ((double)width)/2.0,        /* x=a when y=0 */
3225:         b = ((double)height)/2.0,       /* y=b when x=0 */
3226:         abmajor = (islandscape? a : b), /* max2(a,b) */
3227:         abminor = (islandscape? b : a), /* min2(a,b) */
3228:         abmajor2 = abmajor*abmajor,     /* abmajor^2 */
3229:         abminor2 = abminor*abminor;     /* abminor^2 */
3230: /* --- other stuff --- */
3231: int     imajor=0, nmajor=max2(width,height), /*index, #pixels on major axis*/
3232:         iminor=0, nminor=min2(width,height); /* solved index on minor axis */
3233: int     irow, icol,                     /* raster indexes at circumference */
3234:         rsign=1, csign=1;               /* row,col signs, both +1 in quad 1*/
3235: double  midrow= ((double)(row0+row1))/2.0, /* center row */
3236:         midcol= ((double)(col0+col1))/2.0; /* center col */
3237: double  xy, xy2,                        /* major axis ellipse coord */
3238:         yx2, yx;                        /* solved minor ellipse coord */
3239: int     isokay = 1;                     /* true if no pixels out-of-bounds */
3240: char    *qptr=NULL, *allquads="1234";   /* quadrants if input quads==NULL */
3241: int     /*circle_recurse(),*/ isrecurse=1; /*true to draw ellipse recursively*/
3242: /* -------------------------------------------------------------------------
3243: pixel-by-pixel along positive major axis, quit when it goes negative
3244: -------------------------------------------------------------------------- */
3245: if ( quads == NULL ) quads = allquads;  /* draw all quads, or only user's */
3246: if ( msgfp!=NULL && msglevel>=39 )      /* debugging */
3247:   fprintf(msgfp,"circle_raster> width,height;quads=%d,%d,%s\n",
3248:   width,height,quads);
3249: if ( nmajor < 1 ) isokay = 0;           /* problem with input args */
3250: else
3251:  {
3252:  if ( isrecurse )                       /* use recursive algorithm */
3253:   {
3254:   for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */
3255:    {
3256:    double theta0=0.0, theta1=0.0;       /* set thetas based on quadrant */
3257:    switch ( *qptr )                     /* check for quadrant 1,2,3,4 */
3258:     { default:                          /* unrecognized, assume quadrant 1 */
3259:       case '1': theta0=  0.0; theta1= 90.0; break;   /* first quadrant */
3260:       case '2': theta0= 90.0; theta1=180.0; break;   /* second quadrant */
3261:       case '3': theta0=180.0; theta1=270.0; break;   /* third quadrant */
3262:       case '4': theta0=270.0; theta1=360.0; break; } /* fourth quadrant */
3263:    circle_recurse(rp,row0,col0,row1,col1,thickness,theta0,theta1);
3264:    } /* --- end-of-for(qptr) --- */
3265:   return ( 1 );
3266:   } /* --- end-of-if(isrecurse) --- */
3267:  for ( imajor=(nmajor+1)/2; ; imajor-- )
3268:   {
3269:   /* --- xy is coord along major axis, yx is "solved" along minor axis --- */
3270:   xy  = ((double)imajor);               /* xy = abmajor ... 0 */
3271:   if ( xy < 0.0 ) break;                /* negative side symmetrical */
3272:   yx2 = abminor2*(1.0 - xy*xy/abmajor2); /* "solve" ellipse equation */
3273:   yx  = (yx2>0.0? sqrt(yx2) : 0.0);     /* take sqrt if possible */
3274:   iminor = iround(yx);                  /* nearest integer */
3275:   /* --- set pixels for each requested quadrant --- */
3276:   for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */
3277:    {
3278:    rsign = (-1);  csign = 1;            /* init row,col in user quadrant 1 */
3279:    switch ( *qptr )                     /* check for quadrant 1,2,3,4 */
3280:     { default: break;                   /* unrecognized, assume quadrant 1 */
3281:       case '4': rsign = 1; break;       /* row,col both pos in quadrant 4 */
3282:       case '3': rsign = 1;              /* row pos, col neg in quadrant 3 */
3283:       case '2': csign = (-1); break; }  /* row,col both neg in quadrant 2 */
3284:    irow = iround(midrow + (double)rsign*(islandscape?yx:xy));
3285:    irow = min2(hirow,max2(lorow,irow)); /* keep irow in bounds */
3286:    icol = iround(midcol + (double)csign*(islandscape?xy:yx));
3287:    icol = min2(hicol,max2(locol,icol)); /* keep icol in bounds */
3288:    if ( msgfp!=NULL && msglevel>=49 )   /* debugging */
3289:      fprintf(msgfp,"\t...imajor=%d; iminor,quad,irow,icol=%d,%c,%d,%d\n",
3290:      imajor,iminor,*qptr,irow,icol);
3291:    if ( irow<0 || irow>=rp->height      /* row outside raster */
3292:    ||   icol<0 || icol>=rp->width )     /* col outside raster */
3293:       { isokay = 0;                     /* signal out-of-bounds pixel */
3294:         continue; }                     /* but still try remaining points */
3295:    setpixel(rp,irow,icol,255);          /* set pixel at irow,icol */
3296:    } /* --- end-of-for(qptr) --- */
3297:   } /* --- end-of-for(imajor) --- */
3298:  /* ------------------------------------------------------------------------
3299:  now do it _again_ along minor axis to avoid "gaps"
3300:  ------------------------------------------------------------------------- */
3301:  if ( 1 && iminor>0 )
3302:   for ( iminor=(nminor+1)/2; ; iminor-- )
3303:    {
3304:    /* --- yx is coord along minor axis, xy is "solved" along major axis --- */
3305:    yx  = ((double)iminor);              /* yx = abminor ... 0 */
3306:    if ( yx < 0.0 ) break;               /* negative side symmetrical */
3307:    xy2 = abmajor2*(1.0 - yx*yx/abminor2); /* "solve" ellipse equation */
3308:    xy  = (xy2>0.0? sqrt(xy2) : 0.0);    /* take sqrt if possible */
3309:    imajor = iround(xy);                 /* nearest integer */
3310:    /* --- set pixels for each requested quadrant --- */
3311:    for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */
3312:     {
3313:     rsign = (-1);  csign = 1;           /* init row,col in user quadrant 1 */
3314:     switch ( *qptr )                    /* check for quadrant 1,2,3,4 */
3315:      { default: break;                  /* unrecognized, assume quadrant 1 */
3316:        case '4': rsign = 1; break;      /* row,col both pos in quadrant 4 */
3317:        case '3': rsign = 1;             /* row pos, col neg in quadrant 3 */
3318:        case '2': csign = (-1); break; } /* row,col both neg in quadrant 2 */
3319:     irow = iround(midrow + (double)rsign*(islandscape?yx:xy));
3320:     irow = min2(hirow,max2(lorow,irow)); /* keep irow in bounds */
3321:     icol = iround(midcol + (double)csign*(islandscape?xy:yx));
3322:     icol = min2(hicol,max2(locol,icol)); /* keep icol in bounds */
3323:     if ( msgfp!=NULL && msglevel>=49 )  /* debugging */
3324:      fprintf(msgfp,"\t...iminor=%d; imajor,quad,irow,icol=%d,%c,%d,%d\n",
3325:      iminor,imajor,*qptr,irow,icol);
3326:     if ( irow<0 || irow>=rp->height     /* row outside raster */
3327:     ||   icol<0 || icol>=rp->width )    /* col outside raster */
3328:       { isokay = 0;                     /* signal out-of-bounds pixel */
3329:         continue; }                     /* but still try remaining points */
3330:     setpixel(rp,irow,icol,255);         /* set pixel at irow,icol */
3331:     } /* --- end-of-for(qptr) --- */
3332:    } /* --- end-of-for(iminor) --- */
3333:  } /* --- end-of-if/else(nmajor<1) --- */
3334: return ( isokay );
3335: } /* --- end-of-function circle_raster() --- */
3336: 
3337: 
3338: /* ==========================================================================
3339:  * Function:    circle_recurse ( rp,  row0, col0,  row1, col1,
3340:  *              thickness, theta0, theta1 )
3341:  * Purpose:     Recursively draws arc theta0<=theta<=theta1 of the ellipse
3342:  *              in box determined by diagonally opposite corner points
3343:  *              (row0,col0) and (row1,col1), of thickness pixels in raster rp.
3344:  * --------------------------------------------------------------------------
3345:  * Arguments:   rp (I)          raster *  to raster in which an ellipse
3346:  *                              will be drawn
3347:  *              row0 (I)        int containing 1st corner row bounding ellipse
3348:  *                              (0 is topmost)
3349:  *              col0 (I)        int containing 1st corner col bounding ellipse
3350:  *                              (0 is leftmost)
3351:  *              row1 (I)        int containing 2nd corner row bounding ellipse
3352:  *                              (rp->height-1 is bottom-most)
3353:  *              col1 (I)        int containing 2nd corner col bounding ellipse
3354:  *                              (rp->width-1 is rightmost)
3355:  *              thickness (I)   int containing number of pixels/bits
3356:  *                              thick the ellipse arc line will be
3357:  *              theta0 (I)      double containing first angle -360 -> +360
3358:  *              theta1 (I)      double containing second angle -360 -> +360
3359:  *                              0=x-axis, positive moving counterclockwise
3360:  * --------------------------------------------------------------------------
3361:  * Returns:     ( int )         1 if ellipse drawn okay,
3362:  *                              or 0 for any error.
3363:  * --------------------------------------------------------------------------
3364:  * Notes:     o row0==row1 or col0==col1 are errors
3365:  *            o using ellipse equation x^2/a^2 + y^2/b^2 = 1
3366:  *              Then, with x=r*cos(theta), y=r*sin(theta), ellipse
3367:  *              equation is r = ab/sqrt(a^2*sin^2(theta)+b^2*cos^2(theta))
3368:  * ======================================================================= */
3369: /* --- entry point --- */
3370: FUNCSCOPE int circle_recurse ( raster *rp, int row0, int col0, int row1,
3371:                      int col1, int thickness, double theta0, double theta1 )
3372: {
3373: /* -------------------------------------------------------------------------
3374: Allocations and Declarations
3375: -------------------------------------------------------------------------- */
3376: /* --- lower-left and upper-right bounding points (in our coords) --- */
3377: int     lorow = min2(row0,row1),        /* lower bounding row (top of box) */
3378:         locol = min2(col0,col1),        /* lower bounding col (left of box)*/
3379:         hirow = max2(row0,row1),        /* upper bounding row (bot of box) */
3380:         hicol = max2(col0,col1);        /* upper bounding col (right of box)*/
3381: /* --- a and b ellipse params --- */
3382: int     width = hicol-locol+1,          /* width of bounding box */
3383:         height= hirow-lorow+1;          /* height of bounding box */
3384: double  a = ((double)width)/2.0,        /* col x=a when row y=0 */
3385:         b = ((double)height)/2.0,       /* row y=b when col x=0 */
3386:         ab=a*b, a2=a*a, b2=b*b;         /* product and squares */
3387: /* --- arc parameters --- */
3388: double  rads = 0.017453292,             /* radians per degree = 1/57.29578 */
3389:         lotheta = rads*dmod(min2(theta0,theta1),360), /* smaller angle */
3390:         hitheta = rads*dmod(max2(theta0,theta1),360), /* larger angle */
3391:         locos=cos(lotheta), losin=sin(lotheta), /* trigs for lotheta */
3392:         hicos=cos(hitheta), hisin=sin(hitheta), /* trigs for hitheta */
3393:         rlo = ab/sqrt(b2*locos*locos+a2*losin*losin), /* r for lotheta */
3394:         rhi = ab/sqrt(b2*hicos*hicos+a2*hisin*hisin), /* r for hitheta */
3395:         xlo=rlo*locos, ylo=rlo*losin,   /*col,row pixel coords for lotheta*/
3396:         xhi=rhi*hicos, yhi=rhi*hisin,   /*col,row pixel coords for hitheta*/
3397:         xdelta=fabs(xhi-xlo), ydelta=fabs(yhi-ylo), /* col,row deltas */
3398:         tolerance = 0.5;                /* convergence tolerance */
3399: /* -------------------------------------------------------------------------
3400: recurse if either delta > tolerance
3401: -------------------------------------------------------------------------- */
3402: if ( ydelta > tolerance                 /* row hasn't converged */
3403: ||   xdelta > tolerance )               /* col hasn't converged */
3404:   { double midtheta = 0.5*(theta0+theta1); /* mid angle for arc */
3405:     circle_recurse(rp,row0,col0,row1,col1,thickness,theta0,midtheta);  /*lo*/
3406:     circle_recurse(rp,row0,col0,row1,col1,thickness,midtheta,theta1); }/*hi*/
3407: /* -------------------------------------------------------------------------
3408: draw converged point
3409: -------------------------------------------------------------------------- */
3410: else
3411:   { double xcol=0.5*(xlo+xhi), yrow=0.5*(ylo+yhi),    /* relative to center*/
3412:         centerrow = 0.5*((double)(lorow+hirow)),      /* ellipse y-center */
3413:         centercol = 0.5*((double)(locol+hicol)),      /* ellipse x-center */
3414:         midrow=centerrow-yrow, midcol=centercol+xcol; /* pixel coords */
3415:     setpixel(rp,iround(midrow),iround(midcol),255); } /* set midrow,midcol */
3416: return ( 1 );
3417: } /* --- end-of-function circle_recurse() --- */
3418: 
3419: 
3420: /* ==========================================================================
3421:  * Function:    bezier_raster ( rp, r0,c0, r1,c1, rt,ct )
3422:  * Purpose:     Recursively draw bezier from r0,c0 to r1,c1
3423:  *              (with tangent point rt,ct) in existing raster rp.
3424:  * --------------------------------------------------------------------------
3425:  * Arguments:   rp (I)          raster *  to raster in which a line
3426:  *                              will be drawn
3427:  *              r0 (I)          double containing row at which
3428:  *                              bezier will start (0 is topmost)
3429:  *              c0 (I)          double containing col at which
3430:  *                              bezier will start (0 is leftmost)
3431:  *              r1 (I)          double containing row at which
3432:  *                              bezier will end (rp->height-1 is bottom-most)
3433:  *              c1 (I)          double containing col at which
3434:  *                              bezier will end (rp->width-1 is rightmost)
3435:  *              rt (I)          double containing row for tangent point
3436:  *              ct (I)          double containing col for tangent point
3437:  * --------------------------------------------------------------------------
3438:  * Returns:     ( int )         1 if line drawn okay,
3439:  *                              or 0 for any error.
3440:  * --------------------------------------------------------------------------
3441:  * Notes:     o Recurses, drawing left- and right-halves of bezier curve
3442:  *              until a point is found
3443:  * ======================================================================= */
3444: /* --- entry point --- */
3445: FUNCSCOPE int bezier_raster ( raster *rp, double r0, double c0,
3446:                               double r1, double c1, double rt, double ct )
3447: {
3448: /* -------------------------------------------------------------------------
3449: Allocations and Declarations
3450: -------------------------------------------------------------------------- */
3451: double  delrow = fabs(r1-r0),           /* 0 if same row */
3452:         delcol = fabs(c1-c0),           /* 0 if same col */
3453:         tolerance = 0.5;                /* draw curve when it goes to point*/
3454: double  midrow = 0.5*(r0+r1),           /* midpoint row */
3455:         midcol = 0.5*(c0+c1);           /* midpoint col */
3456: int     irow=0, icol=0;                 /* point to be drawn */
3457: int     status = 1;                     /* return status */
3458: /* -------------------------------------------------------------------------
3459: recurse if either delta > tolerance
3460: -------------------------------------------------------------------------- */
3461: if ( delrow > tolerance                 /* row hasn't converged */
3462: ||   delcol > tolerance )               /* col hasn't converged */
3463:   { bezier_raster(rp, r0,c0,            /* left half */
3464:         0.5*(rt+midrow), 0.5*(ct+midcol),
3465:         0.5*(r0+rt), 0.5*(c0+ct) );
3466:     bezier_raster(rp, 0.5*(rt+midrow), 0.5*(ct+midcol), /* right half */
3467:         r1,c1,
3468:         0.5*(r1+rt), 0.5*(c1+ct) );
3469:     return ( 1 ); }
3470: /* -------------------------------------------------------------------------
3471: draw converged point
3472: -------------------------------------------------------------------------- */
3473: /* --- get integer point --- */
3474: irow = iround(midrow);                  /* row pixel coord */
3475: icol = iround(midcol);                  /* col pixel coord */
3476: /* --- bounds check --- */
3477: if ( irow>=0 && irow<rp->height         /* row in bounds */
3478: &&   icol>=0 && icol<rp->width )        /* col in bounds */
3479:         setpixel(rp,irow,icol,255);     /* so set pixel at irow,icol*/
3480: else    status = 0;                     /* bad status if out-of-bounds */
3481: return ( status );
3482: } /* --- end-of-function bezier_raster() --- */
3483: 
3484: 
3485: /* ==========================================================================
3486:  * Function:    border_raster ( rp, ntop, nbot, isline, isfree )
3487:  * Purpose:     Allocate a new raster containing a copy of input rp,
3488:  *              along with ntop extra rows at top and nbot at bottom,
3489:  *              and whose width is either adjusted correspondingly,
3490:  *              or is automatically enlarged to a multiple of 8
3491:  *              with original bitmap centered
3492:  * --------------------------------------------------------------------------
3493:  * Arguments:   rp (I)          raster *  to raster on which a border
3494:  *                              is to be placed
3495:  *              ntop (I)        int containing number extra rows at top.
3496:  *                              if negative, abs(ntop) used, and same
3497:  *                              number of extra cols added at left.
3498:  *              nbot (I)        int containing number extra rows at bottom.
3499:  *                              if negative, abs(nbot) used, and same
3500:  *                              number of extra cols added at right.
3501:  *              isline (I)      int containing 0 to leave border pixels clear
3502:  *                              or >0 to draw a line around border of
3503:  *                              thickness isline pixels.  See Notes below.
3504:  *              isfree (I)      int containing true to free rp before return
3505:  * --------------------------------------------------------------------------
3506:  * Returns:     ( raster * )    ptr to bordered raster,
3507:  *                              or NULL for any error.
3508:  * --------------------------------------------------------------------------
3509:  * Notes:     o The isline arg also controls which sides border lines
3510:  *              are drawn for.  To do this, isline is interpreted as
3511:  *              thickness + 100*sides  so that, e.g., passing isline=601
3512:  *              is interpreted as sides=6 and thickness=1.  And
3513:  *              sides is further interpreted as 1=left side, 2=top,
3514:  *              4=right and 8=bottom.  For example, sides=6 where 6=2+4
3515:  *              draws the top and right borders only.  15 draws all four
3516:  *              sides.  And 0 (no sides value embedded in isline)
3517:  *              draws all four sides, too.
3518:  * ======================================================================= */
3519: /* --- entry point --- */
3520: FUNCSCOPE raster *border_raster ( raster *rp, int ntop, int nbot,
3521:                                   int isline, int isfree )
3522: {
3523: /* -------------------------------------------------------------------------
3524: Allocations and Declarations
3525: -------------------------------------------------------------------------- */
3526: raster  /**new_raster(),*/ *bp=(raster *)NULL;  /*raster back to caller*/
3527: /*int   rastput();*/            /* overlay rp in new bordered raster */
3528: int     width  = (rp==NULL?0:rp->width),  /* width of raster */
3529:         height = (rp==NULL?0:rp->height), /* height of raster */
3530:         istopneg=0, isbotneg=0,         /* true if ntop or nbot negative */
3531:         leftmargin = 0;         /* adjust width to whole number of bytes */
3532: int     left=1, top=1, right=1, bot=1;  /* frame sides to draw */
3533: int     delete_raster();                /* free input rp if isfree is true */
3534: /* -------------------------------------------------------------------------
3535: Initialization
3536: -------------------------------------------------------------------------- */
3537: if ( rp == NULL ) goto end_of_job;      /* no input raster provided */
3538: if ( isstring || (1 && rp->height==1) ) /* explicit string signal or infer */
3539:   { bp=rp; goto end_of_job; }           /* return ascii string unchanged */
3540: /* --- check for negative args --- */
3541: if ( ntop < 0 ) { ntop = -ntop; istopneg=1; } /*flip positive and set flag*/
3542: if ( nbot < 0 ) { nbot = -nbot; isbotneg=1; } /*flip positive and set flag*/
3543: /* --- adjust height for ntop and nbot margins --- */
3544: height += (ntop+nbot);                  /* adjust height for margins */
3545: /* --- adjust width for left and right margins --- */
3546: if ( istopneg || isbotneg )     /*caller wants nleft=ntop and/or nright=nbot*/
3547:   { /* --- adjust width (and leftmargin) as requested by caller -- */
3548:     if ( istopneg ) { width += ntop; leftmargin = ntop; }
3549:     if ( isbotneg )   width += nbot;  }
3550: else
3551:   { /* --- or adjust width (and leftmargin) to whole number of bytes --- */
3552:     leftmargin = (width%8==0? 0 : 8-(width%8)); /*makes width multiple of 8*/
3553:     width += leftmargin;                /* width now multiple of 8 */
3554:     leftmargin /= 2; }                  /* center original raster */
3555: /* --- check which sides to draw --- */
3556: if ( isline > 100 ) {                   /* sides arg embedded in isline */
3557:   int iside=0, sides=isline/100;        /* index, sides=1-15 from 101-1599 */
3558:   isline -= 100*sides;                  /* and remove sides from isline */
3559:   for ( iside=1; iside<=4; iside++ ) {  /* check left, top, right, bot */
3560:     int shift = sides/2;                /* shift sides left one bit */
3561:     if ( sides == 2*shift )             /* low-order bit is >>not<< set */
3562:       switch ( iside ) {                /* don't draw corresponding side */
3563:         default: break;                 /* internal error */
3564:         case 1: left = 0; break;        /* 1 = left side */
3565:         case 2: top  = 0; break;        /* 2 = top side */
3566:         case 3: right= 0; break;        /* 4 = tight side */
3567:         case 4: bot  = 0; break; }      /* 8 = bottom side */
3568:     sides = shift;                      /* ready for next side */
3569:     } /* --- end-of-for(iside) --- */
3570:   } /* --- end-of-if(isline>100) --- */
3571: /* -------------------------------------------------------------------------
3572: allocate bordered raster, and embed rp within it
3573: -------------------------------------------------------------------------- */
3574: /* --- allocate bordered raster --- */
3575: if ( (bp=new_raster(width,height,rp->pixsz))  /*allocate bordered raster*/
3576: ==   (raster *)NULL ) goto end_of_job;  /* and quit if failed */
3577: /* --- embed rp in it --- */
3578: rastput(bp,rp,ntop,leftmargin,1);       /* rp embedded in bp */
3579: /* -------------------------------------------------------------------------
3580: draw border if requested
3581: -------------------------------------------------------------------------- */
3582: if ( isline )
3583:  { int  irow, icol, nthick=isline;      /*height,width index, line thickness*/
3584:   /* --- draw left- and right-borders --- */
3585:   for ( irow=0; irow<height; irow++ )   /* for each row of bp */
3586:     for ( icol=0; icol<nthick; icol++ ) /* and each pixel of thickness */
3587:       { if(left){setpixel(bp,irow,icol,255);}           /* left border */
3588:         if(right){setpixel(bp,irow,width-1-icol,255);} } /* right border */
3589:   /* --- draw top- and bottom-borders --- */
3590:   for ( icol=0; icol<width; icol++ )    /* for each col of bp */
3591:     for ( irow=0; irow<nthick; irow++ ) /* and each pixel of thickness */
3592:       { if(top){setpixel(bp,irow,icol,255);}            /* top border */
3593:         if(bot){setpixel(bp,height-1-irow,icol,255);} } /* bottom border */
3594:  } /* --- end-of-if(isline) --- */
3595: /* -------------------------------------------------------------------------
3596: free rp if no longer needed
3597: -------------------------------------------------------------------------- */
3598: if ( isfree )                                   /*caller no longer needs rp*/
3599:   delete_raster(rp);                            /* so free it for him */
3600: /* -------------------------------------------------------------------------
3601: Back to caller with bordered raster (or null for any error)
3602: -------------------------------------------------------------------------- */
3603: end_of_job:
3604:   return ( bp );                        /* back with bordered or null ptr */
3605: } /* --- end-of-function border_raster() --- */
3606: 
3607: 
3608: /* ==========================================================================
3609:  * Function:    backspace_raster ( rp, nback, pback, minspace, isfree )
3610:  * Purpose:     Allocate a new raster containing a copy of input rp,
3611:  *              but with trailing nback columns removed.
3612:  *              If minspace>=0 then (at least) that many columns
3613:  *              of whitespace will be left in place, regardless of nback.
3614:  *              If minspace<0 then existing black pixels will be deleted
3615:  *              as required.
3616:  * --------------------------------------------------------------------------
3617:  * Arguments:   rp (I)          raster *  to raster on which a border
3618:  *                              is to be placed
3619:  *              nback (I)       int containing number of columns to
3620:  *                              backspace (>=0)
3621:  *              pback (O)       ptr to int returning #pixels actually
3622:  *                              backspaced (or NULL to not use)
3623:  *              minspace (I)    int containing number of columns
3624:  *                              of whitespace to be left in place
3625:  *              isfree (I)      int containing true to free rp before return
3626:  * --------------------------------------------------------------------------
3627:  * Returns:     ( raster * )    ptr to backspaced raster,
3628:  *                              or NULL for any error.
3629:  * --------------------------------------------------------------------------
3630:  * Notes:     o For \! negative space, for \hspace{-10}, etc.
3631:  * ======================================================================= */
3632: /* --- entry point --- */
3633: FUNCSCOPE raster *backspace_raster ( raster *rp, int nback, int *pback,
3634:                                      int minspace, int isfree )
3635: {
3636: /* -------------------------------------------------------------------------
3637: Allocations and Declarations
3638: -------------------------------------------------------------------------- */
3639: raster  /**new_raster(),*/ *bp=(raster *)NULL;  /*raster returned to caller*/
3640: /*int   delete_raster();*/              /* free input rp if isfree is true */
3641: int     width  = (rp==NULL?0:rp->width),  /* original width of raster */
3642:         height = (rp==NULL?0:rp->height), /* height of raster */
3643:         mback = nback,                  /* nback adjusted for minspace */
3644:         newwidth = width,               /* adjusted width after backspace */
3645:         icol=0, irow=0;                 /* col,row index */
3646: if ( rp == NULL ) goto end_of_job;      /* no input given */
3647: /* -------------------------------------------------------------------------
3648: locate rightmost column of rp containing ink, and determine backspaced width
3649: -------------------------------------------------------------------------- */
3650: /* --- locate rightmost column of rp containing ink --- */
3651: if ( minspace >= 0 )                    /* only needed if given minspace */
3652:  for ( icol=width-1; icol>=0; icol-- )  /* find first non-empty col in row */
3653:   for ( irow=0; irow<height; irow++ )   /* for each row inside rp */
3654:    if ( getpixel(rp,irow,icol) != 0 ) { /* found a set pixel */
3655:      int whitecols = (width-1) - icol;  /* #cols containing white space */
3656:      mback = min2(nback,max2(0,whitecols-minspace)); /*leave minspace cols*/
3657:      goto gotright; }                   /* no need to look further */
3658: /* --- determine width of new backspaced raster --- */
3659: gotright:                               /* found col with ink (or rp empty)*/
3660:   if ( mback > width ) mback = width;   /* can't backspace before beginning*/
3661:   newwidth = max2(1,width-mback);       /* #cols in backspaced raster */
3662:   if ( pback != NULL ) *pback = width-newwidth; /* caller wants #pixels */
3663: /* -------------------------------------------------------------------------
3664: allocate new raster and fill it with leftmost cols of rp
3665: -------------------------------------------------------------------------- */
3666: /* --- allocate backspaced raster --- */
3667: if ( (bp=new_raster(newwidth,height,rp->pixsz)) /*allocate backspaced raster*/
3668: ==   (raster *)NULL ) goto end_of_job;  /* and quit if failed */
3669: /* --- fill new raster --- */
3670: if ( 1 || width-nback > 0 )             /* don't fill 1-pixel wide empty bp*/
3671:  for ( icol=0; icol<newwidth; icol++ )  /* find first non-empty col in row */
3672:   for ( irow=0; irow<height; irow++ )   /* for each row inside rp */
3673:     { int value = getpixel(rp,irow,icol); /* original pixel at irow,icol */
3674:       setpixel(bp,irow,icol,value); }   /* saved in backspaced raster */
3675: /* -------------------------------------------------------------------------
3676: Back to caller with backspaced raster (or null for any error)
3677: -------------------------------------------------------------------------- */
3678: end_of_job:
3679:   if ( msgfp!=NULL && msglevel>=999 ) { fprintf(msgfp, /* diagnostics */
3680:    "backspace_raster> nback=%d,minspace=%d,mback=%d, width:old=%d,new=%d\n",
3681:    nback,minspace,mback,width,newwidth); fflush(msgfp); }
3682:   if ( isfree && bp!=NULL ) delete_raster(rp); /* free original raster */
3683:   return ( bp );                        /* back with backspaced or null ptr*/
3684: } /* --- end-of-function backspace_raster() --- */
3685: 
3686: 
3687: /* ==========================================================================
3688:  * Function:    type_raster ( rp, fp )
3689:  * Purpose:     Emit an ascii dump representing rp, on fp.
3690:  * --------------------------------------------------------------------------
3691:  * Arguments:   rp (I)          ptr to raster struct for which an
3692:  *                              ascii dump is to be constructed.
3693:  *              fp (I)          File ptr to output device (defaults to
3694:  *                              stdout if passed as NULL).
3695:  * --------------------------------------------------------------------------
3696:  * Returns:     ( int )         1 if completed successfully,
3697:  *                              or 0 otherwise (for any error).
3698:  * --------------------------------------------------------------------------
3699:  * Notes:
3700:  * ======================================================================= */
3701: /* --- entry point --- */
3702: FUNCSCOPE int type_raster ( raster *rp, FILE *fp )
3703: {
3704: /* -------------------------------------------------------------------------
3705: Allocations and Declarations
3706: -------------------------------------------------------------------------- */
3707: static  int display_width = 72;         /* max columns for display */
3708: static  char display_chars[16] =        /* display chars for bytemap */
3709:         { ' ','1','2','3','4','5','6','7','8','9','A','B','C','D','E','*' };
3710: char    scanline[133];                  /* ascii image for one scan line */
3711: int     scan_width;                     /* #chars in scan (<=display_width)*/
3712: int     irow, locol,hicol=(-1);         /* height index, width indexes */
3713: raster  /**gftobitmap(),*/ *bitmaprp=rp; /* convert .gf to bitmap if needed */
3714: /*int   delete_raster();*/              /*free bitmap converted for display*/
3715: /* --------------------------------------------------------------------------
3716: initialization
3717: -------------------------------------------------------------------------- */
3718: /* --- redirect null fp --- */
3719: if ( fp == (FILE *)NULL ) fp = stdout;  /* default fp to stdout if null */
3720: if ( msglevel >= 999 ) { fprintf(fp,    /* debugging diagnostics */
3721:   "type_raster> width=%d height=%d ...\n",
3722:   rp->width,rp->height); fflush(fp); }
3723: /* --- check for ascii string --- */
3724: if ( isstring                           /* pixmap has string, not raster */
3725: ||   (0 && rp->height==1) )             /* infer input rp is a string */
3726:  {
3727:  char *string = (char *)(rp->pixmap);   /*interpret pixmap as ascii string*/
3728:  int width = strlen(string);            /* #chars in ascii string */
3729:  while ( width > display_width-2 )      /* too big for one line */
3730:   { fprintf(fp,"\"%.*s\"\n",display_width-2,string); /*display leading chars*/
3731:     string += (display_width-2);        /* bump string past displayed chars*/
3732:     width -= (display_width-2); }       /* decrement remaining width */
3733:  fprintf(fp,"\"%.*s\"\n",width,string); /* display trailing chars */
3734:  return ( 1 );
3735:  } /* --- end-of-if(isstring) --- */
3736: /* --------------------------------------------------------------------------
3737: display ascii dump of bitmap image (in segments if display_width < rp->width)
3738: -------------------------------------------------------------------------- */
3739: if ( rp->format == 2                    /* input is .gf-formatted */
3740: ||   rp->format == 3 )
3741:   bitmaprp = gftobitmap(rp);            /* so convert it for display */
3742: if ( bitmaprp != NULL )                 /* if we have image for display */
3743:  while ( (locol=hicol+1) < rp->width )  /*start where prev segment left off*/
3744:   {
3745:   /* --- set hicol for this pass (locol set above) --- */
3746:   hicol += display_width;               /* show as much as display allows */
3747:   if (hicol >= rp->width) hicol = rp->width - 1; /*but not more than raster*/
3748:   scan_width = hicol-locol+1;           /* #chars in this scan */
3749:   if ( locol > 0 ) fprintf(fp,"----------\n"); /*separator between segments*/
3750:   /* ------------------------------------------------------------------------
3751:   display all scan lines for this local...hicol segment range
3752:   ------------------------------------------------------------------------ */
3753:   for ( irow=0; irow<rp->height; irow++ )  /* all scan lines for col range */
3754:     {
3755:     /* --- allocations and declarations --- */
3756:     int ipix,                           /* pixmap[] index for this scan */
3757:         lopix = irow*rp->width + locol; /*first pixmap[] pixel in this scan*/
3758:     /* --- set chars in scanline[] based on pixels in rp->pixmap[] --- */
3759:     for ( ipix=0; ipix<scan_width; ipix++ ) /* set each char */
3760:       if ( bitmaprp->pixsz == 1 )       /*' '=0 or '*'=1 to display bitmap*/
3761:         scanline[ipix]=(getlongbit(bitmaprp->pixmap,lopix+ipix)==1? '*':'.');
3762:       else                              /* should have a bytemap */
3763:        if ( bitmaprp->pixsz == 8 )      /* double-check pixsz for bytemap */
3764:         { int pixval = (int)((bitmaprp->pixmap)[lopix+ipix]), /*byte value*/
3765:           ichar = min2(15,pixval/16);   /* index for ' ', '1'...'e', '*' */
3766:           scanline[ipix] = display_chars[ichar]; } /*set ' ' for 0-15, etc*/
3767:     /* --- display completed scan line --- */
3768:     fprintf(fp,"%.*s\n",scan_width,scanline);
3769:     } /* --- end-of-for(irow) --- */
3770:   } /* --- end-of-while(hicol<rp->width) --- */
3771: /* -------------------------------------------------------------------------
3772: Back to caller with 1=okay, 0=failed.
3773: -------------------------------------------------------------------------- */
3774: if ( rp->format == 2                    /* input was .gf-format */
3775: ||   rp->format == 3 )
3776:   if ( bitmaprp != NULL )               /* and we converted it for display */
3777:     delete_raster(bitmaprp);            /* no longer needed, so free it */
3778: return ( 1 );
3779: } /* --- end-of-function type_raster() --- */
3780: 
3781: 
3782: /* ==========================================================================
3783:  * Function:    type_bytemap ( bp, grayscale, width, height, fp )
3784:  * Purpose:     Emit an ascii dump representing bp, on fp.
3785:  * --------------------------------------------------------------------------
3786:  * Arguments:   bp (I)          intbyte * to bytemap for which an
3787:  *                              ascii dump is to be constructed.
3788:  *              grayscale (I)   int containing #gray shades, 256 for 8-bit
3789:  *              width (I)       int containing #cols in bytemap
3790:  *              height (I)      int containing #rows in bytemap
3791:  *              fp (I)          File ptr to output device (defaults to
3792:  *                              stdout if passed as NULL).
3793:  * --------------------------------------------------------------------------
3794:  * Returns:     ( int )         1 if completed successfully,
3795:  *                              or 0 otherwise (for any error).
3796:  * --------------------------------------------------------------------------
3797:  * Notes:
3798:  * ======================================================================= */
3799: /* --- entry point --- */
3800: FUNCSCOPE int type_bytemap ( intbyte *bp, int grayscale,
3801:                              int width, int height, FILE *fp )
3802: {
3803: /* -------------------------------------------------------------------------
3804: Allocations and Declarations
3805: -------------------------------------------------------------------------- */
3806: static  int display_width = 72;         /* max columns for display */
3807: int     byte_width = 3,                 /* cols to display byte (ff+space) */
3808:         maxbyte = 0;                    /* if maxbyte<16, set byte_width=2 */
3809: int     white_byte = 0,                 /* show dots for white_byte's */
3810:         black_byte = grayscale-1;       /* show stars for black_byte's */
3811: char    scanline[133];                  /* ascii image for one scan line */
3812: int     scan_width,                     /* #chars in scan (<=display_width)*/
3813:         scan_cols;                      /* #cols in scan (hicol-locol+1) */
3814: int     ibyte,                          /* bp[] index */
3815:         irow, locol,hicol=(-1);         /* height index, width indexes */
3816: /* --------------------------------------------------------------------------
3817: initialization
3818: -------------------------------------------------------------------------- */
3819: /* --- check args --- */
3820: if ( bp == NULL ) goto end_of_job;      /* nothing to do */
3821: /* --- redirect null fp --- */
3822: if ( fp == (FILE *)NULL ) fp = stdout;  /* default fp to stdout if null */
3823: /* --- check for ascii string --- */
3824: if ( isstring )                         /* bp has ascii string, not raster */
3825:  { width = strlen((char *)bp);          /* #chars in ascii string */
3826:    height = 1; }                        /* default */
3827: /* --- see if we can get away with byte_width=1 --- */
3828: for ( ibyte=0; ibyte<width*height; ibyte++ )  /* check all bytes */
3829:   { int byteval = (int)bp[ibyte];       /* current byte value */
3830:     if ( byteval < black_byte )         /* if it's less than black_byte */
3831:       maxbyte = max2(maxbyte,byteval); } /* then find max non-black value */
3832: if ( maxbyte < 16 )                     /* bytevals will fit in one column */
3833:   byte_width = 1;                       /* so reset display byte_width */
3834: /* --------------------------------------------------------------------------
3835: display ascii dump of bitmap image (in segments if display_width < rp->width)
3836: -------------------------------------------------------------------------- */
3837: while ( (locol=hicol+1) < width )       /*start where prev segment left off*/
3838:   {
3839:   /* --- set hicol for this pass (locol set above) --- */
3840:   hicol += display_width/byte_width;    /* show as much as display allows */
3841:   if (hicol >= width) hicol = width - 1; /* but not more than bytemap */
3842:   scan_cols = hicol-locol+1;            /* #cols in this scan */
3843:   scan_width = byte_width*scan_cols;    /* #chars in this scan */
3844:   if ( locol>0 && !isstring ) fprintf(fp,"----------\n"); /* separator */
3845:   /* ------------------------------------------------------------------------
3846:   display all scan lines for this local...hicol segment range
3847:   ------------------------------------------------------------------------ */
3848:   for ( irow=0; irow<height; irow++ )   /* all scan lines for col range */
3849:     {
3850:     /* --- allocations and declarations --- */
3851:     int  lobyte = irow*width + locol;   /* first bp[] byte in this scan */
3852:     char scanbyte[32];                  /* sprintf() buffer for byte */
3853:     /* --- set chars in scanline[] based on bytes in bytemap bp[] --- */
3854:     memset(scanline,' ',scan_width);    /* blank out scanline */
3855:     for ( ibyte=0; ibyte<scan_cols; ibyte++ ) /* set chars for each col */
3856:       { int byteval = (int)bp[lobyte+ibyte];  /* value of current byte */
3857:         memset(scanbyte,'.',byte_width); /* dot-fill scanbyte */
3858:         if ( byteval == black_byte )    /* but if we have a black byte */
3859:           memset(scanbyte,'*',byte_width); /* star-fill scanbyte instead */
3860:         if ( byte_width > 1 )           /* don't blank out single char */
3861:           scanbyte[byte_width-1] = ' '; /* blank-fill rightmost character */
3862:         if ( byteval != white_byte      /* format bytes that are non-white */
3863:         &&   byteval != black_byte )    /* and that are non-black */
3864:           sprintf(scanbyte,"%*x ",max2(1,byte_width-1),byteval); /*hex-format*/
3865:         memcpy(scanline+ibyte*byte_width,scanbyte,byte_width); } /*in line*/
3866:     /* --- display completed scan line --- */
3867:     fprintf(fp,"%.*s\n",scan_width,scanline);
3868:     } /* --- end-of-for(irow) --- */
3869:   } /* --- end-of-while(hicol<width) --- */
3870: /* -------------------------------------------------------------------------
3871: Back to caller with 1=okay, 0=failed.
3872: -------------------------------------------------------------------------- */
3873: end_of_job: return ( 1 );
3874: } /* --- end-of-function type_bytemap() --- */
3875: 
3876: 
3877: /* ==========================================================================
3878:  * Function:    xbitmap_raster ( rp, fp )
3879:  * Purpose:     Emit a mime xbitmap representing rp, on fp.
3880:  * --------------------------------------------------------------------------
3881:  * Arguments:   rp (I)          ptr to raster struct for which a mime
3882:  *                              xbitmap is to be constructed.
3883:  *              fp (I)          File ptr to output device (defaults to
3884:  *                              stdout if passed as NULL).
3885:  * --------------------------------------------------------------------------
3886:  * Returns:     ( int )         1 if completed successfully,
3887:  *                              or 0 otherwise (for any error).
3888:  * --------------------------------------------------------------------------
3889:  * Notes:
3890:  * ======================================================================= */
3891: /* --- entry point --- */
3892: FUNCSCOPE int xbitmap_raster ( raster *rp, FILE *fp )
3893: {
3894: /* -------------------------------------------------------------------------
3895: Allocations and Declarations
3896: -------------------------------------------------------------------------- */
3897: char    *title = "image";               /* dummy title */
3898: /*int   hex_bitmap();*/                 /* dump bitmap as hex bytes */
3899: /* --------------------------------------------------------------------------
3900: emit text to display mime xbitmap representation of rp->bitmap image
3901: -------------------------------------------------------------------------- */
3902: /* --- first redirect null fp --- */
3903: if ( fp == (FILE *)NULL ) fp = stdout;  /* default fp to stdout if null */
3904: /* --- check for ascii string --- */
3905: if ( isstring )                         /* pixmap has string, not raster */
3906:  return ( 0 );                          /* can't handle ascii string */
3907: /* --- emit prologue strings and hex dump of bitmap for mime xbitmap --- */
3908: fprintf( fp, "Content-type: image/x-xbitmap\n\n" );
3909: fprintf( fp, "#define %s_width %d\n#define %s_height %d\n",
3910:         title,rp->width, title,rp->height );
3911: fprintf( fp, "static char %s_bits[] = {\n", title );
3912: hex_bitmap(rp,fp,0,0);                  /* emit hex dump of bitmap bytes */
3913: fprintf (fp,"};\n");                    /* ending with "};" for C array */
3914: /* -------------------------------------------------------------------------
3915: Back to caller with 1=okay, 0=failed.
3916: -------------------------------------------------------------------------- */
3917: return ( 1 );
3918: } /* --- end-of-function xbitmap_raster() --- */
3919: 
3920: 
3921: /* ==========================================================================
3922:  * Function:    type_pbmpgm ( rp, ptype, file )
3923:  * Purpose:     Write pbm or pgm image of rp to file
3924:  * --------------------------------------------------------------------------
3925:  * Arguments:   rp (I)          ptr to raster struct for which
3926:  *                              a pbm/pgm file is to be written.
3927:  *              ptype (I)       int containing 1 for pbm, 2 for pgm, or
3928:  *                              0 to determine ptype from values in rp
3929:  *              file (I)        ptr to null-terminated char string
3930:  *                              containing name of fuke to be written
3931:  *                              (see notes below).
3932:  * --------------------------------------------------------------------------
3933:  * Returns:     ( int )         total #bytes written,
3934:  *                              or 0 for any error.
3935:  * --------------------------------------------------------------------------
3936:  * Notes:     o (a) If file==NULL, output is written to stdout;
3937:  *              (b) if *file=='\000' then file is taken as the
3938:  *                  address of an output buffer to which output
3939:  *                  is written (and is followed by a terminating
3940:  *                  '\0' which is not counted in #bytes returned);
3941:  *              (c) otherwise file is the filename (opened and
3942:  *                  closed internally) to which output is written,
3943:  *                  except that any final .ext extension is replaced
3944:  *                  by .pbm or .pgm depending on ptype.
3945:  * ======================================================================= */
3946: /* --- entry point --- */
3947: FUNCSCOPE int type_pbmpgm ( raster *rp, int ptype, char *file )
3948: {
3949: /* -------------------------------------------------------------------------
3950: Allocations and Declarations
3951: -------------------------------------------------------------------------- */
3952: int     isokay=0, nbytes=0;     /* completion flag, total #bytes written */
3953: int     irow=0, jcol=0;         /*height(row), width(col) indexes in raster*/
3954: int     pixmin=9999, pixmax=(-9999), /* min, max pixel value in raster */
3955:         ngray = 0;              /* #gray scale values */
3956: FILE    /* *fopen(), */ *fp=NULL; /* pointer to output file (or NULL) */
3957: char    outline[1024], outfield[256], /* output line, field */
3958:         cr[16] = "\n\000";      /* cr at end-of-line */
3959: int     maxlinelen = 70;        /* maximum allowed line length */
3960: int     pixfrac=6;              /* use (pixmax-pixmin)/pixfrac as step */
3961: static  char
3962:         *suffix[] = { NULL, ".pbm", ".pgm" },   /* file.suffix[ptype] */
3963:         *magic[] = { NULL, "P1", "P2" },        /*identifying "magic number"*/
3964:         *mode[] = { NULL, "w", "w" };           /* fopen() mode[ptype] */
3965: /* -------------------------------------------------------------------------
3966: check input, determine grayscale,  and set up output file if necessary
3967: -------------------------------------------------------------------------- */
3968: /* --- check input args --- */
3969: if ( rp == NULL ) goto end_of_job;      /* no input raster provided */
3970: if ( ptype != 0 )                       /* we'll determine ptype below */
3971:  if ( ptype<1 || ptype>2 ) goto end_of_job; /*invalid output graphic format*/
3972: /* --- determine largest (and smallest) value in pixmap --- */
3973: for ( irow=0; irow<rp->height; irow++ ) /* for each row, top-to-bottom */
3974:  for ( jcol=0; jcol<rp->width; jcol++ ) /* for each col, left-to-right */
3975:   { int pixval = getpixel(rp,irow,jcol);  /* value of pixel at irow,jcol */
3976:     pixmin = min2(pixmin,pixval);       /* new minimum */
3977:     pixmax = max2(pixmax,pixval); }     /* new maximum */
3978: ngray = 1 + (pixmax-pixmin);            /* should be 2 for b/w bitmap */
3979: if ( ptype == 0 )                       /* caller wants us to set ptype */
3980:   ptype = (ngray>=3?2:1);               /* use grayscale if >2 shades */
3981: /* --- open output file if necessary --- */
3982: if ( file == NULL ) fp = stdout;        /*null ptr signals output to stdout*/
3983: else if ( *file != '\000' ) {           /* explicit filename provided, so...*/
3984:   char  fname[512], *pdot=NULL;         /* file.ext, ptr to last . in fname*/
3985:   strncpy(fname,file,255);              /* local copy of file name */
3986:   fname[255] = '\000';                  /* make sure it's null terminated */
3987:   if ( (pdot=strrchr(fname,'.')) == NULL ) /*no extension on original name*/
3988:     strcat(fname,suffix[ptype]);        /* so add extension */
3989:   else                                  /* we already have an extension */
3990:     strcpy(pdot,suffix[ptype]);         /* so replace original extension */
3991:   if ( (fp = fopen(fname,mode[ptype]))  /* open output file */
3992:   ==   (FILE *)NULL ) goto end_of_job;  /* quit if failed to open */
3993:   } /* --- ens-of-if(*file!='\0') --- */
3994: /* -------------------------------------------------------------------------
3995: emit http headers if running as cgi
3996: -------------------------------------------------------------------------- */
3997: /* --- emit mime content-type line --- */
3998: if ( isquery && fp==stdout )            /* writing to http server */
3999:  { fprintf( stdout, "Cache-Control: max-age=9999\n" );
4000:    fprintf( stdout, "Content-type: text/plain\n\n" ); }
4001: /* -------------------------------------------------------------------------
4002: format and write header
4003: -------------------------------------------------------------------------- */
4004: /* --- format header info --- */
4005: *outline = '\000';                      /* initialize line buffer */
4006: strcat(outline,magic[ptype]);           /* begin file with "magic number" */
4007: strcat(outline,cr);                     /* followed by cr to end line */
4008: sprintf(outfield,"%d %d",rp->width,rp->height); /* format width and height */
4009: strcat(outline,outfield);               /* add width and height to header */
4010: strcat(outline,cr);                     /* followed by cr to end line */
4011: if ( ptype == 2 )                       /* need max grayscale value */
4012:   { sprintf(outfield,"%d",pixmax);      /* format maximum pixel value */
4013:     strcat(outline,outfield);           /* add max value to header */
4014:     strcat(outline,cr); }               /* followed by cr to end line */
4015: /* --- write header to file or memory buffer --- */
4016: if ( fp == NULL )                       /* if we have no open file... */
4017:   strcat(file,outline);                 /* add header to caller's buffer */
4018: else                                    /* or if we have an open file... */
4019:   if ( fputs(outline,fp)                /* try writing header to open file */
4020:   ==   EOF ) goto end_of_job;           /* return with error if failed */
4021: nbytes += strlen(outline);              /* bump output byte count */
4022: /* -------------------------------------------------------------------------
4023: format and write pixels
4024: -------------------------------------------------------------------------- */
4025: *outline = '\000';                      /* initialize line buffer */
4026: for ( irow=0; irow<=rp->height; irow++ ) /* for each row, top-to-bottom */
4027:  for ( jcol=0; jcol<rp->width; jcol++ ) { /* for each col, left-to-right */
4028:   /* --- format value at irow,jcol--- */
4029:   *outfield = '\000';                   /* init empty field */
4030:   if ( irow < rp->height ) {            /* check row index */
4031:     int pixval = getpixel(rp,irow,jcol);  /* value of pixel at irow,jcol */
4032:     if ( ptype == 1 )                   /* pixval must be 1 or 0 */
4033:       pixval = (pixval>pixmin+((pixmax-pixmin)/pixfrac)?1:0);
4034:     sprintf(outfield,"%d ",pixval); }   /* format pixel value */
4035:   /* --- write line if this value won't fit on it (or last line) --- */
4036:   if ( strlen(outline)+strlen(outfield)+strlen(cr) >= maxlinelen /*won't fit*/
4037:   ||   irow >= rp->height ) {           /* force writing last line */
4038:     strcat(outline,cr);                 /* add cr to end current line */
4039:     if ( fp == NULL )                   /* if we have no open file... */
4040:       strcat(file,outline);             /* add header to caller's buffer */
4041:     else                                /* or if we have an open file... */
4042:       if ( fputs(outline,fp)            /* try writing header to open file */
4043:       ==   EOF ) goto end_of_job;       /* return with error if failed */
4044:     nbytes += strlen(outline);          /* bump output byte count */
4045:     *outline = '\000';                  /* re-initialize line buffer */
4046:     } /* --- end-of-if(strlen>=maxlinelen) --- */
4047:   if ( irow >= rp->height ) break;      /* done after writing last line */
4048:   /* --- concatanate value to line -- */
4049:   strcat(outline,outfield);             /* concatanate value to line */
4050:   } /* --- end-of-for(jcol,irow) --- */
4051: isokay = 1;                             /* signal successful completion */
4052: /* -------------------------------------------------------------------------
4053: Back to caller with total #bytes written, or 0=failed.
4054: -------------------------------------------------------------------------- */
4055: end_of_job:
4056:   if ( fp != NULL                       /* output written to an open file */
4057:   &&   fp != stdout )                   /* and it's not just stdout */
4058:     fclose(fp);                         /* so close file before returning */
4059:   return ( (isokay?nbytes:0) );         /*back to caller with #bytes written*/
4060: } /* --- end-of-function type_pbmpgm() --- */
4061: 
4062: 
4063: /* ==========================================================================
4064:  * Function:    read_pbm ( fp, sf )
4065:  * Purpose:     read already-opened file (or pipe) containing pbm,
4066:  *              and return subraster corresponding to its bitmap image
4067:  * --------------------------------------------------------------------------
4068:  * Arguments:   fp (I)          FILE *ptr to already-opened file or pipe
4069:  *                              (the latter for \mathtex{} and maybe others)
4070:  *              sf (I)          double containing "shrink factor" .01<sf<.99
4071:  *                              to be applied to image
4072:  * --------------------------------------------------------------------------
4073:  * Returns:     ( subraster * ) ptr to subraster representing image of pbm
4074:  *                              or NULL for any error.
4075:  * --------------------------------------------------------------------------
4076:  * Notes:     o
4077:  * ======================================================================= */
4078: /* --- entry point --- */
4079: FUNCSCOPE subraster *read_pbm ( FILE *fp, double sf )
4080: {
4081: /* -------------------------------------------------------------------------
4082: Allocations and Declarations
4083: -------------------------------------------------------------------------- */
4084: subraster /**new_subraster(),*/ *sp = NULL; /*subraster corresponding to pbm*/
4085: raster  *rp = NULL;                     /* sp->image raster in subraster */
4086: raster  *ssrp=NULL /*, *imgsupsamp()*/; /* supersampled/shrunk rp */
4087: /*int   delete_subraster(),*/           /* in case of problem/error */
4088: /*      delete_raster();*/              /* in case we shrink/supersample rp */
4089: unsigned char *pixels = NULL;           /* pixels returned in pbm image */
4090: char    fline[8192],                    /* fgets(fline,8190,fp) buffer */
4091:         *lineptr=NULL, *endptr=NULL;    /* strtol ptr to 1st char after num*/
4092: char    *magicnumber = "P1";            /* filetype identification */
4093: int     gotmagic = 0;                   /* set true when magicnumber found */
4094: int     ncols=0, nrows=0,               /* image width,height from wget */
4095:         jcol=0, irow=0,                 /* col~width, row~height indexes */
4096:         ipixel=0, npixels=0,            /* pixel index, npixels=ncols*nrows */
4097:         pixval=0;                       /* pixval=strtol() from pipeline */
4098: int     pixsz = 1,                      /* bitmap in returned raster's pixmap*/
4099:         ssgrayscale = 2; /*256;*/       /* grayscale for imgsupsamp() */
4100: /* ---
4101:  * check input
4102:  * -------------- */
4103: if ( fp == NULL ) goto end_of_job;
4104: /* ---
4105:  * read the first few lines to get width, height
4106:  * ------------------------------------------------ */
4107: while ( 1 ) {                           /* read lines (shouldn't find eof) */
4108:   if ( fgets(fline,8190,fp) == NULL ) goto end_of_job; /* premature eof */
4109:   if ( gotmagic ) {                     /* next line has "width height" */
4110:     char *p = strchr(fline,(int)('#')); /* first look for comment char */
4111:     if ( p != (char *)NULL ) *p = '\000'; /* and terminate line at comment */
4112:     p = fline;  skipwhite(p);           /* now find first non-whitespace char*/
4113:     if ( *p == '\000' ) continue;       /* and skip empty lines */
4114:     ncols = (int)strtol(fline,&endptr,10); /* convert width */
4115:     nrows = (int)strtol(endptr,NULL,10); /* and convert height */
4116:     break; }                            /* and we're ready to pixelize... */
4117:   if ( strstr(fline,magicnumber) != NULL ) /* found magicnumber */
4118:     gotmagic = 1;                       /* set flag */
4119:   } /* --- end-of-while(1) --- */
4120: /* ---
4121:  * allocate pixels and xlate remainder of pipe
4122:  * ---------------------------------------------- */
4123: /* --- allocate pixels --- */
4124: npixels = nrows*ncols;                  /* that's what we need */
4125: if ( npixels<1 || npixels>9999999 ) goto end_of_job; /* sanity check */
4126: if ( (pixels = (unsigned char *)malloc(npixels)) /*ask for it if not insane*/
4127: ==   NULL ) goto end_of_job;            /* and quit if request denied */
4128: /* --- xlate remainder of pipe --- */
4129: ipixel = 0;                             /* start with first pixel */
4130: while ( 1 ) {                           /* read lines (shouldn't find eof) */
4131:   /* --- read next line --- */
4132:   if ( fgets(fline,511,fp) == NULL ) {  /* premature eof */
4133:     free(pixels);  pixels=NULL;         /* failed to xlate completely */
4134:     goto end_of_job; }                  /* free, reset return ptr, quit */
4135:   /* --- xlate 0's and 1's on the line --- */
4136:   lineptr = fline;                      /* start at beginning of line */
4137:   while ( ipixel < npixels ) {
4138:     while ( *lineptr != '\000' )        /* skip leading non-digits */
4139:       if ( isdigit(*lineptr) ) break;   /* done at first digit */
4140:       else lineptr++;                   /* or skip leading non-digit */
4141:     if ( *lineptr == '\000' ) break;    /* no more numbers on this line */
4142:     pixval = (int)strtol(lineptr,&endptr,10); /*convert next num to 0 or 1*/
4143:     pixels[ipixel++] = (unsigned char)pixval; /*i.e., let's hope it's 0 or 1*/
4144:     lineptr = endptr;                   /* move on to next number */
4145:     } /* --- end-of-while(ipixels<npixels) --- */
4146:   if ( ipixel >= npixels ) break;       /* pixelized image complete */
4147:   } /* --- end-of-while(1) --- */
4148: /* ---
4149:  * create subraster for pixelized image
4150:  * --------------------------------------- */
4151: if ( (sp = new_subraster(ncols,nrows,pixsz)) /* allocate/init subraster */
4152: ==   NULL ) goto end_of_job;            /* or quit if failed */
4153: if ( (rp = sp->image)                   /* image raster in new subraster */
4154: ==   NULL ) {                           /* apparently some internal problem */
4155:   delete_subraster(sp);                 /* free "buggy" subraster */
4156:   sp=NULL; goto end_of_job; }           /* return error to caller */
4157: sp->type = IMAGERASTER;                 /* set raster type as image */
4158: sp->baseline = (nrows+1)/2;             /* default baseline at image center */
4159: sp->toprow = sp->leftcol = 0;           /* init/unused */
4160: ipixel = 0;                             /* start with first pixel */
4161: /* --- copy 0/1 pixels from pbm to sp->image raster --- */
4162: for ( irow=0; irow<nrows; irow++ )      /* for each row, top-to-bottom */
4163:   for ( jcol=0; jcol<ncols; jcol++ ) {  /* for each col, left-to-right */
4164:     pixval = (int)pixels[ipixel++];     /* pbm image stored row-wise */
4165:     setpixel(rp,irow,jcol,pixval);      /* set corresponding value in image */
4166:     } /* --- end-of-for(irow,jcol) --- */
4167: /* --- apply sf (shrink factor) if requested --- */
4168: if ( sf>0.01 && sf<0.99 ) {             /* valid sf means shrink requested */
4169:   if ( (ssrp = imgsupsamp(rp,sf,ssgrayscale)) /* supersample rp to shrink it */
4170:   != NULL ) {                           /* succeeded, so replace rp with ssrp*/
4171:     /* printf("read_pbm> imgsupsamp() succeeded\n"); */
4172:     sp->image = ssrp;                   /* replaced rp with ssrp */
4173:     sp->baseline = (int)( (sf*((double)nrows)+1.0)/2.0 + 0.5 );
4174:     delete_raster(rp); }                /* free no-longer-needed rp */
4175:   /* else printf("read_pbm> imgsupsamp() failed\n"); */
4176:   } /* --- end-of-if(sf>.01&&sf<.99) --- */
4177: end_of_job:
4178:   if ( pixels != NULL ) free((void *)pixels); /* raster's pixmap has image */
4179:   return ( sp );                        /*back to caller with pbm subraster*/
4180: } /* --- end-of-function read_pbm() --- */
4181: 
4182: 
4183: /* ==========================================================================
4184:  * Function:    cstruct_chardef ( cp, fp, col1 )
4185:  * Purpose:     Emit a C struct of cp on fp, starting in col1.
4186:  * --------------------------------------------------------------------------
4187:  * Arguments:   cp (I)          ptr to chardef struct for which
4188:  *                              a C struct is to be generated.
4189:  *              fp (I)          File ptr to output device (defaults to
4190:  *                              stdout if passed as NULL).
4191:  *              col1 (I)        int containing 0...65; output lines
4192:  *                              are preceded by col1 blanks.
4193:  * --------------------------------------------------------------------------
4194:  * Returns:     ( int )         1 if completed successfully,
4195:  *                              or 0 otherwise (for any error).
4196:  * --------------------------------------------------------------------------
4197:  * Notes:
4198:  * ======================================================================= */
4199: /* --- entry point --- */
4200: FUNCSCOPE int cstruct_chardef ( chardef *cp, FILE *fp, int col1 )
4201: {
4202: /* -------------------------------------------------------------------------
4203: Allocations and Declarations
4204: -------------------------------------------------------------------------- */
4205: char    field[64];              /* field within output line */
4206: /*int   cstruct_raster(),*/     /* emit a raster */
4207: /*      emit_string();*/        /* emit a string and comment */
4208: /* -------------------------------------------------------------------------
4209: emit   charnum, location, name  /  hirow, hicol,  lorow, locol
4210: -------------------------------------------------------------------------- */
4211: /* --- charnum, location, name --- */
4212: sprintf(field,"{ %3d,%5d,\n", cp->charnum,cp->location);  /*char#,location*/
4213: emit_string ( fp, col1, field, "character number, location");
4214: /* --- toprow, topleftcol,   botrow, botleftcol  --- */
4215: sprintf(field,"  %3d,%2d,  %3d,%2d,\n",         /* format... */
4216:   cp->toprow,cp->topleftcol,                    /* toprow, topleftcol, */
4217:   cp->botrow,cp->botleftcol);                   /* and botrow, botleftcol */
4218: emit_string ( fp, col1, field, "topleft row,col, and botleft row,col");
4219: /* -------------------------------------------------------------------------
4220: emit raster and chardef's closing brace, and then return to caller
4221: -------------------------------------------------------------------------- */
4222: cstruct_raster(&cp->image,fp,col1+4);           /* emit raster */
4223: emit_string ( fp, 0, "  }", NULL);              /* emit closing brace */
4224: return ( 1 );                   /* back to caller with 1=okay, 0=failed */
4225: } /* --- end-of-function cstruct_chardef() --- */
4226: 
4227: 
4228: /* ==========================================================================
4229:  * Function:    cstruct_raster ( rp, fp, col1 )
4230:  * Purpose:     Emit a C struct of rp on fp, starting in col1.
4231:  * --------------------------------------------------------------------------
4232:  * Arguments:   rp (I)          ptr to raster struct for which
4233:  *                              a C struct is to be generated.
4234:  *              fp (I)          File ptr to output device (defaults to
4235:  *                              stdout if passed as NULL).
4236:  *              col1 (I)        int containing 0...65; output lines
4237:  *                              are preceded by col1 blanks.
4238:  * --------------------------------------------------------------------------
4239:  * Returns:     ( int )         1 if completed successfully,
4240:  *                              or 0 otherwise (for any error).
4241:  * --------------------------------------------------------------------------
4242:  * Notes:
4243:  * ======================================================================= */
4244: /* --- entry point --- */
4245: FUNCSCOPE int cstruct_raster ( raster *rp, FILE *fp, int col1 )
4246: {
4247: /* -------------------------------------------------------------------------
4248: Allocations and Declarations
4249: -------------------------------------------------------------------------- */
4250: char    field[64];              /* field within output line */
4251: char    typecast[64] = "(pixbyte *)"; /* type cast for pixmap string */
4252: /*int   hex_bitmap();*/         /* to emit raster bitmap */
4253: /*int   emit_string();*/        /* emit a string and comment */
4254: /* -------------------------------------------------------------------------
4255: emit width and height
4256: -------------------------------------------------------------------------- */
4257: sprintf(field,"{ %2d,  %3d,%2d,%2d, %s\n", /* format width,height,pixsz */
4258:         rp->width,rp->height,rp->format,rp->pixsz,typecast);
4259: emit_string ( fp, col1, field, "width,ht, fmt,pixsz,map...");
4260: /* -------------------------------------------------------------------------
4261: emit bitmap and closing brace, and return to caller
4262: -------------------------------------------------------------------------- */
4263: hex_bitmap(rp,fp,col1+2,1);     /* emit bitmap */
4264: emit_string ( fp, 0, " }", NULL); /* emit closing brace */
4265: return ( 1 );                   /* back to caller with 1=okay, 0=failed */
4266: } /* --- end-of-function cstruct_raster() --- */
4267: 
4268: 
4269: /* ==========================================================================
4270:  * Function:    hex_bitmap ( rp, fp, col1, isstr )
4271:  * Purpose:     Emit a hex dump of the bitmap of rp on fp, starting in col1.
4272:  *              If isstr (is string) is true, the dump is of the form
4273:  *                      "\x01\x02\x03\x04\x05..."
4274:  *              Otherwise, if isstr is false, the dump is of the form
4275:  *                      0x01,0x02,0x03,0x04,0x05...
4276:  * --------------------------------------------------------------------------
4277:  * Arguments:   rp (I)          ptr to raster struct for which
4278:  *                              a hex dump is to be constructed.
4279:  *              fp (I)          File ptr to output device (defaults to
4280:  *                              stdout if passed as NULL).
4281:  *              col1 (I)        int containing 0...65; output lines
4282:  *                              are preceded by col1 blanks.
4283:  *              isstr (I)       int specifying dump format as described above
4284:  * --------------------------------------------------------------------------
4285:  * Returns:     ( int )         1 if completed successfully,
4286:  *                              or 0 otherwise (for any error).
4287:  * --------------------------------------------------------------------------
4288:  * Notes:
4289:  * ======================================================================= */
4290: /* --- entry point --- */
4291: FUNCSCOPE int hex_bitmap ( raster *rp, FILE *fp, int col1, int isstr )
4292: {
4293: /* -------------------------------------------------------------------------
4294: Allocations and Declarations
4295: -------------------------------------------------------------------------- */
4296: int     ibyte,                          /* pixmap[ibyte] index */
4297:         nbytes = pixbytes(rp);          /*#bytes in bitmap or .gf-formatted*/
4298: char    stub[64]="                                ";/* col1 leading blanks */
4299: int     linewidth = 64,                 /* (roughly) rightmost column */
4300:         colwidth = (isstr? 4:5);        /* #cols required for each byte */
4301: int     ncols = (linewidth-col1)/colwidth; /* new line after ncols bytes */
4302: /* --------------------------------------------------------------------------
4303: initialization
4304: -------------------------------------------------------------------------- */
4305: /* --- redirect null fp --- */
4306: if ( fp == (FILE *)NULL ) fp = stdout;  /* default fp to stdout if null */
4307: /* --- emit initial stub if wanted --- */
4308: if ( col1 > 0 ) fprintf(fp,"%.*s",col1,stub); /* stub preceding 1st line */
4309: /* --------------------------------------------------------------------------
4310: emit hex dump of rp->bitmap image
4311: -------------------------------------------------------------------------- */
4312: if ( isstr ) fprintf(fp,"\"");          /* opening " before first line */
4313: for ( ibyte=0; ibyte<nbytes; ibyte++ )  /* one byte at a time */
4314:   {
4315:   /* --- display a byte as hex char or number, depending on isstr --- */
4316:   if ( isstr )                          /* string format wanted */
4317:     fprintf(fp,"\\x%02x",(rp->pixmap)[ibyte]);  /*print byte as hex char*/
4318:   else                                  /* comma-separated format wanted */
4319:     fprintf(fp,"0x%02x",(rp->pixmap)[ibyte]);   /*print byte as hex number*/
4320:   /* --- add a separator and newline, etc, as necessary --- */
4321:   if ( ibyte < nbytes-1)                /* not the last byte yet */
4322:     {
4323:     if ( !isstr ) fprintf(fp,",");      /* follow hex number with comma */
4324:     if ( (ibyte+1)%ncols==0 ) {         /* need new line after every ncols */
4325:       if ( !isstr )                     /* for hex numbers format ... */
4326:         fprintf(fp,"\n%.*s",col1,stub); /* ...just need newline and stub */
4327:       else                              /* for string format... */
4328:         fprintf(fp,"\"\n%.*s\"",col1,stub); } /*...need closing, opening "s*/
4329:     } /* --- end-of-if(ibyte<nbytes-1) --- */
4330:   } /* --- end-of-for(ibyte) --- */
4331: if ( isstr ) fprintf(fp,"\"");          /* closing " after last line */
4332: return ( 1 );                           /* back with 1=okay, 0=failed */
4333: } /* --- end-of-function hex_bitmap() --- */
4334: 
4335: 
4336: /* ==========================================================================
4337:  * Function:    emit_string ( fp, col1, string, comment )
4338:  * Purpose:     Emit string on fp, starting in col1,
4339:  *              and followed by right-justified comment.
4340:  * --------------------------------------------------------------------------
4341:  * Arguments:   fp (I)          File ptr to output device (defaults to
4342:  *                              stdout if passed as NULL).
4343:  *              col1 (I)        int containing 0 or #blanks preceding string
4344:  *              string (I)      char *  containing string to be emitted.
4345:  *                              If last char of string is '\n',
4346:  *                              the emitted line ends with a newline,
4347:  *                              otherwise not.
4348:  *              comment (I)     NULL or char * containing right-justified
4349:  *                              comment (we enclose between /star and star/)
4350:  * --------------------------------------------------------------------------
4351:  * Returns:     ( int )         1 if completed successfully,
4352:  *                              or 0 otherwise (for any error).
4353:  * --------------------------------------------------------------------------
4354:  * Notes:     o
4355:  * ======================================================================= */
4356: /* --- entry point --- */
4357: FUNCSCOPE int emit_string ( FILE *fp, int col1, char *string, char *comment )
4358: {
4359: /* -------------------------------------------------------------------------
4360: Allocations and Declarations
4361: -------------------------------------------------------------------------- */
4362: char    line[256];              /* construct line with caller's fields */
4363: int     fieldlen;               /* #chars in one of caller's fields */
4364: int     linelen = 72;           /*line length (for right-justified comment)*/
4365: int     isnewline = 0;          /* true to emit \n at end of line */
4366: /* --------------------------------------------------------------------------
4367: construct line containing prolog, string, epilog, and finally comment
4368: -------------------------------------------------------------------------- */
4369: /* --- init line --- */
4370: memset(line,' ',255);                   /* start line with blanks */
4371: /* --- embed string into line --- */
4372: if ( string != NULL )                   /* if caller gave us a string... */
4373:   { fieldlen = strlen(string);          /* #cols required for string */
4374:     if ( string[fieldlen-1] == '\n' )   /* check last char for newline */
4375:       { isnewline = 1;                  /* got it, so set flag */
4376:         fieldlen--; }                   /* but don't print it yet */
4377:     memcpy(line+col1,string,fieldlen);  /* embid string starting at col1 */
4378:     col1 += fieldlen; }                 /* bump col past epilog */
4379: /* --- embed comment into line --- */
4380: if ( comment != NULL )                  /* if caller gave us a comment... */
4381:   { fieldlen = 6 + strlen(comment);     /* plus  /star, star/, 2 spaces */
4382:     if ( linelen-fieldlen < col1 )      /* comment won't fit */
4383:       fieldlen -= (col1 - (linelen-fieldlen)); /* truncate comment to fit */
4384:     if ( fieldlen > 6 )                 /* can fit all or part of comment */
4385:       sprintf(line+linelen-fieldlen,"/%c %.*s %c/", /* so embed it in line */
4386:         '*', fieldlen-6,comment, '*');
4387:     col1 = linelen; }                   /* indicate line filled */
4388: /* --- line completed --- */
4389: line[col1] = '\000';                    /* null-terminate completed line */
4390: /* -------------------------------------------------------------------------
4391: emit line, then back to caller with 1=okay, 0=failed.
4392: -------------------------------------------------------------------------- */
4393: /* --- first redirect null fp --- */
4394: if ( fp == (FILE *)NULL ) fp = stdout;  /* default fp to stdout if null */
4395: /* --- emit line (and optional newline) --- */
4396: fprintf(fp,"%.*s",linelen,line);        /* no more than linelen chars */
4397: if ( isnewline ) fprintf(fp,"\n");      /*caller wants terminating newline*/
4398: return ( 1 );
4399: } /* --- end-of-function emit_string() --- */
4400: 
4401: 
4402: /* ==========================================================================
4403:  * Function:    gftobitmap ( gf )
4404:  * Purpose:     convert .gf-like pixmap to bitmap image
4405:  * --------------------------------------------------------------------------
4406:  * Arguments:   gf (I)          raster * to struct in .gf-format
4407:  * --------------------------------------------------------------------------
4408:  * Returns:     ( raster * )    image-format raster * if successful,
4409:  *                              or NULL for any error.
4410:  * --------------------------------------------------------------------------
4411:  * Notes:     o
4412:  * ======================================================================= */
4413: /* --- entry point --- */
4414: FUNCSCOPE raster *gftobitmap ( raster *gf )
4415: {
4416: /* -------------------------------------------------------------------------
4417: Allocations and Declarations
4418: -------------------------------------------------------------------------- */
4419: raster  /**new_raster(),*/ *rp=NULL;    /* image raster retuned to caller */
4420: int     width=0, height=0, totbits=0;   /* gf->width, gf->height, #bits */
4421: int     format=0, icount=0, ncounts=0,  /*.gf format, count index, #counts*/
4422:         ibit=0, bitval=0;               /* bitmap index, bit value */
4423: int     isrepeat = 1,                   /* true to process repeat counts */
4424:         repeatcmds[2] = {255,15},       /*opcode for repeat/duplicate count*/
4425:         nrepeats=0, irepeat=0,          /* scan line repeat count,index */
4426:         wbits = 0;                      /* count bits to width of scan line*/
4427: /* -------------------------------------------------------------------------
4428: initialization
4429: -------------------------------------------------------------------------- */
4430: /* --- check args --- */
4431: if ( gf == NULL ) goto end_of_job;      /* input raster not provided */
4432: format = gf->format;                    /* 2 or 3 */
4433: if ( format!=2 && format!=3 ) goto end_of_job; /* invalid raster format */
4434: ncounts = gf->pixsz;                    /*pixsz is really #counts in pixmap*/
4435: /* --- allocate output raster with proper dimensions for bitmap --- */
4436: width=gf->width;  height=gf->height;    /* dimensions of raster */
4437: if ( (rp = new_raster(width,height,1))  /* allocate new raster and bitmap */
4438: ==   NULL ) goto end_of_job;            /* quit if failed to allocate */
4439: totbits = width*height;                 /* total #bits in image */
4440: /* -------------------------------------------------------------------------
4441: fill bitmap
4442: -------------------------------------------------------------------------- */
4443: for ( icount=0,bitval=0; icount<ncounts; icount++ )
4444:   {
4445:   int   nbits = (int)(getbyfmt(format,gf->pixmap,icount)); /*#bits to set*/
4446:   if ( isrepeat                         /* we're proxessing repeat counts */
4447:   &&   nbits == repeatcmds[format-2] ) { /* and repeat opcode found */
4448:    if ( nrepeats == 0 )                 /* recursive repeat is error */
4449:     { nrepeats = (int)(getbyfmt(format,gf->pixmap,icount+1));/*repeat count*/
4450:       nbits = (int)(getbyfmt(format,gf->pixmap,icount+2)); /*#bits to set*/
4451:       icount += 2; }                    /* bump byte/nibble count */
4452:    else                                 /* some internal error occurred */
4453:     if ( msgfp!=NULL && msglevel>=1 )   /* report error */
4454:      fprintf(msgfp,"gftobitmap> found embedded repeat command\n"); }
4455:   if ( 0 )
4456:     fprintf(stdout,
4457:     "gftobitmap> icount=%d bitval=%d nbits=%d ibit=%d totbits=%d\n",
4458:     icount,bitval,nbits,ibit,totbits);
4459:   for ( ; nbits>0; nbits-- )            /* count down */
4460:     { if ( ibit >= totbits ) goto end_of_job; /* overflow check */
4461:       for ( irepeat=0; irepeat<=nrepeats; irepeat++ )
4462:        if ( bitval == 1 )               /* set pixel */
4463:         { setlongbit(rp->pixmap,(ibit+irepeat*width)); }
4464:        else                             /* clear pixel */
4465:         { unsetlongbit(rp->pixmap,(ibit+irepeat*width)); }
4466:       if ( nrepeats > 0 ) wbits++;      /* count another repeated bit */
4467:       ibit++; }                         /* bump bit index */
4468:   bitval = 1-bitval;                    /* flip bit value */
4469:   if ( wbits >= width ) {               /* completed repeats */
4470:    ibit += nrepeats*width;              /*bump bit count past repeated scans*/
4471:    if ( wbits > width )                 /* out-of alignment error */
4472:     if ( msgfp!=NULL && msglevel>=1 )   /* report error */
4473:      fprintf(msgfp,"gftobitmap> width=%d wbits=%d\n",width,wbits);
4474:    wbits = nrepeats = 0; }              /* reset repeat counts */
4475:   } /* --- end-of-for(icount) --- */
4476: end_of_job:
4477:   return ( rp );                        /* back to caller with image */
4478: } /* --- end-of-function gftobitmap() --- */
4479: 
4480: 
4481: /* ==========================================================================
4482:  * Function:    get_symdef ( symbol )
4483:  * Purpose:     returns mathchardef struct for symbol
4484:  * --------------------------------------------------------------------------
4485:  * Arguments:   symbol (I)      char *  containing symbol
4486:  *                              whose corresponding mathchardef is wanted
4487:  * --------------------------------------------------------------------------
4488:  * Returns:     ( mathchardef * )  pointer to struct defining symbol,
4489:  *                              or NULL for any error
4490:  * --------------------------------------------------------------------------
4491:  * Notes:     o Input symbol need only contain a leading substring to match,
4492:  *              e.g., \gam passed in symbol will match \gamma in the table.
4493:  *              If the table contains two or more possible matches,
4494:  *              the shortest is returned, e.g., input \e will return with
4495:  *              data for \eta rather than \epsilon.  To get \epsilon,
4496:  *              you must pass a leading substring long enough to eliminate
4497:  *              shorter table matches, i.e., in this case \ep
4498:  * ======================================================================= */
4499: /* --- entry point --- */
4500: FUNCSCOPE mathchardef *get_symdef ( char *symbol )
4501: {
4502: /* -------------------------------------------------------------------------
4503: Allocations and Declarations
4504: -------------------------------------------------------------------------- */
4505: mathchardef *symdefs = symtable;        /* table of mathchardefs */
4506: int     ligdef=0, get_ligature();       /* or we may have a ligature */
4507: int     idef = 0,                       /* symdefs[] index */
4508:         bestdef = (-9999);              /*index of shortest matching symdef*/
4509: int     symlen = strlen(symbol),        /* length of input symbol */
4510:         deflen, minlen=9999;            /*length of shortest matching symdef*/
4511: int     /*alnumsym = (symlen==1 && isalnum(*symbol)),*/ /*alphanumeric sym*/
4512:         alphasym = (symlen==1 && isalpha(*symbol)), /* or alpha symbol */
4513:         slashsym = (*symbol=='\\');     /* or \backslashed symbol */
4514: int     family = fontinfo[fontnum].family; /* current font family */
4515: static  char *displaysyms[][2] = {      /*xlate to Big sym for \displaystyle*/
4516:         /* --- see table on page 536 in TLC2 --- */
4517:         {"\\int",       "\\Bigint"},
4518:         {"\\oint",      "\\Bigoint"},
4519:         {"\\sum",       "\\Bigsum"},
4520:         {"\\prod",      "\\Bigprod"},
4521:         {"\\coprod",    "\\Bigcoprod"},
4522:         /* --- must be 'big' when related to similar binary operators --- */
4523:         {"\\bigcup",    "\\Bigcup"},
4524:         {"\\bigsqcup",  "\\Bigsqcup"},
4525:         {"\\bigcap",    "\\Bigcap"},
4526:         /*{"\\bigsqcap", "\\sqcap"},*/  /* don't have \Bigsqcap */
4527:         {"\\bigodot",   "\\Bigodot"},
4528:         {"\\bigoplus",  "\\Bigoplus"},
4529:         {"\\bigominus", "\\ominus"},
4530:         {"\\bigotimes", "\\Bigotimes"},
4531:         {"\\bigoslash", "\\oslash"},
4532:         {"\\biguplus",  "\\Biguplus"},
4533:         {"\\bigwedge",  "\\Bigwedge"},
4534:         {"\\bigvee",    "\\Bigvee"},
4535:         {NULL, NULL} };
4536: /* -------------------------------------------------------------------------
4537: First check for ligature
4538: -------------------------------------------------------------------------- */
4539: isligature = 0;                         /* init signal for no ligature */
4540: if ( family == CYR10 )                  /*only check for cyrillic ligatures*/
4541:  if ( (ligdef=get_ligature(subexprptr,family)) /* check for ligature */
4542:  >=    0  )                             /* found a ligature */
4543:   { bestdef = ligdef;                   /* set bestdef for ligature */
4544:     isligature = 1;                     /* signal we found a ligature */
4545:     goto end_of_job; }                  /* so just give it to caller */
4546: /* -------------------------------------------------------------------------
4547: If in \displaystyle mode, first xlate int to Bigint, etc.
4548: -------------------------------------------------------------------------- */
4549: if ( isdisplaystyle > 1 )               /* we're in \displaystyle mode */
4550:   for ( idef=0; ; idef++ ) {            /* lookup symbol in displaysyms */
4551:     char *fromsym = displaysyms[idef][0], /* look for this symbol */
4552:          *tosym = displaysyms[idef][1];   /* and xlate it to this symbol */
4553:     if ( fromsym == NULL ) break;       /* end-of-table */
4554:     if ( !strcmp(symbol,fromsym) )      /* found a match */
4555:       { if ( msglevel>=99 && msgfp!=NULL ) /* debugging output */
4556:          { fprintf(msgfp,"get_symdef> isdisplaystyle=%d, xlated %s to %s\n",
4557:            isdisplaystyle,symbol,tosym); fflush(msgfp); }
4558:         symbol = tosym;                 /* so look up tosym instead */
4559:         symlen = strlen(symbol);        /* reset symbol length */
4560:         break; }                        /* no need to search further */
4561:     } /* --- end-of-for(idef) --- */
4562: /* -------------------------------------------------------------------------
4563: search symdefs[] in order for first occurrence of symbol
4564: -------------------------------------------------------------------------- */
4565: for ( idef=0; ;idef++ )                 /* until trailer record found */
4566:   if ( symdefs[idef].symbol == NULL ) break; /* reached end-of-table */
4567:   else                                  /* check against caller's symbol */
4568:     if ( strncmp(symbol,symdefs[idef].symbol,symlen) == 0 ) /* found match */
4569:      if ( (fontnum==0||family==CYR10)   /* mathmode, so check every match */
4570:      || (1 && symdefs[idef].handler!=NULL) /* or check every directive */
4571:      || (1 && istextmode && slashsym)   /*text mode and \backslashed symbol*/
4572:      || (0 && istextmode && (!alphasym  /* text mode and not alpha symbol */
4573:         || symdefs[idef].handler!=NULL))   /* or text mode and directive */
4574:      || (symdefs[idef].family==family   /* have correct family */
4575:         && symdefs[idef].handler==NULL) )  /* and not a handler collision */
4576: #if 0
4577:      || (fontnum==1 && symdefs[idef].family==CMR10)   /*textmode && rm text*/
4578:      || (fontnum==2 && symdefs[idef].family==CMMI10)  /*textmode && it text*/
4579:      || (fontnum==3 && symdefs[idef].family==BBOLD10  /*textmode && bb text*/
4580:         && symdefs[idef].handler==NULL)
4581:      || (fontnum==4 && symdefs[idef].family==CMMIB10  /*textmode && bf text*/
4582:         && symdefs[idef].handler==NULL) )
4583: #endif
4584:       if ( (deflen=strlen(symdefs[idef].symbol)) < minlen ) /*new best match*/
4585:         { bestdef = idef;               /* save index of new best match */
4586:           if ( (minlen = deflen)        /* and save its len for next test */
4587:           ==  symlen ) break; }         /*perfect match, so return with it*/
4588: if ( bestdef < 0 )                      /* failed to look up symbol */
4589:   if ( fontnum != 0 )                   /* we're in a restricted font mode */
4590:     { int oldfontnum = fontnum;         /* save current font family */
4591:       mathchardef *symdef = NULL;       /* lookup result with fontnum=0 */
4592:       fontnum = 0;                      /*try to look up symbol in any font*/
4593:       symdef = get_symdef(symbol);      /* repeat lookup with fontnum=0 */
4594:       fontnum = oldfontnum;             /* reset font family */
4595:       return symdef; }                  /* caller gets fontnum=0 lookup */
4596: end_of_job:
4597:  if ( msgfp!=NULL && msglevel>=999 )    /* debugging output */
4598:   { fprintf(msgfp,
4599:     "get_symdef> symbol=%s matches symtable[%d]=%s (isligature=%d)\n",
4600:     symbol,bestdef,(bestdef<0?"NotFound":symdefs[bestdef].symbol),isligature);
4601:     fflush(msgfp); }
4602:  return ( (bestdef<0? NULL : &(symdefs[bestdef])) );/*NULL or best symdef[]*/
4603: } /* --- end-of-function get_symdef() --- */
4604: 
4605: 
4606: /* ==========================================================================
4607:  * Function:    get_ligature ( expression, family )
4608:  * Purpose:     returns symtable[] index for ligature
4609:  * --------------------------------------------------------------------------
4610:  * Arguments:   expression (I)  char *  containing ligature
4611:  *                              whose corresponding mathchardef is wanted
4612:  *              family (I)      int containing NOVALUE for any family,
4613:  *                              or, e.g., CYR10 for cyrillic, etc.
4614:  * --------------------------------------------------------------------------
4615:  * Returns:     ( int )         symtable[] index defining ligature,
4616:  *                              or -9999 if no ligature found or for any error
4617:  * --------------------------------------------------------------------------
4618:  * Notes:     o
4619:  * ======================================================================= */
4620: /* --- entry point --- */
4621: FUNCSCOPE int get_ligature ( char *expression, int family )
4622: {
4623: /* -------------------------------------------------------------------------
4624: Allocations and Declarations
4625: -------------------------------------------------------------------------- */
4626: mathchardef *symdefs = symtable;        /* table of mathchardefs */
4627: char    *ligature = expression /*- 1*/, /* expression ptr */
4628:         *symbol = NULL;                 /* symdefs[idef].symbol */
4629: int     liglen = strlen(ligature);      /* #chars remaining in expression */
4630: int     iscyrfam = (family==CYR10);     /* true for cyrillic families */
4631: int     idef = 0,                       /* symdefs[] index */
4632:         bestdef = (-9999),              /*index of longest matching symdef*/
4633:         maxlen=(-9999);                 /*length of longest matching symdef*/
4634: /* -------------------------------------------------------------------------
4635: search symdefs[] in order for first occurrence of symbol
4636: -------------------------------------------------------------------------- */
4637: if ( !isstring ) {                      /* no ligatures in "string" mode */
4638:  for ( idef=0; ;idef++ )                /* until trailer record found */
4639:   if ( (symbol=symdefs[idef].symbol) == NULL ) break; /* end-of-table */
4640:   else {                                /* check against caller's ligature */
4641:     int symlen = strlen(symbol);        /* #chars in symbol */
4642:     if ( ( symlen>1 || iscyrfam )       /*ligature >1 char long or cyrillic*/
4643:     &&   symlen <= liglen               /* and enough remaining chars */
4644:     &&   ( *symbol!='\\' || iscyrfam )  /* not escaped or cyrillic */
4645:     &&   symdefs[idef].handler == NULL ) /* and not a handler */
4646:      if ( strncmp(ligature,symbol,symlen) == 0 ) /* found match */
4647:       if ( family < 0                   /* no family specifies */
4648:       ||   symdefs[idef].family == family ) /* or have correct family */
4649:        if ( symlen > maxlen )           /* new longest ligature */
4650:         { bestdef = idef;               /* save index of new best match */
4651:           maxlen = symlen; }            /* and save its len for next test */
4652:     } /* --- end-of-if/else(symbol==NULL) --- */
4653:  if ( msgfp!=NULL && msglevel>=999 )    /* debugging output */
4654:   { fprintf(msgfp,"get_ligature> ligature=%.4s matches symtable[%d]=%s\n",
4655:     ligature,bestdef,(bestdef<0?"NotFound":symdefs[bestdef].symbol));
4656:     fflush(msgfp); }
4657:  } /* --- end-of-if(!isstring) --- */
4658: return ( bestdef );                     /* -9999 or index of best symdef[] */
4659: } /* --- end-of-function get_ligature --- */
4660: 
4661: 
4662: /* ==========================================================================
4663:  * Function:    get_chardef ( symdef, size )
4664:  * Purpose:     returns chardef ptr containing data for symdef at given size
4665:  * --------------------------------------------------------------------------
4666:  * Arguments:   symdef (I)      mathchardef *  corresponding to symbol
4667:  *                              whose corresponding chardef is wanted
4668:  *              size (I)        int containing 0-5 for desired size
4669:  * --------------------------------------------------------------------------
4670:  * Returns:     ( chardef * )   pointer to struct defining symbol at size,
4671:  *                              or NULL for any error
4672:  * --------------------------------------------------------------------------
4673:  * Notes:     o if size unavailable, the next-closer-to-normalsize
4674:  *              is returned instead.
4675:  * ======================================================================= */
4676: /* --- entry point --- */
4677: FUNCSCOPE chardef *get_chardef ( mathchardef *symdef, int size )
4678: {
4679: /* -------------------------------------------------------------------------
4680: Allocations and Declarations
4681: -------------------------------------------------------------------------- */
4682: fontfamily  *fonts = fonttable;         /* table of font families */
4683: chardef **fontdef,                      /*tables for desired font, by size*/
4684:         *gfdata = (chardef *)NULL;      /* chardef for symdef,size */
4685: int     ifont;                          /* fonts[] index */
4686: int     family, charnum;                /* indexes retrieved from symdef */
4687: int     sizeinc = 0,                    /*+1 or -1 to get closer to normal*/
4688:         normalsize = 2;                 /* this size always present */
4689: int     isBig = 0;                      /*true if symbol's 1st char is upper*/
4690: char    *symptr = NULL;                 /* look for 1st alpha of symbol */
4691: /* -------------------------------------------------------------------------
4692: initialization
4693: -------------------------------------------------------------------------- */
4694: /* --- check symdef --- */
4695: if ( symdef == NULL ) goto end_of_job;  /* get_symdef() probably failed */
4696: /* --- get local copy of indexes from symdef --- */
4697: family = symdef->family;                /* font family containing symbol */
4698: charnum = symdef->charnum;              /* char# of symbol within font */
4699: /* --- check for supersampling --- */
4700: if ( issupersampling )                  /* check for supersampling fonts */
4701:  if ( fonts != ssfonttable )            /* uh oh--probably internal error */
4702:   { fonts = ssfonttable; }              /* force it */
4703: /* --- check requested size, and set size increment --- */
4704: if ( 0 && issupersampling )             /* set size index for supersampling */
4705:   size = LARGESTSIZE+1;                 /* index 1 past largest size */
4706: else                                    /* low pass indexes 0...LARGESTSIZE */
4707:   {
4708:   if( size<0 ) size = 0;                /* size was definitely too small */
4709:   if( size>LARGESTSIZE ) size = LARGESTSIZE;  /* or definitely too large */
4710:   if( size<normalsize ) sizeinc = (+1); /*use next larger if size too small*/
4711:   if( size>normalsize ) sizeinc = (-1); /*or next smaller if size too large*/
4712:   }
4713: /* --- check for really big symbol (1st char of symbol name uppercase) --- */
4714: for ( symptr=symdef->symbol; *symptr!='\000'; symptr++ ) /*skip leading \'s*/
4715:   if ( isalpha(*symptr) )               /* found leading alpha char */
4716:     { isBig = isupper(*symptr);         /* is 1st char of name uppercase? */
4717:       if ( !isBig                       /* 1st char lowercase */
4718:       &&   strlen(symptr) >= 4 )        /* but followed by at least 3 chars */
4719:        isBig = !memcmp(symptr,"big\\",4) /* isBig if name starts with big\ */
4720:         || !memcmp(symptr,"bigg",4);    /* or with bigg */
4721:       break; }                          /* don't check beyond 1st char */
4722: /* -------------------------------------------------------------------------
4723: find font family in table of fonts[]
4724: -------------------------------------------------------------------------- */
4725: /* --- look up font family --- */
4726: for ( ifont=0; ;ifont++ )               /* until trailer record found */
4727:   if ( fonts[ifont].family < 0 ) {      /* error, no such family */
4728:     if ( msgfp!=NULL && msglevel>=99 ) { /* emit error */
4729:      fprintf(msgfp,"get_chardef> failed to find font family %d\n",
4730:      family); fflush(msgfp); }
4731:     goto end_of_job; }                  /* quit if can't find font family*/
4732:   else if ( fonts[ifont].family == family ) break; /* found font family */
4733: /* --- get local copy of table for this family by size --- */
4734: fontdef = fonts[ifont].fontdef;         /* font by size */
4735: /* -------------------------------------------------------------------------
4736: get font in desired size, or closest available size, and return symbol
4737: -------------------------------------------------------------------------- */
4738: /* --- get font in desired size --- */
4739: while ( 1 )                             /* find size or closest available */
4740:   if ( fontdef[size] != NULL ) break;   /* found available size */
4741:   else                                  /* adjust size closer to normal */
4742:     if ( size == NORMALSIZE             /* already normal so no more sizes,*/
4743:     || sizeinc == 0 ) {                 /* or must be supersampling */
4744:       if ( msgfp!=NULL && msglevel>=99 ) { /* emit error */
4745:         fprintf(msgfp,"get_chardef> failed to find font size %d\n",
4746:         size); fflush(msgfp); }
4747:       goto end_of_job; }                /* quit if can't find desired size */
4748:     else                                /*bump size 1 closer to NORMALSIZE*/
4749:       size += sizeinc;                  /* see if adjusted size available */
4750: /* --- ptr to chardef struct --- */
4751: gfdata = &((fontdef[size])[charnum]);   /*ptr to chardef for symbol in size*/
4752: /* -------------------------------------------------------------------------
4753: kludge to tweak CMEX10 (which appears to have incorrect descenders)
4754: -------------------------------------------------------------------------- */
4755: if ( family == CMEX10 )                 /* cmex10 needs tweak */
4756:   { int height = gfdata->toprow - gfdata->botrow + 1; /*total height of char*/
4757:     gfdata->botrow = (isBig? (-height/3) : (-height/4));
4758:     gfdata->toprow = gfdata->botrow + gfdata->image.height; }
4759: /* -------------------------------------------------------------------------
4760: return subraster containing chardef data for symbol in requested size
4761: -------------------------------------------------------------------------- */
4762: end_of_job:
4763:  if ( msgfp!=NULL && msglevel>=999 )
4764:   { if (symdef == NULL) fprintf(msgfp,"get_chardef> input symdef==NULL\n");
4765:     else
4766:      fprintf(msgfp,"get_chardef> requested symbol=\"%s\" size=%d  %s\n",
4767:      symdef->symbol,size,(gfdata==NULL?"FAILED":"Succeeded"));
4768:     fflush(msgfp); }
4769:  return ( gfdata );                     /*ptr to chardef for symbol in size*/
4770: } /* --- end-of-function get_chardef() --- */
4771: 
4772: 
4773: /* ==========================================================================
4774:  * Function:    get_charsubraster ( symdef, size )
4775:  * Purpose:     returns new subraster ptr containing
4776:  *              data for symdef at given size
4777:  * --------------------------------------------------------------------------
4778:  * Arguments:   symdef (I)      mathchardef *  corresponding to symbol whose
4779:  *                              corresponding chardef subraster is wanted
4780:  *              size (I)        int containing 0-5 for desired size
4781:  * --------------------------------------------------------------------------
4782:  * Returns:     ( subraster * ) pointer to struct defining symbol at size,
4783:  *                              or NULL for any error
4784:  * --------------------------------------------------------------------------
4785:  * Notes:     o just wraps a subraster envelope around get_chardef()
4786:  * ======================================================================= */
4787: /* --- entry point --- */
4788: FUNCSCOPE subraster *get_charsubraster ( mathchardef *symdef, int size )
4789: {
4790: /* -------------------------------------------------------------------------
4791: Allocations and Declarations
4792: -------------------------------------------------------------------------- */
4793: chardef /**get_chardef(),*/ *gfdata=NULL; /* chardef struct for symdef,size */
4794: /*int   get_baseline();*/               /* baseline of gfdata */
4795: subraster /**new_subraster(),*/ *sp=NULL; /* subraster containing gfdata */
4796: raster  *bitmaprp=NULL /*, *gftobitmap()*/; /* convert .gf-format to bitmap */
4797: raster  *rotp=NULL /*, *rastrot3d()*/;  /* rotate character if utheta>0 */
4798: /*int   delete_subraster();*/           /* in case gftobitmap() fails */
4799: /*int   delete_raster();*/              /* in case IMAGERASTER replaced */
4800: int     /*aasupsamp(),*/                /*antialias char with supersampling*/
4801:         grayscale=256;                  /* aasupersamp() parameters */
4802: /* -------------------------------------------------------------------------
4803: look up chardef for symdef at size, and embed data (gfdata) in subraster
4804: -------------------------------------------------------------------------- */
4805: if ( (gfdata=get_chardef(symdef,size))  /* look up chardef for symdef,size */
4806: !=   NULL )                             /* and check that we found it */
4807:  if ( (sp=new_subraster(0,0,0))         /* allocate subraster "envelope" */
4808:  !=   NULL )                            /* and check that we succeeded */
4809:   {
4810:   raster *image = &(gfdata->image);     /* ptr to chardef's bitmap or .gf */
4811:   int format = image->format;           /* 1=bitmap, else .gf */
4812:   sp->symdef = symdef;                  /* replace NULL with caller's arg */
4813:   sp->size = size;                      /*replace default with caller's size*/
4814:   sp->baseline = get_baseline(gfdata);  /* get baseline of character */
4815:   if ( format == 1 )                    /* already a bitmap */
4816:    { sp->type = CHARASTER;              /* static char raster */
4817:      sp->image = image; }               /* store ptr to its bitmap */
4818:   else                                  /* need to convert .gf-to-bitmap */
4819:    if ( (bitmaprp = gftobitmap(image))  /* convert */
4820:    !=   (raster *)NULL )                /* successful */
4821:     { sp->type = IMAGERASTER;           /* allocated raster will be freed */
4822:       sp->image = bitmaprp; }           /* store ptr to converted bitmap */
4823:    else                                 /* conversion failed */
4824:     { delete_subraster(sp);             /* free unneeded subraster */
4825:       sp = (subraster *)NULL;           /* signal error to caller */
4826:       goto end_of_job; }                /* quit */
4827:   if ( issupersampling )                /* antialias character right here */
4828:     {
4829:     raster *aa = NULL;                  /* antialiased char raster */
4830:     int status = aasupsamp(sp->image,&aa,shrinkfactor,grayscale);
4831:     if ( status )                       /* supersampled successfully */
4832:       { int baseline = sp->baseline;    /* baseline before supersampling */
4833:         int height = gfdata->image.height; /* #rows before supersampling */
4834:         sp->image = aa;                 /* replace chardef with ss image */
4835:         if ( baseline >= height-1 )     /* baseline at bottom of char */
4836:           sp->baseline = aa->height -1; /* so keep it at bottom */
4837:         else                            /* char has descenders */
4838:           sp->baseline /= shrinkfactor; /* rescale baseline */
4839:         sp->type = IMAGERASTER; }       /* character is an image raster */
4840:     } /* --- end-of-if(issupersampling) --- */
4841:   if ( absval(utheta) > 1.0e-6 ) {      /* character 3d-rotation wanted */
4842:     rotp = rastrot3d(sp->image,&uaxis,utheta); /* rotate image around uaxis */
4843:     if ( sp->type != CHARASTER )        /* not a static character raster */
4844:       delete_raster(sp->image);         /* so free currently allocated image */
4845:     sp->image = rotp;                   /* and replace it with rotated image */
4846:     sp->type = IMAGERASTER;             /* allocated raster will be freed */
4847:     } /* --- end-of-if(absval(utheta)>1.0e-6) --- */
4848:   } /* --- end-of-if(sp!=NULL) --- */
4849: end_of_job:
4850:  if ( msgfp!=NULL && msglevel>=999 )
4851:   { fprintf(msgfp,"get_charsubraster> requested symbol=\"%s\" baseline=%d"
4852:     " %s %s\n", symdef->symbol, (sp==NULL?0:sp->baseline),
4853:     (sp==NULL?"FAILED":"Succeeded"), (gfdata==NULL?"(gfdata=NULL)":" "));
4854:     fflush(msgfp); }
4855: return ( sp );                          /* back to caller */
4856: } /* --- end-of-function get_charsubraster() --- */
4857: 
4858: 
4859: /* ==========================================================================
4860:  * Function:    get_symsubraster ( symbol, size )
4861:  * Purpose:     returns new subraster ptr containing
4862:  *              data for symbol at given size
4863:  * --------------------------------------------------------------------------
4864:  * Arguments:   symbol (I)      char *  corresponding to symbol
4865:  *                              whose corresponding subraster is wanted
4866:  *              size (I)        int containing 0-5 for desired size
4867:  * --------------------------------------------------------------------------
4868:  * Returns:     ( subraster * ) pointer to struct defining symbol at size,
4869:  *                              or NULL for any error
4870:  * --------------------------------------------------------------------------
4871:  * Notes:     o just combines get_symdef() and get_charsubraster()
4872:  * ======================================================================= */
4873: /* --- entry point --- */
4874: FUNCSCOPE subraster *get_symsubraster ( char *symbol, int size )
4875: {
4876: /* -------------------------------------------------------------------------
4877: Allocations and Declarations
4878: -------------------------------------------------------------------------- */
4879: subraster *sp=NULL /*, *get_charsubraster()*/; /*subraster containing gfdata*/
4880: mathchardef *symdef=NULL /*, *get_symdef()*/;/*mathchardef lookup for symbol*/
4881: /* -------------------------------------------------------------------------
4882: look up mathchardef for symbol
4883: -------------------------------------------------------------------------- */
4884: if ( symbol != NULL )                   /* user supplied input symbol */
4885:   symdef = get_symdef(symbol);          /*look up corresponding mathchardef*/
4886: /* -------------------------------------------------------------------------
4887: look up chardef for mathchardef and wrap a subraster structure around data
4888: -------------------------------------------------------------------------- */
4889: if ( symdef != NULL )                   /* lookup succeeded */
4890:   sp = get_charsubraster(symdef,size);  /* so get symbol data in subraster */
4891: return ( sp );                          /* back to caller with sp or NULL */
4892: } /* --- end-of-function get_symsubraster() --- */
4893: 
4894: 
4895: /* ==========================================================================
4896:  * Function:    get_baseline ( gfdata )
4897:  * Purpose:     returns baseline for a chardef struct
4898:  * --------------------------------------------------------------------------
4899:  * Arguments:   gfdata (I)      chardef *  containing chardef for symbol
4900:  *                              whose baseline is wanted
4901:  * --------------------------------------------------------------------------
4902:  * Returns:     ( int )         baseline for symdef,
4903:  *                              or -1 for any error
4904:  * --------------------------------------------------------------------------
4905:  * Notes:     o Unlike TeX, the top-left corners of our rasters are (0,0),
4906:  *              with (row,col) increasing as you move down and right.
4907:  *              Baselines are calculated with respect to this scheme,
4908:  *              so 0 would mean the very top row is on the baseline
4909:  *              and everything else descends below the baseline.
4910:  * ======================================================================= */
4911: /* --- entry point --- */
4912: FUNCSCOPE int get_baseline ( chardef *gfdata )
4913: {
4914: /* -------------------------------------------------------------------------
4915: Allocations and Declarations
4916: -------------------------------------------------------------------------- */
4917: int     /*toprow = gfdata->toprow,*/    /*TeX top row from .gf file info*/
4918:         botrow = gfdata->botrow,        /*TeX bottom row from .gf file info*/
4919:         height = gfdata->image.height;  /* #rows comprising symbol */
4920: /* -------------------------------------------------------------------------
4921: give caller baseline
4922: -------------------------------------------------------------------------- */
4923: return ( (height-1) + botrow );         /* note: descenders have botrow<0 */
4924: } /* --- end-of-function get_baseline() --- */
4925: 
4926: 
4927: /* ==========================================================================
4928:  * Function:    get_delim ( char *symbol, int height, int family )
4929:  * Purpose:     returns subraster corresponding to the samllest
4930:  *              character containing symbol, but at least as large as height,
4931:  *              and in caller's family (if specified).
4932:  *              If no symbol character as large as height is available,
4933:  *              then the largest availabale character is returned instead.
4934:  * --------------------------------------------------------------------------
4935:  * Arguments:   symbol (I)      char *  containing (substring of) desired
4936:  *                              symbol, e.g., if symbol="(", then any
4937:  *                              mathchardef like "(" or "\\(", etc, match.
4938:  *              height (I)      int containing minimum acceptable height
4939:  *                              for returned character
4940:  *              family (I)      int containing -1 to consider all families,
4941:  *                              or, e.g., CMEX10 for only that family
4942:  * --------------------------------------------------------------------------
4943:  * Returns:     ( subraster * ) best matching character available,
4944:  *                              or NULL for any error
4945:  * --------------------------------------------------------------------------
4946:  * Notes:     o If height is passed as negative, its absolute value is used
4947:  *              but the best-fit width is searched for (rather than height)
4948:  * ======================================================================= */
4949: /* --- entry point --- */
4950: FUNCSCOPE subraster *get_delim ( char *symbol, int height, int family )
4951: {
4952: /* -------------------------------------------------------------------------
4953: Allocations and Declarations
4954: -------------------------------------------------------------------------- */
4955: mathchardef *symdefs = symtable;        /* table of mathchardefs */
4956: subraster /**get_charsubraster(),*/ *sp=(subraster *)NULL; /*best match char*/
4957: /*subraster *make_delim();*/            /* construct delim if can't find it*/
4958: chardef /**get_chardef(),*/ *gfdata=NULL; /*get chardef struct for a symdef*/
4959: char    lcsymbol[256], *symptr,         /* lowercase symbol for comparison */
4960:         *unescsymbol = symbol;          /* unescaped symbol */
4961: int     symlen = (symbol==NULL?0:strlen(symbol)), /* #chars in caller's sym*/
4962:         deflen = 0;                     /* length of symdef (aka lcsymbol) */
4963: int     idef = 0,                       /* symdefs[] index */
4964:         bestdef = (-9999),              /* index of best fit symdef */
4965:         bigdef = (-9999);               /*index of biggest (in case no best)*/
4966: int     size = 0,                       /* size index 0...LARGESTSIZE */
4967:         bestsize = (-9999),             /* index of best fit size */
4968:         bigsize = (-9999);              /*index of biggest (in case no best)*/
4969: int     defheight, bestheight=9999,     /* height of best fit symdef */
4970:         bigheight = (-9999);            /*height of biggest(in case no best)*/
4971: int     iswidth = 0;                    /* true if best-fit width desired */
4972: int     isunesc = 0,                    /* true if leading escape removed */
4973:         issq=0, isoint=0;               /* true for \sqcup,etc, \oint,etc */
4974: int     iscurly = 0;                    /* true for StMary's curly symbols */
4975: char    *bigint="bigint", *bigoint="bigoint"; /* substitutes for int, oint */
4976: /* -------------------------------------------------------------------------
4977: determine if searching height or width, and search symdefs[] for best-fit
4978: -------------------------------------------------------------------------- */
4979: /* --- arg checks --- */
4980: if ( symlen < 1 ) return (sp);          /* no input symbol suplied */
4981: if ( strcmp(symbol,"e") == 0 ) return(sp); /* e causes segfault??? */
4982: if ( strstr(symbol,"curly") != NULL ) iscurly=1; /* user wants curly delim */
4983: /* --- ignore leading escapes for CMEX10 --- */
4984: if ( 1 )                                /* ignore leading escape */
4985:  if ( (family==CMEX10 || family==CMSYEX) ) { /* for CMEX10 or CMSYEX */
4986:   if ( strstr(symbol,"sq") != NULL )    /* \sq symbol requested */
4987:      issq = 1;                          /* seq \sq signal */
4988:   if ( strstr(symbol,"oint") != NULL )  /* \oint symbol requested */
4989:      isoint = 1;                        /* seq \oint signal */
4990:   if ( *symbol=='\\' )                  /* have leading \ */
4991:    { unescsymbol = symbol+1;            /* push past leading \ */
4992:      if ( --symlen < 1 ) return(sp);    /* one less char */
4993:      if ( strcmp(unescsymbol,"int") == 0 ) /* \int requested by caller */
4994:        unescsymbol = bigint;            /* but big version looks better */
4995:      if ( strcmp(unescsymbol,"oint") == 0 ) /* \oint requested by caller */
4996:        unescsymbol = bigoint;           /* but big version looks better */
4997:      symlen = strlen(unescsymbol);      /* explicitly recalculate length */
4998:      isunesc = 1; }                     /* signal leading escape removed */
4999:   } /* --- end-of-if(family) --- */
5000: /* --- determine whether searching for best-fit height or width --- */
5001: if ( height < 0 )                       /* negative signals width search */
5002:   { height = (-height);                 /* flip "height" positive */
5003:     iswidth = 1; }                      /* set flag for width search */
5004: /* --- search symdefs[] for best-fit height (or width) --- */
5005: for ( idef=0; ;idef++ )                 /* until trailer record found */
5006:  {
5007:  char *defsym = symdefs[idef].symbol;   /* local copies */
5008:  int  deffam  = symdefs[idef].family;
5009:  if ( defsym == NULL ) break;           /* reached end-of-table */
5010:  else                                   /* check against caller's symbol */
5011:   if ( family<0 || deffam == family     /* if explicitly in caller's family*/
5012:   ||  (family==CMSYEX && (deffam==CMSY10||deffam==CMEX10||deffam==STMARY10)) )
5013:     {
5014:     strcpy(lcsymbol,defsym);            /* local copy of symdefs[] symbol */
5015:     if ( isunesc && *lcsymbol=='\\' )   /* ignored leading \ in symbol */
5016:      {strsqueeze(lcsymbol,1);}          /*so squeeze it out of lcsymbol too*/
5017:     if ( 0 )                            /* don't ignore case */
5018:      for ( symptr=lcsymbol; *symptr!='\000'; symptr++ )/*for each symbol ch*/
5019:       if ( isalpha(*symptr) ) *symptr=tolower(*symptr);/*lowercase the char*/
5020:     deflen = strlen(lcsymbol);          /* #chars in symbol we're checking */
5021:     if ((symptr=strstr(lcsymbol,unescsymbol)) != NULL) /*found caller's sym*/
5022:      if ( (isoint || strstr(lcsymbol,"oint")==NULL) /* skip unwanted "oint"*/
5023:      &&   (issq || strstr(lcsymbol,"sq")==NULL) ) /* skip unwanted "sq" */
5024:       if ( ( deffam == CMSY10 ?         /* CMSY10 or not CMSY10 */
5025:           symptr == lcsymbol            /* caller's sym is a prefix */
5026:           && deflen == symlen:          /* and same length */
5027:           (iscurly || strstr(lcsymbol,"curly")==NULL) &&/*not unwanted curly*/
5028:           (symptr == lcsymbol           /* caller's sym is a prefix */
5029:           || symptr == lcsymbol+deflen-symlen) ) ) /* or a suffix */
5030:        for ( size=0; size<=LARGESTSIZE; size++ ) /* check all font sizes */
5031:         if ( (gfdata=get_chardef(&(symdefs[idef]),size)) != NULL ) /*got one*/
5032:           { defheight = gfdata->image.height;   /* height of this character */
5033:             if ( iswidth )              /* width search wanted instead... */
5034:               defheight = gfdata->image.width;  /* ...so substitute width */
5035:             leftsymdef = &(symdefs[idef]);      /* set symbol class, etc */
5036:             if ( defheight>=height && defheight<bestheight ) /*new best fit*/
5037:               { bestdef=idef; bestsize=size;    /* save indexes of best fit */
5038:                 bestheight = defheight; }       /* and save new best height */
5039:             if ( defheight >= bigheight )       /* new biggest character */
5040:               { bigdef=idef; bigsize=size;      /* save indexes of biggest */
5041:                 bigheight = defheight; }        /* and save new big height */
5042:           } /* --- end-of-if(gfdata!=NULL) --- */
5043:     } /* --- end-of-if(family) --- */
5044:  } /* --- end-of-for(idef) --- */
5045: /* -------------------------------------------------------------------------
5046: construct subraster for best fit character, and return it to caller
5047: -------------------------------------------------------------------------- */
5048: if ( bestdef >= 0 )                     /* found a best fit for caller */
5049:   sp = get_charsubraster(&(symdefs[bestdef]),bestsize); /* best subraster */
5050: if ( (sp==NULL && height-bigheight>5)   /* try to construct delim */
5051: ||   bigdef < 0 )                       /* delim not in font tables */
5052:   sp = make_delim(symbol,(iswidth?-height:height)); /* try to build delim */
5053: if ( sp==NULL && bigdef>=0 )            /* just give biggest to caller */
5054:   sp = get_charsubraster(&(symdefs[bigdef]),bigsize); /* biggest subraster */
5055: if ( msgfp!=NULL && msglevel>=99 )
5056:     fprintf(msgfp,"get_delim> symbol=%.50s, height=%d family=%d isokay=%s\n",
5057:     (symbol==NULL?"null":symbol),height,family,(sp==NULL?"fail":"success"));
5058: return ( sp );
5059: } /* --- end-of-function get_delim() --- */
5060: 
5061: 
5062: /* ==========================================================================
5063:  * Function:    make_delim ( char *symbol, int height )
5064:  * Purpose:     constructs subraster corresponding to symbol
5065:  *              exactly as large as height,
5066:  * --------------------------------------------------------------------------
5067:  * Arguments:   symbol (I)      char *  containing, e.g., if symbol="("
5068:  *                              for desired delimiter
5069:  *              height (I)      int containing height
5070:  *                              for returned character
5071:  * --------------------------------------------------------------------------
5072:  * Returns:     ( subraster * ) constructed delimiter
5073:  *                              or NULL for any error
5074:  * --------------------------------------------------------------------------
5075:  * Notes:     o If height is passed as negative, its absolute value is used
5076:  *              and interpreted as width (rather than height)
5077:  * ======================================================================= */
5078: /* --- entry point --- */
5079: FUNCSCOPE subraster *make_delim ( char *symbol, int height )
5080: {
5081: /* -------------------------------------------------------------------------
5082: Allocations and Declarations
5083: -------------------------------------------------------------------------- */
5084: subraster *sp = (subraster *)NULL       /* subraster returned to caller */
5085:         /*,*new_subraster()*/;          /* allocate subraster */
5086: subraster /**get_symsubraster(),*/      /* look up delim pieces in cmex10 */
5087:         *symtop=NULL, *symbot=NULL, *symmid=NULL, *symbar=NULL, /* pieces */
5088:         *topsym=NULL, *botsym=NULL, *midsym=NULL, *barsym=NULL  /* +filler */
5089:         /*,*rastack(), *rastcat()*/;    /* stack pieces, concat filler */
5090: int     isdrawparen = 0;                /*1=draw paren, 0=build from pieces*/
5091: raster  *rasp = (raster *)NULL;         /* sp->image */
5092: int     isokay=0 /*, delete_subraster()*/; /*set true if delimiter drawn ok*/
5093: int     pixsz = 1,                      /* pixels are one bit each */
5094:         symsize = 0;                    /* size arg for get_symsubraster() */
5095: int     thickness = 1;                  /* drawn lines are one pixel thick */
5096: int     aspectratio = 8;                /* default height/width for parens */
5097: int     iswidth = 0,                    /*true if width specified by height*/
5098:         width = height;                 /* #pixels width (e.g., of ellipse)*/
5099: char    *lp=NULL,  *rp=NULL,            /* check symbol for left or right */
5100:         *lp2=NULL, *rp2=NULL,           /* synonym for lp,rp */
5101:         *lp3=NULL, *rp3=NULL,           /* synonym for lp,rp */
5102:         *lp4=NULL, *rp4=NULL;           /* synonym for lp,rp */
5103: /*int   circle_raster(),*/              /* ellipse for ()'s in sp->image */
5104: /*      rule_rsater(),*/                /* horizontal or vertical lines */
5105: /*      line_raster();*/                /* line between two points */
5106: /*subraster *uparrow_subraster();*/     /* up/down arrows */
5107: int     isprealloc = 1;                 /*pre-alloc subraster, except arrow*/
5108: int     oldsmashmargin = smashmargin,   /* save original smashmargin */
5109:         wasnocatspace = isnocatspace;   /* save original isnocatspace */
5110: /* -------------------------------------------------------------------------
5111: initialization
5112: -------------------------------------------------------------------------- */
5113: /* --- determine whether constructing height or width --- */
5114: if ( height < 0 )                       /* negative "height" signals width */
5115:   { width = height = (-height);         /* flip height positive */
5116:     iswidth = 1; }                      /* set flag for width */
5117: if ( height < 3 ) goto end_of_job;      /* too small, must be error */
5118: /* --- set default width (or height) accordingly --- */
5119: if ( iswidth ) height =  (width+(aspectratio+1)/2)/aspectratio;
5120: else            width = (height+(aspectratio+1)/2)/aspectratio;
5121: if ( strchr(symbol,'=') != NULL         /* left or right || bracket wanted */
5122: ||   strstr(symbol,"\\|") != NULL       /* same || in standard tex notation*/
5123: ||   strstr(symbol,"dbl") != NULL )     /* semantic bracket with ||'s */
5124:   width = max2(width,6);                /* need space between two |'s */
5125: if ( width < 2 ) width=2;               /* set min width */
5126: if ( strchr(symbol,'(') != NULL         /* if left ( */
5127: ||   strchr(symbol,')') != NULL )       /* or right ) paren wanted */
5128:   { width = (3*width)/2;                /* adjust width */
5129:     if ( !isdrawparen ) isprealloc=0; } /* don't prealloc if building */
5130: if ( strchr(symbol,'/') != NULL         /* left / */
5131: ||   strstr(symbol,"\\\\") != NULL      /* or \\ for right \ */
5132: ||   strstr(symbol,"backsl") != NULL )  /* or \backslash for \ */
5133:   width = max2(height/3,5);
5134: if ( strstr(symbol,"arrow") != NULL )   /* arrow wanted */
5135:   { width = min2(height/3,20);          /* adjust width */
5136:     isprealloc = 0; }                   /* don't preallocate subraster */
5137: if ( strchr(symbol,'{') != NULL         /* if left { */
5138: ||   strchr(symbol,'}') != NULL )       /* or right } brace wanted */
5139:   { isprealloc = 0; }                   /* don't preallocate */
5140: /* --- allocate and initialize subraster for constructed delimiter --- */
5141: if ( isprealloc )                       /* pre-allocation wanted */
5142:  { if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
5143:    ==   NULL )  goto end_of_job;        /* quit if failed */
5144:    /* --- initialize delimiter subraster parameters --- */
5145:    sp->type = IMAGERASTER;              /* image */
5146:    sp->symdef = NULL;                   /* not applicable for image */
5147:    sp->baseline = height/2 + 2;         /* is a little above center good? */
5148:    sp->size = NORMALSIZE;               /* size (probably unneeded) */
5149:    rasp = sp->image; }                  /* pointer to image in subraster */
5150: /* -------------------------------------------------------------------------
5151: ( ) parens
5152: -------------------------------------------------------------------------- */
5153: if ( (lp=strchr(symbol,'(')) != NULL    /* left ( paren wanted */
5154: ||   (rp=strchr(symbol,')')) != NULL )  /* right ) paren wanted */
5155:   {
5156:   if ( isdrawparen ) {                  /* draw the paren */
5157:    int  mywidth = min2(width,20);       /* max width for ()'s */
5158:    circle_raster ( rasp,                /* embedded raster image */
5159:         0, 0,                           /* row0,col0 are upper-left corner */
5160:         height-1, mywidth-1,            /* row1,col1 are lower-right */
5161:         thickness,                      /* line thickness is 1 pixel */
5162:         (rp==NULL?"23":"41") );         /* "1234" quadrants to be drawn */
5163:    isokay = 1; }                        /* set flag */
5164:   else {
5165:    int  isleft = (lp!=NULL?1:0);        /* true for left, false for right */
5166:    char *parentop = (isleft?"\\leftparentop":"\\rightparentop"),
5167:         *parenbot = (isleft?"\\leftparenbot":"\\rightparenbot"),
5168:         *parenbar = (isleft?"\\leftparenbar":"\\rightparenbar");
5169:    int  baseht=0, barht=0,              /* height of base=top+bot, bar */
5170:         ibar=0, nbars=0;                /* bar index, #bars between top&bot*/
5171:    int  largestsize = min2(2,LARGESTSIZE), /* largest size for parens */
5172:         topfill=(isleft?0:0), botfill=(isleft?0:0),
5173:         barfill=(isleft?0:7);           /* alignment fillers */
5174:    /* --- get pieces at largest size smaller than total height --- */
5175:    for ( symsize=largestsize; symsize>=0; symsize-- ) /*largest to smallest*/
5176:     {
5177:     /* --- get pieces at current test size --- */
5178:     isokay = 1;                         /* check for all pieces */
5179:     if ( (symtop=get_symsubraster(parentop,symsize)) == NULL ) isokay=0;
5180:     if ( (symbot=get_symsubraster(parenbot,symsize)) == NULL ) isokay=0;
5181:     if ( (symbar=get_symsubraster(parenbar,symsize)) == NULL ) isokay=0;
5182:     /* --- check sum of pieces against total desired height --- */
5183:     if ( isokay ) {                     /* all pieces retrieved */
5184:       baseht = (symtop->image)->height + (symbot->image)->height; /*top+bot*/
5185:       barht  = (symbar->image)->height; /* bar height */
5186:       if ( baseht < height+5 ) break;   /* largest base that's not too big */
5187:       if ( symsize < 1 ) break;         /* or smallest available base */
5188:       } /* --- end-of-if(isokay) --- */
5189:     /* --- free test pieces that were too big --- */
5190:     if ( symtop != NULL ) delete_subraster(symtop); /* free top */
5191:     if ( symbot != NULL ) delete_subraster(symbot); /* free bot */
5192:     if ( symbar != NULL ) delete_subraster(symbar); /* free bar */
5193:     isokay = 0;                         /* nothing available */
5194:     if ( symsize < 1 ) break;           /* leave isokay=0 after smallest */
5195:     } /* --- end-of-for(symsize) --- */
5196:    /* --- construct brace from pieces --- */
5197:    if ( isokay ) {                      /* we have the pieces */
5198:     /* --- add alignment fillers --- */
5199:     smashmargin=0;  isnocatspace=99;    /*turn off rastcat smashing,space*/
5200:     topsym = (topfill>0?rastcat(new_subraster(topfill,1,1),symtop,3):symtop);
5201:     botsym = (botfill>0?rastcat(new_subraster(botfill,1,1),symbot,3):symbot);
5202:     barsym = (barfill>0?rastcat(new_subraster(barfill,1,1),symbar,3):symbar);
5203:     smashmargin = oldsmashmargin;       /* reset smashmargin */
5204:     isnocatspace = wasnocatspace;       /* reset isnocatspace */
5205:     /* --- #bars needed between top and bot --- */
5206:     nbars = (barht<1?0:max2(0,1+(height-baseht)/barht)); /* #bars needed */
5207:     /* --- stack pieces --- */
5208:     sp = topsym;                        /* start with top piece */
5209:     if ( nbars > 0 )                    /* need nbars between top and bot */
5210:       for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2);
5211:     sp = rastack(botsym,sp,1,0,0,3);    /* bottom below bars or middle */
5212:     delete_subraster(barsym);           /* barsym no longer needed */
5213:     } /* --- end-of-if(isokay) --- */
5214:    } /* --- end-of-if/else(isdrawparen) --- */
5215:   } /* --- end-of-if(left- or right-() paren wanted) --- */
5216: /* -------------------------------------------------------------------------
5217: { } braces
5218: -------------------------------------------------------------------------- */
5219: else
5220:  if ( (lp=strchr(symbol,'{')) != NULL   /* left { brace wanted */
5221:  ||   (rp=strchr(symbol,'}')) != NULL ) /* right } brace wanted */
5222:   {
5223:   int   isleft = (lp!=NULL?1:0);        /* true for left, false for right */
5224:   char  *bracetop = (isleft?"\\leftbracetop":"\\rightbracetop"),
5225:         *bracebot = (isleft?"\\leftbracebot":"\\rightbracebot"),
5226:         *bracemid = (isleft?"\\leftbracemid":"\\rightbracemid"),
5227:         *bracebar = (isleft?"\\leftbracebar":"\\rightbracebar");
5228:   int   baseht=0, barht=0,              /* height of base=top+bot+mid, bar */
5229:         ibar=0, nbars=0;                /* bar index, #bars above,below mid*/
5230:   int   largestsize = min2(2,LARGESTSIZE), /* largest size for braces */
5231:         topfill=(isleft?4:0), botfill=(isleft?4:0),
5232:         midfill=(isleft?0:4), barfill=(isleft?4:4); /* alignment fillers */
5233:   /* --- get pieces at largest size smaller than total height --- */
5234:   for ( symsize=largestsize; symsize>=0; symsize-- ) /*largest to smallest*/
5235:     {
5236:     /* --- get pieces at current test size --- */
5237:     isokay = 1;                         /* check for all pieces */
5238:     if ( (symtop=get_symsubraster(bracetop,symsize)) == NULL ) isokay=0;
5239:     if ( (symbot=get_symsubraster(bracebot,symsize)) == NULL ) isokay=0;
5240:     if ( (symmid=get_symsubraster(bracemid,symsize)) == NULL ) isokay=0;
5241:     if ( (symbar=get_symsubraster(bracebar,symsize)) == NULL ) isokay=0;
5242:     /* --- check sum of pieces against total desired height --- */
5243:     if ( isokay ) {                     /* all pieces retrieved */
5244:       baseht = (symtop->image)->height + (symbot->image)->height
5245:         + (symmid->image)->height;      /* top+bot+mid height */
5246:       barht = (symbar->image)->height;  /* bar height */
5247:       if ( baseht < height+5 ) break;   /* largest base that's not too big */
5248:       if ( symsize < 1 ) break;         /* or smallest available base */
5249:       } /* --- end-of-if(isokay) --- */
5250:     /* --- free test pieces that were too big --- */
5251:     if ( symtop != NULL ) delete_subraster(symtop); /* free top */
5252:     if ( symbot != NULL ) delete_subraster(symbot); /* free bot */
5253:     if ( symmid != NULL ) delete_subraster(symmid); /* free mid */
5254:     if ( symbar != NULL ) delete_subraster(symbar); /* free bar */
5255:     isokay = 0;                         /* nothing available */
5256:     if ( symsize < 1 ) break;           /* leave isokay=0 after smallest */
5257:     } /* --- end-of-for(symsize) --- */
5258:   /* --- construct brace from pieces --- */
5259:   if ( isokay ) {                       /* we have the pieces */
5260:     /* --- add alignment fillers --- */
5261:     smashmargin=0;  isnocatspace=99;    /*turn off rastcat smashing,space*/
5262:     topsym = (topfill>0?rastcat(new_subraster(topfill,1,1),symtop,3):symtop);
5263:     botsym = (botfill>0?rastcat(new_subraster(botfill,1,1),symbot,3):symbot);
5264:     midsym = (midfill>0?rastcat(new_subraster(midfill,1,1),symmid,3):symmid);
5265:     barsym = (barfill>0?rastcat(new_subraster(barfill,1,1),symbar,3):symbar);
5266:     smashmargin = oldsmashmargin;       /* reset smashmargin */
5267:     isnocatspace = wasnocatspace;       /* reset isnocatspace */
5268:     /* --- #bars needed on each side of mid piece --- */
5269:     nbars = (barht<1?0:max2(0,1+(height-baseht)/barht/2)); /*#bars per side*/
5270:     /* --- stack pieces --- */
5271:     sp = topsym;                        /* start with top piece */
5272:     if ( nbars > 0 )                    /* need nbars above middle */
5273:       for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2);
5274:     sp = rastack(midsym,sp,1,0,0,3);    /*mid after top or bars*/
5275:     if ( nbars > 0 )                    /* need nbars below middle */
5276:       for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2);
5277:     sp = rastack(botsym,sp,1,0,0,3);    /* bottom below bars or middle */
5278:     delete_subraster(barsym);           /* barsym no longer needed */
5279:     } /* --- end-of-if(isokay) --- */
5280:   } /* --- end-of-if(left- or right-{} brace wanted) --- */
5281: /* -------------------------------------------------------------------------
5282: [ ] brackets
5283: -------------------------------------------------------------------------- */
5284: else
5285:  if ( (lp=strchr(symbol,'[')) != NULL   /* left [ bracket wanted */
5286:  ||   (rp=strchr(symbol,']')) != NULL   /* right ] bracket wanted */
5287:  ||   (lp2=strstr(symbol,"lceil")) != NULL /* left ceiling wanted */
5288:  ||   (rp2=strstr(symbol,"rceil")) != NULL /* right ceiling wanted */
5289:  ||   (lp3=strstr(symbol,"lfloor")) != NULL /* left floor wanted */
5290:  ||   (rp3=strstr(symbol,"rfloor")) != NULL /* right floor wanted */
5291:  ||   (lp4=strstr(symbol,"llbrack")) != NULL /* left semantic bracket */
5292:  ||   (rp4=strstr(symbol,"rrbrack")) != NULL ) /* right semantic bracket */
5293:   {
5294:   /* --- use rule_raster ( rasp, top, left, width, height, type=0 ) --- */
5295:   int   mywidth = min2(width,12),       /* max width for horizontal bars */
5296:         wthick = 1;                     /* thickness of top.bottom bars */
5297:   thickness = (height<25?1:2);          /* set lines 1 or 2 pixels thick */
5298:   if ( lp2!=NULL || rp2!=NULL || lp3!=NULL || rp3 !=NULL ) /*ceil or floor*/
5299:     wthick = thickness;                 /* same thickness for top/bot bar */
5300:   if ( lp3==NULL && rp3==NULL )         /* set top bar if floor not wanted */
5301:     rule_raster(rasp, 0,0, mywidth,wthick, 0); /* top horizontal bar */
5302:   if ( lp2==NULL && rp2==NULL )         /* set bot bar if ceil not wanted */
5303:     rule_raster(rasp, height-wthick,0, mywidth,thickness, 0); /* bottom */
5304:   if ( lp!=NULL || lp2!=NULL || lp3!=NULL || lp4!=NULL ) /* left bracket */
5305:    rule_raster(rasp, 0,0, thickness,height, 0); /* left vertical bar */
5306:   if ( lp4 != NULL )                    /* 2nd left vertical bar needed */
5307:    rule_raster(rasp, 0,thickness+1, 1,height, 0); /* 2nd left vertical bar */
5308:   if ( rp!=NULL || rp2!=NULL || rp3!=NULL || rp4!=NULL ) /* right bracket */
5309:    rule_raster(rasp, 0,mywidth-thickness, thickness,height, 0); /* right */
5310:   if ( rp4 != NULL )                    /* 2nd right vertical bar needed */
5311:    rule_raster(rasp, 0,mywidth-thickness-2, 1,height, 0); /*2nd right vert*/
5312:   isokay = 1;                           /* set flag */
5313:   } /* --- end-of-if(left- or right-[] bracket wanted) --- */
5314: /* -------------------------------------------------------------------------
5315: < > brackets
5316: -------------------------------------------------------------------------- */
5317: else
5318:  if ( (lp=strchr(symbol,'<')) != NULL   /* left < bracket wanted */
5319:  ||   (rp=strchr(symbol,'>')) != NULL ) /* right > bracket wanted */
5320:   {
5321:   /* --- use line_raster( rasp,  row0, col0,  row1, col1,  thickness ) --- */
5322:   int   mywidth = min2(width,12),       /* max width for brackets */
5323:         mythick = 1;                    /* all lines one pixel thick */
5324:   thickness = (height<25?1:2);          /* set line pixel thickness */
5325:   if ( lp != NULL )                     /* left < bracket wanted */
5326:     { line_raster(rasp,height/2,0,0,mywidth-1,mythick);
5327:       if ( thickness>1 )
5328:         line_raster(rasp,height/2,1,0,mywidth-1,mythick);
5329:       line_raster(rasp,height/2,0,height-1,mywidth-1,mythick);
5330:       if ( thickness>1 )
5331:         line_raster(rasp,height/2,1,height-1,mywidth-1,mythick); }
5332:   if ( rp != NULL )                     /* right > bracket wanted */
5333:     { line_raster(rasp,height/2,mywidth-1,0,0,mythick);
5334:       if ( thickness>1 )
5335:         line_raster(rasp,height/2,mywidth-2,0,0,mythick);
5336:       line_raster(rasp,height/2,mywidth-1,height-1,0,mythick);
5337:       if ( thickness>1 )
5338:         line_raster(rasp,height/2,mywidth-2,height-1,0,mythick); }
5339:   isokay = 1;                           /* set flag */
5340:   } /* --- end-of-if(left- or right-<> bracket wanted) --- */
5341: /* -------------------------------------------------------------------------
5342: / \ delimiters
5343: -------------------------------------------------------------------------- */
5344: else
5345:  if ( (lp=strchr(symbol,'/')) != NULL   /* left /  wanted */
5346:  ||   (rp=strstr(symbol,"\\\\")) != NULL /* right \ wanted */
5347:  ||   (rp2=strstr(symbol,"backsl")) != NULL ) /* right \ wanted */
5348:   {
5349:   /* --- use line_raster( rasp,  row0, col0,  row1, col1,  thickness ) --- */
5350:   int   mywidth = width;                /* max width for / \ */
5351:   thickness = 1;                        /* set line pixel thickness */
5352:   if ( lp != NULL )                     /* left / wanted */
5353:     line_raster(rasp,0,mywidth-1,height-1,0,thickness);
5354:   if ( rp!=NULL || rp2!=NULL )          /* right \ wanted */
5355:     line_raster(rasp,0,0,height-1,mywidth-1,thickness);
5356:   isokay = 1;                           /* set flag */
5357:   } /* --- end-of-if(left- or right-/\ delimiter wanted) --- */
5358: /* -------------------------------------------------------------------------
5359: arrow delimiters
5360: -------------------------------------------------------------------------- */
5361: else
5362:  if ( strstr(symbol,"arrow") != NULL )  /* arrow delimiter wanted */
5363:   {
5364:   /* --- use uparrow_subraster(width,height,pixsz,drctn,isBig) --- */
5365:   int   mywidth = width;                /* max width for / \ */
5366:   int   isBig = (strstr(symbol,"Up")!=NULL /* isBig if we have an Up */
5367:                 || strstr(symbol,"Down")!=NULL); /* or a Down */
5368:   int   drctn = +1;                     /* init for uparrow */
5369:   if ( strstr(symbol,"down")!=NULL      /* down if we have down */
5370:   ||   strstr(symbol,"Down")!=NULL )    /* or Down */
5371:    { drctn = (-1);                      /* reset direction to down */
5372:      if ( strstr(symbol,"up")!=NULL     /* updown if we have up or Up */
5373:      ||   strstr(symbol,"Up")!=NULL )   /* and down or Down */
5374:       drctn = 0; }                      /* reset direction to updown */
5375:   sp = uparrow_subraster(mywidth,height,pixsz,drctn,isBig);
5376:   if ( sp != NULL )
5377:    { sp->type = IMAGERASTER;            /* image */
5378:      sp->symdef = NULL;                 /* not applicable for image */
5379:      sp->baseline = height/2 + 2;       /* is a little above center good? */
5380:      sp->size = NORMALSIZE;             /* size (probably unneeded) */
5381:      isokay = 1; }                      /* set flag */
5382:   } /* --- end-of-if(arrow delimiter wanted) --- */
5383: /* -------------------------------------------------------------------------
5384: \- for | | brackets or \= for || || brackets
5385: -------------------------------------------------------------------------- */
5386: else
5387:  if ( (lp=strchr(symbol,'-')) != NULL   /* left or right | bracket wanted */
5388:  ||  (lp2=strchr(symbol,'|')) != NULL   /* synonym for | bracket */
5389:  ||   (rp=strchr(symbol,'=')) != NULL   /* left or right || bracket wanted */
5390:  || (rp2=strstr(symbol,"\\|"))!= NULL ) /* || in standard tex notation */
5391:   {
5392:   /* --- rule_raster ( rasp, top, left, width, height, type=0 ) --- */
5393:   int   midcol = width/2;               /* middle col, left of mid if even */
5394:   if ( rp  != NULL                      /* left or right || bracket wanted */
5395:   ||   rp2 != NULL )                    /* or || in standard tex notation */
5396:    { thickness = (height<75?1:2);       /* each | of || 1 or 2 pixels thick*/
5397:      rule_raster(rasp, 0,max2(0,midcol-2), thickness,height, 0); /* left */
5398:      rule_raster(rasp, 0,min2(width,midcol+2), thickness,height, 0); }
5399:   else                                  /*nb, lp2 spuriously set if rp2 set*/
5400:    if ( lp  != NULL                     /* left or right | bracket wanted */
5401:    ||   lp2 != NULL )                   /* ditto for synomym */
5402:     { thickness = (height<75?1:2);      /* set | 1 or 2 pixels thick */
5403:       rule_raster(rasp, 0,midcol, thickness,height, 0); } /*mid vertical bar*/
5404:   isokay = 1;                           /* set flag */
5405:   } /* --- end-of-if(left- or right-[] bracket wanted) --- */
5406: /* -------------------------------------------------------------------------
5407: back to caller
5408: -------------------------------------------------------------------------- */
5409: end_of_job:
5410:   if ( msgfp!=NULL && msglevel>=99 )
5411:     fprintf(msgfp,"make_delim> symbol=%.50s, isokay=%d\n",
5412:     (symbol==NULL?"null":symbol),isokay);
5413:   if ( !isokay )                        /* don't have requested delimiter */
5414:     { if (sp!=NULL) delete_subraster(sp); /* so free unneeded structure */
5415:       sp = NULL; }                      /* and signal error to caller */
5416:   return ( sp );                        /*back to caller with delim or NULL*/
5417: } /* --- end-of-function make_delim() --- */
5418: 
5419: 
5420: /* ==========================================================================
5421:  * Function:    texchar ( expression, chartoken )
5422:  * Purpose:     scans expression, returning either its first character,
5423:  *              or the next \sequence if that first char is \,
5424:  *              and a pointer to the first expression char past that.
5425:  * --------------------------------------------------------------------------
5426:  * Arguments:   expression (I)  char * to first char of null-terminated
5427:  *                              string containing valid LaTeX expression
5428:  *                              to be scanned
5429:  *              chartoken (O)   char * to null-terminated string returning
5430:  *                              either the first (non-whitespace) character
5431:  *                              of expression if that char isn't \, or else
5432:  *                              the \ and everything following it up to
5433:  *                              the next non-alphabetic character (but at
5434:  *                              least one char following the \ even if
5435:  *                              it's non-alpha)
5436:  * --------------------------------------------------------------------------
5437:  * Returns:     ( char * )      ptr to the first char of expression
5438:  *                              past returned chartoken,
5439:  *                              or NULL for any parsing error.
5440:  * --------------------------------------------------------------------------
5441:  * Notes:     o Does *not* skip leading whitespace, but simply
5442:  *              returns any whitespace character as the next character.
5443:  * ======================================================================= */
5444: /* --- entry point --- */
5445: FUNCSCOPE char *texchar ( char *expression, char *chartoken )
5446: {
5447: /* -------------------------------------------------------------------------
5448: Allocations and Declarations
5449: -------------------------------------------------------------------------- */
5450: int     esclen = 0,                             /*length of escape sequence*/
5451:         maxesclen = 128;                        /* max len of esc sequence */
5452: char    *ptoken = chartoken;                    /* ptr into chartoken */
5453: int     iprefix = 0;                            /* prefix index */
5454: static  char *prefixes[] =                      /*e.g., \big followed by ( */
5455:         { /* "\\left", "\\right", */
5456:           "\\big",  "\\Big",  "\\bigg",  "\\Bigg",
5457:           "\\bigl", "\\Bigl", "\\biggl", "\\Biggl",
5458:           "\\bigr", "\\Bigr", "\\biggr", "\\Biggr", NULL };
5459: static  char *starred[] =                       /* may be followed by * */
5460:         { "\\hspace",  "\\!",  NULL };
5461: /* -------------------------------------------------------------------------
5462: just return the next char if it's not \
5463: -------------------------------------------------------------------------- */
5464: /* --- error check for end-of-string --- */
5465: *ptoken = '\000';                               /* init in case of error */
5466: if ( expression == NULL ) return(NULL);         /* nothing to scan */
5467: if ( *expression == '\000' ) return(NULL);      /* nothing to scan */
5468: /* --- always returning first character (either \ or some other char) --- */
5469: *ptoken++ = *expression++;                      /* here's first character */
5470: /* --- if first char isn't \, then just return it to caller --- */
5471: if ( !isthischar(*(expression-1),ESCAPE) )      /* not a \, so return char */
5472:   { *ptoken = '\000';                           /* add a null terminator */
5473:     goto end_of_job; }                          /* ptr past returned char */
5474: if ( *expression == '\000' )                    /* \ is very last char */
5475:   { *chartoken = '\000';                        /* flush bad trailing \ */
5476:     return(NULL); }                             /* and signal end-of-job */
5477: /* -------------------------------------------------------------------------
5478: we have an escape sequence, so return all alpha chars following \
5479: -------------------------------------------------------------------------- */
5480: /* --- accumulate chars until first non-alpha char found --- */
5481: for ( ; isalpha(*expression); esclen++ )        /* till first non-alpha... */
5482:   { if ( esclen < maxesclen-3 )                 /* more room in chartoken */
5483:       *ptoken++ = *expression;                  /*copy alpha char, bump ptr*/
5484:     expression++; }                             /* bump expression ptr */
5485: /* --- if we have a prefix, append next texchar, e.g., \big( --- */
5486: *ptoken = '\000';                               /* set null for compare */
5487: for ( iprefix=0; prefixes[iprefix] != NULL; iprefix++ ) /* run thru list */
5488:  if ( strcmp(chartoken,prefixes[iprefix]) == 0 ) /* have an exact match */
5489:   { char nextchar[256];  int nextlen=0;         /* texchar after prefix */
5490:     skipwhite(expression);                      /* skip space after prefix*/
5491:     expression = texchar(expression,nextchar);  /* get nextchar */
5492:     if ( (nextlen = strlen(nextchar)) > 0 )     /* #chars in nextchar */
5493:       { strcpy(ptoken,nextchar);                /* append nextchar */
5494:         ptoken += strlen(nextchar);             /* point to null terminator*/
5495:         esclen += strlen(nextchar); }           /* and bump escape length */
5496:     break; }                                    /* stop checking prefixes */
5497: /* --- every \ must be followed by at least one char, e.g., \[ --- */
5498: if ( esclen < 1 )                               /* \ followed by non-alpha */
5499:   *ptoken++ = *expression++;                    /*copy non-alpha, bump ptrs*/
5500: *ptoken = '\000';                               /* null-terminate token */
5501: /* --- check for \hspace* or other starred commands --- */
5502: for ( iprefix=0; starred[iprefix] != NULL; iprefix++ ) /* run thru list */
5503:  if ( strcmp(chartoken,starred[iprefix]) == 0 ) /* have an exact match */
5504:   if ( *expression == '*' )                     /* follows by a * */
5505:    { *ptoken++ = *expression++;                 /* copy * and bump ptr */
5506:      *ptoken = '\000';                          /* null-terminate token */
5507:      break; }                                   /* stop checking */
5508: /* --- respect spaces in text mode, except first space after \escape --- */
5509: if ( esclen >= 1 ) {                            /*only for alpha \sequences*/
5510:   if ( istextmode )                             /* in \rm or \it text mode */
5511:    if ( isthischar(*expression,WHITEDELIM) )    /* delim follows \sequence */
5512:     expression++; }                             /* so flush delim */
5513: /* --- back to caller --- */
5514: end_of_job:
5515:   if ( msgfp!=NULL && msglevel>=999 )
5516:     { fprintf(msgfp,"texchar> returning token = \"%s\"\n",chartoken);
5517:       fflush(msgfp); }
5518:   return ( expression );                        /*ptr to 1st non-alpha char*/
5519: } /* --- end-of-function texchar() --- */
5520: 
5521: 
5522: /* ==========================================================================
5523:  * Function:    texsubexpr (expression,subexpr,maxsubsz,
5524:  *              left,right,isescape,isdelim)
5525:  * Purpose:     scans expression, returning everything between a balanced
5526:  *              left{...right} subexpression if the first non-whitespace
5527:  *              char of expression is an (escaped or unescaped) left{,
5528:  *              or just the next texchar() otherwise,
5529:  *              and a pointer to the first expression char past that.
5530:  * --------------------------------------------------------------------------
5531:  * Arguments:   expression (I)  char * to first char of null-terminated
5532:  *                              string containing valid LaTeX expression
5533:  *                              to be scanned
5534:  *              subexpr (O)     char * to null-terminated string returning
5535:  *                              either everything between a balanced {...}
5536:  *                              subexpression if the first char is {,
5537:  *                              or the next texchar() otherwise.
5538:  *              maxsubsz (I)    int containing max #bytes returned
5539:  *                              in subexpr buffer (0 means unlimited)
5540:  *              left (I)        char * specifying allowable left delimiters
5541:  *                              that begin subexpression, e.g., "{[(<"
5542:  *              right (I)       char * specifying matching right delimiters
5543:  *                              in the same order as left, e.g., "}])>"
5544:  *              isescape (I)    int controlling whether escaped and/or
5545:  *                              unescaped left,right are matched;
5546:  *                              see isbrace() comments below for details.
5547:  *              isdelim (I)     int containing true (non-zero) to return
5548:  *                              the leading left and trailing right delims
5549:  *                              (if any were found) along with subexpr,
5550:  *                              or containing false=0 to return subexpr
5551:  *                              without its delimiters
5552:  * --------------------------------------------------------------------------
5553:  * Returns:     ( char * )      ptr to the first char of expression
5554:  *                              past returned subexpr (see Notes),
5555:  *                              or NULL for any parsing error.
5556:  * --------------------------------------------------------------------------
5557:  * Notes:     o If subexpr is of the form left{...right},
5558:  *              the outer {}'s are returned as part of subexpr
5559:  *              if isdelim is true; if isdelim is false the {}'s aren't
5560:  *              returned.  In either case the returned pointer is
5561:  *              *always* bumped past the closing right}, even if
5562:  *              that closing right} isn't returned in subexpr.
5563:  *            o If subexpr is not of the form left{...right},
5564:  *              the returned pointer is on the character immediately
5565:  *              following the last character returned in subexpr
5566:  *            o \. acts as LaTeX \right. and matches any \left(
5567:  *              And it also acts as a LaTeX \left. and matches any \right)
5568:  * ======================================================================= */
5569: /* --- entry point --- */
5570: FUNCSCOPE char *texsubexpr ( char *expression, char *subexpr, int maxsubsz,
5571:                        char *left, char *right, int isescape, int isdelim )
5572: {
5573: /* -------------------------------------------------------------------------
5574: Allocations and Declarations
5575: -------------------------------------------------------------------------- */
5576: /*char  *texchar();*/           /*next char (or \sequence) from expression*/
5577: char    *leftptr, leftdelim[256] = "(\000", /* left( found in expression */
5578:         rightdelim[256] = ")\000"; /* and matching right) */
5579: char    *origexpression=expression, *origsubexpr=subexpr; /*original inputs*/
5580: /*char  *strtexchr(), *texleft();*/ /* check for \left, and get it */
5581: int     gotescape = 0,          /* true if leading char of expression is \ */
5582:         prevescape = 0;         /* while parsing, true if preceding char \ */
5583: int     isbrace();              /* check for left,right braces */
5584: int     isanyright = 1;         /* true matches any right with left, (...] */
5585: int     isleftdot = 0;          /* true if left brace is a \. */
5586: int     nestlevel = 1;          /* current # of nested braces */
5587: int     subsz=0 /*,maxsubsz=MAXSUBXSZ*/; /*#chars in returned subexpr buffer*/
5588: /* -------------------------------------------------------------------------
5589: skip leading whitespace and just return the next char if it's not {
5590: -------------------------------------------------------------------------- */
5591: /* --- skip leading whitespace and error check for end-of-string --- */
5592: *subexpr = '\000';                              /* init in case of error */
5593: if ( expression == NULL ) return(NULL);         /*can't dereference null ptr*/
5594: skipwhite(expression);                          /* leading whitespace gone */
5595: if ( *expression == '\000' ) return(NULL);      /* nothing left to scan */
5596: /* --- set maxsubsz --- */
5597: if ( maxsubsz < 1 ) maxsubsz = MAXSUBXSZ-2;     /* input 0 means unlimited */
5598: /* --- check for escape --- */
5599: if ( isthischar(*expression,ESCAPE) )           /* expression is escaped */
5600:   gotescape = 1;                                /* so set flag accordingly */
5601: /* --- check for \left...\right --- */
5602: if ( gotescape )                                /* begins with \ */
5603:  if ( memcmp(expression+1,"left",4) )           /* and followed by left */
5604:   if ( strchr(left,'l') != NULL )               /* caller wants \left's */
5605:    if ( strtexchr(expression,"\\left") == expression ) /*expression=\left...*/
5606:     { char *pright = texleft(expression,subexpr,maxsubsz, /* find ...\right*/
5607:         (isdelim?NULL:leftdelim),rightdelim);
5608:       if ( isdelim ) strcat(subexpr,rightdelim); /* caller wants delims */
5609:       return ( pright );                        /*back to caller past \right*/
5610:     } /* --- end-of-if(expression=="\\left") --- */
5611: /* --- if first char isn't left{ or script, just return it to caller --- */
5612: if ( !isbrace(expression,left,isescape) ) {     /* not a left{ */
5613:   if ( !isthischar(*expression,SCRIPTS) )       /* and not a script */
5614:     return ( texchar(expression,subexpr) );     /* next char to caller */
5615:   else /* --- kludge for super/subscripts to accommodate texscripts() --- */
5616:     { *subexpr++ = *expression;                 /* signal script */
5617:       *subexpr = '\000';                        /* null-terminate subexpr */
5618:       return ( expression ); } }                /* leave script in stream */
5619: /* --- extract left and find matching right delimiter --- */
5620: *leftdelim  = *(expression+gotescape);          /* the left( in expression */
5621: if ( (gotescape && *leftdelim == '.')           /* we have a left \. */
5622: ||   (gotescape && isanyright) )                /*or are matching any right*/
5623:   { isleftdot = 1;                              /* so just set flag */
5624:     *leftdelim = '\000'; }                      /* and reset leftdelim */
5625: else                                            /* find matching \right */
5626:   if ( (leftptr=strchr(left,*leftdelim)) != NULL ) /* ptr to that left( */
5627:     *rightdelim = right[(int)(leftptr-left)];   /* get the matching right) */
5628:   else                                          /* can't happen -- pgm bug */
5629:     return ( NULL );                            /*just signal eoj to caller*/
5630: /* -------------------------------------------------------------------------
5631: accumulate chars between balanced {}'s, i.e., till nestlevel returns to 0
5632: -------------------------------------------------------------------------- */
5633: /* --- first initialize by bumping past left{ or \{ --- */
5634: if ( isdelim )   *subexpr++ = *expression++;    /*caller wants { in subexpr*/
5635:   else expression++;                            /* always bump past left{ */
5636: if ( gotescape ) {                              /*need to bump another char*/
5637:   if ( isdelim ) *subexpr++ = *expression++;    /* caller wants char, too */
5638:   else expression++; }                          /* else just bump past it */
5639: /* --- set maximum size for numerical arguments --- */
5640: if ( 0 )                                        /* check turned on or off? */
5641:  if ( !isescape && !isdelim )                   /*looking for numerical arg*/
5642:   maxsubsz = 96;                                /* set max arg size */
5643: /* --- search for matching right} --- */
5644: while ( 1 )                                     /*until balanced right} */
5645:   {
5646:   /* --- error check for end-of-string --- */
5647:   if ( *expression == '\000' )                  /* premature end-of-string */
5648:     { if ( 0 && (!isescape && !isdelim) )       /*looking for numerical arg,*/
5649:         { expression = origexpression;          /* so end-of-string is error*/
5650:           subexpr = origsubexpr; }              /* so reset all ptrs */
5651:       if ( isdelim ) {                          /* generate fake right */
5652:         if ( gotescape )                        /* need escaped right */
5653:           { *subexpr++ = '\\';                  /* set escape char */
5654:             *subexpr++ = '.'; }                 /* and fake \right. */
5655:         else                                    /* escape not wanted */
5656:             *subexpr++ = *rightdelim; }         /* so fake actual right */
5657:       *subexpr = '\000';                        /* null-terminate subexpr */
5658:       return ( expression ); }                  /* back with final token */
5659:   /* --- check preceding char for escape --- */
5660:   if ( isthischar(*(expression-1),ESCAPE) )     /* previous char was \ */
5661:         prevescape = 1-prevescape;              /* so flip escape flag */
5662:   else  prevescape = 0;                         /* or turn flag off */
5663:   /* --- check for { and } (un/escaped as per leading left) --- */
5664:   if ( gotescape == prevescape )                /* escaped iff leading is */
5665:     { /* --- check for (closing) right delim and see if we're done --- */
5666:       if ( isthischar(*expression,rightdelim)   /* found a right} */
5667:       ||   (isleftdot && isthischar(*expression,right)) /*\left. matches all*/
5668:       ||   (prevescape && isthischar(*expression,".")) ) /*or found \right. */
5669:         if ( --nestlevel < 1 )                  /*\right balances 1st \left*/
5670:           { if ( isdelim )                      /*caller wants } in subexpr*/
5671:               *subexpr++ = *expression;         /* so end subexpr with } */
5672:             else                                /*check for \ before right}*/
5673:               if ( prevescape )                 /* have unwanted \ */
5674:                 *(subexpr-1) = '\000';          /* so replace it with null */
5675:             *subexpr = '\000';                  /* null-terminate subexpr */
5676:             return ( expression+1 ); }          /* back with char after } */
5677:       /* --- check for (another) left{ --- */
5678:       if ( isthischar(*expression,leftdelim)    /* found another left{ */
5679:       ||   (isleftdot && isthischar(*expression,left)) ) /* any left{ */
5680:         nestlevel++;
5681:     } /* --- end-of-if(gotescape==prevescape) --- */
5682:   /* --- not done, so copy char to subexpr and continue with next char --- */
5683:   if ( ++subsz < maxsubsz-5 )                   /* more room in subexpr */
5684:     *subexpr++ = *expression;                   /* so copy char and bump ptr*/
5685:   expression++;                                 /* bump expression ptr */
5686:   } /* --- end-of-while(1) --- */
5687: } /* --- end-of-function texsubexpr() --- */
5688: 
5689: 
5690: /* ==========================================================================
5691:  * Function:    texleft (expression,subexpr,maxsubsz,ldelim,rdelim)
5692:  * Purpose:     scans expression, starting after opening \left,
5693:  *              and returning ptr after matching closing \right.
5694:  *              Everything between is returned in subexpr, if given.
5695:  *              Likewise, if given, ldelim returns delimiter after \left
5696:  *              and rdelim returns delimiter after \right.
5697:  *              If ldelim is given, the returned subexpr doesn't include it.
5698:  *              If rdelim is given, the returned pointer is after that delim.
5699:  * --------------------------------------------------------------------------
5700:  * Arguments:   expression (I)  char * to first char of null-terminated
5701:  *                              string immediately following opening \left
5702:  *              subexpr (O)     char * to null-terminated string returning
5703:  *                              either everything between balanced
5704:  *                              \left ... \right.  If leftdelim given,
5705:  *                              subexpr does _not_ contain that delimiter.
5706:  *              maxsubsz (I)    int containing max #bytes returned
5707:  *                              in subexpr buffer (0 means unlimited)
5708:  *              ldelim (O)      char * returning delimiter following
5709:  *                              opening \left
5710:  *              rdelim (O)      char * returning delimiter following
5711:  *                              closing \right
5712:  * --------------------------------------------------------------------------
5713:  * Returns:     ( char * )      ptr to the first char of expression
5714:  *                              past closing \right, or past closing
5715:  *                              right delimiter if rdelim!=NULL,
5716:  *                              or NULL for any error.
5717:  * --------------------------------------------------------------------------
5718:  * Notes:     o
5719:  * ======================================================================= */
5720: /* --- entry point --- */
5721: FUNCSCOPE char *texleft ( char *expression, char *subexpr, int maxsubsz,
5722:                           char *ldelim, char *rdelim )
5723: {
5724: /* -------------------------------------------------------------------------
5725: Allocations and Declarations
5726: -------------------------------------------------------------------------- */
5727: char    /**texchar(),*/                 /* get delims after \left,\right */
5728:         /**strtexchr(),*/ *pright=expression; /* locate matching \right */
5729: static  char left[16]="\\left", right[16]="\\right"; /* tex delimiters */
5730: int     sublen = 0;                     /* #chars between \left...\right */
5731: /* -------------------------------------------------------------------------
5732: initialization
5733: -------------------------------------------------------------------------- */
5734: /* --- init output --- */
5735: if ( subexpr != NULL ) *subexpr = '\000'; /* init subexpr, if given */
5736: if ( ldelim  != NULL ) *ldelim  = '\000'; /* init ldelim,  if given */
5737: if ( rdelim  != NULL ) *rdelim  = '\000'; /* init rdelim,  if given */
5738: /* --- check args --- */
5739: if ( expression == NULL ) goto end_of_job; /* no input supplied */
5740: if ( *expression == '\000' ) goto end_of_job; /* nothing after \left */
5741: /* --- determine left delimiter  --- */
5742: if ( ldelim != NULL )                   /* caller wants left delim */
5743:  { skipwhite(expression);               /* interpret \left ( as \left( */
5744:    expression = texchar(expression,ldelim); } /*delim from expression*/
5745: /* -------------------------------------------------------------------------
5746: locate \right balancing opening \left
5747: -------------------------------------------------------------------------- */
5748: /* --- first \right following \left --- */
5749: if ( (pright=strtexchr(expression,right)) /* look for \right after \left */
5750: !=   NULL ) {                           /* found it */
5751:  /* --- find matching \right by pushing past any nested \left's --- */
5752:  char *pleft = expression;              /* start after first \left( */
5753:  while ( 1 ) {                          /*break when matching \right found*/
5754:   /* -- locate next nested \left if there is one --- */
5755:   if ( (pleft=strtexchr(pleft,left))    /* find next \left */
5756:   ==   NULL ) break;                    /*no more, so matching \right found*/
5757:   pleft += strlen(left);                /* push ptr past \left token */
5758:   if ( pleft >= pright ) break;         /* not nested if \left after \right*/
5759:   /* --- have nested \left, so push forward to next \right --- */
5760:   if ( (pright=strtexchr(pright+strlen(right),right)) /* find next \right */
5761:   ==   NULL ) break;                    /* ran out of \right's */
5762:   } /* --- end-of-while(1) --- */
5763:  } /* --- end-of-if(pright!=NULL) --- */
5764: /* --- set subexpression length, push pright past \right --- */
5765: if ( pright != (char *)NULL )           /* found matching \right */
5766:  { sublen = (int)(pright-expression);   /* #chars between \left...\right */
5767:    pright += strlen(right); }           /* so push pright past \right */
5768: /* -------------------------------------------------------------------------
5769: get rightdelim and subexpr between \left...\right
5770: -------------------------------------------------------------------------- */
5771: /* --- get delimiter following \right --- */
5772: if ( rdelim != NULL ) {                 /* caller wants right delim */
5773:  if ( pright == (char *)NULL )          /* assume \right. at end of exprssn*/
5774:   { strcpy(rdelim,".");                 /* set default \right. */
5775:     sublen = strlen(expression);        /* use entire remaining expression */
5776:     pright = expression + sublen; }     /* and push pright to end-of-string*/
5777:  else                                   /* have explicit matching \right */
5778:   { skipwhite(pright);                  /* interpret \right ) as \right) */
5779:     pright = texchar(pright,rdelim);    /* pull delim from expression */
5780:     if ( *rdelim == '\000' ) strcpy(rdelim,"."); } } /* or set \right. */
5781: /* --- get subexpression between \left...\right --- */
5782: if ( sublen > 0 )                       /* have subexpr */
5783:  if ( subexpr != NULL ) {               /* and caller wants it */
5784:   if ( maxsubsz > 0 ) sublen = min2(sublen,maxsubsz-1); /* max buffer size */
5785:   memcpy(subexpr,expression,sublen);    /* stuff between \left...\right */
5786:   subexpr[sublen] = '\000'; }           /* null-terminate subexpr */
5787: end_of_job:
5788:   if ( msglevel>=99 && msgfp!=NULL )
5789:     { fprintf(msgfp,"texleft> ldelim=%s, rdelim=%s, subexpr=%.128s\n",
5790:       (ldelim==NULL?"none":ldelim),(rdelim==NULL?"none":rdelim),
5791:       (subexpr==NULL?"none":subexpr)); fflush(msgfp); }
5792:   return ( pright );
5793: } /* --- end-of-function texleft --- */
5794: 
5795: 
5796: /* ==========================================================================
5797:  * Function:    texscripts ( expression, subscript, superscript, which )
5798:  * Purpose:     scans expression, returning subscript and/or superscript
5799:  *              if expression is of the form _x^y or ^{x}_{y},
5800:  *              or any (valid LaTeX) permutation of the above,
5801:  *              and a pointer to the first expression char past "scripts"
5802:  * --------------------------------------------------------------------------
5803:  * Arguments:   expression (I)  char * to first char of null-terminated
5804:  *                              string containing valid LaTeX expression
5805:  *                              to be scanned
5806:  *              subscript (O)   char * to null-terminated string returning
5807:  *                              subscript (without _), if found, or "\000"
5808:  *              superscript (O) char * to null-terminated string returning
5809:  *                              superscript (without ^), if found, or "\000"
5810:  *              which (I)       int containing 1 for subscript only,
5811:  *                              2 for superscript only, >=3 for either/both
5812:  * --------------------------------------------------------------------------
5813:  * Returns:     ( char * )      ptr to the first char of expression
5814:  *                              past returned "scripts" (unchanged
5815:  *                              except for skipped whitespace if
5816:  *                              neither subscript nor superscript found),
5817:  *                              or NULL for any parsing error.
5818:  * --------------------------------------------------------------------------
5819:  * Notes:     o an input expression like ^a^b_c will return superscript="b",
5820:  *              i.e., totally ignoring all but the last "script" encountered
5821:  * ======================================================================= */
5822: /* --- entry point --- */
5823: FUNCSCOPE char *texscripts ( char *expression, char *subscript,
5824:                              char *superscript, int which )
5825: {
5826: /* -------------------------------------------------------------------------
5827: Allocations and Declarations
5828: -------------------------------------------------------------------------- */
5829: /*char  *texsubexpr();*/        /* next subexpression from expression */
5830: int     gotsub=0, gotsup=0;     /* check that we don't eat, e.g., x_1_2 */
5831: /* -------------------------------------------------------------------------
5832: init "scripts"
5833: -------------------------------------------------------------------------- */
5834: if ( subscript != NULL ) *subscript = '\000';   /*init in case no subscript*/
5835: if ( superscript!=NULL ) *superscript = '\000'; /*init in case no super*/
5836: /* -------------------------------------------------------------------------
5837: get subscript and/or superscript from expression
5838: -------------------------------------------------------------------------- */
5839: while ( expression != NULL ) {
5840:   skipwhite(expression);                        /* leading whitespace gone */
5841:   if ( *expression == '\000' ) return(expression); /* nothing left to scan */
5842:   if ( isthischar(*expression,SUBSCRIPT)        /* found _ */
5843:   &&   (which==1 || which>2 ) )                 /* and caller wants it */
5844:     { if ( gotsub                               /* found 2nd subscript */
5845:       ||   subscript == NULL ) break;           /* or no subscript buffer */
5846:       gotsub = 1;                               /* set subscript flag */
5847:       expression = texsubexpr(expression+1,subscript,0,"{","}",0,0); }
5848:   else                                          /* no _, check for ^ */
5849:     if ( isthischar(*expression,SUPERSCRIPT)    /* found ^ */
5850:     &&   which>=2  )                            /* and caller wants it */
5851:       { if ( gotsup                             /* found 2nd superscript */
5852:         ||   superscript == NULL ) break;       /* or no superscript buffer*/
5853:         gotsup = 1;                             /* set superscript flag */
5854:         expression = texsubexpr(expression+1,superscript,0,"{","}",0,0); }
5855:     else                                        /* neither _ nor ^ */
5856:       return ( expression );                    /*return ptr past "scripts"*/
5857:   } /* --- end-of-while(expression!=NULL) --- */
5858: return ( expression );
5859: } /* --- end-of-function texscripts() --- */
5860: 
5861: 
5862: /* ==========================================================================
5863:  * Function:    isbrace ( expression, braces, isescape )
5864:  * Purpose:     checks leading char(s) of expression for a brace,
5865:  *              either escaped or unescaped depending on isescape,
5866:  *              except that { and } are always matched, if they're
5867:  *              in braces, regardless of isescape.
5868:  * --------------------------------------------------------------------------
5869:  * Arguments:   expression (I)  char * to first char of null-terminated
5870:  *                              string containing a valid LaTeX expression
5871:  *                              whose leading char(s) are checked for braces
5872:  *                              that begin subexpression, e.g., "{[(<"
5873:  *              braces (I)      char * specifying matching brace delimiters
5874:  *                              to be checked for, e.g., "{[(<" or "}])>"
5875:  *              isescape (I)    int containing 0 to match only unescaped
5876:  *                              braces, e.g., (...) or {...}, etc,
5877:  *                              or containing 1 to match only escaped
5878:  *                              braces, e.g., \(...\) or \[...\], etc,
5879:  *                              or containing 2 to match either.
5880:  *                              But note: if {,} are in braces
5881:  *                              then they're *always* matched whether
5882:  *                              escaped or not, regardless of isescape.
5883:  * --------------------------------------------------------------------------
5884:  * Returns:     ( int )         1 if the leading char(s) of expression
5885:  *                              is a brace, or 0 if not.
5886:  * --------------------------------------------------------------------------
5887:  * Notes:     o
5888:  * ======================================================================= */
5889: /* --- entry point --- */
5890: FUNCSCOPE int isbrace ( char *expression, char *braces, int isescape )
5891: {
5892: /* -------------------------------------------------------------------------
5893: Allocations and Declarations
5894: -------------------------------------------------------------------------- */
5895: int     gotescape = 0,          /* true if leading char is an escape */
5896:         gotbrace = 0;           /*true if first non-escape char is a brace*/
5897: /* -------------------------------------------------------------------------
5898: check for brace
5899: -------------------------------------------------------------------------- */
5900: /* --- first check for end-of-string or \= ligature --- */
5901: if ( *expression == '\000'                      /* nothing to check */
5902: ||   isligature ) goto end_of_job;              /* have a \= ligature */
5903: /* --- check leading char for escape --- */
5904: if ( isthischar(*expression,ESCAPE) )           /* expression is escaped */
5905:   { gotescape = 1;                              /* so set flag accordingly */
5906:     expression++; }                             /* and bump past escape */
5907: /* --- check (maybe next char) for brace --- */
5908: if ( isthischar(*expression,braces) )           /* expression is braced */
5909:   gotbrace = 1;                                 /* so set flag accordingly */
5910: if ( gotescape && *expression == '.' )          /* \. matches any brace */
5911:   gotbrace = 1;                                 /* set flag */
5912: /* --- check for TeX brace { or } --- */
5913: if ( gotbrace && isthischar(*expression,"{}") ) /*expression has TeX brace*/
5914:   if ( isescape ) isescape = 2;                 /* reset escape flag */
5915: /* -------------------------------------------------------------------------
5916: back to caller
5917: -------------------------------------------------------------------------- */
5918: end_of_job:
5919:  if ( msglevel>=999 && msgfp!=NULL )
5920:   { fprintf(msgfp,"isbrace> expression=%.8s, gotbrace=%d (isligature=%d)\n",
5921:     expression,gotbrace,isligature); fflush(msgfp); }
5922:  if ( gotbrace &&                               /* found a brace */
5923:      ( isescape==2 ||                           /* escape irrelevant */
5924:        gotescape==isescape )                    /* un/escaped as requested */
5925:    ) return ( 1 );  return ( 0 );               /* return 1,0 accordingly */
5926: } /* --- end-of-function isbrace() --- */
5927: 
5928: 
5929: /* ==========================================================================
5930:  * Function:    preamble ( expression, size, subexpr )
5931:  * Purpose:     parses $-terminated preamble, if present, at beginning
5932:  *              of expression, re-setting size if necessary, and
5933:  *              returning any other parameters besides size in subexpr.
5934:  * --------------------------------------------------------------------------
5935:  * Arguments:   expression (I)  char * to first char of null-terminated
5936:  *                              string containing LaTeX expression possibly
5937:  *                              preceded by $-terminated preamble
5938:  *              size (I/O)      int *  containing 0-4 default font size,
5939:  *                              and returning size modified by first
5940:  *                              preamble parameter (or unchanged)
5941:  *              subexpr(O)      char *  returning any remaining preamble
5942:  *                              parameters past size
5943:  * --------------------------------------------------------------------------
5944:  * Returns:     ( char * )      ptr to first char past preamble in expression
5945:  *                              or NULL for any parsing error.
5946:  * --------------------------------------------------------------------------
5947:  * Notes:     o size can be any number >=0. If preceded by + or -, it's
5948:  *              interpreted as an increment to input size; otherwise
5949:  *              it's interpreted as the size.
5950:  *            o if subexpr is passed as NULL ptr, then returned expression
5951:  *              ptr will have "flushed" and preamble parameters after size
5952:  * ======================================================================= */
5953: /* --- entry point --- */
5954: FUNCSCOPE char *preamble ( char *expression, int *size, char *subexpr )
5955: {
5956: /* -------------------------------------------------------------------------
5957: Allocations and Declarations
5958: -------------------------------------------------------------------------- */
5959: char    pretext[512], *prep=expression, /*pream from expression, ptr into it*/
5960:         *dollar, *comma;                /* preamble delimiters */
5961: int     prelen = 0,                     /* preamble length */
5962:         sizevalue = 0,                  /* value of size parameter */
5963:         isfontsize = 0,                 /*true if leading fontsize present*/
5964:         isdelta = 0;                    /*true to increment passed size arg*/
5965: /* -------------------------------------------------------------------------
5966: initialization
5967: -------------------------------------------------------------------------- */
5968: if ( subexpr != NULL )                  /* caller passed us an address */
5969:   *subexpr = '\000';                    /* so init assuming no preamble */
5970: if ( expression == NULL ) goto end_of_job; /* no input */
5971: if ( *expression == '\000' ) goto end_of_job; /* input is an empty string */
5972: /* -------------------------------------------------------------------------
5973: process preamble if present
5974: -------------------------------------------------------------------------- */
5975: /*process_preamble:*/
5976: if ( (dollar=strchr(expression,'$'))    /* $ signals preceding preamble */
5977: !=   NULL ) {                           /* found embedded $ */
5978:  if ( (prelen = (int)(dollar-expression)) /*#chars in expression preceding $*/
5979:  > 0 ) {                                /* must have preamble preceding $ */
5980:   if ( prelen < 65 ) {                  /* too long for a prefix */
5981:    memcpy(pretext,expression,prelen);   /* local copy of preamble */
5982:    pretext[prelen] = '\000';            /* null-terminated */
5983:    if ( strchr(pretext,*(ESCAPE))==NULL /*shouldn't be an escape in preamble*/
5984:    &&   strchr(pretext,'{') == NULL ) { /*shouldn't be a left{ in preamble*/
5985:     /* --- skip any leading whitespace  --- */
5986:     prep = pretext;                     /* start at beginning of preamble */
5987:     skipwhite(prep);                    /* skip any leading white space */
5988:     /* --- check for embedded , or leading +/- (either signalling size) --- */
5989:     if ( isthischar(*prep,"+-") )       /* have leading + or - */
5990:      isdelta = 1;                       /* so use size value as increment */
5991:     comma = strchr(pretext,',');        /* , signals leading size param */
5992:     /* --- process leading size parameter if present --- */
5993:     if ( comma != NULL                  /* size param explicitly signalled */
5994:     ||   isdelta || isdigit(*prep) ) {  /* or inferred implicitly */
5995:       /* --- parse size parameter and reset size accordingly --- */
5996:       if( comma != NULL ) *comma = '\000';/*, becomes null, terminating size*/
5997:       sizevalue = atoi(prep);           /* convert size string to integer */
5998:       if ( size != NULL )               /* caller passed address for size */
5999:         *size = (isdelta? *size+sizevalue : sizevalue); /* so reset size */
6000:       /* --- finally, set flag and shift size parameter out of preamble --- */
6001:       isfontsize = 1;                   /*set flag showing font size present*/
6002:       if ( comma != NULL )              /*2/15/12-isn't this superfluous???*/
6003:         {strsqueezep(pretext,comma+1);} /* squeeze out leading size param */
6004:      } /* --- end-of-if(comma!=NULL||etc) --- */
6005:     /* --- copy any preamble params following size to caller's subexpr --- */
6006:     if ( comma != NULL || !isfontsize ) /*preamb contains params past size*/
6007:      if ( subexpr != NULL )             /* caller passed us an address */
6008:       strcpy(subexpr,pretext);          /*so return extra params to caller*/
6009:     /* --- finally, set prep to shift preamble out of expression --- */
6010:     prep = expression + prelen+1;       /* set prep past $ in expression */
6011:     } /* --- end-of-if(strchr(pretext,*ESCAPE)==NULL) --- */
6012:    } /* --- end-of-if(prelen<65) --- */
6013:   } /* --- end-of-if(prelen>0) --- */
6014:  else {                                 /* $ is first char of expression */
6015:   int ndollars = 0;                     /* number of $...$ pairs removed */
6016:   prep = expression;                    /* start at beginning of expression*/
6017:   while ( *prep == '$' ) {              /* remove all matching $...$'s */
6018:    int  explen = strlen(prep)-1;        /* index of last char in expression*/
6019:    if ( explen < 2 ) break;             /* no $...$'s left to remove */
6020:    if ( prep[explen] != '$' ) break;    /* unmatched $ */
6021:    prep[explen] = '\000';               /* remove trailing $ */
6022:    prep++;                              /* and remove matching leading $ */
6023:    ndollars++;                          /* count another pair removed */
6024:    } /* --- end-of-while(*prep=='$') --- */
6025:   ispreambledollars = ndollars;         /* set flag to fix \displaystyle */
6026:   if ( ndollars == 1 )                  /* user submitted $...$ expression */
6027:     isdisplaystyle = 0;                 /* so set \textstyle */
6028:   if ( ndollars > 1 )                   /* user submitted $$...$$ */
6029:     isdisplaystyle = 2;                 /* so set \displaystyle */
6030:   /*goto process_preamble;*/            /*check for preamble after leading $*/
6031:   } /* --- end-of-if/else(prelen>0) --- */
6032:  } /* --- end-of-if(dollar!=NULL) --- */
6033: /* -------------------------------------------------------------------------
6034: back to caller
6035: -------------------------------------------------------------------------- */
6036: end_of_job:
6037:   return ( prep );                      /*expression, or ptr past preamble*/
6038: } /* --- end-of-function preamble() --- */
6039: 
6040: 
6041: /* ==========================================================================
6042:  * Function:    mimeprep ( expression )
6043:  * Purpose:     preprocessor for mimeTeX input, e.g.,
6044:  *              (a) removes comments,
6045:  *              (b) converts \left( to \( and \right) to \),
6046:  *              (c) xlates &html; special chars to equivalent latex
6047:  *              Should only be called once (after unescape_url())
6048:  * --------------------------------------------------------------------------
6049:  * Arguments:   expression (I/O) char * to first char of null-terminated
6050:  *                              string containing mimeTeX/LaTeX expression,
6051:  *                              and returning preprocessed string
6052:  * --------------------------------------------------------------------------
6053:  * Returns:     ( char * )      ptr to input expression,
6054:  *                              or NULL for any parsing error.
6055:  * --------------------------------------------------------------------------
6056:  * Notes:     o
6057:  * ======================================================================= */
6058: /* --- entry point --- */
6059: FUNCSCOPE char *mimeprep ( char *expression )
6060: {
6061: /* -------------------------------------------------------------------------
6062: Allocations and Declarations
6063: -------------------------------------------------------------------------- */
6064: char    *expptr=expression,             /* ptr within expression */
6065:         *tokptr=NULL,                   /*ptr to token found in expression*/
6066:         /**texsubexpr(),*/ argval[8192]; /*parse for macro args after token*/
6067: /*char  *strchange();*/                 /* change leading chars of string */
6068: /*int   strreplace();*/                 /* replace nnn with actual num, etc*/
6069: /*char  *strwstr();*/                   /*use strwstr() instead of strstr()*/
6070: /*char  *findbraces();*/                /*find left { and right } for \atop*/
6071: int     idelim=0,                       /* left- or right-index */
6072:         isymbol=0;                      /*symbols[],rightcomment[],etc index*/
6073: int     xlateleft = 0;                  /* true to xlate \left and \right */
6074: /* ---
6075:  * comments
6076:  * -------- */
6077: char    *leftptr=NULL;                  /* find leftcomment in expression */
6078: static  char *leftcomment = "%%",       /* open comment */
6079:         *rightcomment[] = {"\n", "%%", NULL}; /* close comments */
6080: /* ---
6081:  * special long (more than 1-char) \left and \right delimiters
6082:  * ----------------------------------------------------------- */
6083: static  char *leftfrom[] =              /* xlate any \left suffix... */
6084:    { "\\|",                             /* \left\| */
6085:      "\\{",                             /* \left\{ */
6086:      "\\langle",                        /* \left\langle */
6087:      NULL } ; /* --- end-of-leftfrom[] --- */
6088: static  char *leftto[] =                /* ...to this instead */
6089:    { "=",                               /* = */
6090:      "{",                               /* { */
6091:      "<",                               /* < */
6092:      NULL } ; /* --- end-of-leftto[] --- */
6093: static  char *rightfrom[] =             /* xlate any \right suffix... */
6094:    { "\\|",                             /* \right\| */
6095:      "\\}",                             /* \right\} */
6096:      "\\rangle",                        /* \right\rangle */
6097:      NULL } ; /* --- end-of-rightfrom[] --- */
6098: static  char *rightto[] =               /* ...to this instead */
6099:    { "=",                               /* = */
6100:      "}",                               /* } */
6101:      ">",                               /* > */
6102:      NULL } ; /* --- end-of-rightto[] --- */
6103: /* ---
6104:  * { \atop }-like commands
6105:  * ----------------------- */
6106: char    *atopsym=NULL;                  /* atopcommands[isymbol] */
6107: static  char *atopcommands[] =          /* list of {a+b\command c+d}'s */
6108:    { "\\over",                          /* plain tex for \frac */
6109:      "\\choose",                        /* binomial coefficient */
6110:    #ifndef NOATOP                       /*noatop preserves old mimeTeX rule*/
6111:      "\\atop",
6112:    #endif
6113:      NULL } ; /* --- end-of-atopcommands[] --- */
6114: static  char *atopdelims[] =            /* delims for atopcommands[] */
6115:    { NULL, NULL,                        /* \\over has no delims */
6116:      "\\left(", "\\right)",             /* \\choose has ( ) delims*/
6117:    #ifndef NOATOP                       /*noatop preserves old mimeTeX rule*/
6118:      NULL, NULL,                        /* \\atop has no delims */
6119:    #endif
6120:      NULL, NULL } ; /* --- end-of-atopdelims[] --- */
6121: /* ---
6122:  * html special/escape chars converted to latex equivalents
6123:  * -------------------------------------------------------- */
6124: char    *htmlsym=NULL;                  /* symbols[isymbol].html */
6125: static  struct { char *html; char *args; char *latex; } symbols[] =
6126:  { /* --------------------------------------------
6127:      user-supplied newcommands
6128:    -------------------------------------------- */
6129:    #ifdef NEWCOMMANDS                   /* -DNEWCOMMANDS=\"filename.h\" */
6130:      #include NEWCOMMANDS
6131:    #endif
6132:    /* --------------------------------------------
6133:      Specials        termchar  value...
6134:    -------------------------------------------- */
6135:    { "\\version",       NULL, "{\\small\\red\\text \\fbox{\\begin{gather}"
6136:         "mime\\TeX version \\versionnumber \\\\"
6137:         "last revised \\revisiondate \\\\ \\copyrighttext \\\\"
6138:         "see \\homepagetext for details \\end{gather}}}" },
6139:    { "\\copyright",     NULL,
6140:         "{\\small\\red\\text \\fbox{\\begin{gather}"
6141:         "mimeTeX \\copyrighttext \\\\"
6142:         "see \\homepagetext for details \\end{gather}}}" },
6143:    { "\\versionnumber", NULL, "{\\text " VERSION "}" },
6144:    { "\\revisiondate",  NULL, "{\\text " REVISIONDATE "}" },
6145:    { "\\copyrighttext", NULL, "{\\text " COPYRIGHTTEXT ".}" },
6146:    { "\\homepagetext",  NULL,
6147:         "{\\text http://www.forkosh.com/mimetex.html}" },
6148:    /* --------------------------------------------
6149:      Cyrillic  termchar  mimeTeX equivalent...
6150:    -------------------------------------------- */
6151:    { "\\\'G",   "embed\\","{\\acute{G}}" },
6152:    { "\\\'g",   "embed\\","{\\acute{g}}" },
6153:    { "\\\'K",   "embed\\","{\\acute{K}}" },
6154:    { "\\\'k",   "embed\\","{\\acute{k}}" },
6155:    { "\\u U",   "embed\\","{\\breve{U}}" },
6156:    { "\\u u",   "embed\\","{\\breve{u}}" },
6157:    /*{ "\\\"E", "embed\\","{\\ddot{E}}" },*/
6158:    /*{ "\\\"e", "embed\\","{\\ddot{e}}" },*/
6159:    { "\\\"I",   "embed\\","{\\ddot{\\=I}}" },
6160:    { "\\\"\\i", "embed\\","{\\ddot{\\=\\i}}" },
6161:    /* --------------------------------------------
6162:      LaTeX Macro #args,default  template...
6163:    -------------------------------------------- */
6164:    { "\\lvec",  "2n",   "{#2_1,\\cdots,#2_{#1}}" },
6165:    { "\\grave", "1",    "{\\stackrel{\\Huge\\gravesym}{#1}}" }, /* \grave */
6166:    { "\\acute", "1",    "{\\stackrel{\\Huge\\acutesym}{#1}}" }, /* \acute */
6167:    { "\\check", "1",    "{\\stackrel{\\Huge\\checksym}{#1}}" }, /* \check */
6168:    { "\\breve", "1",    "{\\stackrel{\\Huge\\brevesym}{#1}}" }, /* \breve */
6169:    { "\\buildrel","3",  "{\\stackrel{#1}{#3}}" }, /* ignore #2 = \over */
6170:    { "\\overset", NULL, "\\stackrel" },         /* just an alias */
6171:    { "\\underset", "2", "\\relstack{#2}{#1}" }, /* reverse args */
6172:    { "\\dfrac", "2",    "{\\frac{#1}{#2}}" },
6173:    { "\\binom", "2",    "{\\begin{pmatrix}{#1}\\\\{#2}\\end{pmatrix}}" },
6174:    { "\\aangle","26",   "{\\boxaccent{#1}{#2}}" },
6175:    { "\\actuarial","2 ","{#1\\boxaccent{6}{#2}}" }, /*comprehensive sym list*/
6176:    { "\\boxaccent","2", "{\\fbox[,#1]{#2}}" },
6177:    { "\\password", "1", "\000" },
6178:    { "\\comment", "1",  "\000" },
6179:    /* --------------------------------------------
6180:      html char termchar  LaTeX equivalent...
6181:    -------------------------------------------- */
6182:    { "&quot",   ";",    "\"" },         /* &quot; is first, &#034; */
6183:    { "&amp",    ";",    "&" },
6184:    { "&lt",     ";",    "<" },
6185:    { "&gt",     ";",    ">" },
6186:    /*{ "&#092", ";",    "\\" },*/       /* backslash */
6187:    { "&backslash",";",  "\\" },
6188:    { "&nbsp",   ";",    "~" },
6189:    { "&iexcl",  ";",    "{\\raisebox{-2}{\\rotatebox{180}{!}}}" },
6190:    { "&brvbar", ";",    "|" },
6191:    { "&plusmn", ";",    "\\pm" },
6192:    { "&sup2",   ";",    "{{}^2}" },
6193:    { "&sup3",   ";",    "{{}^3}" },
6194:    { "&micro",  ";",    "\\mu" },
6195:    { "&sup1",   ";",    "{{}^1}" },
6196:    { "&frac14", ";",    "{\\frac14}" },
6197:    { "&frac12", ";",    "{\\frac12}" },
6198:    { "&frac34", ";",    "{\\frac34}" },
6199:    { "&iquest", ";",    "{\\raisebox{-2}{\\rotatebox{180}{?}}}" },
6200:    { "&Acirc",  ";",    "{\\rm~\\hat~A}" },
6201:    { "&Atilde", ";",    "{\\rm~\\tilde~A}" },
6202:    { "&Auml",   ";",    "{\\rm~\\ddot~A}" },
6203:    { "&Aring",  ";",    "{\\rm~A\\limits^{-1$o}}" },
6204:    { "&atilde", ";",    "{\\rm~\\tilde~a}" },
6205:    { "&yuml",   ";",    "{\\rm~\\ddot~y}" }, /* &yuml; is last, &#255; */
6206:    { "&#",      ";",    "{[\\&\\#nnn?]}" },  /* all other explicit &#nnn's */
6207:    /* --------------------------------------------
6208:      html tag     termchar    LaTeX equivalent...
6209:    -------------------------------------------- */
6210:    { "< br >",    "embed\\i", "\\\\" },
6211:    { "< br / >",  "embed\\i", "\\\\" },
6212:    { "< dd >",    "embed\\i", " \000" },
6213:    { "< / dd >",  "embed\\i", " \000" },
6214:    { "< dl >",    "embed\\i", " \000" },
6215:    { "< / dl >",  "embed\\i", " \000" },
6216:    { "< p >",     "embed\\i", " \000" },
6217:    { "< / p >",   "embed\\i", " \000" },
6218:    /* --------------------------------------------
6219:      garbage      termchar  LaTeX equivalent...
6220:    -------------------------------------------- */
6221:    { "< tex >",   "embed\\i", " \000" },
6222:    { "< / tex >", "embed\\i", " \000" },
6223:    /* --------------------------------------------
6224:      LaTeX   termchar   mimeTeX equivalent...
6225:    -------------------------------------------- */
6226:    { "\\AA",    NULL,   "{\\rm~A\\limits^{-1$o}}" },
6227:    { "\\aa",    NULL,   "{\\rm~a\\limits^{-1$o}}" },
6228:    { "\\bmod",  NULL,   "{\\hspace2{\\rm~mod}\\hspace2}" },
6229:    { "\\mod",   NULL,   "{\\hspace2{\\rm~mod}\\hspace2}" },
6230:    { "\\vdots", NULL,   "{\\raisebox3{\\rotatebox{90}{\\ldots}}}" },
6231:    { "\\dots",  NULL,   "{\\cdots}" },
6232:    { "\\cdots", NULL,   "{\\raisebox3{\\ldots}}" },
6233:    { "\\ldots", NULL,   "{\\fs4.\\hspace1.\\hspace1.}" },
6234:    { "\\ddots", NULL,   "{\\fs4\\raisebox8.\\hspace1\\raisebox4."
6235:                         "\\hspace1\\raisebox0.}"},
6236:    { "\\cong",  NULL,   "{\\raisebox{-4}{\\approx\\atop-}}" },
6237:    { "\\notin", NULL,   "{\\not\\in}" },
6238:    { "\\neq",   NULL,   "{\\not=}" },
6239:    { "\\ne",    NULL,   "{\\not=}" },
6240:    { "\\pm",    NULL,   "{\\math\\plusminus}" }, /* don't have \bf\pm */
6241:    { "\\mapsto", NULL,  "{\\rule[fs/2]{1}{5+fs}\\hspace{-99}\\to}" },
6242:    { "\\hbar",  NULL,   "{\\compose~h{{\\fs{-1}-\\atop\\vspace3}}}" },
6243:    { "\\angle", NULL, "{\\compose{\\hspace{3}\\lt}{\\circle(10,15;-80,80)}}"},
6244:    { "\\textcelsius", NULL, "{\\textdegree C}"},
6245:    { "\\textdegree", NULL, "{\\Large^{^{\\tiny\\mathbf o}}}"},
6246:    { "\\cr",    NULL,   "\\\\" },
6247:    /*{ "\\colon",       NULL,   "{:}" },*/
6248:    { "\\iiint", NULL,   "{\\int\\int\\int}\\limits" },
6249:    { "\\iint",  NULL,   "{\\int\\int}\\limits" },
6250:    { "\\Bigiint", NULL, "{\\Bigint\\Bigint}\\limits" },
6251:    { "\\bigsqcap",NULL, "{\\fs{+4}\\sqcap}" },
6252:    { "\\_",     "embed","{\\underline{\\ }}" }, /* displayed underscore */
6253:    { "!`",      NULL,   "{\\raisebox{-2}{\\rotatebox{180}{!}}}" },
6254:    { "?`",      NULL,   "{\\raisebox{-2}{\\rotatebox{180}{?}}}" },
6255:    { "^\'",     "embed","\'" }, /* avoid ^^ when re-xlating \' below */
6256:    { "\'\'\'\'","embed","^{\\fs{-1}\\prime\\prime\\prime\\prime}" },
6257:    { "\'\'\'",  "embed","^{\\fs{-1}\\prime\\prime\\prime}" },
6258:    { "\'\'",    "embed","^{\\fs{-1}\\prime\\prime}" },
6259:    { "\'",      "embed","^{\\fs{-1}\\prime}" },
6260:    { "\\rightleftharpoons",NULL,"{\\rightharpoonup\\atop\\leftharpoondown}" },
6261:    { "\\therefore",NULL,"{\\Huge\\raisebox{-4}{.\\atop.\\,.}}" },
6262:    { "\\LaTeX", NULL,   "{\\rm~L\\raisebox{3}{\\fs{-1}A}\\TeX}" },
6263:    { "\\TeX",   NULL,   "{\\rm~T\\raisebox{-3}{E}X}" },
6264:    { "\\cyan",  NULL,   "{\\reverse\\red\\reversebg}" },
6265:    { "\\magenta",NULL,  "{\\reverse\\green\\reversebg}" },
6266:    { "\\yellow",NULL,   "{\\reverse\\blue\\reversebg}" },
6267:    { "\\cancel",NULL,   "\\Not" },
6268:    { "\\hhline",NULL,   "\\Hline" },
6269:    { "\\Hline", NULL,   "\\hline\\,\\\\\\hline" },
6270:    /* -----------------------------------------------------------------------
6271:      As per emails with Zbigniew Fiedorowicz <fiedorow@math.ohio-state.edu>
6272:      "Algebra Syntax"  termchar   mimeTeX/LaTeX equivalent...
6273:    ----------------------------------------------------------------------- */
6274:    { "sqrt",    "1",    "{\\sqrt{#1}}" },
6275:    { "sin",     "1",    "{\\sin{#1}}" },
6276:    { "cos",     "1",    "{\\cos{#1}}" },
6277:    { "asin",    "1",    "{\\sin^{-1}{#1}}" },
6278:    { "acos",    "1",    "{\\cos^{-1}{#1}}" },
6279:    { "exp",     "1",    "{{\\rm~e}^{#1}}" },
6280:    { "det",     "1",    "{\\left|{#1}\\right|}" },
6281:    /* --------------------------------------------
6282:      LaTeX Constant    termchar   value...
6283:    -------------------------------------------- */
6284:    { "\\thinspace",     NULL,   "\\," },
6285:    { "\\thinmathspace", NULL,   "\\," },
6286:    { "\\textwidth",     NULL,   "400" },
6287:    /* --- end-of-table indicator --- */
6288:    { NULL,      NULL,   NULL }
6289:  } ; /* --- end-of-symbols[] --- */
6290: /* ---
6291:  * html &#nn chars converted to latex equivalents
6292:  * ---------------------------------------------- */
6293: int     htmlnum=0;                      /* numbers[inum].html */
6294: static  struct { int html; char *latex; } numbers[] =
6295:  { /* ---------------------------------------
6296:     html num  LaTeX equivalent...
6297:    --------------------------------------- */
6298:    { 9,         " " },                  /* horizontal tab */
6299:    { 10,        " " },                  /* line feed */
6300:    { 13,        " " },                  /* carriage return */
6301:    { 32,        " " },                  /* space */
6302:    { 33,        "!" },                  /* exclamation point */
6303:    { 34,        "\"" },                 /* &quot; */
6304:    { 35,        "#" },                  /* hash mark */
6305:    { 36,        "$" },                  /* dollar */
6306:    { 37,        "%" },                  /* percent */
6307:    { 38,        "&" },                  /* &amp; */
6308:    { 39,        "\'" },                 /* apostrophe (single quote) */
6309:    { 40,        ")" },                  /* left parenthesis */
6310:    { 41,        ")" },                  /* right parenthesis */
6311:    { 42,        "*" },                  /* asterisk */
6312:    { 43,        "+" },                  /* plus */
6313:    { 44,        "," },                  /* comma */
6314:    { 45,        "-" },                  /* hyphen (minus) */
6315:    { 46,        "." },                  /* period */
6316:    { 47,        "/" },                  /* slash */
6317:    { 58,        ":" },                  /* colon */
6318:    { 59,        ";" },                  /* semicolon */
6319:    { 60,        "<" },                  /* &lt; */
6320:    { 61,        "=" },                  /* = */
6321:    { 62,        ">" },                  /* &gt; */
6322:    { 63,        "\?" },                 /* question mark */
6323:    { 64,        "@" },                  /* commercial at sign */
6324:    { 91,        "[" },                  /* left square bracket */
6325:    { 92,        "\\" },                 /* backslash */
6326:    { 93,        "]" },                  /* right square bracket */
6327:    { 94,        "^" },                  /* caret */
6328:    { 95,        "_" },                  /* underscore */
6329:    { 96,        "`" },                  /* grave accent */
6330:    { 123,       "{" },                  /* left curly brace */
6331:    { 124,       "|" },                  /* vertical bar */
6332:    { 125,       "}" },                  /* right curly brace */
6333:    { 126,       "~" },                  /* tilde */
6334:    { 160,       "~" },                  /* &nbsp; (use tilde for latex) */
6335:    { 166,       "|" },                  /* &brvbar; (broken vertical bar) */
6336:    { 173,       "-" },                  /* &shy; (soft hyphen) */
6337:    { 177,       "{\\pm}" },             /* &plusmn; (plus or minus) */
6338:    { 215,       "{\\times}" },          /* &times; (plus or minus) */
6339:    { -999,      NULL }
6340:  } ; /* --- end-of-numbers[] --- */
6341: /* -------------------------------------------------------------------------
6342: first remove comments
6343: -------------------------------------------------------------------------- */
6344: expptr = expression;                    /* start search at beginning */
6345: while ( (leftptr=strstr(expptr,leftcomment)) != NULL ) /*found leftcomment*/
6346:   {
6347:   char  *rightsym=NULL;                 /* rightcomment[isymbol] */
6348:   expptr = leftptr+strlen(leftcomment); /* start rightcomment search here */
6349:   /* --- check for any closing rightcomment, in given precedent order --- */
6350:   if ( *expptr != '\000' )              /*have chars after this leftcomment*/
6351:    for(isymbol=0; (rightsym=rightcomment[isymbol]) != NULL; isymbol++)
6352:     if ( (tokptr=strstr(expptr,rightsym)) != NULL ) /*found rightcomment*/
6353:      { tokptr += strlen(rightsym);      /* first char after rightcomment */
6354:        if ( *tokptr == '\000' )         /*nothing after this rightcomment*/
6355:         { *leftptr = '\000';            /*so terminate expr at leftcomment*/
6356:           break; }                      /* and stop looking for comments */
6357:        *leftptr = '~';                  /* replace entire comment by ~ */
6358:        strsqueezep(leftptr+1,tokptr);   /* squeeze out comment */
6359:        goto next_comment; }             /* stop looking for rightcomment */
6360:   /* --- no rightcomment after opening leftcomment --- */
6361:   *leftptr = '\000';                    /* so terminate expression */
6362:   /* --- resume search past squeezed-out comment --- */
6363:   next_comment:
6364:     if ( *leftptr == '\000' ) break;    /* reached end of expression */
6365:     expptr = leftptr+1;                 /*resume search after this comment*/
6366:   } /* --- end-of-while(leftptr!=NULL) --- */
6367: /* -------------------------------------------------------------------------
6368: run thru table, converting all occurrences of each macro to its expansion
6369: -------------------------------------------------------------------------- */
6370: for(isymbol=0; (htmlsym=symbols[isymbol].html) != NULL; isymbol++)
6371:   {
6372:   int   htmllen = strlen(htmlsym);      /* length of escape, _without_ ; */
6373:   int   isalgebra = isalpha((int)(*htmlsym)); /* leading char alphabetic */
6374:   int   isembedded = 0,                 /* true to xlate even if embedded */
6375:         istag=0, isamp=0,               /* true for <tag>, &char; symbols */
6376:         isstrwstr = 0,                  /* true to use strwstr() */
6377:         wstrlen = 0;                    /* length of strwstr() match */
6378:   char  *aleft="{([<|", *aright="})]>|"; /*left,right delims for alg syntax*/
6379:   char  embedkeywd[99] = "embed",       /* keyword to signal embedded token*/
6380:         embedterm = '\000';             /* char immediately after embed */
6381:   int   embedlen = strlen(embedkeywd);  /* #chars in embedkeywd */
6382:   char  *args = symbols[isymbol].args,  /* number {}-args, optional []-arg */
6383:         *htmlterm = args,               /*if *args nonumeric, then html term*/
6384:         *latexsym = symbols[isymbol].latex, /*latex replacement for htmlsym*/
6385:         errorsym[256];                  /*or latexsym may point to error msg*/
6386:   char  abuff[8192];  int iarg,nargs=0; /* macro expansion params */
6387:   char  wstrwhite[99];                  /* whitespace chars for strwstr() */
6388:   skipwhite(htmlsym);                   /*skip any bogus leading whitespace*/
6389:   htmllen = strlen(htmlsym);            /* reset length of html token */
6390:   istag = (isthischar(*htmlsym,"<")?1:0); /* html <tag> starts with < */
6391:   isamp = (isthischar(*htmlsym,"&")?1:0); /* html &char; starts with & */
6392:   if ( args != NULL )                   /*we have args (or htmlterm) param*/
6393:    if ( *args != '\000' ) {             /* and it's not an empty string */
6394:     if ( strchr("0123456789",*args) != NULL ) /* is 1st char #args=0-9 ? */
6395:      { htmlterm = NULL;                 /* if so, then we have no htmlterm */
6396:        *abuff = *args;  abuff[1] = '\000'; /* #args char in ascii buffer */
6397:        nargs = atoi(abuff); }           /* interpret #args to numeric */
6398:     else if ( strncmp(args,embedkeywd,embedlen) == 0 )/*xlate embedded token*/
6399:      { int arglen = strlen(args);       /* length of "embed..." string */
6400:        htmlterm = NULL;                 /* if so, then we have no htmlterm */
6401:        isembedded = 1 ;                 /* turn on embedded flag */
6402:        if ( arglen > embedlen )         /* have embed "allow escape" flag */
6403:          embedterm = args[embedlen];    /* char immediately after "embed" */
6404:        if (arglen > embedlen+1) {       /* have embed,flag,white for strwstr*/
6405:          isstrwstr = 1;                 /* turn on strwtsr flag */
6406:          strcpy(wstrwhite,args+embedlen+1); } } /*and set its whitespace arg*/
6407:     } /* --- end-of-if(*args!='\000') --- */
6408:   expptr = expression;                  /* re-start search at beginning */
6409:   while ( ( tokptr=(!isstrwstr?strstr(expptr,htmlsym): /* just use strtsr */
6410:   strwstr(expptr,htmlsym,wstrwhite,&wstrlen)) ) /* or use our strwstr */
6411:   != NULL ) {                           /* found another sym */
6412:       int  toklen = (!isstrwstr?htmllen:wstrlen); /* length of matched sym */
6413:       char termchar = *(tokptr+toklen), /* char terminating html sequence */
6414:            prevchar = (tokptr==expptr?' ':*(tokptr-1));/*char preceding html*/
6415:       int  isescaped = (isthischar(prevchar,ESCAPE)?1:0); /* token escaped?*/
6416:       int  escapelen = toklen;          /* total length of escape sequence */
6417:       int  isflush = 0;                 /* true to flush (don't xlate) */
6418:       /* --- check odd/even backslashes preceding tokens --- */
6419:       if ( isescaped ) {                /* have one preceding backslash */
6420:         char *p = tokptr-1;             /* ptr to that preceding backslash */
6421:         while ( p != expptr ) {         /* and we may have more preceding */
6422:           p--; if(!isthischar(*p,ESCAPE))break; /* but we don't, so quit */
6423:           isescaped = 1-isescaped; } }  /* or flip isescaped flag if we do */
6424:       /* --- init with "trivial" abuff,escapelen from symbols[] table --- */
6425:       *abuff = '\000';                  /* default to empty string */
6426:       if ( latexsym != NULL )           /* table has .latex xlation */
6427:        if ( *latexsym != '\000' )       /* and it's not an empty string */
6428:         strcpy(abuff,latexsym);         /* so get local copy */
6429:       if ( !isembedded )                /*embedded sequences not terminated*/
6430:        if ( htmlterm != NULL )          /* sequence may have terminator */
6431:         escapelen += (isthischar(termchar,htmlterm)?1:0); /*add terminator*/
6432:       /* --- don't xlate if we just found prefix of longer symbol, etc --- */
6433:       if ( !isembedded ) {              /* not embedded */
6434:         if ( isescaped )                /* escaped */
6435:           isflush = 1;                  /* set flag to flush escaped token */
6436:         if ( !istag && isalpha((int)termchar) ) /* followed by alpha */
6437:           isflush = 1;                  /* so just a prefix of longer symbol*/
6438:         if ( isalpha((int)(*htmlsym)) ) /* symbol starts with alpha */
6439:           if ( (!isspace(prevchar)&&isalpha(prevchar)) ) /* just a suffix*/
6440:             isflush = 1; }              /* set flag to flush token */
6441:       if ( isembedded )                 /* for embedded token */
6442:        if ( isescaped )                 /* and embedded \token escaped */
6443:         if ( !isthischar(embedterm,ESCAPE) ) /* don't xlate escaped \token */
6444:           isflush = 1;                  /* set flag to flush token */
6445:       if ( isflush )                    /* don't xlate this token */
6446:         { expptr = tokptr+1;/*toklen;*/ /* just resume search after token */
6447:           continue; }                   /* but don't replace it */
6448:       /* --- check for &# prefix signalling &#nnn; --- */
6449:       if ( strcmp(htmlsym,"&#") == 0 ) { /* replacing special &#nnn; chars */
6450:        /* --- accumulate chars comprising number following &# --- */
6451:        char anum[32];                   /* chars comprising number after &# */
6452:        int  inum = 0;                   /* no chars accumulated yet */
6453:        while ( termchar != '\000' ) {   /* don't go past end-of-string */
6454:          if ( !isdigit((int)termchar) ) break; /* and don't go past digits */
6455:          if ( inum > 10 ) break;        /* some syntax error in expression */
6456:          anum[inum] = termchar;         /* accumulate this digit */
6457:          inum++;  toklen++;             /* bump field length, token length */
6458:          termchar = *(tokptr+toklen); } /* char terminating html sequence */
6459:        anum[inum] = '\000';             /* null-terminate anum */
6460:        escapelen = toklen;              /* length of &#nnn; sequence */
6461:        if ( htmlterm != NULL )          /* sequence may have terminator */
6462:          escapelen += (isthischar(termchar,htmlterm)?1:0); /*add terminator*/
6463:        /* --- look up &#nnn in number[] table --- */
6464:        htmlnum = atoi(anum);            /* convert anum[] to an integer */
6465:        strninit(errorsym,latexsym,128); /* init error message */
6466:        latexsym = errorsym;             /* init latexsym as error message */
6467:        strreplace(latexsym,"nnn",anum,1); /*place actual &#num in message*/
6468:        for ( inum=0; numbers[inum].html>=0; inum++ ) /* run thru numbers[] */
6469:          if ( htmlnum ==  numbers[inum].html ) { /* till we find a match */
6470:            latexsym = numbers[inum].latex; /* latex replacement */
6471:            break; }                     /* no need to look any further */
6472:        if ( latexsym != NULL )          /* table has .latex xlation */
6473:         if ( *latexsym != '\000' )      /* and it's not an empty string */
6474:          strcpy(abuff,latexsym);        /* so get local copy */
6475:        } /* --- end-of-if(strcmp(htmlsym,"&#")==0) --- */
6476:       /* --- substitute macro arguments --- */
6477:       if ( nargs > 0 )                  /*substitute #1,#2,... in latexsym*/
6478:        {
6479:        char *arg1ptr = tokptr+escapelen;/* nargs begin after macro literal */
6480:        char *optarg = args+1;           /* ptr 1 char past #args digit 0-9 */
6481:        expptr = arg1ptr;                /* ptr to beginning of next arg */
6482:        for ( iarg=1; iarg<=nargs; iarg++ ) /* one #`iarg` arg at a time */
6483:         {
6484:         char argsignal[32] = "#1",      /* #1...#9 signals arg replacement */
6485:         *argsigptr = NULL;              /* ptr to argsignal in abuff[] */
6486:         /* --- get argument value --- */
6487:         *argval = '\000';               /* init arg as empty string */
6488:         skipwhite(expptr);              /* and skip leading white space */
6489:         if ( iarg==1 && *optarg!='\000' /* check for optional [arg] */
6490:         &&   !isalgebra )               /* but not in "algebra syntax" */
6491:          { strcpy(argval,optarg);       /* init with default value */
6492:            if ( *expptr == '[' )        /* but user gave us [argval] */
6493:              expptr = texsubexpr(expptr,argval,0,"[","]",0,0); } /*so get it*/
6494:         else                            /* not optional, so get {argval} */
6495:          if ( *expptr != '\000' ) {     /* check that some argval provided */
6496:            if ( !isalgebra )            /* only { } delims for latex macro */
6497:              expptr = texsubexpr(expptr,argval,0,"{","}",0,0);/*get {argval}*/
6498:            else {                       /*any delim for algebra syntax macro*/
6499:              expptr = texsubexpr(expptr,argval,0,aleft,aright,0,1);
6500:              if ( isthischar(*argval,aleft) ) /* have delim-enclosed arg */
6501:                if ( *argval != '{' ) {  /* and it's not { }-enclosed */
6502:                  strchange(0,argval,"\\left"); /* insert opening \left, */
6503:                  strchange(0,argval+strlen(argval)-1,"\\right"); } }/*\right*/
6504:           } /* --- end-of-if(*expptr!='\000') --- */
6505:         /* --- (recursively) call mimeprep() to prep the argument --- */
6506:         if ( !isempty(argval) )         /* have an argument */
6507:           mimeprep(argval);             /* so (recursively) prep it */
6508:         /* --- "specials" (e.g., \password) check --- */
6509:         if ( strstr(htmlsym,"password") != NULL ) { /* have \password{} */
6510:           argval[64] = '\000';          /* make sure it's not too long */
6511:           strcpy(password,argval); }    /* save it for http_referer checks */
6512:         /* --- replace #`iarg` in macro with argval --- */
6513:         sprintf(argsignal,"#%d",iarg);  /* #1...#9 signals argument */
6514:         while ( (argsigptr=strstr(argval,argsignal)) != NULL ) /* #1...#9 */
6515:          {strsqueeze(argsigptr,strlen(argsignal));} /* can't be in argval */
6516:         while ( (argsigptr=strstr(abuff,argsignal)) != NULL ) /* #1...#9 */
6517:          strchange(strlen(argsignal),argsigptr,argval); /*replaced by argval*/
6518:         } /* --- end-of-for(iarg) --- */
6519:        escapelen += ((int)(expptr-arg1ptr)); /* add in length of all args */
6520:        } /* --- end-of-if(nargs>0) --- */
6521:       strchange(escapelen,tokptr,abuff); /*replace macro or html symbol*/
6522:       expptr = tokptr + strlen(abuff); /*resume search after macro / html*/
6523:       } /* --- end-of-while(tokptr!=NULL) --- */
6524:   } /* --- end-of-for(isymbol) --- */
6525: /* -------------------------------------------------------------------------
6526: convert \left( to \(  and  \right) to \),  etc.
6527: -------------------------------------------------------------------------- */
6528: if ( xlateleft )                        /* \left...\right xlation wanted */
6529:  for ( idelim=0; idelim<2; idelim++ )   /* 0 for \left  and  1 for \right */
6530:   {
6531:   char  *lrstr  = (idelim==0?"\\left":"\\right"); /* \left on 1st pass */
6532:   int   lrlen   = (idelim==0?5:6);      /* strlen() of \left or \right */
6533:   char  *braces = (idelim==0?LEFTBRACES ".":RIGHTBRACES "."), /*([{<or)]}>*/
6534:         **lrfrom= (idelim==0?leftfrom:rightfrom), /* long braces like \| */
6535:         **lrto  = (idelim==0?leftto:rightto), /* xlated to 1-char like = */
6536:         *lrsym  = NULL;                 /* lrfrom[isymbol] */
6537:   expptr = expression;                  /* start search at beginning */
6538:   while ( (tokptr=strstr(expptr,lrstr)) != NULL ) /* found \left or \right */
6539:     {
6540:     if ( isthischar(*(tokptr+lrlen),braces) ) /* followed by a 1-char brace*/
6541:       { strsqueeze((tokptr+1),(lrlen-1));/*so squeeze out "left" or "right"*/
6542:         expptr = tokptr+2; }            /* and resume search past brace */
6543:     else                                /* may be a "long" brace like \| */
6544:       {
6545:       expptr = tokptr+lrlen;            /*init to resume search past\left\rt*/
6546:       for(isymbol=0; (lrsym=lrfrom[isymbol]) != NULL; isymbol++)
6547:         { int symlen = strlen(lrsym);   /* #chars in delim, e.g., 2 for \| */
6548:           if ( memcmp(tokptr+lrlen,lrsym,symlen) == 0 ) /* found long delim*/
6549:             { strsqueeze((tokptr+1),(lrlen+symlen-2)); /*squeeze out delim*/
6550:               *(tokptr+1) = *(lrto[isymbol]); /* last char now 1-char delim*/
6551:               expptr = tokptr+2 - lrlen; /* resume search past 1-char delim*/
6552:               break; }                  /* no need to check more lrsym's */
6553:         } /* --- end-of-for(isymbol) --- */
6554:       } /* --- end-of-if/else(isthischar()) --- */
6555:     } /* --- end-of-while(tokptr!=NULL) --- */
6556:   } /* --- end-of-for(idelim) --- */
6557: /* -------------------------------------------------------------------------
6558: run thru table, converting all {a+b\atop c+d} to \atop{a+b}{c+d}
6559: -------------------------------------------------------------------------- */
6560: for(isymbol=0; (atopsym=atopcommands[isymbol]) != NULL; isymbol++)
6561:   {
6562:   int   atoplen = strlen(atopsym);      /* #chars in \atop */
6563:   expptr = expression;                  /* re-start search at beginning */
6564:   while ( (tokptr=strstr(expptr,atopsym)) != NULL ) /* found another atop */
6565:     { char *leftbrace=NULL, *rightbrace=NULL; /*ptr to opening {, closing }*/
6566:       char termchar = *(tokptr+atoplen); /* \atop followed by terminator */
6567:       if ( msgfp!=NULL && msglevel>=999 )
6568:         { fprintf(msgfp,"mimeprep> offset=%d rhs=\"%s\"\n",
6569:           (int)(tokptr-expression),tokptr);
6570:           fflush(msgfp); }
6571:       if ( isalpha((int)termchar) )     /*we just have prefix of longer sym*/
6572:         { expptr = tokptr+atoplen;      /* just resume search after prefix */
6573:           continue; }                   /* but don't process it */
6574:       leftbrace  = findbraces(expression,tokptr);     /* find left { */
6575:       rightbrace = findbraces(NULL,tokptr+atoplen-1); /* find right } */
6576:       if ( leftbrace==NULL || rightbrace==NULL )
6577:         { expptr += atoplen;  continue; } /* skip command if didn't find */
6578:       else                              /* we have bracketed { \atop } */
6579:         {
6580:         int  leftlen  = (int)(tokptr-leftbrace) - 1, /* #chars in left arg */
6581:              rightlen = (int)(rightbrace-tokptr) - atoplen, /* and in right*/
6582:              totlen   = (int)(rightbrace-leftbrace) + 1; /*tot in { \atop }*/
6583:         char *open=atopdelims[2*isymbol], *close=atopdelims[2*isymbol+1];
6584:         char arg[8192], command[8192];  /* left/right args, new \atop{}{} */
6585:         *command = '\000';              /* start with null string */
6586:         if (open!=NULL) strcat(command,open); /* add open delim if needed */
6587:         strcat(command,atopsym);        /* add command with \atop */
6588:         arg[0] = '{';                   /* arg starts with { */
6589:         memcpy(arg+1,leftbrace+1,leftlen); /* extract left-hand arg */
6590:         arg[leftlen+1] = '\000';        /* and null terminate it */
6591:         strcat(command,arg);            /* concatanate {left-arg to \atop */
6592:         strcat(command,"}{");           /* close left-arg, open right-arg */
6593:         memcpy(arg,tokptr+atoplen,rightlen); /* right-hand arg */
6594:         arg[rightlen] = '}';            /* add closing } */
6595:         arg[rightlen+1] = '\000';       /* and null terminate it */
6596:         if ( isthischar(*arg,WHITEMATH) ) /* 1st char was mandatory space */
6597:           {strsqueeze(arg,1);}          /* so squeeze it out */
6598:         strcat(command,arg);            /* concatanate right-arg} */
6599:         if (close!=NULL) strcat(command,close); /* add close delim if needed*/
6600:         strchange(totlen-2,leftbrace+1,command); /* {\atop} --> {\atop{}{}} */
6601:         expptr = leftbrace+strlen(command); /*resume search past \atop{}{}*/
6602:         }
6603:     } /* --- end-of-while(tokptr!=NULL) --- */
6604:   } /* --- end-of-for(isymbol) --- */
6605: /* -------------------------------------------------------------------------
6606: back to caller with preprocessed expression
6607: -------------------------------------------------------------------------- */
6608: if ( msgfp!=NULL && msglevel>=99 )      /* display preprocessed expression */
6609:   { fprintf(msgfp,"mimeprep> expression=\"\"%s\"\"\n",expression);
6610:     fflush(msgfp); }
6611: return ( expression );
6612: } /* --- end-of-function mimeprep() --- */
6613: 
6614: 
6615: /* ==========================================================================
6616:  * Function:    strchange ( int nfirst, char *from, char *to )
6617:  * Purpose:     Changes the nfirst leading chars of `from` to `to`.
6618:  *              For example, to change char x[99]="12345678" to "123ABC5678"
6619:  *              call strchange(1,x+3,"ABC")
6620:  * --------------------------------------------------------------------------
6621:  * Arguments:   nfirst (I)      int containing #leading chars of `from`
6622:  *                              that will be replace by `to`
6623:  *              from (I/O)      char * to null-terminated string whose nfirst
6624:  *                              leading chars will be replaced by `to`
6625:  *              to (I)          char * to null-terminated string that will
6626:  *                              replace the nfirst leading chars of `from`
6627:  * --------------------------------------------------------------------------
6628:  * Returns:     ( char * )      ptr to first char of input `from`
6629:  *                              or NULL for any error.
6630:  * --------------------------------------------------------------------------
6631:  * Notes:     o If strlen(to)>nfirst, from must have memory past its null
6632:  *              (i.e., we don't do a realloc)
6633:  * ======================================================================= */
6634: /* --- entry point --- */
6635: FUNCSCOPE char *strchange ( int nfirst, char *from, char *to )
6636: {
6637: /* -------------------------------------------------------------------------
6638: Allocations and Declarations
6639: -------------------------------------------------------------------------- */
6640: int     tolen = (to==NULL?0:strlen(to)), /* #chars in replacement string */
6641:         nshift = abs(tolen-nfirst);     /*need to shift from left or right*/
6642: /* -------------------------------------------------------------------------
6643: shift from left or right to accommodate replacement of its nfirst chars by to
6644: -------------------------------------------------------------------------- */
6645: if ( tolen < nfirst )                   /* shift left is easy */
6646:   {strsqueeze(from,nshift);}            /* memmove avoids overlap memory */
6647: if ( tolen > nfirst )                   /* need more room at start of from */
6648:   { char *pfrom = from+strlen(from);    /* ptr to null terminating from */
6649:     for ( ; pfrom>=from; pfrom-- )      /* shift all chars including null */
6650:       *(pfrom+nshift) = *pfrom; }       /* shift chars nshift places right */
6651: /* -------------------------------------------------------------------------
6652: from has exactly the right number of free leading chars, so just put to there
6653: -------------------------------------------------------------------------- */
6654: if ( tolen != 0 )                       /* make sure to not empty or null */
6655:   memcpy(from,to,tolen);                /* chars moved into place */
6656: return ( from );                        /* changed string back to caller */
6657: } /* --- end-of-function strchange() --- */
6658: 
6659: 
6660: /* ==========================================================================
6661:  * Function:    strreplace (char *string, char *from, char *to, int nreplace)
6662:  * Purpose:     Changes the first nreplace occurrences of 'from' to 'to'
6663:  *              in string, or all occurrences if nreplace=0.
6664:  * --------------------------------------------------------------------------
6665:  * Arguments:   string (I/0)    char * to null-terminated string in which
6666:  *                              occurrence of 'from' will be replaced by 'to'
6667:  *              from (I)        char * to null-terminated string
6668:  *                              to be replaced by 'to'
6669:  *              to (I)          char * to null-terminated string that will
6670:  *                              replace 'from'
6671:  *              nreplace (I)    int containing (maximum) number of
6672:  *                              replacements, or 0 to replace all.
6673:  * --------------------------------------------------------------------------
6674:  * Returns:     ( int )         number of replacements performed,
6675:  *                              or 0 for no replacements or -1 for any error.
6676:  * --------------------------------------------------------------------------
6677:  * Notes:     o
6678:  * ======================================================================= */
6679: /* --- entry point --- */
6680: FUNCSCOPE int strreplace ( char *string, char *from, char *to, int nreplace )
6681: {
6682: /* -------------------------------------------------------------------------
6683: Allocations and Declarations
6684: -------------------------------------------------------------------------- */
6685: int     fromlen = (from==NULL?0:strlen(from)), /* #chars to be replaced */
6686:         tolen = (to==NULL?0:strlen(to)); /* #chars in replacement string */
6687: char    *pfrom = (char *)NULL,          /*ptr to 1st char of from in string*/
6688:         *pstring = string               /*ptr past previously replaced from*/
6689:         /*,*strchange()*/;              /* change 'from' to 'to' */
6690: int     nreps = 0;                      /* #replacements returned to caller*/
6691: /* -------------------------------------------------------------------------
6692: repace occurrences of 'from' in string to 'to'
6693: -------------------------------------------------------------------------- */
6694: if ( string == (char *)NULL             /* no input string */
6695: ||   (fromlen<1 && nreplace<=0) )       /* replacing empty string forever */
6696:   nreps = (-1);                         /* so signal error */
6697: else                                    /* args okay */
6698:   while (nreplace<1 || nreps<nreplace)  /* up to #replacements requested */
6699:     {
6700:     if ( fromlen > 0 )                  /* have 'from' string */
6701:       pfrom = strstr(pstring,from);     /*ptr to 1st char of from in string*/
6702:     else  pfrom = pstring;              /*or empty from at start of string*/
6703:     if ( pfrom == (char *)NULL ) break; /*no more from's, so back to caller*/
6704:     if ( strchange(fromlen,pfrom,to)    /* leading 'from' changed to 'to' */
6705:     ==   (char *)NULL ) { nreps=(-1); break; } /* signal error to caller */
6706:     nreps++;                            /* count another replacement */
6707:     pstring = pfrom+tolen;              /* pick up search after 'to' */
6708:     if ( *pstring == '\000' ) break;    /* but quit at end of string */
6709:     } /* --- end-of-while() --- */
6710: return ( nreps );                       /* #replacements back to caller */
6711: } /* --- end-of-function strreplace() --- */
6712: 
6713: 
6714: /* ==========================================================================
6715:  * Function:    strwstr (char *string, char *substr, char *white, int *sublen)
6716:  * Purpose:     Find first substr in string, but wherever substr contains
6717:  *              a whitespace char (in white), string may contain any number
6718:  *              (including 0) of whitespace chars. If white contains I or i,
6719:  *              then match is case-insensitive (and I,i _not_ whitespace).
6720:  * --------------------------------------------------------------------------
6721:  * Arguments:   string (I)      char * to null-terminated string in which
6722:  *                              first occurrence of substr will be found
6723:  *              substr (I)      char * to null-terminated string containing
6724:  *                              "template" that will be searched for
6725:  *              white (I)       char * to null-terminated string containing
6726:  *                              whitespace chars.  If NULL or empty, then
6727:  *                              "~ \t\n\r\f\v" (WHITEMATH in mimetex.h) used.
6728:  *                              If white contains I or i, then match is
6729:  *                              case-insensitive (and I,i _not_ considered
6730:  *                              whitespace).
6731:  *              sublen (O)      address of int returning "length" of substr
6732:  *                              found in string (which may be longer or
6733:  *                              shorter than substr itself).
6734:  * --------------------------------------------------------------------------
6735:  * Returns:     ( char * )      ptr to first char of substr in string
6736:  *                              or NULL if not found or for any error.
6737:  * --------------------------------------------------------------------------
6738:  * Notes:     o Wherever a single whitespace char appears in substr,
6739:  *              the corresponding position in string may contain any
6740:  *              number (including 0) of whitespace chars, e.g.,
6741:  *              string="abc   def" and string="abcdef" both match
6742:  *              substr="c d" at offset 2 of string.
6743:  *            o If substr="c  d" (two spaces between c and d),
6744:  *              then string must have at least one space, so now "abcdef"
6745:  *              doesn't match.  In general, the minimum number of spaces
6746:  *              in string is the number of spaces in substr minus 1
6747:  *              (so 1 space in substr permits 0 spaces in string).
6748:  *            o Embedded spaces are counted in sublen, e.g.,
6749:  *              string="c   d" (three spaces) matches substr="c d"
6750:  *              with sublen=5 returned.  But string="ab   c   d" will
6751:  *              also match substr="  c d" returning sublen=5 and
6752:  *              a ptr to the "c".  That is, the mandatory preceding
6753:  *              space is _not_ counted as part of the match.
6754:  *              But all the embedded space is counted.
6755:  *              (An inconsistent bug/feature is that mandatory
6756:  *              terminating space is counted.)
6757:  *            o Moreover, string="c   d" matches substr="  c d", i.e.,
6758:  *              the very beginning of a string is assumed to be preceded
6759:  *              by "virtual blanks".
6760:  * ======================================================================= */
6761: /* --- entry point --- */
6762: FUNCSCOPE char *strwstr(char *string, char *substr, char *white, int *sublen)
6763: {
6764: /* -------------------------------------------------------------------------
6765: Allocations and Declarations
6766: -------------------------------------------------------------------------- */
6767: char    *psubstr=substr, *pstring=string,/*ptr to current char in substr,str*/
6768:         *pfound = (char *)NULL;         /*ptr to found substr back to caller*/
6769: char    *pwhite=NULL, whitespace[256];  /* callers white whithout i,I */
6770: int     iscase = (white==NULL?1:        /* case-sensitive if i,I in white */
6771:         strchr(white,'i')==NULL && strchr(white,'I')==NULL);
6772: int     foundlen = 0;                   /* length of substr found in string*/
6773: int     nstrwhite=0, nsubwhite=0,       /* #leading white chars in str,sub */
6774:         nminwhite=0;                    /* #mandatory leading white in str */
6775: int     nstrchars=0, nsubchars=0,       /* #non-white chars to be matched */
6776:         isncmp=0;                       /*strncmp() or strncasecmp() result*/
6777: /* -------------------------------------------------------------------------
6778: Initialization
6779: -------------------------------------------------------------------------- */
6780: /* --- set up whitespace --- */
6781: strcpy(whitespace,WHITEMATH);           /*default if no user input for white*/
6782: if ( white != NULL )                    /*user provided ptr to white string*/
6783:  if ( *white != '\000' ) {              /*and it's not just an empty string*/
6784:    strcpy(whitespace,white);            /* so use caller's white spaces */
6785:    while ( (pwhite=strchr(whitespace,'i')) != NULL ) /* have an embedded i */
6786:      {strsqueeze(pwhite,1);}            /* so squeeze it out */
6787:    while ( (pwhite=strchr(whitespace,'I')) != NULL ) /* have an embedded I */
6788:      {strsqueeze(pwhite,1);}            /* so squeeze it out */
6789:    if ( *whitespace == '\000' )         /* caller's white just had i,I */
6790:      strcpy(whitespace,WHITEMATH); }    /* so revert back to default */
6791: /* -------------------------------------------------------------------------
6792: Find first occurrence of substr in string
6793: -------------------------------------------------------------------------- */
6794: if ( string != NULL )                   /* caller passed us a string ptr */
6795:  while ( *pstring != '\000' ) {         /* break when string exhausted */
6796:   char  *pstrptr = pstring;             /* (re)start at next char in string*/
6797:   int   leadingwhite = 0;               /* leading whitespace */
6798:   psubstr = substr;                     /* start at beginning of substr */
6799:   foundlen = 0;                         /* reset length of found substr */
6800:   if ( substr != NULL )                 /* caller passed us a substr ptr */
6801:    while ( *psubstr != '\000' ) {       /*see if pstring begins with substr*/
6802:     /* --- check for end-of-string before finding match --- */
6803:     if ( *pstrptr == '\000' )           /* end-of-string without a match */
6804:       goto nextstrchar;                 /* keep trying with next char */
6805:     /* --- actual amount of whitespace in string and substr --- */
6806:     nsubwhite = strspn(psubstr,whitespace); /* #leading white chars in sub */
6807:     nstrwhite = strspn(pstrptr,whitespace); /* #leading white chars in str */
6808:     nminwhite = max2(0,nsubwhite-1);    /* #mandatory leading white in str */
6809:     /* --- check for mandatory leading whitespace in string --- */
6810:     if ( pstrptr != string )            /*not mandatory at start of string*/
6811:       if ( nstrwhite < nminwhite )      /* too little leading white space */
6812:         goto nextstrchar;               /* keep trying with next char */
6813:     /* ---hold on to #whitespace chars in string preceding substr match--- */
6814:     if ( pstrptr == pstring )           /* whitespace at start of substr */
6815:       leadingwhite = nstrwhite;         /* save it as leadingwhite */
6816:     /* --- check for optional whitespace --- */
6817:     if ( psubstr != substr )            /* always okay at start of substr */
6818:       if ( nstrwhite>0 && nsubwhite<1 ) /* too much leading white space */
6819:         goto nextstrchar;               /* keep trying with next char */
6820:     /* --- skip any leading whitespace in substr and string --- */
6821:     psubstr += nsubwhite;               /* push past leading sub whitespace*/
6822:     pstrptr += nstrwhite;               /* push past leading str whitespace*/
6823:     /* --- now get non-whitespace chars that we have to match --- */
6824:     nsubchars = strcspn(psubstr,whitespace); /* #non-white chars in sub */
6825:     nstrchars = strcspn(pstrptr,whitespace); /* #non-white chars in str */
6826:     if ( nstrchars < nsubchars )        /* too few chars for match */
6827:       goto nextstrchar;                 /* keep trying with next char */
6828:     /* --- see if next nsubchars are a match --- */
6829:     isncmp = (iscase? strncmp(pstrptr,psubstr,nsubchars): /*case sensitive*/
6830:                 strncasecmp(pstrptr,psubstr,nsubchars)); /*case insensitive*/
6831:     if ( isncmp != 0 )                  /* no match */
6832:       goto nextstrchar;                 /* keep trying with next char */
6833:     /* --- push past matched chars --- */
6834:     psubstr += nsubchars;  pstrptr += nsubchars;  /*nsubchars were matched*/
6835:     } /* --- end-of-while(*psubstr!='\000') --- */
6836:   pfound = pstring + leadingwhite;      /* found match starting at pstring */
6837:   foundlen = (int)(pstrptr-pfound);     /* consisting of this many chars */
6838:   goto end_of_job;                      /* back to caller */
6839:   /* ---failed to find substr, continue trying with next char in string--- */
6840:   nextstrchar:                          /* continue outer loop */
6841:     pstring++;                          /* bump to next char in string */
6842:   } /* --- end-of-while(*pstring!='\000') --- */
6843: /* -------------------------------------------------------------------------
6844: Back to caller with ptr to first occurrence of substr in string
6845: -------------------------------------------------------------------------- */
6846: end_of_job:
6847:   if ( msglevel>=999 && msgfp!=NULL) {  /* debugging/diagnostic output */
6848:     fprintf(msgfp,"strwstr> str=\"%.72s\" sub=\"%s\" found at offset %d\n",
6849:     string,substr,(pfound==NULL?(-1):(int)(pfound-string))); fflush(msgfp); }
6850:   if ( sublen != NULL )                 /*caller wants length of found substr*/
6851:     *sublen = foundlen;                 /* give it to him along with ptr */
6852:   return ( pfound );                    /*ptr to first found substr, or NULL*/
6853: } /* --- end-of-function strwstr() --- */
6854: 
6855: 
6856: /* ==========================================================================
6857:  * Function:    strdetex ( s, mode )
6858:  * Purpose:     Removes/replaces any LaTeX math chars in s
6859:  *              so that s can be displayed "verbatim",
6860:  *              e.g., for error messages.
6861:  * --------------------------------------------------------------------------
6862:  * Arguments:   s (I)           char * to null-terminated string
6863:  *                              whose math chars are to be removed/replaced
6864:  *              mode (I)        int containing 0 to _not_ use macros (i.e.,
6865:  *                              mimeprep won't be called afterwards),
6866:  *                              or containing 1 to use macros that will
6867:  *                              be expanded by a subsequent call to mimeprep.
6868:  * --------------------------------------------------------------------------
6869:  * Returns:     ( char * )      ptr to "cleaned" copy of s
6870:  *                              or "" (empty string) for any error.
6871:  * --------------------------------------------------------------------------
6872:  * Notes:     o The returned pointer addresses a static buffer,
6873:  *              so don't call strdetex() again until you're finished
6874:  *              with output from the preceding call.
6875:  * ======================================================================= */
6876: /* --- entry point --- */
6877: FUNCSCOPE char *strdetex ( char *s, int mode )
6878: {
6879: /* -------------------------------------------------------------------------
6880: Allocations and Declarations
6881: -------------------------------------------------------------------------- */
6882: static  char sbuff[4096];               /* copy of s with no math chars */
6883: /*int   strreplace();*/                 /* replace _ with -, etc */
6884: /* -------------------------------------------------------------------------
6885: Make a clean copy of s
6886: -------------------------------------------------------------------------- */
6887: /* --- check input --- */
6888: *sbuff = '\000';                        /* initialize in case of error */
6889: if ( isempty(s) ) goto end_of_job;      /* no input */
6890: /* --- start with copy of s --- */
6891: strninit(sbuff,s,2048);                 /* leave room for replacements */
6892: /* --- make some replacements -- we *must* replace \ { } first --- */
6893: strreplace(sbuff,"\\","\\backslash~\\!\\!",0);  /*change all \'s to text*/
6894: strreplace(sbuff,"{", "\\lbrace~\\!\\!",0);     /*change all {'s to \lbrace*/
6895: strreplace(sbuff,"}", "\\rbrace~\\!\\!",0);     /*change all }'s to \rbrace*/
6896: /* --- now our further replacements may contain \directives{args} --- */
6897: if( mode >= 1 ) strreplace(sbuff,"_","\\_",0);  /* change all _'s to \_ */
6898: else strreplace(sbuff,"_","\\underline{\\qquad}",0); /*change them to text*/
6899: if(0)strreplace(sbuff,"<","\\textlangle ",0);   /* change all <'s to text */
6900: if(0)strreplace(sbuff,">","\\textrangle ",0);   /* change all >'s to text */
6901: if(0)strreplace(sbuff,"$","\\textdollar ",0);   /* change all $'s to text */
6902: strreplace(sbuff,"$","\\$",0);                  /* change all $'s to \$ */
6903: strreplace(sbuff,"&","\\&",0);                  /* change all &'s to \& */
6904: strreplace(sbuff,"%","\\%",0);                  /* change all %'s to \% */
6905: strreplace(sbuff,"#","\\#",0);                  /* change all #'s to \# */
6906: /*strreplace(sbuff,"~","\\~",0);*/              /* change all ~'s to \~ */
6907: strreplace(sbuff,"^","{\\fs{+2}\\^}",0);        /* change all ^'s to \^ */
6908: end_of_job:
6909:   return ( sbuff );                     /* back with clean copy of s */
6910: } /* --- end-of-function strdetex() --- */
6911: 
6912: 
6913: /* ==========================================================================
6914:  * Function:    strtexchr ( char *string, char *texchr )
6915:  * Purpose:     Find first texchr in string, but texchr must be followed
6916:  *              by non-alpha
6917:  * --------------------------------------------------------------------------
6918:  * Arguments:   string (I)      char * to null-terminated string in which
6919:  *                              first occurrence of delim will be found
6920:  *              texchr (I)      char * to null-terminated string that
6921:  *                              will be searched for
6922:  * --------------------------------------------------------------------------
6923:  * Returns:     ( char * )      ptr to first char of texchr in string
6924:  *                              or NULL if not found or for any error.
6925:  * --------------------------------------------------------------------------
6926:  * Notes:     o texchr should contain its leading \, e.g., "\\left"
6927:  * ======================================================================= */
6928: /* --- entry point --- */
6929: FUNCSCOPE char *strtexchr ( char *string, char *texchr )
6930: {
6931: /* -------------------------------------------------------------------------
6932: Allocations and Declarations
6933: -------------------------------------------------------------------------- */
6934: char    delim, *ptexchr=(char *)NULL;   /* ptr returned to caller*/
6935: char    *pstring = string;              /* start or continue up search here*/
6936: int     texchrlen = (texchr==NULL?0:strlen(texchr)); /* #chars in texchr */
6937: /* -------------------------------------------------------------------------
6938: locate texchr in string
6939: -------------------------------------------------------------------------- */
6940: if ( string != (char *)NULL             /* check that we got input string */
6941: &&   texchrlen > 0 ) {                  /* and a texchr to search for */
6942:  while ( (ptexchr=strstr(pstring,texchr)) /* look for texchr in string */
6943:  != (char *)NULL )                      /* found it */
6944:   if ( (delim = ptexchr[texchrlen])     /* char immediately after texchr */
6945:   ==   '\000' ) break;                  /* texchr at very end of string */
6946:   else                                  /* if there are chars after texchr */
6947:    if ( isalpha(delim)                  /*texchr is prefix of longer symbol*/
6948:    ||   0 )                             /* other tests to be determined */
6949:     pstring = ptexchr + texchrlen;      /* continue search after texchr */
6950:    else                                 /* passed all tests */
6951:     break; }                            /*so return ptr to texchr to caller*/
6952: return ( ptexchr );                     /* ptr to texchar back to caller */
6953: } /* --- end-of-function strtexchr() --- */
6954: 
6955: 
6956: /* ==========================================================================
6957:  * Function:    findbraces ( char *expression, char *command )
6958:  * Purpose:     If expression!=NULL, finds opening left { preceding command;
6959:  *              if expression==NULL, finds closing right } after command.
6960:  *              For example, to parse out {a+b\over c+d} call findbraces()
6961:  *              twice.
6962:  * --------------------------------------------------------------------------
6963:  * Arguments:   expression (I)  NULL to find closing right } after command,
6964:  *                              or char * to null-terminated string to find
6965:  *                              left opening { preceding command.
6966:  *              command (I)     char * to null-terminated string whose
6967:  *                              first character is usually the \ of \command
6968:  * --------------------------------------------------------------------------
6969:  * Returns:     ( char * )      ptr to either opening { or closing },
6970:  *                              or NULL for any error.
6971:  * --------------------------------------------------------------------------
6972:  * Notes:     o
6973:  * ======================================================================= */
6974: /* --- entry point --- */
6975: FUNCSCOPE char *findbraces ( char *expression, char *command )
6976: {
6977: /* -------------------------------------------------------------------------
6978: Allocations and Declarations
6979: -------------------------------------------------------------------------- */
6980: int     isopen = (expression==NULL?0:1); /* true to find left opening { */
6981: char    *left="{", *right="}",          /* delims bracketing {x\command y} */
6982:         *delim = (isopen?left:right),   /* delim we want,  { if isopen */
6983:         *match = (isopen?right:left),   /* matching delim, } if isopen */
6984:         *brace = NULL;                  /* ptr to delim returned to caller */
6985: int     inc = (isopen?-1:+1);           /* pointer increment */
6986: int     level = 1;                      /* nesting level, for {{}\command} */
6987: char    *ptr = command;                 /* start search here */
6988: int     setbrace = 1;                   /* true to set {}'s if none found */
6989: /* -------------------------------------------------------------------------
6990: search for left opening { before command, or right closing } after command
6991: -------------------------------------------------------------------------- */
6992: while ( 1 )                             /* search for brace, or until end */
6993:   {
6994:   /* --- next char to check for delim --- */
6995:   ptr += inc;                           /* bump ptr left or right */
6996:   /* --- check for beginning or end of expression --- */
6997:   if ( isopen )                         /* going left, check for beginning */
6998:        { if ( ptr < expression ) break; } /* went before start of string */
6999:   else { if ( *ptr == '\000' ) break; } /* went past end of string */
7000:   /* --- don't check this char if it's escaped --- */
7001:   if ( !isopen || ptr>expression )      /* very first char can't be escaped*/
7002:     if ( isthischar(*(ptr-1),ESCAPE) )  /* escape char precedes current */
7003:       continue;                         /* so don't check this char */
7004:   /* --- check for delim --- */
7005:   if ( isthischar(*ptr,delim) )         /* found delim */
7006:     if ( --level == 0 )                 /* and it's not "internally" nested*/
7007:       { brace = ptr;                    /* set ptr to brace */
7008:         goto end_of_job; }              /* and return it to caller */
7009:   /* --- check for matching delim --- */
7010:   if ( isthischar(*ptr,match) )         /* found matching delim */
7011:     level++;                            /* so bump nesting level */
7012:   } /* --- end-of-while(1) --- */
7013: end_of_job:
7014:   if ( brace == (char *)NULL )          /* open{ or close} not found */
7015:     if ( setbrace )                     /* want to force one at start/end? */
7016:       brace = ptr;                      /* { before expressn, } after cmmnd*/
7017:   return ( brace );                     /*back to caller with delim or NULL*/
7018: } /* --- end-of-function findbraces() --- */
7019: 
7020: 
7021: #if 0  /* --- enhanced version from mathtex below --- */
7022: /* ==========================================================================
7023:  * Function:    strpspn ( char *s, char *reject, char *segment )
7024:  * Purpose:     finds the initial segment of s containing no chars
7025:  *              in reject that are outside (), [] and {} parens, e.g.,
7026:  *                 strpspn("abc(---)def+++","+-",segment) returns
7027:  *                 segment="abc(---)def" and a pointer to the first '+' in s
7028:  *              because the -'s are enclosed in () parens.
7029:  * --------------------------------------------------------------------------
7030:  * Arguments:   s (I)           (char *)pointer to null-terminated string
7031:  *                              whose initial segment is desired
7032:  *              reject (I)      (char *)pointer to null-terminated string
7033:  *                              containing the "reject chars"
7034:  *              segment (O)     (char *)pointer returning null-terminated
7035:  *                              string comprising the initial segment of s
7036:  *                              that contains non-rejected chars outside
7037:  *                              (),[],{} parens, i.e., all the chars up to
7038:  *                              but not including the returned pointer.
7039:  *                              (That's the entire string if no non-rejected
7040:  *                              chars are found.)
7041:  * --------------------------------------------------------------------------
7042:  * Returns:     ( char * )      pointer to first reject-char found in s
7043:  *                              outside parens, or a pointer to the
7044:  *                              terminating '\000' of s if there are
7045:  *                              no reject chars in s outside all () parens.
7046:  * --------------------------------------------------------------------------
7047:  * Notes:     o the return value is _not_ like strcspn()'s
7048:  *            o improperly nested (...[...)...] are not detected,
7049:  *              but are considered "balanced" after the ]
7050:  *            o if reject not found, segment returns the entire string s
7051:  *            o leading/trailing whitespace is trimmed from returned segment
7052:  * ======================================================================= */
7053: /* --- entry point --- */
7054: FUNCSCOPE char *strpspn ( char *s, char *reject, char *segment )
7055: {
7056: /* -------------------------------------------------------------------------
7057: Allocations and Declarations
7058: -------------------------------------------------------------------------- */
7059: char    *ps = s;                        /* current pointer into s */
7060: int     depth = 0;                      /* () paren nesting level */
7061: int     seglen=0, maxseg=2047;          /* segment length, max allowed */
7062: /* -------------------------------------------------------------------------
7063: initialization
7064: -------------------------------------------------------------------------- */
7065: /* --- check arguments --- */
7066: if ( isempty(s) )                       /* no input string supplied */
7067:   goto end_of_job;                      /* no reject chars supplied */
7068: /* -------------------------------------------------------------------------
7069: find first char from s outside () parens (and outside ""'s) and in reject
7070: -------------------------------------------------------------------------- */
7071: while ( *ps != '\000' ) {               /* search till end of input string */
7072:   if ( isthischar(*ps,"([{") ) depth++; /* push another paren */
7073:   if ( isthischar(*ps,")]}") ) depth--; /* or pop another paren */
7074:   if ( depth < 1 ) {                    /* we're outside all parens */
7075:     if ( isempty(reject) ) break;       /* no reject so break immediately */
7076:     if ( isthischar(*ps,reject) ) break; } /* only break on a reject char */
7077:   if ( segment != NULL )                /* caller gave us segment */
7078:     if ( seglen < maxseg )              /* don't overflow segment buffer */
7079:       memcpy(segment+seglen,ps,1);      /* so copy non-reject char */
7080:   seglen += 1;  ps += 1;                /* bump to next char */
7081:   } /* --- end-of-while(*ps!=0) --- */
7082: end_of_job:
7083:   if ( segment != NULL ) {              /* caller gave us segment */
7084:     if ( isempty(reject) ) {            /* no reject char */
7085:       segment[min2(seglen,maxseg)] = *ps;  seglen++; } /*closing )]} to seg*/
7086:     segment[min2(seglen,maxseg)] = '\000'; /* null-terminate the segment */
7087:     trimwhite(segment); }               /* trim leading/trailing whitespace*/
7088:   return ( ps );                        /* back to caller */
7089: } /* --- end-of-function strpspn() --- */
7090: #endif
7091: 
7092: 
7093: /* ==========================================================================
7094:  * Function:    isstrstr ( char *string, char *snippets, int iscase )
7095:  * Purpose:     determine whether any substring of 'string'
7096:  *              matches any of the comma-separated list of 'snippets',
7097:  *              ignoring case if iscase=0.
7098:  * --------------------------------------------------------------------------
7099:  * Arguments:   string (I)      char * containing null-terminated
7100:  *                              string that will be searched for
7101:  *                              any one of the specified snippets
7102:  *              snippets (I)    char * containing null-terminated,
7103:  *                              comma-separated list of snippets
7104:  *                              to be searched for in string
7105:  *              iscase (I)      int containing 0 for case-insensitive
7106:  *                              comparisons, or 1 for case-sensitive
7107:  * --------------------------------------------------------------------------
7108:  * Returns:     ( int )         1 if any snippet is a substring of
7109:  *                              string, 0 if not
7110:  * --------------------------------------------------------------------------
7111:  * Notes:     o
7112:  * ======================================================================= */
7113: /* --- entry point --- */
7114: FUNCSCOPE int isstrstr ( char *string, char *snippets, int iscase )
7115: {
7116: /* -------------------------------------------------------------------------
7117: Allocations and Declarations
7118: -------------------------------------------------------------------------- */
7119: int     status = 0;                     /*1 if any snippet found in string*/
7120: char    snip[256], *snipptr = snippets, /* munge through each snippet */
7121:         delim = ',', *delimptr = NULL;  /* separated by delim's */
7122: char    stringcp[4096], *cp = stringcp; /*maybe lowercased copy of string*/
7123: /* -------------------------------------------------------------------------
7124: initialization
7125: -------------------------------------------------------------------------- */
7126: /* --- arg check --- */
7127: if ( string==NULL || snippets==NULL ) goto end_of_job; /* missing arg */
7128: if ( *string=='\000' || *snippets=='\000' ) goto end_of_job; /* empty arg */
7129: /* --- copy string and lowercase it if case-insensitive --- */
7130: strninit(stringcp,string,4064);         /* local copy of string */
7131: if ( !iscase )                          /* want case-insensitive compares */
7132:   for ( cp=stringcp; *cp != '\000'; cp++ ) /* so for each string char */
7133:     if ( isupper(*cp) ) *cp = tolower(*cp); /*lowercase any uppercase chars*/
7134: /* -------------------------------------------------------------------------
7135: extract each snippet and see if it's a substring of string
7136: -------------------------------------------------------------------------- */
7137: while ( snipptr != NULL )               /* while we still have snippets */
7138:   {
7139:   /* --- extract next snippet --- */
7140:   if ( (delimptr = strchr(snipptr,delim)) /* locate next comma delim */
7141:   ==   NULL )                           /*not found following last snippet*/
7142:     { strninit(snip,snipptr,255);       /* local copy of last snippet */
7143:       snipptr = NULL; }                 /* signal end-of-string */
7144:   else                                  /* snippet ends just before delim */
7145:     { int sniplen = (int)(delimptr-snipptr) - 1;  /* #chars in snippet */
7146:       memcpy(snip,snipptr,sniplen);     /* local copy of snippet chars */
7147:       snip[sniplen] = '\000';           /* null-terminated snippet */
7148:       snipptr = delimptr + 1; }         /* next snippet starts after delim */
7149:   /* --- lowercase snippet if case-insensitive --- */
7150:   if ( !iscase )                        /* want case-insensitive compares */
7151:     for ( cp=snip; *cp != '\000'; cp++ ) /* so for each snippet char */
7152:       if ( isupper(*cp) ) *cp=tolower(*cp); /*lowercase any uppercase chars*/
7153:   /* --- check if snippet in string --- */
7154:   if ( strstr(stringcp,snip) != NULL )  /* found snippet in string */
7155:     { status = 1;                       /* so reset return status */
7156:       break; }                          /* no need to check any further */
7157:   } /* --- end-of-while(*snipptr!=0) --- */
7158: end_of_job: return ( status );          /*1 if snippet found in list, else 0*/
7159: } /* --- end-of-function isstrstr() --- */
7160: 
7161: 
7162: /* ==========================================================================
7163:  * Function:    isnumeric ( s )
7164:  * Purpose:     determine if s is an integer
7165:  * --------------------------------------------------------------------------
7166:  * Arguments:   s (I)           (char *)pointer to null-terminated string
7167:  *                              that's checked for a leading + or -
7168:  *                              followed by digits
7169:  * --------------------------------------------------------------------------
7170:  * Returns:     ( int )         1 if s is numeric, 0 if it is not
7171:  * --------------------------------------------------------------------------
7172:  * Notes:     o
7173:  * ======================================================================= */
7174: /* --- entry point --- */
7175: FUNCSCOPE int isnumeric ( char *s )
7176: {
7177: /* -------------------------------------------------------------------------
7178: determine whether s is an integer
7179: -------------------------------------------------------------------------- */
7180: int     status = 0;                     /* return 0 if not numeric, 1 if is*/
7181: char    *p = s;                         /* pointer into s */
7182: if ( isempty(s) ) goto end_of_job;      /* missing arg or empty string */
7183: skipwhite(p);                           /*check for leading +or- after space*/
7184: if ( *p=='+' || *p=='-' ) p++;          /* skip leading + or - */
7185: for ( ; *p != '\000'; p++ ) {           /* check rest of s for digits */
7186:   if ( isdigit(*p) ) continue;          /* still got uninterrupted digits */
7187:   if ( !isthischar(*p,WHITESPACE) ) goto end_of_job; /* non-numeric char */
7188:   skipwhite(p);                         /* skip all subsequent whitespace */
7189:   if ( *p == '\000' ) break;            /* trailing whitespace okay */
7190:   goto end_of_job;                      /* embedded whitespace non-numeric */
7191:   } /* --- end-of-for(*p) --- */
7192: status = 1;                             /* numeric after checks succeeded */
7193: end_of_job:
7194:   return ( status );                    /*back to caller with 1=string, 0=no*/
7195: } /* --- end-of-function isnumeric() --- */
7196: 
7197: 
7198: /* ==========================================================================
7199:  * Function:    evalterm ( STORE *store, char *term )
7200:  * Purpose:     evaluates a term
7201:  * --------------------------------------------------------------------------
7202:  * Arguments:   store (I/O)     STORE * containing environment
7203:  *                              in which term is to be evaluated
7204:  *              term (I)        char * containing null-terminated string
7205:  *                              with a term like "3" or "a" or "a+3"
7206:  *                              whose value is to be determined
7207:  * --------------------------------------------------------------------------
7208:  * Returns:     ( int )         value of term,
7209:  *                              or NOVALUE for any error
7210:  * --------------------------------------------------------------------------
7211:  * Notes:     o Also evaluates index?a:b:c:etc, returning a if index<=0,
7212:  *              b if index=1, etc, and the last value if index is too large.
7213:  *              Each a:b:c:etc can be another expression, including another
7214:  *              (index?a:b:c:etc) which must be enclosed in parentheses.
7215:  * ======================================================================= */
7216: /* --- entry point --- */
7217: FUNCSCOPE int evalterm ( STORE *store, char *term )
7218: {
7219: /* -------------------------------------------------------------------------
7220: Allocations and Declarations
7221: -------------------------------------------------------------------------- */
7222: int     termval = 0;                    /* term value returned to caller */
7223: char    token[2048] = "\000",           /* copy term */
7224:         *delim = NULL;                  /* delim '(' or '?' in token */
7225: /*int   evalwff(),*/                    /* recurse to evaluate terms */
7226: /*      evalfunc();*/                   /* evaluate function(arg1,arg2,...)*/
7227: char    *strpspn();                     /* span delims */
7228: int     getstore();                     /* lookup variables */
7229: int     isnumeric();                    /* numeric=constant, else variable */
7230: static  int evaltermdepth = 0;          /* recursion depth */
7231: int     novalue = (-89123456);          /* dummy (for now) error signal */
7232: /* -------------------------------------------------------------------------
7233: Initialization
7234: -------------------------------------------------------------------------- */
7235: if ( ++evaltermdepth > 99 ) goto end_of_job; /*probably recursing forever*/
7236: if ( store==NULL || isempty(term) ) goto end_of_job; /*check for missing arg*/
7237: skipwhite(term);                        /* skip any leading whitespace */
7238: /* -------------------------------------------------------------------------
7239: First look for conditional of the form term?term:term:...
7240: -------------------------------------------------------------------------- */
7241: /* ---left-hand part of conditional is chars preceding "?" outside ()'s--- */
7242: delim = strpspn(term,"?",token);        /* chars preceding ? outside () */
7243: if ( *delim != '\000' ) {               /* found conditional expression */
7244:   int ncolons = 0;                      /* #colons we've found so far */
7245:   if ( *token != '\000' )               /* evaluate "index" value on left */
7246:     if ( (termval=evalterm(store,token)) /* evaluate left-hand term */
7247:     == novalue ) goto end_of_job;       /* return error if failed */
7248:   while ( *delim != '\000' ) {          /* still have chars in term */
7249:     delim++; *token='\000';             /* initialize for next "value:" */
7250:     if ( *delim == '\000' ) break;      /* no more values */
7251:     delim = strpspn(delim,":",token);   /* chars preceding : outside () */
7252:     if ( ncolons++ >= termval ) break;  /* have corresponding term */
7253:     } /* --- end-of-while(*delim!='\000')) --- */
7254:   if ( *token != '\000' )               /* have x:x:value:x:x on right */
7255:     termval=evalterm(store,token);      /* so evaluate it */
7256:   goto end_of_job;                      /* return result to caller */
7257:   } /* --- end-of-if(*delim!='\000')) --- */
7258: /* -------------------------------------------------------------------------
7259: evaluate a+b recursively
7260: -------------------------------------------------------------------------- */
7261: /* --- left-hand part of term is chars preceding "/+-*%" outside ()'s --- */
7262: term = strpspn(term,"/+-*%",token);     /* chars preceding /+-*% outside ()*/
7263: /* --- evaluate a+b, a-b, etc --- */
7264: if ( *term != '\000' ) {                /* found arithmetic operation */
7265:   int leftval=0, rightval=0;            /* init leftval for unary +a or -a */
7266:   if ( *token != '\000' )               /* or eval for binary a+b or a-b */
7267:     if ( (leftval=evalterm(store,token)) /* evaluate left-hand term */
7268:     == novalue ) goto end_of_job;       /* return error if failed */
7269:   if ( (rightval=evalterm(store,term+1)) /* evaluate right-hand term */
7270:   == novalue ) goto end_of_job;         /* return error if failed */
7271:   switch ( *term ) {                    /* perform requested arithmetic */
7272:     default: break;                     /* internal error */
7273:     case '+': termval = leftval+rightval;  break;  /* addition */
7274:     case '-': termval = leftval-rightval;  break;  /* subtraction */
7275:     case '*': termval = leftval*rightval;  break;  /* multiplication */
7276:     case '/': if ( rightval != 0 )      /* guard against divide by zero */
7277:                 termval = leftval/rightval;  break; /* integer division */
7278:     case '%': if ( rightval != 0 )      /* guard against divide by zero */
7279:                 termval = leftval%rightval;  break; /*left modulo right */
7280:     } /* --- end-of-switch(*relation) --- */
7281:   goto end_of_job;                      /* return result to caller */
7282:   } /* --- end-of-if(*term!='\000')) --- */
7283: /* -------------------------------------------------------------------------
7284: check for parenthesized expression or term of the form function(arg1,arg2,...)
7285: -------------------------------------------------------------------------- */
7286: if ( (delim = strchr(token,'(')) != NULL ) { /* token contains a ( */
7287:   /* --- strip trailing paren (if there hopefully is one) --- */
7288:   int  toklen = strlen(token);          /* total #chars in token */
7289:   if ( token[toklen-1] == ')' )         /* found matching ) at end of token*/
7290:     token[--toklen] = '\000';           /* remove trailing ) */
7291:   /* --- handle parenthesized subexpression --- */
7292:   if ( *token == '(' ) {                /* have parenthesized expression */
7293:     strsqueeze(token,1);                /* so squeeze out leading ( */
7294:     /* --- evaluate edited term --- */
7295:     trimwhite(token);                   /* trim leading/trailing whitespace*/
7296:     termval = evalterm(store,token); }  /* evaluate token recursively */
7297:   /* --- handle function(arg1,arg2,...) --- */
7298:   else {                                /* have function(arg1,arg2,...) */
7299:     *delim = '\000';                    /* separate function name and args */
7300:     /*termval = evalfunc(store,token,delim+1);*/ } /* evaluate function */
7301:   goto end_of_job; }                    /* return result to caller */
7302: /* -------------------------------------------------------------------------
7303: evaluate constants directly, or recursively evaluate variables, etc
7304: -------------------------------------------------------------------------- */
7305: if ( *token != '\000' ) {               /* empty string */
7306:   if ( isnumeric(token) )               /* have a constant */
7307:     termval = atoi(token);              /* convert ascii-to-int */
7308:   else {                                /* variable or "stored proposition"*/
7309:     termval = getstore(store,token); }  /* look up token */
7310:   } /* --- end-of-if(*token!=0) --- */
7311: /* -------------------------------------------------------------------------
7312: back to caller with truth value of proposition
7313: -------------------------------------------------------------------------- */
7314: end_of_job:
7315:   /* --- back to caller --- */
7316:   if ( evaltermdepth > 0 ) evaltermdepth--;  /* pop recursion depth */
7317:   return ( termval );                   /* back to caller with value */
7318: } /* --- end-of-function evalterm() --- */
7319: 
7320: 
7321: /* ==========================================================================
7322:  * Function:    getstore ( store, identifier )
7323:  * Purpose:     finds identifier in store and returns corresponding value
7324:  * --------------------------------------------------------------------------
7325:  * Arguments:   store (I)       (STORE *)pointer to store containing
7326:  *                              the desired identifier
7327:  *              identifier (I)  (char *)pointer to null-terminated string
7328:  *                              containing the identifier whose value
7329:  *                              is to be returned
7330:  * --------------------------------------------------------------------------
7331:  * Returns:     ( int )         identifier's corresponding value,
7332:  *                              or 0 if identifier not found (or any error)
7333:  * --------------------------------------------------------------------------
7334:  * Notes:     o
7335:  * ======================================================================= */
7336: /* --- entry point --- */
7337: FUNCSCOPE int getstore ( STORE *store, char *identifier )
7338: {
7339: /* -------------------------------------------------------------------------
7340: Allocations and Declarations
7341: -------------------------------------------------------------------------- */
7342: int     value = 0;              /* store[istore].value for identifier */
7343: int     istore=0;               /* store[] index containing identifier */
7344: char    seek[512], hide[512];   /* identifier arg, identifier in store */
7345: /* --- first check args --- */
7346: if ( store==NULL || isempty(identifier)) goto end_of_job; /* missing arg */
7347: strninit(seek,identifier,500);  /* local copy of caller's identifier */
7348: trimwhite(seek);                /* remove leading/trailing whitespace */
7349: /* --- loop over store --- */
7350: for ( istore=0; istore<MAXSTORE; istore++ ) { /* until end-of-table */
7351:   char *idstore = store[istore].identifier; /* ptr to identifier in store */
7352:   if ( isempty(idstore) )       /* empty id signals eot */
7353:     break;                      /* de-reference any default/error value */
7354:   strninit(hide,idstore,500);   /* local copy of store[] identifier */
7355:   trimwhite(hide);              /* remove leading/trailing whitespace */
7356:   if ( !strcmp(hide,seek) )     /* found match */
7357:     break;                      /* de-reference corresponding value */
7358:   } /* --- end-of-for(istore) --- */
7359: if ( store[istore].value != NULL ) /* address of int supplied */
7360:   value = *(store[istore].value);  /* return de-referenced int */
7361: end_of_job:
7362:   return ( value );                     /* store->values[istore] or NULL */
7363: } /* --- end-of-function getstore() --- */
7364: 
7365: 
7366: /* ==========================================================================
7367:  * Function:    getdirective(string, directive, iscase, isvalid, nargs, args)
7368:  * Purpose:     Locates the first \directive{arg1}...{nargs} in string,
7369:  *              returns arg1...nargs in args[],
7370:  *              and removes \directive and its args from string.
7371:  * --------------------------------------------------------------------------
7372:  * Arguments:   string (I/0)    char * to null-terminated string from which
7373:  *                              the first occurrence of \directive will be
7374:  *                              interpreted and removed
7375:  *              directive (I)   char * to null-terminated string containing
7376:  *                              the \directive to be interpreted in string
7377:  *              iscase (I)      int containing 1 if match of \directive
7378:  *                              in string should be case-sensitive,
7379:  *                              or 0 if match is case-insensitive.
7380:  *              isvalid (I)     int containing validity check option:
7381:  *                              0=no checks, 1=must be numeric
7382:  *              nargs (I)       int containing (maximum) number of
7383:  *                              {args} following \directive, or 0 if none.
7384:  *              args (O)        void * interpreted as (char *) if nargs=1
7385:  *                              to return the one and only arg,
7386:  *                              or interpreted as (char **) if nargs>1
7387:  *                              to array of returned arg strings
7388:  * --------------------------------------------------------------------------
7389:  * Returns:     ( char * )      ptr to first char after removed \directive, or
7390:  *                              NULL if \directive not found, or any error.
7391:  * --------------------------------------------------------------------------
7392:  * Notes:     o If optional [arg]'s are found, they're stored in the global
7393:  *              optionalargs[] buffer, and the noptional counter is bumped.
7394:  *            o set global argformat's decimal digits for each arg,
7395:  *              e.g., 1357... means 1 for 1st arg, 3 for 2nd, 5 for 3rd, etc.
7396:  *              0 for an arg is the default format (i.e., argformat=0),
7397:  *              and means it's formatted as a LaTeX {arg} or [arg].
7398:  *              1 for an arg means arg terminated by first non-alpha char
7399:  *              2 means arg terminated by {   (e.g., as for /def)
7400:  *              8 means arg terminated by first whitespace char
7401:  * ======================================================================= */
7402: /* --- entry point --- */
7403: FUNCSCOPE char *getdirective ( char *string, char *directive,
7404:                                int iscase, int isvalid, int nargs, void *args )
7405: {
7406: /* -------------------------------------------------------------------------
7407: Allocations and Declarations
7408: -------------------------------------------------------------------------- */
7409: int     iarg = (-1);                    /* init to signal error */
7410: char    *pfirst = NULL,                 /* ptr to 1st char of directive */
7411:         *plast = NULL,                  /* ptr past last char of last arg */
7412:         *plbrace=NULL, *prbrace=NULL;   /* ptr to left,right brace of arg */
7413: int     fldlen = 0;                     /* #chars between { and } delims */
7414: char    argfld[512];                    /* {arg} characters */
7415: int     nfmt=0, /*isnegfmt=0,*/         /* {arg} format */
7416:         argfmt[9]={0,0,0,0,0,0,0,0,0};  /* argformat digits */
7417: int     gotargs = (args==NULL?0:1);     /* true if args array supplied */
7418: int     isdalpha = 1;                   /* true if directive ends with alpha*/
7419: /*char  *strpspn(char *s,char *reject,char *segment);*/ /*non-() not in rej*/
7420: #if 0 /* --- declared globally above --- */
7421: /* ---
7422:  * mathtex global variables (not used by mimetex)
7423:  * ------------------------------------------------- */
7424: int     noptional = 0;
7425: char    optionalargs[10][32];
7426: int     argformat = 0;
7427: int     optionalpos = 0;
7428: #endif
7429: /* -------------------------------------------------------------------------
7430: Find first \directive in string
7431: -------------------------------------------------------------------------- */
7432: noptional = 0;                          /* no optional [args] yet */
7433: for ( iarg=0; iarg<8; iarg++ )          /* for each one... */
7434:   *optionalargs[iarg] = '\000';         /* re-init optional [arg] buffer */
7435: if ( argformat != 0 ) {                 /* have argformat */
7436:   int   myfmt = argformat;              /* local copy */
7437:   if ( myfmt < 0 ) { /*isnegfmt=1;*/ myfmt=(-myfmt); } /* check sign */
7438:   while ( myfmt>0 && nfmt<9 ) {         /* have more format digits */
7439:     argfmt[nfmt] = myfmt%10;            /* store low-order decimal digit */
7440:     myfmt /= 10;                        /* and shift it out */
7441:     nfmt++; }                           /* count another format digit */
7442:   } /* --- end-of-if(argformat!=0) --- */
7443: if ( isempty(directive) ) goto end_of_job; /* no input \directive given */
7444: if ( !isalpha((int)(directive[strlen(directive)-1])) )isdalpha=0;/*not alpha*/
7445: pfirst = string;                        /* start at beginning of string */
7446: while ( 1 ) {                           /* until we find \directive */
7447:   if ( !isempty(pfirst) )               /* still have string from caller */
7448:     pfirst =                            /* ptr to 1st char of directive */
7449:      (iscase>0? strstr(pfirst,directive): /* case-sensistive match */
7450:       strcasestr(pfirst,directive));    /* case-insensistive match */
7451:   if ( isempty(pfirst) ) {              /* \directive not found in string */
7452:     pfirst = NULL;                      /* signal \directive not found */
7453:     goto end_of_job; }                  /* quit, signalling error to caller*/
7454:   plast = pfirst + strlen(directive);   /*ptr to fist char past directive*/
7455:   if ( !isdalpha || !isalpha((int)(*plast)) ) break; /* found \directive */
7456:   pfirst = plast;                       /* keep looking */
7457:   plast = NULL;                         /* reset plast */
7458:   } /* --- end-of-while(1) --- */
7459: if ( nargs < 0 ) {                      /* optional [arg] may be present */
7460:   nargs = -nargs;                       /* flip sign back to positive */
7461:   /*noptional = 1;*/ }                  /* and set optional flag */
7462: /* -------------------------------------------------------------------------
7463: Get arguments
7464: -------------------------------------------------------------------------- */
7465: iarg = 0;                               /* no args yet */
7466: if ( nargs > 0 )                        /* \directive has {args} */
7467:   while ( iarg < nargs+noptional ) {    /* get each arg */
7468:     int karg = iarg-noptional;          /* non-optional arg index */
7469:     int kfmt = (nfmt<=karg?0:argfmt[nfmt-karg-1]); /* arg format digit */
7470:     /* --- find left { and right } arg delimiters --- */
7471:     plbrace = plast;                    /*ptr to fist char past previous arg*/
7472:     skipwhite(plbrace);                 /* push it to first non-white char */
7473:     if ( isempty(plbrace) ) break;      /* reached end-of-string */
7474:     /* --- check LaTeX for single-char arg or {arg} or optional [arg] --- */
7475:     if ( kfmt == 0 ) {                  /* interpret LaTeX {arg} format */
7476:      if ( !isthischar(*plbrace,(iarg==optionalpos+noptional?"{[":"{")) ) {
7477:       /* --- single char argument --- */
7478:       plast = plbrace + 1;              /* first char after single-char arg*/
7479:       argfld[0] = *plbrace;             /* arg field is this one char */
7480:       argfld[1] = '\000'; }             /* null-terminate field */
7481:      else {                             /* have {arg} or optional [arg] */
7482:       /* note: to use for validation, need robust {} match like strpspn() */
7483:       if ( (prbrace = strchr(plbrace,(*plbrace=='{'?'}':']'))) /*right }or]*/
7484:       ==   NULL ) break;                /*and no more args if no right brace*/
7485:       if ( 1 )                          /* true to use strpspn() */
7486:         prbrace = strpspn(plbrace,NULL,NULL); /* push to matching } or ] */
7487:       plast = prbrace + 1;              /* first char after right brace */
7488:       /* --- extract arg field between { and } delimiters --- */
7489:       fldlen = (int)(prbrace-plbrace) - 1; /* #chars between { and } delims*/
7490:       if ( fldlen >= 256 ) fldlen=255;  /* don't overflow argfld[] buffer */
7491:       if ( fldlen > 0 )                 /* have chars in field */
7492:         memcpy(argfld,plbrace+1,fldlen); /*copy field chars to local buffer*/
7493:       argfld[fldlen] = '\000';          /* and null-terminate field */
7494:       trimwhite(argfld);                /* trim whitespace from argfld */
7495:       } /* --- end-of-if/else(!isthischar(*plbrace,...)) --- */
7496:      } /* --- end-of-if(kfmt==0) --- */
7497:     /* --- check plain TeX for arg terminated by whitespace --- */
7498:     if ( kfmt != 0 ) {                  /* interpret plain TeX arg format */
7499:      char *parg = NULL;                 /* ptr into arg, used as per kfmt */
7500:      plast = plbrace;                   /* start at first char of arg */
7501:      if ( *plast == '\\' ) plast++;     /* skip leading \command backslash */
7502:      /* --- interpret arg according to its format --- */
7503:      switch ( kfmt ) {
7504:        case 1:
7505:        default: skipcommand(plast); break; /* push ptr to non-alpha char */
7506:        case 2: parg = strchr(plast,'{'); /* next arg always starts with { */
7507:         if ( parg != NULL ) plast=parg; else plast++; /* up to { or 1 char */
7508:         break;
7509:        case 8: findwhite(plast); break; /*ptr to whitespace after last char*/
7510:        } /* --- end-of-switch(kfmt) --- */
7511:      /* --- extract arg field --- */
7512:      fldlen = (int)(plast-plbrace);     /* #chars between in field */
7513:      if ( fldlen >= 256 ) fldlen=255;   /* don't overflow argfld[] buffer */
7514:      if ( fldlen > 0 )                  /* have chars in field */
7515:        memcpy(argfld,plbrace,fldlen);   /*copy field chars to local buffer*/
7516:      argfld[fldlen] = '\000';           /* and null-terminate field */
7517:      if ( 1 ) { trimwhite(argfld); }    /* trim whitespace from argfld */
7518:      } /* --- end-of-if(kfmt!=0) --- */
7519:     if ( isvalid != 0 ) {               /* argfld[] validity check desired */
7520:      if ( isvalid == 1 ) {              /* numeric check wanted */
7521:        int validlen = strspn(argfld," +-.0123456789"); /*very simple check*/
7522:        argfld[validlen] = '\000'; }     /* truncate invalid chars */
7523:      } /* --- end-of-if(isvalid!=0) --- */
7524:     /* --- store argument field in caller's array --- */
7525:     if ( kfmt==0 && *plbrace=='[' ) {   /*store [arg] as optionalarg instead*/
7526:      if ( noptional < 8 ) {             /* don't overflow our buffer */
7527:        strninit(optionalargs[noptional],argfld,254); } /*copy to optionalarg*/
7528:      noptional++; }                     /* count another optional [arg] */
7529:     else                                /*{args} returned in caller's array*/
7530:      if ( gotargs ) {                   /*caller supplied address or array*/
7531:       if ( nargs < 2 )                  /*just one arg, so it's an address*/
7532:         strcpy((char *)args,argfld);    /* so copy arg field there */
7533:       else {                            /* >1 arg, so it's a ptr array */
7534:         char *argptr = ((char **)args)[karg]; /* arg ptr in array of ptrs */
7535:         if ( argptr != NULL )           /* array has iarg-th address */
7536:           strcpy(argptr,argfld);        /* so copy arg field there */
7537:         else gotargs = 0; } }           /* no more addresses in array */
7538:     /* --- completed this arg --- */
7539:     iarg++;                             /* bump arg count */
7540:     } /* --- end-of-while(iarg<nargs) --- */
7541: /* -------------------------------------------------------------------------
7542: Back to caller
7543: -------------------------------------------------------------------------- */
7544: end_of_job:
7545:   if ( 1 ) argformat = 0;               /* always/never reset global arg */
7546:   if ( 1 ) optionalpos = 0;             /* always/never reset global arg */
7547:   if ( pfirst!=NULL && plast!=NULL )    /* have directive field delims */
7548:     {strsqueeze(pfirst,((int)(plast-pfirst)));} /* squeeze out directive */
7549:   return ( pfirst );                    /* ptr to 1st char after directive */
7550: } /* --- end-of-function getdirective() --- */
7551: 
7552: 
7553: /* ==========================================================================
7554:  * Function:    strpspn ( char *s, char *reject, char *segment )
7555:  * Purpose:     finds the initial segment of s containing no chars
7556:  *              in reject that are outside (), [] and {} parens, e.g.,
7557:  *                 strpspn("abc(---)def+++","+-",segment) returns
7558:  *                 segment="abc(---)def" and a pointer to the first '+' in s
7559:  *              because the -'s are enclosed in () parens.
7560:  * --------------------------------------------------------------------------
7561:  * Arguments:   s (I)           (char *)pointer to null-terminated string
7562:  *                              whose initial segment is desired
7563:  *              reject (I)      (char *)pointer to null-terminated string
7564:  *                              containing the "reject chars"
7565:  *                              If reject contains a " or a ', then the
7566:  *                              " or ' isn't itself a reject char,
7567:  *                              but other reject chars within quoted
7568:  *                              strings (or substrings of s) are spanned.
7569:  *              segment (O)     (char *)pointer returning null-terminated
7570:  *                              string comprising the initial segment of s
7571:  *                              that contains non-rejected chars outside
7572:  *                              (),[],{} parens, i.e., all the chars up to
7573:  *                              but not including the returned pointer.
7574:  *                              (That's the entire string if no non-rejected
7575:  *                              chars are found.)
7576:  * --------------------------------------------------------------------------
7577:  * Returns:     ( char * )      pointer to first reject-char found in s
7578:  *                              outside parens, or a pointer to the
7579:  *                              terminating '\000' of s if there are
7580:  *                              no reject chars in s outside all () parens.
7581:  *                              But if reject is empty, returns pointer
7582:  *                              to matching )]} outside all parens.
7583:  * --------------------------------------------------------------------------
7584:  * Notes:     o the return value is _not_ like strcspn()'s
7585:  *            o improperly nested (...[...)...] are not detected,
7586:  *              but are considered "balanced" after the ]
7587:  *            o if reject not found, segment returns the entire string s
7588:  *            o but if reject is empty, returns segment up to and including
7589:  *              matching )]}
7590:  *            o leading/trailing whitespace is trimmed from returned segment
7591:  * ======================================================================= */
7592: /* --- entry point --- */
7593: FUNCSCOPE char *strpspn ( char *s, char *reject, char *segment )
7594: {
7595: /* -------------------------------------------------------------------------
7596: Allocations and Declarations
7597: -------------------------------------------------------------------------- */
7598: char    *ps = s;                        /* current pointer into s */
7599: /*char  *strqspn(char *s,char *q,int isunescape);*/ /*span quoted string*/
7600: char    qreject[256]="\000", *pq=qreject, *pr=reject; /*find "or' in reject*/
7601: int     isqspan = 0;                    /* true to span quoted strings */
7602: int     depth = 0;                      /* () paren nesting level */
7603: int     seglen=0, maxseg=2047;          /* segment length, max allowed */
7604: int     isescaped=0, checkescapes=1;    /* signals escaped chars */
7605: /* -------------------------------------------------------------------------
7606: initialization
7607: -------------------------------------------------------------------------- */
7608: /* --- check arguments --- */
7609: if ( isempty(s)                         /* no input string supplied */
7610: /*||   isempty(reject)*/ ) goto end_of_job; /* no reject chars supplied */
7611: /* --- set up qreject w/o quotes --- */
7612: if ( !isempty(reject) )                 /* have reject string from caller */
7613:   while ( *pr != '\000' ) {             /* until end-of-reject string */
7614:     if ( !isthischar(*pr,"\"\'") )      /* not a " or ' */
7615:       *pq++ = *pr;                      /* copy actual reject char */
7616:     else isqspan = 1;                   /* span rejects in quoted strings */
7617:     pr++; }                             /* next reject char from caller */
7618: *pq = '\000';                           /* null-terminate qreject */
7619: /* -------------------------------------------------------------------------
7620: find first char from s outside () parens (and outside ""'s) and in reject
7621: -------------------------------------------------------------------------- */
7622: while ( *ps != '\000' ) {               /* search till end of input string */
7623:   int spanlen = 1;                      /*span 1 non-reject, non-quoted char*/
7624:   if ( !isescaped ) {                   /* ignore escaped \(,\[,\{,\),\],\}*/
7625:     if ( isthischar(*ps,"([{") ) depth++;   /* push another paren */
7626:     if ( isthischar(*ps,")]}") ) depth--; } /* or pop another paren */
7627:   if ( depth < 1 ) {                    /* we're outside all parens */
7628:     if ( isqspan )                      /* span rejects in quoted strings */
7629:       if ( isthischar(*ps,"\"\'") ) {   /* and we're at opening quote */
7630:         pq = strqspn(ps,NULL,0);        /* locate matching closing quote */
7631:         if ( pq != ps )                 /* detected start of quoted string */
7632:          if ( *pq == *ps )              /* and found closing quote */
7633:           spanlen = ((int)(pq-ps)) + 1; } /* span the entire quoted string */
7634:     if ( isempty(qreject) ) break;      /* no reject so break immediately */
7635:     if ( isthischar(*ps,qreject) ) break; } /* only break on a reject char */
7636:   if ( checkescapes )                   /* if checking escape sequences */
7637:     isescaped = (*ps=='\\'?1:0);        /* reset isescaped signal */
7638:   if ( segment != NULL ) {              /* caller gave us segment */
7639:     int copylen = min2(spanlen,maxseg-seglen); /* don't overflow segment */
7640:     if ( copylen > 0 )                  /* have room in segment buffer */
7641:       memcpy(segment+seglen,ps,copylen); } /* so copy non-reject chars */
7642:   seglen += spanlen;  ps += spanlen;    /* bump to next char */
7643:   } /* --- end-of-while(*ps!=0) --- */
7644: end_of_job:
7645:   if ( segment != NULL ) {              /* caller gave us segment */
7646:     if ( isempty(qreject) && !isempty(s) ) { /* no reject char */
7647:       segment[min2(seglen,maxseg)] = *ps;  seglen++; } /*closing )]} to seg*/
7648:     segment[min2(seglen,maxseg)] = '\000'; /* null-terminate the segment */
7649:     trimwhite(segment); }               /* trim leading/trailing whitespace*/
7650:   return ( ps );                        /* back to caller */
7651: } /* --- end-of-function strpspn() --- */
7652: 
7653: 
7654: /* ==========================================================================
7655:  * Function:    strqspn ( char *s, char *q, int isunescape )
7656:  * Purpose:     finds matching/closing " or ' in quoted string
7657:  *              that begins with " or ', and optionally changes
7658:  *              escaped quotes to unescaped quotes.
7659:  * --------------------------------------------------------------------------
7660:  * Arguments:   s (I)           (char *)pointer to null-terminated string
7661:  *                              that begins with " or ',
7662:  *              q (O)           (char *)pointer returning null-terminated
7663:  *                              quoted token, with or without outer quotes,
7664:  *                              and with or without escaped inner quotes
7665:  *                              changed to unescaped quotes, depending
7666:  *                              on isunescape.
7667:  *              isunescape (I)  int containing 1 to change \" to " if s
7668:  *                              is "quoted" or change \' to ' if 'quoted',
7669:  *                              or containing 2 to change both \" and \'
7670:  *                              to unescaped quotes.  Other \sequences aren't
7671:  *                              changed.  Note that \\" emits \".
7672:  *                              isunescape=0 makes no changes at all.
7673:  *                              Note: the following not implemented yet --
7674:  *                              If unescape is negative, its abs() is used,
7675:  *                              but outer quotes aren't included in q.
7676:  * --------------------------------------------------------------------------
7677:  * Returns:     ( char * )      pointer to matching/closing " or '
7678:  *                              (or to char after quote if isunescape<0),
7679:  *                              or terminating '\000' if none found,
7680:  *                              or unchanged (same as s) if not quoted string
7681:  * --------------------------------------------------------------------------
7682:  * Notes:     o
7683:  * ======================================================================= */
7684: /* --- entry point --- */
7685: FUNCSCOPE char *strqspn ( char *s, char *q, int isunescape )
7686: {
7687: /* -------------------------------------------------------------------------
7688: Allocations and Declarations
7689: -------------------------------------------------------------------------- */
7690: char    *ps = s,  *pq = q;              /* current pointer into s, q */
7691: char    quote = '\000';                 /* " or ' quote character */
7692: int     qcopy = (isunescape<0?0:1);     /* true to copy outer quotes */
7693: int     isescaped = 0;                  /* true to signal \escape sequence */
7694: int     maxqlen = 2400;                 /* max length of returned q */
7695: /* -------------------------------------------------------------------------
7696: Initialization
7697: -------------------------------------------------------------------------- */
7698: /* --- check args --- */
7699: if ( s == NULL ) goto end_of_job;       /* no string supplied */
7700: skipwhite(ps);                          /* skip leading whitespace */
7701: if ( *ps == '\000'                      /* string exhausted */
7702: ||   (!isthischar(*ps,"\"\'")) ) {      /* or not a " or ' quoted string */
7703:   ps = s;  goto end_of_job; }           /*signal error/not string to caller*/
7704: if ( isunescape < 0 ) isunescape = (-isunescape); /* flip positive */
7705: /* --- set quote character --- */
7706: quote = *ps;                            /* set quote character */
7707: if ( qcopy && q!=NULL ) *pq++ = quote;  /* and copy it to output token */
7708: /* -------------------------------------------------------------------------
7709: span characters between quotes
7710: -------------------------------------------------------------------------- */
7711: while ( *(++ps) != '\000' ) {           /* end-of-string always terminates */
7712:   /* --- process escaped chars --- */
7713:   if ( isescaped ) {                    /* preceding char was \ */
7714:     if ( *ps != '\\' ) isescaped = 0;   /* reset isescaped flag unless \\ */
7715:     if ( q != NULL ) {                  /* caller wants quoted token */
7716:       if ( isunescape==0                /* don't unescape anything */
7717:       ||   (isunescape==1 && *ps!=quote) /* escaped char not our quote */
7718:       ||   (isunescape==2 && (!isthischar(*ps,"\"\'"))) ) /* not any quote */
7719:         if ( --maxqlen > 0 )            /* so if there's room in token */
7720:           *pq++ = '\\';                 /*keep original \ in returned token*/
7721:       if ( !isescaped )                 /* will have to check 2nd \ in \\ */
7722:         if ( --maxqlen > 0 )            /* if there's room in token */
7723:           *pq++ = *ps; }                /* put escaped char in token */
7724:     continue; }                         /* go on to next char in string */
7725:   /* --- check if next char escaped --- */
7726:   if ( *ps == '\\' ) {                  /* found escape char */
7727:     isescaped=1; continue; }            /*set flag and process escaped char*/
7728:   /* --- check for unescaped closing quote --- */
7729:   if ( *ps == quote ) {                 /* got an unescaped quote */
7730:     if ( qcopy && q!=NULL ) *pq++ = quote; /* copy it to output token */
7731:     if ( 0 && !qcopy ) ps++;            /* return ptr to char after quote */
7732:     goto end_of_job; }                  /* back to caller */
7733:   /* --- process other chars --- */
7734:   if ( q != NULL )                      /* caller want token returned */
7735:     if ( --maxqlen > 0 )                /* and there's still room in token */
7736:       *pq++ = *ps;                      /* put char in  token */
7737:   } /* --- end-of-while(*(++ps)!='\000') --- */
7738: /*ps = NULL;*/  /*pq = q;*/             /* error if no closing quote found */
7739: end_of_job:
7740:   if ( q != NULL ) *pq = '\000';        /* null-terminate returned token */
7741:   return ( ps );                        /* return ptr to " or ', or NULL */
7742: } /* --- end-of-function strqspn() --- */
7743: 
7744: 
7745: /* ==========================================================================
7746:  * Functions:   int  unescape_url ( char *url, int isescape )
7747:  *              char x2c ( char *what )
7748:  * Purpose:     unescape_url replaces 3-character sequences %xx in url
7749:  *                  with the single character represented by hex xx.
7750:  *              x2c returns the single character represented by hex xx
7751:  *                  passed as a 2-character sequence in what.
7752:  * --------------------------------------------------------------------------
7753:  * Arguments:   url (I)         char * containing null-terminated
7754:  *                              string with embedded %xx sequences
7755:  *                              to be converted.
7756:  *              isescape (I)    int containing 1 to _not_ unescape
7757:  *                              \% sequences (0 would be NCSA default)
7758:  *              what (I)        char * whose first 2 characters are
7759:  *                              interpreted as ascii representations
7760:  *                              of hex digits.
7761:  * --------------------------------------------------------------------------
7762:  * Returns:     ( int )         unescape_url always returns 0.
7763:  *              ( char )        x2c returns the single char
7764:  *                              corresponding to hex xx passed in what.
7765:  * --------------------------------------------------------------------------
7766:  * Notes:     o These two functions were taken verbatim from util.c in
7767:  *   ftp://ftp.ncsa.uiuc.edu/Web/httpd/Unix/ncsa_httpd/cgi/ncsa-default.tar.Z
7768:  *            o Not quite "verbatim" -- I added the "isescape logic" 4-Dec-03
7769:  *              so unescape_url() can be safely applied to input which may or
7770:  *              may not have been url-encoded.  (Note: currently, all calls
7771:  *              to unescape_url() pass iescape=0, so it's not used.)
7772:  *            o Added +++'s to blank xlation on 24-Sep-06
7773:  *            o Added ^M,^F,etc to blank xlation 0n 01-Oct-06
7774:  * ======================================================================= */
7775: /* --- entry point --- */
7776: FUNCSCOPE int unescape_url(char *url, int isescape) {
7777:     int x=0,y=0,prevescape=0,gotescape=0;
7778:     int xlateplus = (isplusblank==1?1:0); /* true to xlate plus to blank */
7779:     int strreplace();                   /* replace + with blank, if needed */
7780:     /*char x2c();*/
7781:     static char *hex="0123456789ABCDEFabcdef";
7782:     /* ---
7783:      * xlate ctrl chars to blanks
7784:      * -------------------------- */
7785:     if ( 1 ) {                          /* xlate ctrl chars to blanks */
7786:       char *ctrlchars = "\n\t\v\b\r\f\a\015";
7787:       int  seglen = strspn(url,ctrlchars); /*initial segment with ctrlchars*/
7788:       int  urllen = strlen(url);        /* total length of url string */
7789:       /* --- first, entirely remove ctrlchars from beginning and end --- */
7790:       if ( seglen > 0 ) {               /*have ctrlchars at start of string*/
7791:         strsqueeze(url,seglen);         /* squeeze out initial ctrlchars */
7792:         urllen -= seglen; }             /* string is now shorter */
7793:       while ( --urllen >= 0 )           /* now remove ctrlchars from end */
7794:         if ( isthischar(url[urllen],ctrlchars) ) /* ctrlchar at end */
7795:           url[urllen] = '\000';         /* re-terminate string before it */
7796:         else break;                     /* or we're done */
7797:       urllen++;                         /* length of url string */
7798:       /* --- now, replace interior ctrlchars with ~ blanks --- */
7799:       while ( (seglen=strcspn(url,ctrlchars)) < urllen ) /*found a ctrlchar*/
7800:         url[seglen] = '~';              /* replace ctrlchar with ~ */
7801:       } /* --- end-of-if(1) --- */
7802:     /* ---
7803:      * xlate +'s to blanks if requested or if deemed necessary
7804:      * ------------------------------------------------------- */
7805:     if ( isplusblank == (-1) ) {        /*determine whether or not to xlate*/
7806:       char *searchfor[] = { " ","%20", "%2B","%2b", "+++","++",
7807:         "+=+","+-+", NULL };
7808:       int  isearch = 0,                 /* searchfor[] index */
7809:            nfound[11] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; /*#occurrences*/
7810:       /* --- locate occurrences of searchfor[] strings in url --- */
7811:       for ( isearch=0; searchfor[isearch] != NULL; isearch++ ) {
7812:         char *psearch = url;            /* start search at beginning */
7813:         nfound[isearch] = 0;            /* init #occurrences count */
7814:         while ( (psearch=strstr(psearch,searchfor[isearch])) != NULL ) {
7815:           nfound[isearch] += 1;         /* count another occurrence */
7816:           psearch += strlen(searchfor[isearch]); } /*resume search after it*/
7817:         } /* --- end-of-for(isearch) --- */
7818:       /* --- apply some common-sense logic --- */
7819:       if ( nfound[0] + nfound[1] > 0 )  /* we have actual " "s or "%20"s */
7820:         isplusblank = xlateplus = 0;    /* so +++'s aren't blanks */
7821:       if ( nfound[2] + nfound[3] > 0 ) { /* we have "%2B" for +++'s */
7822:         if ( isplusblank != 0 )         /* and haven't disabled xlation */
7823:           isplusblank = xlateplus = 1;  /* so +++'s are blanks */
7824:         else                            /* we have _both_ "%20" and "%2b" */
7825:           xlateplus = 0; }              /* tough call */
7826:       if ( nfound[4] + nfound[5] > 0    /* we have multiple ++'s */
7827:       ||   nfound[6] + nfound[7] > 0 )  /* or we have a +=+ or +-+ */
7828:         if ( isplusblank != 0 )         /* and haven't disabled xlation */
7829:           xlateplus = 1;                /* so xlate +++'s to blanks */
7830:       } /* --- end-of-if(isplusblank==-1) --- */
7831:     if ( xlateplus > 0 ) {              /* want +'s xlated to blanks */
7832:       char *xlateto[] = { ""," "," "," + "," "," "," "," "," " };
7833:       while ( xlateplus > 0 ) {         /* still have +++'s to xlate */
7834:         char plusses[99] = "++++++++++++++++++++"; /* longest +++ string */
7835:         plusses[xlateplus] = '\000';    /* null-terminate +++'s */
7836:         strreplace(url,plusses,xlateto[xlateplus],0); /* xlate +++'s */
7837:         xlateplus--;                    /* next shorter +++ string */
7838:         } /* --- end-of-while(xlateplus>0) --- */
7839:       } /* --- end-of-if(xlateplus) --- */
7840:     isplusblank = 0;                    /* don't iterate this xlation */
7841:     /* ---
7842:      * xlate %nn to corresponding char
7843:      * ------------------------------- */
7844:     for(;url[y];++x,++y) {
7845:         gotescape = prevescape;
7846:         prevescape = (url[x]=='\\');
7847:         if((url[x] = url[y]) == '%')
7848:          if(!isescape || !gotescape)
7849:           if(isthischar(url[y+1],hex)
7850:           && isthischar(url[y+2],hex))
7851:             { url[x] = x2c(&url[y+1]);
7852:               y+=2; }
7853:     }
7854:     url[x] = '\0';
7855:     return 0;
7856: } /* --- end-of-function unescape_url() --- */
7857: /* --- entry point --- */
7858: FUNCSCOPE char x2c(char *what) {
7859:     char digit;
7860:     digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
7861:     digit *= 16;
7862:     digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
7863:     return(digit);
7864: } /* --- end-of-function x2c() --- */
7865: #endif /* PART2 */
7866: 
7867: /* ---
7868:  * PART3
7869:  * ------ */
7870: #if !defined(PARTS) || defined(PART3)
7871: /* ==========================================================================
7872:  * Function:    rasterize ( expression, size )
7873:  * Purpose:     returns subraster corresponding to (a valid LaTeX) expression
7874:  *              at font size
7875:  * --------------------------------------------------------------------------
7876:  * Arguments:   expression (I)  char * to first char of null-terminated
7877:  *                              string containing valid LaTeX expression
7878:  *                              to be rasterized
7879:  *              size (I)        int containing 0-4 default font size
7880:  * --------------------------------------------------------------------------
7881:  * Returns:     ( subraster * ) ptr to subraster corresponding to expression,
7882:  *                              or NULL for any parsing error.
7883:  * --------------------------------------------------------------------------
7884:  * Notes:     o This is mimeTeX's "main" reusable entry point.  Easy to use:
7885:  *              just call it with a LaTeX expression, and get back a bitmap
7886:  *              of that expression.  Then do what you want with the bitmap.
7887:  * ======================================================================= */
7888: /* --- entry point --- */
7889: FUNCSCOPE subraster *rasterize ( char *expression, int size )
7890: {
7891: /* -------------------------------------------------------------------------
7892: Allocations and Declarations
7893: -------------------------------------------------------------------------- */
7894: char    /**preamble(),*/ pretext[512];  /* process preamble, if present */
7895: char    chartoken[MAXSUBXSZ+1], /**texsubexpr(),*/ /*get subexpr from expr*/
7896:         *subexpr = chartoken;           /* token may be parenthesized expr */
7897: /*int   isbrace();*/                    /* check subexpr for braces */
7898: mathchardef *symdef /*, *get_symdef()*/; /*get mathchardef struct for symbol*/
7899: int     ligdef /*, get_ligature()*/;    /*get symtable[] index for ligature*/
7900: int     natoms=0;                       /* #atoms/tokens processed so far */
7901: /*int   type_raster();*/                /* display debugging output */
7902: /*subraster *rasterize(),*/             /* recurse */
7903: /*      *rastparen(),*/                 /* handle parenthesized subexpr's */
7904: /*      *rastlimits();*/                /* handle sub/superscripted expr's */
7905: /*subraster *rastcat(),*/               /* concatanate atom subrasters */
7906: /*      *subrastcpy(),*/                /* copy final result if a charaster*/
7907: /*      *new_subraster();*/             /* new subraster for isstring mode */
7908: subraster /**get_charsubraster(),*/     /* character subraster */
7909:         *sp=NULL, *prevsp=NULL,         /* raster for current, prev char */
7910:         *expraster = (subraster *)NULL; /* raster returned to caller */
7911: /*int   delete_subraster();*/           /* free everything before returning*/
7912: int     family = fontinfo[fontnum].family; /* current font family */
7913: int     isleftscript = 0,               /* true if left-hand term scripted */
7914:         wasscripted = 0,                /* true if preceding token scripted*/
7915:         wasdelimscript = 0;             /* true if preceding delim scripted*/
7916: /*int   pixsz = 1;*/                    /*default #bits per pixel, 1=bitmap*/
7917: /*char  *strdetex();*/                  /* detex token for error message */
7918: /* --- global values saved/restored at each recursive iteration --- */
7919: int     wasstring = isstring,           /* initial isstring mode flag */
7920:         wasdisplaystyle = isdisplaystyle, /*initial displaystyle mode flag*/
7921:         oldfontnum = fontnum,           /* initial font family */
7922:         oldfontsize = fontsize,         /* initial fontsize */
7923:         olddisplaysize = displaysize,   /* initial \displaystyle size */
7924:         oldshrinkfactor = shrinkfactor, /* initial shrinkfactor */
7925:         oldsmashmargin = smashmargin,   /* initial smashmargin */
7926:         oldissmashdelta = issmashdelta, /* initial issmashdelta */
7927:         oldisexplicitsmash = isexplicitsmash, /* initial isexplicitsmash */
7928:         oldisscripted = isscripted,     /* initial isscripted */
7929:         *oldworkingparam = workingparam; /* initial working parameter */
7930: subraster *oldworkingbox = workingbox,  /* initial working box */
7931:         *oldleftexpression = leftexpression; /*left half rasterized so far*/
7932: double  oldunitlength = unitlength;     /* initial unitlength */
7933: mathchardef *oldleftsymdef = leftsymdef; /* init oldleftsymdef */
7934: /* -------------------------------------------------------------------------
7935: initialization
7936: -------------------------------------------------------------------------- */
7937: recurlevel++;                           /* wind up one more recursion level*/
7938: leftexpression = NULL;                  /* no leading left half yet */
7939: isreplaceleft = 0;                      /* reset replaceleft flag */
7940: if(1)fraccenterline = NOVALUE;          /* reset \frac baseline signal */
7941: /* shrinkfactor = shrinkfactors[max2(0,min2(size,LARGESTSIZE))];*/ /*set sf*/
7942: shrinkfactor = shrinkfactors[max2(0,min2(size,16))]; /* have 17 sf's */
7943: rastlift = 0;                           /* reset global rastraise() lift */
7944: if ( msgfp!=NULL && msglevel >= 9 ) {   /*display expression for debugging*/
7945:  fprintf(msgfp,
7946:  "rasterize> recursion#%d, size=%d,\n\texpression=\"%s\"\n",
7947:  recurlevel,size,(expression==NULL?"<null>":expression)); fflush(msgfp); }
7948: if ( expression == NULL ) goto end_of_job; /* nothing given to do */
7949: /* -------------------------------------------------------------------------
7950: preocess optional $-terminated preamble preceding expression
7951: -------------------------------------------------------------------------- */
7952: expression = preamble(expression,&size,pretext); /* size may be modified */
7953: if ( *expression == '\000' ) goto end_of_job; /* nothing left to do */
7954: fontsize = size;                        /* start at requested size */
7955: if ( isdisplaystyle == 1 )              /* displaystyle enabled but not set*/
7956:  if ( !ispreambledollars )              /* style fixed by $$...$$'s */
7957:   isdisplaystyle = (fontsize>=displaysize? 2:1); /*force at large fontsize*/
7958: /* -------------------------------------------------------------------------
7959: build up raster one character (or subexpression) at a time
7960: -------------------------------------------------------------------------- */
7961: while ( 1 )
7962:   {
7963:   /* --- kludge for \= cyrillic ligature --- */
7964:   isligature = 0;                       /* no ligature found yet */
7965:   family = fontinfo[fontnum].family;    /* current font family */
7966:   if ( family == CYR10 )                /* may have cyrillic \= ligature */
7967:    if ( (ligdef = get_ligature(expression,family)) /*check for any ligature*/
7968:    >=    0 )                            /* got some ligature */
7969:     if ( memcmp(symtable[ligdef].symbol,"\\=",2) == 0 ) /* starts with \= */
7970:      isligature = 1;                    /* signal \= ligature */
7971:   /* --- get next character/token or subexpression --- */
7972:   subexprptr = expression;              /* ptr within expression to subexpr*/
7973:   expression = texsubexpr(expression,chartoken,0,LEFTBRACES,RIGHTBRACES,1,1);
7974:   subexpr = chartoken;                  /* "local" copy of chartoken ptr */
7975:   leftsymdef = NULL;                    /* no character identified yet */
7976:   sp = NULL;                            /* no subraster yet */
7977:   size = fontsize;                      /* in case reset by \tiny, etc */
7978:   /*isleftscript = isdelimscript;*/     /*was preceding term scripted delim*/
7979:   wasscripted = isscripted;             /* true if preceding token scripted*/
7980:   wasdelimscript = isdelimscript;       /* preceding \right delim scripted */
7981:   if(1)isscripted = 0;                  /* no subscripted expression yet */
7982:   isdelimscript = 0;                    /* reset \right delim scripted flag*/
7983:   /* --- debugging output --- */
7984:   if ( msgfp!=NULL && msglevel >= 9 ) { /* display chartoken for debugging */
7985:    fprintf(msgfp,
7986:    "rasterize> recursion#%d,atom#%d=\"%s\" (isligature=%d,isleftscript=%d)\n",
7987:    recurlevel,natoms+1,chartoken,isligature,isleftscript); fflush(msgfp); }
7988:   if ( expression == NULL               /* no more tokens */
7989:   &&   *subexpr == '\000' ) break;      /* and this token empty */
7990:   if ( *subexpr == '\000' ) break;      /* enough if just this token empty */
7991:   /* --- check for parenthesized subexpression --- */
7992:   if ( isbrace(subexpr,LEFTBRACES,1) )  /* got parenthesized subexpression */
7993:     { if ( (sp=rastparen(&subexpr,size,prevsp)) /* rasterize subexpression */
7994:       ==   NULL )  continue; }          /* flush it if failed to rasterize */
7995:   else /* --- single-character atomic token --- */
7996:    if ( !isthischar(*subexpr,SCRIPTS) ) /* scripts handled below */
7997:     {
7998:     /* --- first check for opening $ in \text{ if $n-m$ even} --- */
7999:     if ( istextmode                     /* we're in \text mode */
8000:     &&   *subexpr=='$' && subexpr[1]=='\000' ) { /* and have an opening $ */
8001:      char *endptr=NULL, mathexpr[MAXSUBXSZ+1]; /* $expression$ in \text{ }*/
8002:      int  exprlen = 0;                  /* length of $expression$ */
8003:      int  textfontnum = fontnum;        /* current text font number */
8004:      /*if ( (endptr=strrchr(expression,'$')) != NULL )*/ /*ptr to closing $*/
8005:      if ( (endptr=strchr(expression,'$')) != NULL ) /* ptr to closing $ */
8006:        exprlen = (int)(endptr-expression); /* #chars preceding closing $ */
8007:      else {                             /* no closing $ found */
8008:        exprlen = strlen(expression);    /* just assume entire expression */
8009:        endptr = expression + (exprlen-1); } /*and push expression to '\000'*/
8010:      exprlen = min2(exprlen,MAXSUBXSZ); /* don't overflow mathexpr[] */
8011:      if ( exprlen > 0 ) {               /* have something between $$ */
8012:        memcpy(mathexpr,expression,exprlen); /*local copy of math expression*/
8013:        mathexpr[exprlen] = '\000';      /* null-terminate it */
8014:        fontnum = 0;                     /* set math mode */
8015:        sp = rasterize(mathexpr,size);   /* and rasterize $expression$ */
8016:        fontnum = textfontnum; }         /* set back to text mode */
8017:      expression = endptr+1;             /* push expression past closing $ */
8018:      } /* --- end-of-if(istextmode&&*subexpr=='$') --- */
8019:     else
8020:      /* --- otherwise, look up mathchardef for atomic token in table --- */
8021:      if ( (leftsymdef=symdef=get_symdef(chartoken)) /*mathchardef for token*/
8022:      ==  NULL )                         /* lookup failed */
8023:       { char literal[512] = "[?]";      /*display for unrecognized literal*/
8024:         int  oldfontnum = fontnum;      /* error display in default mode */
8025:         if ( msgfp!=NULL && msglevel >= 29 ) /* display unrecognized symbol*/
8026:          { fprintf(msgfp,"rasterize> get_symdef() failed for \"%s\"\n",
8027:            chartoken); fflush(msgfp); }
8028:         sp = (subraster *)NULL;         /* init to signal failure */
8029:         if ( warninglevel < 1 ) continue; /* warnings not wanted */
8030:         fontnum = 0;                    /* reset from \mathbb, etc */
8031:         if ( isthischar(*chartoken,ESCAPE) ) /* we got unrecognized \escape*/
8032:          { /* --- so display literal {\rm~[\backslash~chartoken?]} ---  */
8033:            strcpy(literal,"{\\rm~[");   /* init error message token */
8034:            strcat(literal,strdetex(chartoken,0)); /* detex the token */
8035:            strcat(literal,"?]}"); }     /* add closing ? and brace */
8036:         sp = rasterize(literal,size-1); /* rasterize literal token */
8037:         fontnum = oldfontnum;           /* reset font family */
8038:         if ( sp == (subraster *)NULL ) continue; }/*flush if rasterize fails*/
8039:      else /* --- check if we have special handler to process this token --- */
8040:       if ( symdef->handler != NULL )    /* have a handler for this token */
8041:        { int arg1=symdef->charnum, arg2=symdef->family, arg3=symdef->class;
8042:          if ( (sp = (subraster *)       /* returned void* is subraster* */
8043:          (*(symdef->handler))(&expression,size,prevsp,arg1,arg2,arg3))==NULL)
8044:            continue; }                  /* flush token if handler failed */
8045:       else /* --- no handler, so just get subraster for this character --- */
8046:        if ( !isstring )                 /* rasterizing */
8047:         { if ( isligature )             /* found a ligature */
8048:            expression = subexprptr + strlen(symdef->symbol); /*push past it*/
8049:           if ( (sp=get_charsubraster(symdef,size)) /* get subraster */
8050:           ==  NULL )  continue; }       /* flush token if failed */
8051:        else                             /* constructing ascii string */
8052:         { char *symbol = symdef->symbol; /* symbol for ascii string */
8053:           int symlen = (symbol!=NULL?strlen(symbol):0); /*#chars in symbol*/
8054:           if ( symlen < 1 ) continue;   /* no symbol for ascii string */
8055:           if ( (sp=new_subraster(symlen+1,1,8)) /* subraster for symbol */
8056:           ==  NULL )  continue;         /* flush token if malloc failed */
8057:           sp->type = ASCIISTRING;       /* set subraster type */
8058:           sp->symdef = symdef;          /* and set symbol definition */
8059:           sp->baseline = 1;             /* default (should be unused) */
8060:           strcpy((char *)((sp->image)->pixmap),symbol); /* copy symbol */
8061:           /*((char *)((sp->image)->pixmap))[symlen] = '\000';*/ } /*null*/
8062:     } /* --- end-of-if(!isthischar(*subexpr,SCRIPTS)) --- */
8063:   /* --- handle any super/subscripts following symbol or subexpression --- */
8064:   sp = rastlimits(&expression,size,sp);
8065:   isleftscript = (wasscripted||wasdelimscript?1:0);/*preceding term scripted*/
8066:   /* --- debugging output --- */
8067:   if ( msgfp!=NULL && msglevel >= 9 ) { /* display raster for debugging */
8068:    fprintf(msgfp,"rasterize> recursion#%d,atom#%d%s\n",
8069:    recurlevel,natoms+1,(sp==NULL?" = <null>":"..."));
8070:    if ( msglevel >= 9 ) fprintf(msgfp,
8071:     "  isleftscript=%d is/wasscripted=%d,%d is/wasdelimscript=%d,%d\n",
8072:     isleftscript,isscripted,wasscripted,isdelimscript,wasdelimscript);
8073:    if ( msglevel >= 99 )
8074:     if(sp!=NULL) type_raster(sp->image,msgfp); /* display raster */
8075:    fflush(msgfp); }                     /* flush msgfp buffer */
8076:   /* --- accumulate atom or parenthesized subexpression --- */
8077:   if ( natoms < 1                       /* nothing previous to concat */
8078:   ||   expraster == NULL                /* or previous was complete error */
8079:   ||   isreplaceleft )                  /* or we're replacing previous */
8080:     { if ( 1 && expraster!=NULL )       /* probably replacing left */
8081:         delete_subraster(expraster);    /* so first free original left */
8082:       expraster = subrastcpy(sp);       /* copy static CHARASTER or left */
8083:       isreplaceleft = 0; }              /* reset replacement flag */
8084:   else                                  /*we've already built up atoms so...*/
8085:    if ( sp != NULL ) {                  /* ...if we have a new term */
8086:     int prevsmashmargin = smashmargin;  /* save current smash margin */
8087:     if ( isleftscript ) {               /* don't smash against scripts */
8088:      isdelimscript = 0;                 /* reset \right delim scripted flag*/
8089:      if ( !isexplicitsmash ) smashmargin = 0; } /* signal no smash wanted */
8090:     expraster = rastcat(expraster,sp,1); /* concat new term, free previous */
8091:     smashmargin = prevsmashmargin; }    /* restore current smash margin */
8092:   delete_subraster(prevsp);             /* free prev (if not a CHARASTER) */
8093:   prevsp = sp;                          /* current becomes previous */
8094:   leftexpression = expraster;           /* left half rasterized so far */
8095:   /* --- bump count --- */
8096:   natoms++;                             /* bump #atoms count */
8097:   } /* --- end-of-while(expression!=NULL) --- */
8098: /* -------------------------------------------------------------------------
8099: back to caller with rasterized expression
8100: -------------------------------------------------------------------------- */
8101: end_of_job:
8102:   delete_subraster(prevsp);             /* free last (if not a CHARASTER) */
8103:   /* --- debugging output --- */
8104:   if ( msgfp!=NULL && msglevel >= 999 ) /* display raster for debugging */
8105:     { fprintf(msgfp,"rasterize> Final recursion level=%d, atom#%d...\n",
8106:       recurlevel,natoms);
8107:       if ( expraster != (subraster *)NULL ) /* i.e., if natoms>0 */
8108:         type_raster(expraster->image,msgfp); /* display completed raster */
8109:       fflush(msgfp); }                  /* flush msgfp buffer */
8110:   /* --- set final raster buffer --- */
8111:   if ( 1 && expraster != (subraster *)NULL ) /* have an expression */
8112:     { int type = expraster->type;       /* type of constructed image */
8113:       if ( type != FRACRASTER )         /* leave \frac alone */
8114:         expraster->type = IMAGERASTER;  /* set type to constructed image */
8115:       if ( istextmode )                 /* but in text mode */
8116:         expraster->type = blanksignal;  /* set type to avoid smash */
8117:       expraster->size = fontsize; }     /* set original input font size */
8118:   /* --- restore flags/values to original saved values --- */
8119:   isstring = wasstring;                 /* string mode reset */
8120:   isdisplaystyle = wasdisplaystyle;     /* displaystyle mode reset */
8121:   fontnum = oldfontnum;                 /* font family reset */
8122:   fontsize = oldfontsize;               /* fontsize reset */
8123:   displaysize = olddisplaysize;         /* \displaystyle size reset */
8124:   shrinkfactor = oldshrinkfactor;       /* shrinkfactor reset */
8125:   smashmargin = oldsmashmargin;         /* smashmargin reset */
8126:   issmashdelta = oldissmashdelta;       /* issmashdelta reset */
8127:   isexplicitsmash = oldisexplicitsmash; /* isexplicitsmash reset */
8128:   isscripted = oldisscripted;           /* isscripted reset */
8129:   workingparam = oldworkingparam;       /* working parameter reset */
8130:   workingbox = oldworkingbox;           /* working box reset */
8131:   leftexpression = oldleftexpression;   /* leftexpression reset */
8132:   leftsymdef = oldleftsymdef;           /* leftsymdef reset */
8133:   unitlength = oldunitlength;           /* unitlength reset */
8134:   iunitlength = (int)(unitlength+0.5);  /* iunitlength reset */
8135:   recurlevel--;                         /* unwind one recursion level */
8136:   /* --- return final subraster to caller --- */
8137:   return ( expraster );
8138: } /* --- end-of-function rasterize() --- */
8139: 
8140: 
8141: /* ==========================================================================
8142:  * Function:    rastparen ( subexpr, size, basesp )
8143:  * Purpose:     parentheses handler, returns a subraster corresponding to
8144:  *              parenthesized subexpression at font size
8145:  * --------------------------------------------------------------------------
8146:  * Arguments:   subexpr (I)     char **  to first char of null-terminated
8147:  *                              string beginning with a LEFTBRACES
8148:  *                              to be rasterized
8149:  *              size (I)        int containing 0-7 default font size
8150:  *              basesp (I)      subraster *  to character (or subexpression)
8151:  *                              immediately preceding leading left{
8152:  *                              (unused, but passed for consistency)
8153:  * --------------------------------------------------------------------------
8154:  * Returns:     ( subraster * ) ptr to subraster corresponding to subexpr,
8155:  *                              or NULL for any parsing error
8156:  * --------------------------------------------------------------------------
8157:  * Notes:     o This "handler" isn't in the mathchardef symbol table,
8158:  *              but is called directly from rasterize(), as necessary.
8159:  *            o Though subexpr is returned unchanged, it's passed as char **
8160:  *              for consistency with other handlers.  Ditto, basesp is unused
8161:  *              but passed for consistency
8162:  * ======================================================================= */
8163: /* --- entry point --- */
8164: FUNCSCOPE subraster *rastparen ( char **subexpr, int size, subraster *basesp )
8165: {
8166: /* -------------------------------------------------------------------------
8167: Allocations and Declarations
8168: -------------------------------------------------------------------------- */
8169: char    *expression = *subexpr;         /* dereference subexpr to get char* */
8170: int     explen = strlen(expression);    /* total #chars, including parens */
8171: int     isescape = 0,                   /* true if parens \escaped */
8172:         isrightdot = 0,                 /* true if right paren is \right. */
8173:         isleftdot = 0;                  /* true if left paren is \left. */
8174: char    left[32], right[32];            /* parens enclosing expresion */
8175: char    noparens[MAXSUBXSZ+1];          /* get subexpr without parens */
8176: subraster /**rasterize(),*/ *sp=NULL;   /* rasterize what's between ()'s */
8177: int     isheight = 1;                   /*true=full height, false=baseline*/
8178: int     height,                         /* height of rasterized noparens[] */
8179:         baseline;                       /* and its baseline */
8180: int     family = /*CMSYEX*/ CMEX10;     /* family for paren chars */
8181: subraster /**get_delim(),*/ *lp=NULL, *rp=NULL; /*left and right paren chars*/
8182: /*subraster *rastcat();*/               /* concatanate subrasters */
8183: /*int   delete_subraster();*/           /*in case of error after allocation*/
8184: /* -------------------------------------------------------------------------
8185: rasterize "interior" of expression, i.e., without enclosing parens
8186: -------------------------------------------------------------------------- */
8187: /* --- first see if enclosing parens are \escaped --- */
8188: if ( isthischar(*expression,ESCAPE) )   /* expression begins with \escape */
8189:   isescape = 1;                         /* so set flag accordingly */
8190: /* --- get expression *without* enclosing parens --- */
8191: strcpy(noparens,expression);            /* get local copy of expression */
8192: noparens[explen-(1+isescape)] = '\000'; /* null-terminate before right} */
8193: strsqueeze(noparens,(1+isescape));      /* and then squeeze out left{ */
8194: /* --- rasterize it --- */
8195: if ( (sp = rasterize(noparens,size))    /*rasterize "interior" of expression*/
8196: ==   NULL ) goto end_of_job;            /* quit if failed */
8197: /* --- no need to add parentheses for unescaped { --- */
8198: if ( !isescape && isthischar(*expression,"{") ) /* don't add parentheses */
8199:   goto end_of_job;                      /* just return sp to caller */
8200: /* -------------------------------------------------------------------------
8201: obtain paren characters to enclose noparens[] raster with
8202: -------------------------------------------------------------------------- */
8203: /* --- first get left and right parens from expression --- */
8204: memset(left,0,16);  memset(right,0,16); /* init parens with nulls */
8205: left[0] = *(expression+isescape);       /* left{ is 1st or 2nd char */
8206: right[0] = *(expression+explen-1);      /* right} is always last char */
8207: isleftdot  = (isescape && isthischar(*left,".")); /* true if \left. */
8208: isrightdot = (isescape && isthischar(*right,".")); /* true if \right. */
8209: /* --- need height of noparens[] raster as minimum parens height --- */
8210: height = (sp->image)->height;           /* height of noparens[] raster */
8211: baseline = sp->baseline;                /* baseline of noparens[] raster */
8212: if ( !isheight ) height = baseline+1;   /* parens only enclose baseline up */
8213: /* --- get best-fit parentheses characters --- */
8214: if ( !isleftdot )                       /* if not \left. */
8215:   lp = get_delim(left,height+1,family); /* get left paren char */
8216: if ( !isrightdot )                      /* and if not \right. */
8217:   rp = get_delim(right,height+1,family); /* get right paren char */
8218: if ( (lp==NULL && !isleftdot)           /* check that we got left( */
8219: ||   (rp==NULL && !isrightdot) )        /* and right) if needed */
8220:   { delete_subraster(sp);               /* if failed, free subraster */
8221:     if ( lp != NULL ) free ((void *)lp);/*free left-paren subraster envelope*/
8222:     if ( rp != NULL ) free ((void *)rp);/*and right-paren subraster envelope*/
8223:     sp = (subraster *)NULL;             /* signal error to caller */
8224:     goto end_of_job; }                  /* and quit */
8225: /* -------------------------------------------------------------------------
8226: set paren baselines to center on noparens[] raster, and concat components
8227: -------------------------------------------------------------------------- */
8228: /* --- set baselines to center paren chars on raster --- */
8229: if ( lp != NULL )                       /* ignore for \left. */
8230:   lp->baseline = baseline + ((lp->image)->height - height)/2;
8231: if ( rp != NULL )                       /* ignore for \right. */
8232:   rp->baseline = baseline + ((rp->image)->height - height)/2;
8233: /* --- concat lp||sp||rp to obtain final result --- */
8234: if ( lp != NULL )                       /* ignore \left. */
8235:   sp = rastcat(lp,sp,3);                /* concat lp||sp and free sp,lp */
8236: if ( sp != NULL )                       /* succeeded or ignored \left. */
8237:   if ( rp != NULL )                     /* ignore \right. */
8238:     sp = rastcat(sp,rp,3);              /* concat sp||rp and free sp,rp */
8239: /* --- back to caller --- */
8240: end_of_job:
8241:   return ( sp );
8242: } /* --- end-of-function rastparen() --- */
8243: 
8244: 
8245: /* ==========================================================================
8246:  * Function:    rastlimits ( expression, size, basesp )
8247:  * Purpose:     \limits, \nolimts, _ and ^ handler,
8248:  *              dispatches call to rastscripts() or to rastdispmath()
8249:  *              as necessary, to handle sub/superscripts following symbol
8250:  * --------------------------------------------------------------------------
8251:  * Arguments:   expression (I)  char **  to first char of null-terminated
8252:  *                              LaTeX expression (unused/unchanged)
8253:  *              size (I)        int containing base font size (not used,
8254:  *                              just stored in subraster)
8255:  *              basesp (I)      subraster *  to current character (or
8256:  *                              subexpression) immediately preceding script
8257:  *                              indicator
8258:  * --------------------------------------------------------------------------
8259:  * Returns:     ( subraster * ) ptr to subraster returned by rastscripts()
8260:  *                              or rastdispmath(), or NULL for any error
8261:  * --------------------------------------------------------------------------
8262:  * Notes:     o
8263:  * ======================================================================= */
8264: /* --- entry point --- */
8265: FUNCSCOPE subraster *rastlimits ( char **expression, int size,
8266:                                   subraster *basesp )
8267: {
8268: /* -------------------------------------------------------------------------
8269: Allocations and Declarations
8270: -------------------------------------------------------------------------- */
8271: subraster /**rastscripts(),*rastdispmath(),*/ /*one of these will do the work*/
8272:         /**rastcat(),*/                 /* may need to concat scripts */
8273:         /**rasterize(),*/               /* may need to construct dummy base*/
8274:         *scriptsp = basesp,             /* and this will become the result */
8275:         *dummybase = basesp;            /* for {}_i construct a dummy base */
8276: int     isdisplay = (-1);               /* set 1 for displaystyle, else 0 */
8277: int     oldsmashmargin = smashmargin;   /* save original smashmargin */
8278: /*int   type_raster();*/                /* display debugging output */
8279: /*int   delete_subraster();*/           /* free dummybase, if necessary */
8280: /*int   rastsmashcheck();*/             /* check if okay to smash scripts */
8281: /* --- to check for \limits or \nolimits preceding scripts --- */
8282: char    /**texchar(),*/ *exprptr=*expression, limtoken[255]; /*check \limits*/
8283: int     toklen=0;                       /* strlen(limtoken) */
8284: mathchardef *tokdef /*, *get_symdef()*/; /* mathchardef struct for limtoken */
8285: int     class=(leftsymdef==NULL?NOVALUE:leftsymdef->class); /*base sym class*/
8286: /* -------------------------------------------------------------------------
8287: determine whether or not to use displaymath
8288: -------------------------------------------------------------------------- */
8289: scriptlevel++;                          /* first, increment subscript level*/
8290: *limtoken = '\000';                     /* no token yet */
8291: isscripted = 0;                         /* signal term not (text) scripted */
8292: if ( msgfp!=NULL && msglevel>=999 )
8293:  { fprintf(msgfp,"rastlimits> scriptlevel#%d exprptr=%.48s\n",
8294:    scriptlevel,(exprptr==NULL?"null":exprptr));  fflush(msgfp); }
8295: if ( isstring ) goto end_of_job;        /* no scripts for ascii string */
8296: /* --- check for \limits or \nolimits --- */
8297: skipwhite(exprptr);                     /* skip white space before \limits */
8298: if ( !isempty(exprptr) )                /* non-empty expression supplied */
8299:   exprptr = texchar(exprptr,limtoken);  /* retrieve next token */
8300: if ( *limtoken != '\000' )              /* have token */
8301:  if ( (toklen=strlen(limtoken)) >= 3 )  /* which may be \[no]limits */
8302:   if ( memcmp("\\limits",limtoken,toklen) == 0     /* may be \limits */
8303:   ||   memcmp("\\nolimits",limtoken,toklen) == 0 ) /* or may be \nolimits */
8304:    if ( (tokdef= get_symdef(limtoken))  /* look up token to be sure */
8305:    !=   NULL ) {                        /* found token in table */
8306:     if ( strcmp("\\limits",tokdef->symbol) == 0 )  /* found \limits */
8307:       isdisplay = 1;                    /* so explicitly set displaymath */
8308:     else                                /* wasn't \limits */
8309:       if ( strcmp("\\nolimits",tokdef->symbol) == 0 ) /* found \nolimits */
8310:         isdisplay = 0; }                /* so explicitly reset displaymath */
8311: /* --- see if we found \[no]limits --- */
8312: if ( isdisplay != (-1) )                /* explicit directive found */
8313:   *expression = exprptr;                /* so bump expression past it */
8314: else                                    /* noexplicit directive */
8315:   { isdisplay = 0;                      /* init displaymath flag off */
8316:     if ( isdisplaystyle ) {             /* we're in displaystyle math mode */
8317:       if ( isdisplaystyle >= 5 )        /* and mode irrevocably forced true */
8318:         { if ( class!=OPENING && class!=CLOSING ) /*don't force ('s and )'s*/
8319:             isdisplay = 1; }            /* set flag if mode forced true */
8320:       else
8321:        if ( isdisplaystyle >= 2 )       /*or mode forced conditionally true*/
8322:         { if ( class!=VARIABLE && class!=ORDINARY /*don't force characters*/
8323:           &&   class!=OPENING  && class!=CLOSING  /*don't force ('s and )'s*/
8324:           &&   class!=BINARYOP          /* don't force binary operators */
8325:           &&   class!=NOVALUE )         /* finally, don't force "images" */
8326:             isdisplay = 1; }            /* set flag if mode forced true */
8327:        else                             /* determine mode from base symbol */
8328:         if ( class == DISPOPER )        /* it's a displaystyle operator */
8329:           isdisplay = 1; } }            /* so set flag */
8330: /* -------------------------------------------------------------------------
8331: dispatch call to create sub/superscripts
8332: -------------------------------------------------------------------------- */
8333: if ( isdisplay )                        /* scripts above/below base symbol */
8334:   scriptsp = rastdispmath(expression,size,basesp); /* everything all done */
8335: else {                                  /* scripts alongside base symbol */
8336:   if ( dummybase == NULL )              /* no base symbol preceding scripts*/
8337:     dummybase = rasterize("\\rule0{10}",size); /*guess a typical base symbol*/
8338:   issmashokay = 1;                      /*haven't found a no-smash char yet*/
8339:   if((scriptsp=rastscripts(expression,size,dummybase)) == NULL) /*no scripts*/
8340:     scriptsp = basesp;                  /* so just return unscripted symbol*/
8341:   else {                                /* symbols followed by scripts */
8342:     isscripted = 1;                     /*signal current term text-scripted*/
8343:     if ( basesp != NULL )               /* have base symbol */
8344:      { /*if(0)smashmargin = 0;*/        /*don't smash script (doesn't work)*/
8345:        /*scriptsp = rastcat(basesp,scriptsp,2);*//*concat scripts to base sym*/
8346:        /* --- smash (or just concat) script raster against base symbol --- */
8347:        if ( !issmashokay )              /* don't smash leading - */
8348:          if ( !isexplicitsmash ) scriptsp->type = blanksignal; /*don't smash*/
8349:        scriptsp = rastcat(basesp,scriptsp,3); /*concat scripts to base sym*/
8350:        if(1) scriptsp->type = IMAGERASTER; /* flip type of composite object */
8351:        /* --- smash (or just concat) scripted term to stuff to its left --- */
8352:        issmashokay = 1;                 /* okay to smash base expression */
8353:        if ( 0 && smashcheck > 1 )       /* smashcheck=2 to check base */
8354:          /* note -- we _don't_ have base expression available to check */
8355:          issmashokay = rastsmashcheck(*expression); /*check if okay to smash*/
8356:        if ( !issmashokay )              /* don't smash leading - */
8357:          if ( !isexplicitsmash ) scriptsp->type = blanksignal; /*don't smash*/
8358:        scriptsp->size = size; } } }     /* and set font size */
8359: end_of_job:
8360:   smashmargin = oldsmashmargin;         /* reset original smashmargin */
8361:   if ( dummybase != basesp ) delete_subraster(dummybase); /*free work area*/
8362:   if ( msgfp!=NULL && msglevel>=99 )
8363:     { fprintf(msgfp,"rastlimits> scriptlevel#%d returning %s\n",
8364:         scriptlevel,(scriptsp==NULL?"null":"..."));
8365:       if ( scriptsp != NULL )           /* have a constructed raster */
8366:         type_raster(scriptsp->image,msgfp); /*display constructed raster*/
8367:       fflush(msgfp); }
8368:   scriptlevel--;                        /*lastly, decrement subscript level*/
8369:   return ( scriptsp );
8370: } /* --- end-of-function rastlimits() --- */
8371: 
8372: 
8373: /* ==========================================================================
8374:  * Function:    rastscripts ( expression, size, basesp )
8375:  * Purpose:     super/subscript handler, returns subraster for the leading
8376:  *              scripts in expression, whose base symbol is at font size
8377:  * --------------------------------------------------------------------------
8378:  * Arguments:   expression (I/O) char **  to first char of null-terminated
8379:  *                              string beginning with a super/subscript,
8380:  *                              and returning ptr immediately following
8381:  *                              last script character processed.
8382:  *              size (I)        int containing 0-7 default font size
8383:  *              basesp (I)      subraster *  to character (or subexpression)
8384:  *                              immediately preceding leading script
8385:  *                              (scripts will be placed relative to base)
8386:  * --------------------------------------------------------------------------
8387:  * Returns:     ( subraster * ) ptr to subraster corresponding to scripts,
8388:  *                              or NULL for any parsing error
8389:  * --------------------------------------------------------------------------
8390:  * Notes:     o This "handler" isn't in the mathchardef symbol table,
8391:  *              but is called directly from rasterize(), as necessary.
8392:  * ======================================================================= */
8393: /* --- entry point --- */
8394: FUNCSCOPE subraster *rastscripts ( char **expression, int size,
8395:                                    subraster *basesp )
8396: {
8397: /* -------------------------------------------------------------------------
8398: Allocations and Declarations
8399: -------------------------------------------------------------------------- */
8400: char    /**texscripts(),*/              /* parse expression for scripts */
8401:         subscript[512], supscript[512]; /* scripts parsed from expression */
8402: subraster /**rasterize(),*/ *subsp=NULL, *supsp=NULL; /* rasterize scripts */
8403: subraster /**new_subraster(),*/ *sp=NULL /*super- over subscript subraster*/
8404:         /*,*rastack()*/;                /*sets scripts in displaymath mode*/
8405: raster  *rp=NULL;                       /* image raster embedded in sp */
8406: int     height=0, width=0,  baseline=0, /* height,width,baseline of sp */
8407:         subht=0,  subwidth=0,  subln=0, /* height,width,baseline of sub */
8408:         supht=0,  supwidth=0,  supln=0, /* height,width,baseline of sup */
8409:         baseht=0, baseln=0;             /* height,baseline of base */
8410: int     bdescend=0, sdescend=0;         /* descender of base, subscript */
8411: int     issub=0, issup=0, isboth=0,     /* true if we have sub,sup,both */
8412:         isbase=0;                       /* true if we have base symbol */
8413: int     szval = min2(max2(size,0),LARGESTSIZE), /* 0...LARGESTSIZE */
8414:         vbetween = 2,                   /* vertical space between scripts */
8415:         vabove   = szval+1,             /*sup's top/bot above base's top/bot*/
8416:         vbelow   = szval+1,             /*sub's top/bot below base's top/bot*/
8417:         vbottom  = szval+1;             /*sup's bot above (sub's below) bsln*/
8418: /*int   istweak = 1;*/                  /* true to tweak script positioning */
8419: /*int   rastput();*/                    /*put scripts in constructed raster*/
8420: /*int   delete_subraster();*/           /* free work areas */
8421: /*int   rastsmashcheck();*/             /* check if okay to smash scripts */
8422: int     pixsz = 1;                      /*default #bits per pixel, 1=bitmap*/
8423: /* -------------------------------------------------------------------------
8424: Obtain subscript and/or superscript expressions, and rasterize them/it
8425: -------------------------------------------------------------------------- */
8426: /* --- parse for sub,superscript(s), and bump expression past it(them) --- */
8427: if ( expression == NULL ) goto end_of_job; /* no *ptr given */
8428: if ( *expression == NULL ) goto end_of_job; /* no expression given */
8429: if ( *(*expression) == '\000' ) goto end_of_job; /* nothing in expression */
8430: *expression = texscripts(*expression,subscript,supscript,3);
8431: /* --- rasterize scripts --- */
8432: if ( *subscript != '\000' )             /* have a subscript */
8433:   subsp = rasterize(subscript,size-1);  /* so rasterize it at size-1 */
8434: if ( *supscript != '\000' )             /* have a superscript */
8435:   supsp = rasterize(supscript,size-1);  /* so rasterize it at size-1 */
8436: /* --- set flags for convenience --- */
8437: issub  = (subsp != (subraster *)NULL);  /* true if we have subscript */
8438: issup  = (supsp != (subraster *)NULL);  /* true if we have superscript */
8439: isboth = (issub && issup);              /* true if we have both */
8440: if (!issub && !issup) goto end_of_job;  /* quit if we have neither */
8441: /* --- check for leading no-smash chars (if enabled) --- */
8442: issmashokay = 0;                        /* default, don't smash scripts */
8443: if ( smashcheck > 0 ) {                 /* smash checking wanted */
8444:  issmashokay = 1;                       /*haven't found a no-smash char yet*/
8445:  if ( issub )                           /* got a subscript */
8446:   issmashokay = rastsmashcheck(subscript); /* check if okay to smash */
8447:  if ( issmashokay )                     /* clean sub, so check sup */
8448:   if ( issup )                          /* got a superscript */
8449:    issmashokay = rastsmashcheck(supscript); /* check if okay to smash */
8450:  } /* --- end-of-if(smashcheck>0) --- */
8451: /* -------------------------------------------------------------------------
8452: get height, width, baseline of scripts,  and height, baseline of base symbol
8453: -------------------------------------------------------------------------- */
8454: /* --- get height and width of components --- */
8455: if ( issub )                            /* we have a subscript */
8456:   { subht    = (subsp->image)->height;  /* so get its height */
8457:     subwidth = (subsp->image)->width;   /* and width */
8458:     subln    =  subsp->baseline; }      /* and baseline */
8459: if ( issup )                            /* we have a superscript */
8460:   { supht    = (supsp->image)->height;  /* so get its height */
8461:     supwidth = (supsp->image)->width;   /* and width */
8462:     supln    =  supsp->baseline; }      /* and baseline */
8463: /* --- get height and baseline of base, and descender of base and sub --- */
8464: if ( basesp == (subraster *)NULL )      /* no base symbol for scripts */
8465:   basesp = leftexpression;              /* try using left side thus far */
8466: if ( basesp != (subraster *)NULL )      /* we have base symbol for scripts */
8467:   { baseht   = (basesp->image)->height; /* height of base symbol */
8468:     baseln   =  basesp->baseline;       /* and its baseline */
8469:     bdescend =  baseht-(baseln+1);      /* and base symbol descender */
8470:     sdescend =  bdescend + vbelow;      /*sub must descend by at least this*/
8471:     if ( baseht > 0 ) isbase = 1; }     /* set flag */
8472: /* -------------------------------------------------------------------------
8473: determine width of constructed raster
8474: -------------------------------------------------------------------------- */
8475: width = max2(subwidth,supwidth);        /*widest component is overall width*/
8476: /* -------------------------------------------------------------------------
8477: determine height and baseline of constructed raster
8478: -------------------------------------------------------------------------- */
8479: /* --- both super/subscript --- */
8480: if ( isboth )                           /*we have subscript and superscript*/
8481:   { height = max2(subht+vbetween+supht, /* script heights + space bewteen */
8482:                 vbelow+baseht+vabove);  /*sub below base bot, sup above top*/
8483:     baseline = baseln + (height-baseht)/2; } /*center scripts on base symbol*/
8484: /* --- superscript only --- */
8485: if ( !issub )                           /* we only have a superscript */
8486:   { height = max3(baseln+1+vabove,      /* sup's top above base symbol top */
8487:                 supht+vbottom,          /* sup's bot above baseln */
8488:                 supht+vabove-bdescend); /* sup's bot above base symbol bot */
8489:     baseline = height-1; }              /*sup's baseline at bottom of raster*/
8490: /* --- subscript only --- */
8491: if ( !issup ) {                         /* we only have a subscript */
8492:   if ( subht > sdescend )               /*sub can descend below base bot...*/
8493:     { height = subht;                   /* ...without extra space on top */
8494:       baseline = height-(sdescend+1);   /* sub's bot below base symbol bot */
8495:       baseline = min2(baseline,max2(baseln-vbelow,0)); }/*top below base top*/
8496:   else                                  /* sub's top will be below baseln */
8497:     { height = sdescend+1;              /* sub's bot below base symbol bot */
8498:       baseline = 0; } }                 /* sub's baseline at top of raster */
8499: /* -------------------------------------------------------------------------
8500: construct raster with superscript over subscript
8501: -------------------------------------------------------------------------- */
8502: /* --- allocate subraster containing constructed raster --- */
8503: if ( (sp=new_subraster(width,height,pixsz)) /*allocate subraster and raster*/
8504: ==   NULL )                             /* and if we fail to allocate */
8505:   goto end_of_job;                      /* quit */
8506: /* --- initialize subraster parameters --- */
8507: sp->type  = IMAGERASTER;                /* set type as constructed image */
8508: sp->size  = size;                       /* set given size */
8509: sp->baseline = baseline;                /* composite scripts baseline */
8510: rp = sp->image;                         /* raster embedded in subraster */
8511: /* --- place super/subscripts in new raster --- */
8512: if ( issup )                            /* we have a superscript */
8513:  rastput(rp,supsp->image,0,0,1);        /* it goes in upper-left corner */
8514: if ( issub )                            /* we have a subscript */
8515:  rastput(rp,subsp->image,height-subht,0,1); /*in lower-left corner*/
8516: /* -------------------------------------------------------------------------
8517: free unneeded component subrasters and return final result to caller
8518: -------------------------------------------------------------------------- */
8519: end_of_job:
8520:   if ( issub ) delete_subraster(subsp); /* free unneeded subscript */
8521:   if ( issup ) delete_subraster(supsp); /* and superscript */
8522:   return ( sp );
8523: } /* --- end-of-function rastscripts() --- */
8524: 
8525: 
8526: /* ==========================================================================
8527:  * Function:    rastdispmath ( expression, size, sp )
8528:  * Purpose:     displaymath handler, returns sp along with
8529:  *              its immediately following super/subscripts
8530:  * --------------------------------------------------------------------------
8531:  * Arguments:   expression (I/O) char **  to first char of null-terminated
8532:  *                              string immediately following sp to be
8533:  *                              rasterized along with its super/subscripts,
8534:  *                              and returning ptr immediately following last
8535:  *                              character processed.
8536:  *              size (I)        int containing 0-7 default font size
8537:  *              sp (I)          subraster *  to display math operator
8538:  *                              to which super/subscripts will be added
8539:  * --------------------------------------------------------------------------
8540:  * Returns:     ( subraster * ) ptr to subraster corresponding to sp
8541:  *                              plus its scripts, or NULL for any error
8542:  * --------------------------------------------------------------------------
8543:  * Notes:     o sp returned unchanged if no super/subscript(s) follow it.
8544:  * ======================================================================= */
8545: /* --- entry point --- */
8546: FUNCSCOPE subraster *rastdispmath (char **expression, int size, subraster *sp)
8547: {
8548: /* -------------------------------------------------------------------------
8549: Allocations and Declarations
8550: -------------------------------------------------------------------------- */
8551: char    /**texscripts(),*/              /* parse expression for scripts */
8552:         subscript[512], supscript[512]; /* scripts parsed from expression */
8553: int     issub=0, issup=0;               /* true if we have sub,sup */
8554: subraster /**rasterize(),*/ *subsp=NULL, *supsp=NULL /* rasterize scripts */
8555:         /*,*rastack(),*/                /* stack operator with scripts */
8556:         /**new_subraster()*/;           /* for dummy base sp, if needed */
8557: int     vspace = 1;                     /* vertical space between scripts */
8558: /* -------------------------------------------------------------------------
8559: Obtain subscript and/or superscript expressions, and rasterize them/it
8560: -------------------------------------------------------------------------- */
8561: /* --- parse for sub,superscript(s), and bump expression past it(them) --- */
8562: if ( expression == NULL ) goto end_of_job; /* no *ptr given */
8563: if ( *expression == NULL ) goto end_of_job; /* no expression given */
8564: if ( *(*expression) == '\000' ) goto end_of_job; /* nothing in expression */
8565: *expression = texscripts(*expression,subscript,supscript,3);
8566: /* --- rasterize scripts --- */
8567: if ( *subscript != '\000' )             /* have a subscript */
8568:   subsp = rasterize(subscript,size-1);  /* so rasterize it at size-1 */
8569: if ( *supscript != '\000' )             /* have a superscript */
8570:   supsp = rasterize(supscript,size-1);  /* so rasterize it at size-1 */
8571: /* --- set flags for convenience --- */
8572: issub  = (subsp != (subraster *)NULL);  /* true if we have subscript */
8573: issup  = (supsp != (subraster *)NULL);  /* true if we have superscript */
8574: if (!issub && !issup) goto end_of_job;  /*return operator alone if neither*/
8575: /* -------------------------------------------------------------------------
8576: stack operator and its script(s)
8577: -------------------------------------------------------------------------- */
8578: /* --- stack superscript atop operator --- */
8579: if ( issup ) {                          /* we have a superscript */
8580:  if ( sp == NULL )                      /* but no base expression */
8581:   sp = supsp;                           /* so just use superscript */
8582:  else                                   /* have base and superscript */
8583:   if ( (sp=rastack(sp,supsp,1,vspace,1,3)) /* stack supsp atop base sp */
8584:   ==   NULL ) goto end_of_job; }        /* and quit if failed */
8585: /* --- stack operator+superscript atop subscript --- */
8586: if ( issub ) {                          /* we have a subscript */
8587:  if ( sp == NULL )                      /* but no base expression */
8588:   sp = subsp;                           /* so just use subscript */
8589:  else                                   /* have base and subscript */
8590:   if ( (sp=rastack(subsp,sp,2,vspace,1,3)) /* stack sp atop base subsp */
8591:   ==   NULL ) goto end_of_job; }        /* and quit if failed */
8592: sp->type = IMAGERASTER;                 /* flip type of composite object */
8593: sp->size = size;                        /* and set font size */
8594: /* -------------------------------------------------------------------------
8595: free unneeded component subrasters and return final result to caller
8596: -------------------------------------------------------------------------- */
8597: end_of_job:
8598:   return ( sp );
8599: } /* --- end-of-function rastdispmath() --- */
8600: 
8601: 
8602: /* ==========================================================================
8603:  * Function:    rastleft ( expression, size, basesp, ildelim, arg2, arg3 )
8604:  * Purpose:     \left...\right handler, returns a subraster corresponding to
8605:  *              delimited subexpression at font size
8606:  * --------------------------------------------------------------------------
8607:  * Arguments:   expression (I)  char **  to first char of null-terminated
8608:  *                              string beginning with a \left
8609:  *                              to be rasterized
8610:  *              size (I)        int containing 0-7 default font size
8611:  *              basesp (I)      subraster *  to character (or subexpression)
8612:  *                              immediately preceding leading left{
8613:  *                              (unused, but passed for consistency)
8614:  *              ildelim (I)     int containing ldelims[] index of
8615:  *                              left delimiter
8616:  *              arg2 (I)        int unused
8617:  *              arg3 (I)        int unused
8618:  * --------------------------------------------------------------------------
8619:  * Returns:     ( subraster * ) ptr to subraster corresponding to subexpr,
8620:  *                              or NULL for any parsing error
8621:  * --------------------------------------------------------------------------
8622:  * Notes:     o
8623:  * ======================================================================= */
8624: /* --- entry point --- */
8625: FUNCSCOPE subraster *rastleft ( char **expression, int size, subraster *basesp,
8626:                                 int ildelim, int arg2, int arg3 )
8627: {
8628: /* -------------------------------------------------------------------------
8629: Allocations and Declarations
8630: -------------------------------------------------------------------------- */
8631: subraster /**rasterize(),*/ *sp=NULL;   /*rasterize between \left...\right*/
8632: subraster /**get_delim(),*/ *lp=NULL, *rp=NULL; /*left and right delim chars*/
8633: /*subraster *rastlimits();*/            /*handle sub/super scripts on lp,rp*/
8634: /*subraster *rastcat();*/               /* concat lp||sp||rp subrasters */
8635: int     family=CMSYEX,                  /* get_delim() family */
8636:         height=0, rheight=0,            /* subexpr, right delim height */
8637:         margin=(size+1),                /* delim height margin over subexpr*/
8638:         opmargin=(5);                   /* extra margin for \int,\sum,\etc */
8639: char    /* *texleft(),*/ subexpr[MAXSUBXSZ+1];/*chars between \left...\right*/
8640: char    /* *texchar(),*/                /* get delims after \left,\right */
8641:         ldelim[256]=".", rdelim[256]="."; /* delims following \left,\right */
8642: char    /**strtexchr(),*/ *pleft, *pright;/*locate \right matching our \left*/
8643: int     isleftdot=0, isrightdot=0;      /* true if \left. or \right. */
8644: int     isleftscript=0, isrightscript=0; /* true if delims are scripted */
8645: int     sublen=0;                       /* strlen(subexpr) */
8646: int     idelim=0;                       /* 1=left,2=right */
8647: /* int  gotldelim = 0; */               /* true if ildelim given by caller */
8648: /*int   delete_subraster();*/           /* free subraster if rastleft fails*/
8649: int     wasdisplaystyle = isdisplaystyle; /* save current displaystyle */
8650: int     istextleft=0, istextright=0;    /* true for non-displaystyle delims*/
8651: /* --- recognized delimiters --- */
8652: static  char left[16]="\\left", right[16]="\\right"; /* tex delimiters */
8653: static  char *ldelims[] = {
8654:    "unused", ".",                       /* 1   for \left., \right. */
8655:         "(", ")",                       /* 2,3 for \left(, \right) */
8656:         "\\{","\\}",                    /* 4,5 for \left\{, \right\} */
8657:         "[", "]",                       /* 6,7 for \left[, \right] */
8658:         "<", ">",                       /* 8,9 for \left<, \right> */
8659:         "|", "\\|",                     /* 10,11 for \left,\right |,\|*/
8660:         NULL };
8661: /* --- recognized operator delimiters --- */
8662: static  char *opdelims[] = {            /* operator delims from cmex10 */
8663:      "int",       "sum",        "prod",
8664:      "cup",       "cap",        "dot",
8665:      "plus",      "times",      "wedge",
8666:      "vee",
8667:      NULL }; /* --- end-of-opdelims[] --- */
8668: /* --- delimiter xlation --- */
8669: static  char *xfrom[] =                 /* xlate any delim suffix... */
8670:    { "\\|",                             /* \| */
8671:      "\\{",                             /* \{ */
8672:      "\\}",                             /* \} */
8673:      "\\lbrace",                        /* \lbrace */
8674:      "\\rbrace",                        /* \rbrace */
8675:      "\\langle",                        /* \langle */
8676:      "\\rangle",                        /* \rangle */
8677:      NULL } ; /* --- end-of-xfrom[] --- */
8678: static  char *xto[] =                   /* ...to this instead */
8679:    { "=",                               /* \| to = */
8680:      "{",                               /* \{ to { */
8681:      "}",                               /* \} to } */
8682:      "{",                               /* \lbrace to { */
8683:      "}",                               /* \rbrace to } */
8684:      "<",                               /* \langle to < */
8685:      ">",                               /* \rangle to > */
8686:      NULL } ; /* --- end-of-xto[] --- */
8687: /* --- non-displaystyle delimiters --- */
8688: static  char *textdelims[] =            /* these delims _aren't_ display */
8689:    { "|", "=",
8690:      "(", ")",
8691:      "[", "]",
8692:      "<", ">",
8693:      "{", "}",
8694:      "dbl",                             /* \lbrackdbl and \rbrackdbl */
8695:      NULL } ; /* --- end-of-textdelims[] --- */
8696: /* -------------------------------------------------------------------------
8697: initialization
8698: -------------------------------------------------------------------------- */
8699: /* --- check args --- */
8700: if ( *(*expression) == '\000' ) goto end_of_job; /* nothing after \left */
8701: /* --- determine left delimiter, and set default \right. delimiter --- */
8702: if ( ildelim!=NOVALUE && ildelim>=1 )   /* called with explicit left delim */
8703:  { strcpy(ldelim,ldelims[ildelim]);     /* so just get a local copy */
8704:    /* gotldelim = 1; */ }               /* and set flag that we got it */
8705: else                                    /* trapped \left without delim */
8706:  { skipwhite(*expression);              /* interpret \left ( as \left( */
8707:    if ( *(*expression) == '\000' )      /* end-of-string after \left */
8708:       goto end_of_job;                  /* so return NULL */
8709:    *expression = texchar(*expression,ldelim); /*pull delim from expression*/
8710:    if ( *expression == NULL             /* probably invalid end-of-string */
8711:    ||   *ldelim == '\000' ) goto end_of_job; } /* no delimiter */
8712: strcpy(rdelim,".");                     /* init default \right. delim */
8713: /* -------------------------------------------------------------------------
8714: locate \right balancing our opening \left
8715: -------------------------------------------------------------------------- */
8716: /* --- first \right following \left --- */
8717: if ( (pright=strtexchr(*expression,right)) /* look for \right after \left */
8718: !=   NULL ) {                           /* found it */
8719:  /* --- find matching \right by pushing past any nested \left's --- */
8720:  pleft = *expression;                   /* start after first \left( */
8721:  while ( 1 ) {                          /*break when matching \right found*/
8722:   /* -- locate next nested \left if there is one --- */
8723:   if ( (pleft=strtexchr(pleft,left))    /* find next \left */
8724:   ==   NULL ) break;                    /*no more, so matching \right found*/
8725:   pleft += strlen(left);                /* push ptr past \left token */
8726:   if ( pleft >= pright ) break;         /* not nested if \left after \right*/
8727:   /* --- have nested \left, so push forward to next \right --- */
8728:   if ( (pright=strtexchr(pright+strlen(right),right)) /* find next \right */
8729:   ==   NULL ) break;                    /* ran out of \right's */
8730:   } /* --- end-of-while(1) --- */
8731:  } /* --- end-of-if(pright!=NULL) --- */
8732: /* -------------------------------------------------------------------------
8733: push past \left(_a^b sub/superscripts, if present
8734: -------------------------------------------------------------------------- */
8735: pleft = *expression;                    /*reset pleft after opening \left( */
8736: if ( (lp=rastlimits(expression,size,lp)) /*dummy call push expression past b*/
8737: !=   NULL )                             /* found actual _a^b scripts, too */
8738:   { delete_subraster(lp);               /* but we don't need them */
8739:     lp = NULL; }                        /* reset pointer, too */
8740: /* -------------------------------------------------------------------------
8741: get \right delimiter and subexpression between \left...\right, xlate delims
8742: -------------------------------------------------------------------------- */
8743: /* --- get delimiter following \right --- */
8744: if ( pright == (char *)NULL ) {         /* assume \right. at end of exprssn*/
8745:   strcpy(rdelim,".");                   /* set default \right. */
8746:   sublen = strlen(*expression);         /* use entire remaining expression */
8747:   memcpy(subexpr,*expression,sublen);   /* copy all remaining chars */
8748:   *expression += sublen; }              /* and push expression to its null */
8749: else {                                  /* have explicit matching \right */
8750:   sublen = (int)(pright-(*expression)); /* #chars between \left...\right */
8751:   memcpy(subexpr,*expression,sublen);   /* copy chars preceding \right */
8752:   *expression = pright+strlen(right);   /* push expression past \right */
8753:   skipwhite(*expression);               /* interpret \right ) as \right) */
8754:   *expression = texchar(*expression,rdelim); /*pull delim from expression*/
8755:   if ( *rdelim == '\000' ) strcpy(rdelim,"."); } /* \right. if no rdelim */
8756: /* --- get subexpression between \left...\right --- */
8757: if ( sublen < 1 ) goto end_of_job;      /* nothing between delimiters */
8758: subexpr[sublen] = '\000';               /* and null-terminate it */
8759: /* --- adjust margin for expressions containing \middle's --- */
8760: if ( strtexchr(subexpr,"\\middle") != NULL ) /* have enclosed \middle's */
8761:   margin = 1;                           /* so don't "overwhelm" them */
8762: /* --- check for operator delimiter --- */
8763: for ( idelim=0; opdelims[idelim]!=NULL; idelim++ )
8764:   if ( strstr(ldelim,opdelims[idelim]) != NULL ) /* found operator */
8765:     { margin += opmargin;               /* extra height for operator */
8766:       if ( *ldelim == '\\' )            /* have leading escape */
8767:         {strsqueeze(ldelim,1);}         /* squeeze it out */
8768:       break; }                          /* no need to check rest of table */
8769: /* --- xlate delimiters and check for textstyle --- */
8770: for ( idelim=1; idelim<=2; idelim++ ) { /* 1=left, 2=right */
8771:   char  *lrdelim  = (idelim==1? ldelim:rdelim); /* ldelim or rdelim */
8772:   int   ix;  char *xdelim;              /* xfrom[] and xto[] index, delim */
8773:   for( ix=0; (xdelim=xfrom[ix]) != NULL; ix++ )
8774:     if ( strcmp(lrdelim,xdelim) == 0 )  /* found delim to xlate */
8775:       { strcpy(lrdelim,xto[ix]);        /* replace with corresponding xto[]*/
8776:         break; }                        /* no need to check further */
8777:   for( ix=0; (xdelim=textdelims[ix]) != NULL; ix++ )
8778:     if ( strstr(lrdelim,xdelim) != 0 )  /* found textstyle delim */
8779:       { if ( idelim == 1 )              /* if it's the \left one */
8780:           istextleft = 1;               /* set left textstyle flag */
8781:         else istextright = 1;           /* else set right textstyle flag */
8782:         break; }                        /* no need to check further */
8783:   } /* --- end-of-for(idelim) --- */
8784: /* --- debugging --- */
8785: if ( msgfp!=NULL && msglevel>=99 )
8786:   fprintf(msgfp,"rastleft> left=\"%s\" right=\"%s\" subexpr=\"%s\"\n",
8787:   ldelim,rdelim,subexpr);
8788: /* -------------------------------------------------------------------------
8789: rasterize subexpression
8790: -------------------------------------------------------------------------- */
8791: /* --- rasterize subexpression --- */
8792: if ( (sp = rasterize(subexpr,size))     /* rasterize chars between delims */
8793: ==   NULL ) goto end_of_job;            /* quit if failed */
8794: height = (sp->image)->height;           /* height of subexpr raster */
8795: rheight = height+margin;                /*default rheight as subexpr height*/
8796: /* -------------------------------------------------------------------------
8797: rasterize delimiters, reset baselines, and add  sub/superscripts if present
8798: -------------------------------------------------------------------------- */
8799: /* --- check for dot delimiter --- */
8800: isleftdot  = (strchr(ldelim,'.')!=NULL); /* true if \left. */
8801: isrightdot = (strchr(rdelim,'.')!=NULL); /* true if \right. */
8802: /* --- get rasters for best-fit delim characters, add sub/superscripts --- */
8803: isdisplaystyle = (istextleft?0:9);      /* force \displaystyle */
8804: if ( !isleftdot )                       /* if not \left. */
8805:  { /* --- first get requested \left delimiter --- */
8806:    lp = get_delim(ldelim,rheight,family); /* get \left delim char */
8807:    /* --- reset lp delim baseline to center delim on subexpr raster --- */
8808:    if ( lp != NULL )                    /* if get_delim() succeeded */
8809:     { int lheight = (lp->image)->height; /* actual height of left delim */
8810:       lp->baseline = sp->baseline + (lheight - height)/2;
8811:       if ( lheight > rheight )          /* got bigger delim than requested */
8812:         rheight = lheight-1; }          /* make sure right delim matches */
8813:    /* --- then add on any sub/superscripts attached to \left( --- */
8814:    lp = rastlimits(&pleft,size,lp);     /*\left(_a^b and push pleft past b*/
8815:    isleftscript = isscripted; }         /* check if left delim scripted */
8816: isdisplaystyle = (istextright?0:9);     /* force \displaystyle */
8817: if ( !isrightdot )                      /* and if not \right. */
8818:  { /* --- first get requested \right delimiter --- */
8819:    rp = get_delim(rdelim,rheight,family); /* get \right delim char */
8820:    /* --- reset rp delim baseline to center delim on subexpr raster --- */
8821:    if ( rp != NULL )                    /* if get_delim() succeeded */
8822:      rp->baseline = sp->baseline + ((rp->image)->height - height)/2;
8823:    /* --- then add on any sub/superscripts attached to \right) --- */
8824:    rp = rastlimits(expression,size,rp); /*\right)_c^d, expression past d*/
8825:    isrightscript = isscripted; }        /* check if right delim scripted */
8826: isdisplaystyle = wasdisplaystyle;       /* original \displystyle default */
8827: /* --- check that we got delimiters --- */
8828: if ( 0 )
8829:  if ( (lp==NULL && !isleftdot)          /* check that we got left( */
8830:  ||   (rp==NULL && !isrightdot) )       /* and right) if needed */
8831:   { if ( lp != NULL ) free ((void *)lp); /* free \left-delim subraster */
8832:     if ( rp != NULL ) free ((void *)rp); /* and \right-delim subraster */
8833:     if (0) { delete_subraster(sp);      /* if failed, free subraster */
8834:              sp = (subraster *)NULL; }  /* signal error to caller */
8835:     goto end_of_job; }                  /* and quit */
8836: /* -------------------------------------------------------------------------
8837: concat  lp || sp || rp  components
8838: -------------------------------------------------------------------------- */
8839: /* --- concat lp||sp||rp to obtain final result --- */
8840: if ( lp != NULL )                       /* ignore \left. */
8841:   sp = rastcat(lp,sp,3);                /* concat lp||sp and free sp,lp */
8842: if ( sp != NULL )                       /* succeeded or ignored \left. */
8843:   if ( rp != NULL )                     /* ignore \right. */
8844:     sp = rastcat(sp,rp,3);              /* concat sp||rp and free sp,rp */
8845: /* --- back to caller --- */
8846: end_of_job:
8847:   isdelimscript = isrightscript;        /* signal if right delim scripted */
8848:   return ( sp );
8849: } /* --- end-of-function rastleft() --- */
8850: 
8851: 
8852: /* ==========================================================================
8853:  * Function:    rastright ( expression, size, basesp, ildelim, arg2, arg3 )
8854:  * Purpose:     ...\right handler, intercepts an unexpected/unbalanced \right
8855:  * --------------------------------------------------------------------------
8856:  * Arguments:   expression (I)  char **  to first char of null-terminated
8857:  *                              string beginning with a \right
8858:  *                              to be rasterized
8859:  *              size (I)        int containing 0-7 default font size
8860:  *              basesp (I)      subraster *  to character (or subexpression)
8861:  *                              immediately preceding leading left{
8862:  *                              (unused, but passed for consistency)
8863:  *              ildelim (I)     int containing rdelims[] index of
8864:  *                              right delimiter
8865:  *              arg2 (I)        int unused
8866:  *              arg3 (I)        int unused
8867:  * --------------------------------------------------------------------------
8868:  * Returns:     ( subraster * ) ptr to subraster corresponding to subexpr,
8869:  *                              or NULL for any parsing error
8870:  * --------------------------------------------------------------------------
8871:  * Notes:     o
8872:  * ======================================================================= */
8873: /* --- entry point --- */
8874: FUNCSCOPE subraster *rastright(char **expression, int size, subraster *basesp,
8875:                                int ildelim, int arg2, int arg3)
8876: {
8877: /* -------------------------------------------------------------------------
8878: Allocations and Declarations
8879: -------------------------------------------------------------------------- */
8880: subraster /* *rasterize(),*/ *sp=NULL;  /*rasterize \right subexpr's*/
8881:   if ( sp != NULL )                     /* returning entire expression */
8882:     {
8883:       isreplaceleft = 1;                /* set flag to replace left half*/
8884:     }
8885: return ( sp );
8886: } /* --- end-of-function rastright() --- */
8887: 
8888: 
8889: /* ==========================================================================
8890:  * Function:    rastmiddle ( expression, size, basesp,  arg1, arg2, arg3 )
8891:  * Purpose:     \middle handler, returns subraster corresponding to
8892:  *              entire expression with \middle delimiter(s) sized to fit.
8893:  * --------------------------------------------------------------------------
8894:  * Arguments:   expression (I/O) char **  to first char of null-terminated
8895:  *                              string immediately following \middle to be
8896:  *                              rasterized, and returning ptr immediately
8897:  *                              to terminating null.
8898:  *              size (I)        int containing 0-7 default font size
8899:  *              basesp (I)      subraster *  to character (or subexpression)
8900:  *                              immediately preceding \middle
8901:  *                              (unused, but passed for consistency)
8902:  *              arg1 (I)        int unused
8903:  *              arg2 (I)        int unused
8904:  *              arg3 (I)        int unused
8905:  * --------------------------------------------------------------------------
8906:  * Returns:     ( subraster * ) ptr to subraster corresponding to expression,
8907:  *                              or NULL for any parsing error
8908:  *                              (expression ptr unchanged if error occurs)
8909:  * --------------------------------------------------------------------------
8910:  * Notes:     o
8911:  * ======================================================================= */
8912: /* --- entry point --- */
8913: FUNCSCOPE subraster *rastmiddle(char **expression, int size, subraster *basesp,
8914:                                 int arg1, int arg2, int arg3)
8915: {
8916: /* -------------------------------------------------------------------------
8917: Allocations and Declarations
8918: -------------------------------------------------------------------------- */
8919: subraster /**rasterize(),*/ *sp=NULL, *subsp[32]; /*rasterize \middle subexpr*/
8920: char    *exprptr = *expression,         /* local copy of ptr to expression */
8921:         /**texchar(),*/ delim[32][132], /* delimiters following \middle's */
8922:         /**strtexchr(),*/               /* locate \middle's */
8923:         subexpr[MAXSUBXSZ+1], *subptr=NULL;/*subexpression between \middle's*/
8924: int     height=0, habove=0, hbelow=0;   /* height, above & below baseline */
8925: int     idelim, ndelims=0,              /* \middle count (max 32) */
8926:         family = CMSYEX;                /* delims from CMSY10 or CMEX10 */
8927: /*subraster *subrastcpy(),*/            /* copy subraster */
8928: /*      *rastcat(),*/                   /* concatanate subraster */
8929: /*      *get_delim();*/                 /* get rasterized delimiter */
8930: /*int   delete_subraster();*/           /* free work area subsp[]'s at eoj */
8931: /* -------------------------------------------------------------------------
8932: initialization
8933: -------------------------------------------------------------------------- */
8934: subsp[0] = leftexpression;              /* expressn preceding 1st \middle */
8935: subsp[1] = NULL;                        /* set first null */
8936: /* -------------------------------------------------------------------------
8937: accumulate subrasters between consecutive \middle\delim...\middle\delim...'s
8938: -------------------------------------------------------------------------- */
8939: while ( ndelims < 30 )                  /* max of 31 \middle's */
8940:   {
8941:   /* --- maintain max height above,below baseline --- */
8942:   if ( subsp[ndelims] != NULL )         /*exprssn preceding current \middle*/
8943:    { int baseline = (subsp[ndelims])->baseline;  /* #rows above baseline */
8944:      height = ((subsp[ndelims])->image)->height; /* tot #rows (height) */
8945:      habove = max2(habove,baseline);    /* max #rows above baseline */
8946:      hbelow = max2(hbelow,height-baseline); } /* max #rows below baseline */
8947:   /* --- get delimter after \middle --- */
8948:   skipwhite(exprptr);                   /*skip space betwn \middle & \delim*/
8949:   exprptr = texchar(exprptr,delim[ndelims]); /* \delim after \middle */
8950:   if ( *(delim[ndelims]) == '\000' )    /* \middle at end-of-expression */
8951:     break;                              /* ignore it and consider job done */
8952:   ndelims++;                            /* count another \middle\delim */
8953:   /* --- get subexpression between \delim and next \middle --- */
8954:   subsp[ndelims] = NULL;                /* no subexpresion yet */
8955:   if ( *exprptr == '\000' )             /* end-of-expression after \delim */
8956:     break;                              /* so we have all subexpressions */
8957:   if ( (subptr = strtexchr(exprptr,"\\middle")) /* find next \middle */
8958:   ==   NULL )                           /* no more \middle's */
8959:    { strncpy(subexpr,exprptr,MAXSUBXSZ); /*get entire remaining expression*/
8960:      subexpr[MAXSUBXSZ] = '\000';       /* make sure it's null-terminated */
8961:      exprptr += strlen(exprptr); }      /* push exprptr to terminating '\0'*/
8962:   else                                  /* have another \middle */
8963:    { int sublen = (int)(subptr-exprptr); /* #chars between \delim...\middle*/
8964:      memcpy(subexpr,exprptr,min2(sublen,MAXSUBXSZ)); /* get subexpression */
8965:      subexpr[min2(sublen,MAXSUBXSZ)] = '\000'; /* and null-terminate it */
8966:      exprptr += (sublen+strlen("\\middle")); } /* push exprptr past \middle*/
8967:   /* --- rasterize subexpression --- */
8968:   subsp[ndelims] = rasterize(subexpr,size); /* rasterize subexpresion */
8969:   } /* --- end-of-while(1) --- */
8970: /* -------------------------------------------------------------------------
8971: construct \middle\delim's and concatanate them between subexpressions
8972: -------------------------------------------------------------------------- */
8973: if ( ndelims < 1                        /* no delims */
8974: ||   (height=habove+hbelow) < 1 )       /* or no subexpressions? */
8975:   goto end_of_job;                      /* just flush \middle directive */
8976: for ( idelim=0; idelim<=ndelims; idelim++ )
8977:   {
8978:   /* --- first add on subexpression preceding delim --- */
8979:   if ( subsp[idelim] != NULL ) {        /* have subexpr preceding delim */
8980:     if ( sp == NULL )                   /* this is first piece */
8981:      { sp = subsp[idelim];              /* so just use it */
8982:        if ( idelim == 0 ) sp = subrastcpy(sp); } /* or copy leftexpression */
8983:     else sp = rastcat(sp,subsp[idelim],(idelim>0?3:1)); } /* or concat it */
8984:   /* --- now construct delimiter --- */
8985:   if ( *(delim[idelim]) != '\000' )     /* have delimter */
8986:    { subraster *delimsp = get_delim(delim[idelim],height,family);
8987:      if ( delimsp != NULL )             /* rasterized delim */
8988:       { delimsp->baseline = habove;     /* set baseline */
8989:         if ( sp == NULL )               /* this is first piece */
8990:           sp = delimsp;                 /* so just use it */
8991:         else sp = rastcat(sp,delimsp,3); } } /*or concat to existing pieces*/
8992:   } /* --- end-of-for(idelim) --- */
8993: /* --- back to caller --- */
8994: end_of_job:
8995:   if ( 0 ) /* now handled above */
8996:     for ( idelim=1; idelim<=ndelims; idelim++ ) /* free subsp[]'s (not 0) */
8997:      if ( subsp[idelim] != NULL )       /* have allocated subraster */
8998:       delete_subraster(subsp[idelim]);  /* so free it */
8999:   if ( sp != NULL )                     /* returning entire expression */
9000:     { int newht = (sp->image)->height;  /* height of returned subraster */
9001:       sp->baseline = min2(newht-1,newht/2+5); /* guess new baseline */
9002:       isreplaceleft = 1;                /* set flag to replace left half*/
9003:       *expression += strlen(*expression); } /* and push to terminating null*/
9004:   return ( sp );
9005: } /* --- end-of-function rastmiddle() --- */
9006: 
9007: 
9008: /* ==========================================================================
9009:  * Function:    rastflags ( expression, size, basesp,  flag, value, arg3 )
9010:  * Purpose:     sets an internal flag, e.g., for \rm, or sets an internal
9011:  *              value, e.g., for \unitlength=<value>, and returns NULL
9012:  *              so nothing is displayed
9013:  * --------------------------------------------------------------------------
9014:  * Arguments:   expression (I)  char **  to first char of null-terminated
9015:  *                              LaTeX expression (unused/unchanged)
9016:  *              size (I)        int containing base font size (not used,
9017:  *                              just stored in subraster)
9018:  *              basesp (I)      subraster *  to character (or subexpression)
9019:  *                              immediately preceding "flags" directive
9020:  *                              (unused but passed for consistency)
9021:  *              flag (I)        int containing #define'd symbol specifying
9022:  *                              internal flag to be set
9023:  *              value (I)       int containing new value of flag
9024:  *              arg3 (I)        int unused
9025:  * --------------------------------------------------------------------------
9026:  * Returns:     ( subraster * ) NULL so nothing is displayed
9027:  * --------------------------------------------------------------------------
9028:  * Notes:     o
9029:  * ======================================================================= */
9030: /* --- entry point --- */
9031: FUNCSCOPE subraster *rastflags(char **expression, int size, subraster *basesp,
9032:                                int flag, int value, int arg3)
9033: {
9034: /* -------------------------------------------------------------------------
9035: Allocations and Declarations
9036: -------------------------------------------------------------------------- */
9037: char    /**texsubexpr(),*/              /* parse expression for... */
9038:         valuearg[1024]="NOVALUE";       /* value from expression, if needed */
9039: int     argvalue=NOVALUE,               /* atoi(valuearg) */
9040:         isdelta=0,                      /* true if + or - precedes valuearg */
9041:         valuelen=0;                     /* strlen(valuearg) */
9042: double  dblvalue=(-99.), strtod();      /*convert ascii {valuearg} to double*/
9043: static  int displaystylelevel = (-99);  /* \displaystyle set at recurlevel */
9044: /* -------------------------------------------------------------------------
9045: set flag or value
9046: -------------------------------------------------------------------------- */
9047: switch ( flag )
9048:   {
9049:   default: break;                       /* unrecognized flag */
9050:   case ISFONTFAM:
9051:     if ( isthischar((*(*expression)),WHITEMATH) ) /* \rm followed by white */
9052:       (*expression)++;                  /* skip leading ~ after \rm */
9053:     fontnum = value;                    /* set font family */
9054:     break;
9055:   case ISSTRING: isstring=value; break; /* set string/image mode */
9056:   case ISDISPLAYSTYLE:                  /* set \displaystyle mode */
9057:     displaystylelevel = recurlevel;     /* \displaystyle set at recurlevel */
9058:     isdisplaystyle=value; break;
9059:   case ISOPAQUE:  istransparent=value; break; /* set transparent/opaque */
9060:   case ISREVERSE:                       /* reverse video */
9061:     if ( value==1 || value==NOVALUE )
9062:       { fgred=255-fgred; fggreen=255-fggreen; fgblue=255-fgblue; }
9063:     if ( value==2 || value==NOVALUE )
9064:       { bgred=255-bgred; bggreen=255-bggreen; bgblue=255-bgblue; }
9065:     if ( value==2 || value==NOVALUE )
9066:       isblackonwhite = !isblackonwhite;
9067:     if ( gammacorrection > 0.0001 )     /* have gamma correction */
9068:       gammacorrection = REVERSEGAMMA;   /* use reverse video gamma instead */
9069:     break;
9070:   case ISSUPER:                         /* set supersampling/lowpass flag */
9071:     #ifndef SSFONTS                     /* don't have ss fonts loaded */
9072:       value = 0;                        /* so force lowpass */
9073:     #endif
9074:     isss = issupersampling = value;
9075:     fonttable = (issupersampling?ssfonttable:aafonttable); /* set fonts */
9076:     break;
9077:   case ISFONTSIZE:                      /* set fontsize */
9078:   case ISMAGSTEP:                       /* set magstep */
9079:   case ISDISPLAYSIZE:                   /* set displaysize */
9080:   case ISCONTENTTYPE:                   /*enable/disable content-type lines*/
9081:   case ISCONTENTCACHED:                 /* write content-type to cache file*/
9082:   case ISSHRINK:                        /* set shrinkfactor */
9083:   case ISAAALGORITHM:                   /* set anti-aliasing algorithm */
9084:   case ISWEIGHT:                        /* set font weight */
9085:   case ISCENTERWT:                      /* set lowpass center pixel weight */
9086:   case ISADJACENTWT:                    /* set lowpass adjacent weight */
9087:   case ISCORNERWT:                      /* set lowpass corner weight */
9088:   case ISCOLOR:                         /* set red(1),green(2),blue(3) */
9089:   case ISSMASH:                         /* set (minimum) "smash" margin */
9090:   case ISGAMMA:                         /* set gamma correction */
9091:   case ISPBMPGM:                        /* set pbmpgm output flag and ptype*/
9092:   case ISUTHETA:                        /* set utheta 3d-rotation angle */
9093:     if ( value != NOVALUE )             /* passed a fixed value to be set */
9094:       { argvalue = value;               /* set given fixed int value */
9095:         dblvalue = (double)value; }     /* or maybe interpreted as double */
9096:     else                                /* get value from expression */
9097:       { *expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0);
9098:         if ( *valuearg != '\000' )      /* guard against empty string */
9099:          if ( !isalpha(*valuearg) )     /* and against alpha string args */
9100:           if ( !isthischar(*valuearg,"?") ) /*leading ? is query for value*/
9101:            { isdelta = isthischar(*valuearg,"+-"); /* leading + or - */
9102:              if ( memcmp(valuearg,"--",2) == 0 ) /* leading -- signals...*/
9103:                { isdelta=0; strsqueeze(valuearg,1); } /* ...not delta */
9104:              switch ( flag ) {          /* convert to double or int */
9105:               default: argvalue = atoi(valuearg); break; /* convert to int */
9106:               case ISGAMMA:
9107:               case ISUTHETA:
9108:                 dblvalue = strtod(valuearg,NULL); break; } /* or to double */
9109:            } /* --- end-of-if(*valuearg!='?') --- */
9110:       } /* --- end-of-if(value==NOVALUE) --- */
9111:     switch ( flag )
9112:       {
9113:       default: break;
9114:       case ISCOLOR:                     /* set color */
9115:         slower(valuearg);               /* convert arg to lower case */
9116:         if ( argvalue==1 || strstr(valuearg,"red") )
9117:           { fggreen = fgblue = (isblackonwhite?0:255);
9118:             fgred = (isblackonwhite?255:0); }
9119:         if ( argvalue==2 || strstr(valuearg,"green") )
9120:           { fgred = fgblue = (isblackonwhite?0:255);
9121:             fggreen = (isblackonwhite?255:0); }
9122:         if ( argvalue==3 || strstr(valuearg,"blue") )
9123:           { fgred = fggreen = (isblackonwhite?0:255);
9124:             fgblue = (isblackonwhite?255:0); }
9125:         if ( argvalue==0 || strstr(valuearg,"black") )
9126:             fgred = fggreen = fgblue = (isblackonwhite?0:255);
9127:         if ( argvalue==7 || strstr(valuearg,"white") )
9128:             fgred = fggreen = fgblue = (isblackonwhite?255:0);
9129:         break;
9130:       case ISFONTSIZE:                  /* set fontsize */
9131:         if ( argvalue != NOVALUE )      /* got a value */
9132:           { int largestsize = (issupersampling?16:LARGESTSIZE);
9133:             fontsize = (isdelta? fontsize+argvalue : argvalue);
9134:             fontsize = max2(0,min2(fontsize,largestsize));
9135:             shrinkfactor = shrinkfactors[fontsize];
9136:             if ( isdisplaystyle == 1    /* displaystyle enabled but not set*/
9137:             ||  (1 && isdisplaystyle==2) /* displaystyle enabled and set */
9138:             ||  (0 && isdisplaystyle==0) )/*\textstyle disabled displaystyle*/
9139:              if ( displaystylelevel != recurlevel ) /*respect \displaystyle*/
9140:               if ( !ispreambledollars ) { /* respect $$...$$'s */
9141:                if ( fontsize >= displaysize )
9142:                 isdisplaystyle = 2;     /* forced */
9143:                else isdisplaystyle = 1; }
9144:             /*displaystylelevel = (-99);*/ } /* reset \displaystyle level */
9145:         else                            /* embed font size in expression */
9146:           { sprintf(valuearg,"%d",fontsize); /* convert size */
9147:             valuelen = strlen(valuearg); /* ought to be 1 */
9148:             if ( *expression != '\000' ) /* ill-formed expression */
9149:              { *expression = (char *)(*expression-valuelen); /*back up buff*/
9150:                memcpy(*expression,valuearg,valuelen); } } /*and put in size*/
9151:         break;
9152:       case ISMAGSTEP:                   /* set magstep */
9153:         if ( argvalue != NOVALUE ) {    /* got a value */
9154:           int largestmag = 10;
9155:           magstep = (isdelta? magstep+argvalue : argvalue);
9156:           magstep = max2(1,min2(magstep,largestmag)); }
9157:         break;
9158:       case ISDISPLAYSIZE:               /* set displaysize */
9159:         if ( argvalue != NOVALUE )      /* got a value */
9160:             displaysize = (isdelta? displaysize+argvalue : argvalue);
9161:         break;
9162:       case ISCONTENTTYPE:               /*enable/disable content-type lines*/
9163:         if ( argvalue != NOVALUE )      /* got a value */
9164:             isemitcontenttype = (argvalue>0?1:0);
9165:         break;
9166:       case ISCONTENTCACHED:             /* write content-type to cache file*/
9167:         if ( argvalue != NOVALUE )      /* got a value */
9168:             iscachecontenttype = (argvalue>0?1:0);
9169:         break;
9170:       case ISPBMPGM:                    /* output pbm/pgm rather than gif */
9171:         ispbmpgm = 1;                   /* always set pbm/pgm flag */
9172:         pbmpgmtype = 1;                 /* and init type (1 for pbm) */
9173:         if ( argvalue != NOVALUE )      /* got a value */
9174:             pbmpgmtype = (argvalue<1?1:argvalue);
9175:         break;
9176:       case ISSMASH:                     /* set (minimum) "smash" margin */
9177:         if ( argvalue != NOVALUE )      /* got a value */
9178:           { smashmargin = argvalue;     /* set value */
9179:             if ( arg3 != NOVALUE ) isdelta=arg3; /* hard-coded isdelta */
9180:             issmashdelta = (isdelta?1:0); } /* and set delta flag */
9181:         smashmargin = max2((isdelta?-5:0),min2(smashmargin,32)); /*sanity*/
9182:         isexplicitsmash = 1;            /* signal explicit \smash directive*/
9183:         break;
9184:       case ISSHRINK:                    /* set shrinkfactor */
9185:         if ( argvalue != NOVALUE )      /* got a value */
9186:           shrinkfactor = (isdelta? shrinkfactor+argvalue : argvalue);
9187:         shrinkfactor = max2(1,min2(shrinkfactor,27)); /* sanity check */
9188:         break;
9189:       case ISAAALGORITHM:               /* set anti-aliasing algorithm */
9190:         if ( argvalue != NOVALUE ) {    /* got a value */
9191:           if ( argvalue >= 0 ) {        /* non-negative to set algorithm */
9192:               aaalgorithm = argvalue;   /* set algorithm number */
9193:             aaalgorithm = max2(0,min2(aaalgorithm,4)); } /* bounds check */
9194:           else maxfollow = abs(argvalue); } /* or maxfollow=abs(negative#) */
9195:         break;
9196:       case ISWEIGHT:                    /* set font weight number */
9197:         value = (argvalue==NOVALUE? NOVALUE : /* don't have a value */
9198:                 (isdelta? weightnum+argvalue : argvalue));
9199:         if ( value>=0 && value<maxaaparams ) /* in range */
9200:           { weightnum   = value;        /* reset weightnum index */
9201:             minadjacent = aaparams[weightnum].minadjacent;
9202:             maxadjacent = aaparams[weightnum].maxadjacent;
9203:             cornerwt    = aaparams[weightnum].cornerwt;
9204:             adjacentwt  = aaparams[weightnum].adjacentwt;
9205:             centerwt    = aaparams[weightnum].centerwt;
9206:             fgalias     = aaparams[weightnum].fgalias;
9207:             fgonly      = aaparams[weightnum].fgonly;
9208:             bgalias     = aaparams[weightnum].bgalias;
9209:             bgonly      = aaparams[weightnum].bgonly; }
9210:         break;
9211:       case ISCENTERWT:                  /* set lowpass center pixel weight */
9212:         if ( argvalue != NOVALUE )      /* got a value */
9213:           centerwt = argvalue;          /* set lowpass center weight */
9214:         break;
9215:       case ISADJACENTWT:                /* set lowpass adjacent weight */
9216:         if ( argvalue != NOVALUE )      /* got a value */
9217:           adjacentwt = argvalue;        /* set lowpass adjacent weight */
9218:         break;
9219:       case ISCORNERWT:                  /* set lowpass corner weight */
9220:         if ( argvalue != NOVALUE )      /* got a value */
9221:           cornerwt = argvalue;          /* set lowpass corner weight */
9222:         break;
9223:       case ISGAMMA:                     /* set gamma correction */
9224:         if ( dblvalue >= 0.0 )          /* got a value */
9225:           gammacorrection = dblvalue;   /* set gamma correction */
9226:         break;
9227:       case ISUTHETA:                    /* set 3d-rotation angle for chars */
9228:         if ( absval(dblvalue) >= 1.0e-6 ) /* got a value */
9229:           utheta = dblvalue;            /* set utheta */
9230:         break;
9231:       } /* --- end-of-switch() --- */
9232:     break;
9233:   case PNMPARAMS:                       /*set fgalias,fgonly,bgalias,bgonly*/
9234:     *expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0);
9235:     valuelen = strlen(valuearg);        /* ought to be 1-4 */
9236:     if ( valuelen>0 && isthischar(toupper(valuearg[0]),"TY1") ) fgalias=1;
9237:     if ( valuelen>0 && isthischar(toupper(valuearg[0]),"FN0") ) fgalias=0;
9238:     if ( valuelen>1 && isthischar(toupper(valuearg[1]),"TY1") ) fgonly =1;
9239:     if ( valuelen>1 && isthischar(toupper(valuearg[1]),"FN0") ) fgonly =0;
9240:     if ( valuelen>2 && isthischar(toupper(valuearg[2]),"TY1") ) bgalias=1;
9241:     if ( valuelen>2 && isthischar(toupper(valuearg[2]),"FN0") ) bgalias=0;
9242:     if ( valuelen>3 && isthischar(toupper(valuearg[3]),"TY1") ) bgonly =1;
9243:     if ( valuelen>3 && isthischar(toupper(valuearg[3]),"FN0") ) bgonly =0;
9244:     break;
9245:   case UNITLENGTH:
9246:     if ( value != NOVALUE )             /* passed a fixed value to be set */
9247:         unitlength = (double)(value);   /* set given fixed value */
9248:     else                                /* get value from expression */
9249:       { *expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0);
9250:         if ( *valuearg != '\000' )      /* guard against empty string */
9251:           unitlength = strtod(valuearg,NULL); } /* convert to double */
9252:     iunitlength = (int)(unitlength+0.5); /* iunitlength reset */
9253:     break;
9254:   } /* --- end-of-switch(flag) --- */
9255: return ( NULL );                        /*just set value, nothing to display*/
9256: } /* --- end-of-function rastflags() --- */
9257: 
9258: 
9259: /* ==========================================================================
9260:  * Function:    rastspace(expression, size, basesp,  width, isfill, isheight)
9261:  * Purpose:     returns a blank/space subraster width wide,
9262:  *              with baseline and height corresponding to basep
9263:  * --------------------------------------------------------------------------
9264:  * Arguments:   expression (I)  char **  to first char of null-terminated
9265:  *                              LaTeX expression (unused/unchanged)
9266:  *              size (I)        int containing base font size (not used,
9267:  *                              just stored in subraster)
9268:  *              basesp (I)      subraster *  to character (or subexpression)
9269:  *                              immediately preceding space, whose baseline
9270:  *                              and height params are transferred to space
9271:  *              width (I)       int containing #bits/pixels for space width
9272:  *              isfill (I)      int containing true to \hfill complete
9273:  *                              expression out to width
9274:  *                              (Kludge: isfill=99 signals \hspace*
9275:  *                              for negative space)
9276:  *              isheight (I)    int containing true (but not NOVALUE)
9277:  *                              to treat width arg as height
9278:  * --------------------------------------------------------------------------
9279:  * Returns:     ( subraster * ) ptr to empty/blank subraster
9280:  *                              or NULL for any error
9281:  * --------------------------------------------------------------------------
9282:  * Notes:     o
9283:  * ======================================================================= */
9284: /* --- entry point --- */
9285: FUNCSCOPE subraster *rastspace(char **expression, int size, subraster *basesp,
9286:                                int width, int isfill, int isheight)
9287: {
9288: /* -------------------------------------------------------------------------
9289: Allocations and Declarations
9290: -------------------------------------------------------------------------- */
9291: subraster /**new_subraster(),*/ *spacesp=NULL; /* subraster for space */
9292: raster  *bp=NULL /*, *backspace_raster()*/; /* for negative space */
9293: /*int   delete_subraster();*/           /* if fail, free unneeded subraster*/
9294: int     baseht=1, baseln=0;             /* height,baseline of base symbol */
9295: int     pixsz = 1;                      /*default #bits per pixel, 1=bitmap*/
9296: int     isstar=0, minspace=0;           /* defaults for negative hspace */
9297: int     maxwidth = 2047;                /* max width of expression in pixels*/
9298: char    /**texsubexpr(),*/ widtharg[256]; /* parse for optional {width} */
9299: int     /*evalterm(),*/ evalue=0;       /* evaluate [args], {args} */
9300: subraster /**rasterize(),*/ *rightsp=NULL; /*rasterize right half of expresn*/
9301: /*subraster *rastcat();*/               /* cat rightsp after \hfill */
9302: /* -------------------------------------------------------------------------
9303: initialization
9304: -------------------------------------------------------------------------- */
9305: if ( isfill > 1 ) { isstar=1; isfill=0; } /* large fill signals \hspace* */
9306: if ( isfill == NOVALUE ) isfill=0;      /* novalue means false */
9307: if ( isheight == NOVALUE ) isheight=0;  /* novalue means false */
9308: minspace = (isstar?(-1):0);             /* reset default minspace */
9309: /* -------------------------------------------------------------------------
9310: determine width if not given (e.g., \hspace{width}, \hfill{width})
9311: -------------------------------------------------------------------------- */
9312: if ( width == 0 ) {                     /* width specified in expression */
9313:   double dwidth;  int widthval;         /* test {width} before using it */
9314:   int minwidth = (isfill||isheight?1:-maxwidth); /* \hspace allows negative */
9315:   /* --- check if optional [minspace] given for negative \hspace --- */
9316:   if ( *(*expression) == '[' ) {        /* [minspace] if leading char is [ */
9317:     /* ---parse [minspace], bump expression past it, evaluate expression--- */
9318:     *expression = texsubexpr(*expression,widtharg,127,"[","]",0,0);
9319:     if ( !isempty(widtharg) ) {         /* got [minspace] */
9320:       evalue = evalterm(mimestore,widtharg); /* evaluate widtharg expr */
9321:       minspace = iround(unitlength*((double)evalue)); } /* in pixels */
9322:     } /* --- end-of-if(*(*expression)=='[') --- */
9323:   width = 1;                            /* set default width */
9324:   *expression = texsubexpr(*expression,widtharg,255,"{","}",0,0);
9325:   dwidth = unitlength*((double)evalterm(mimestore,widtharg)); /* scaled */
9326:   widthval =                            /* convert {width} to integer */
9327:                 (int)( dwidth + (dwidth>=0.0?0.5:(-0.5)) );
9328:   if ( widthval>=minwidth && widthval<=maxwidth ) /* sanity check */
9329:     width = widthval;                   /* replace deafault width */
9330:   } /* --- end-of-if(width==0) --- */
9331: /* -------------------------------------------------------------------------
9332: first check for negative space
9333: -------------------------------------------------------------------------- */
9334: if ( width < 0 ) {                      /* have negative hspace */
9335:  if ( leftexpression != (subraster *)NULL ) /* can't backspace */
9336:   if ( (spacesp=new_subraster(0,0,0))   /* get new subraster for backspace */
9337:   !=   NULL ) {                         /* and if we succeed... */
9338:    int nback=(-width), pback;           /*#pixels wanted,actually backspaced*/
9339:    if ( (bp=backspace_raster(leftexpression->image,nback,&pback,minspace,0))
9340:    !=    NULL ) {                       /* and if backspace succeeds... */
9341:      spacesp->image = bp;               /* save backspaced image */
9342:      /*spacesp->type = leftexpression->type;*/ /* copy original type */
9343:      spacesp->type = blanksignal;       /* need to propagate blanks */
9344:      spacesp->size = leftexpression->size; /* copy original font size */
9345:      spacesp->baseline = leftexpression->baseline; /* and baseline */
9346:      blanksymspace += -(nback-pback);   /* wanted more than we got */
9347:      isreplaceleft = 1; }               /*signal to replace entire expressn*/
9348:    else {                               /* backspace failed */
9349:      delete_subraster(spacesp);         /* free unneeded envelope */
9350:      spacesp = (subraster *)NULL; } }   /* and signal failure */
9351:  goto end_of_job;
9352:  } /* --- end-of-if(width<0) --- */
9353: /* -------------------------------------------------------------------------
9354: see if width is "absolute" or fill width
9355: -------------------------------------------------------------------------- */
9356: if ( isfill                             /* called as \hfill{} */
9357: &&   !isheight )                        /* parameter conflict */
9358:  { if ( leftexpression != NULL )        /* if we have left half */
9359:     width -= (leftexpression->image)->width; /*reduce left width from total*/
9360:    if ( (rightsp=rasterize(*expression,size)) /* rasterize right half */
9361:    != NULL )                            /* succeeded */
9362:     width -= (rightsp->image)->width; } /* reduce right width from total */
9363: /* -------------------------------------------------------------------------
9364: construct blank subraster, and return it to caller
9365: -------------------------------------------------------------------------- */
9366: /* --- get parameters from base symbol --- */
9367: if ( basesp != (subraster *)NULL )      /* we have base symbol for space */
9368:   { baseht = (basesp->image)->height;   /* height of base symbol */
9369:     baseln =  basesp->baseline; }       /* and its baseline */
9370: /* --- flip params for height --- */
9371: if ( isheight )                         /* width is actually height */
9372:   { baseht = width;                     /* use given width as height */
9373:     width = 1; }                        /* and set default width */
9374: /* --- generate and init space subraster --- */
9375: if ( width > 0 )                        /*make sure we have positive width*/
9376:  if ( (spacesp=new_subraster(width,baseht,pixsz)) /*generate space subraster*/
9377:  !=   NULL )                            /* and if we succeed... */
9378:   { /* --- ...re-init subraster parameters --- */
9379:     spacesp->size = size;               /*propagate base font size forward*/
9380:     if(1)spacesp->type = blanksignal;   /* need to propagate blanks (???) */
9381:     spacesp->baseline = baseln; }       /* ditto baseline */
9382: /* -------------------------------------------------------------------------
9383: concat right half if \hfill-ing
9384: -------------------------------------------------------------------------- */
9385: if ( rightsp != NULL )                  /* we have a right half after fill */
9386:   { spacesp = (spacesp==NULL? rightsp:  /* no space, so just use right half*/
9387:         rastcat(spacesp,rightsp,3));    /* or cat right half after space */
9388:     spacesp->type = blanksignal;        /* need to propagate blanks */
9389:     *expression += strlen((*expression)); } /* push expression to its null */
9390: end_of_job:
9391:   return ( spacesp );
9392: } /* --- end-of-function rastspace() --- */
9393: 
9394: 
9395: /* ==========================================================================
9396:  * Function:    rastnewline ( expression, size, basesp,  arg1, arg2, arg3 )
9397:  * Purpose:     \\ handler, returns subraster corresponding to
9398:  *              left-hand expression preceding \\ above right-hand expression
9399:  * --------------------------------------------------------------------------
9400:  * Arguments:   expression (I/O) char **  to first char of null-terminated
9401:  *                              string immediately following \\ to be
9402:  *                              rasterized, and returning ptr immediately
9403:  *                              to terminating null.
9404:  *              size (I)        int containing 0-7 default font size
9405:  *              basesp (I)      subraster *  to character (or subexpression)
9406:  *                              immediately preceding \\
9407:  *                              (unused, but passed for consistency)
9408:  *              arg1 (I)        int unused
9409:  *              arg2 (I)        int unused
9410:  *              arg3 (I)        int unused
9411:  * --------------------------------------------------------------------------
9412:  * Returns:     ( subraster * ) ptr to subraster corresponding to expression,
9413:  *                              or NULL for any parsing error
9414:  *                              (expression ptr unchanged if error occurs)
9415:  * --------------------------------------------------------------------------
9416:  * Notes:     o
9417:  * ======================================================================= */
9418: /* --- entry point --- */
9419: FUNCSCOPE subraster *rastnewline ( char **expression, int size,
9420:                             subraster *basesp, int arg1, int arg2, int arg3 )
9421: {
9422: /* -------------------------------------------------------------------------
9423: Allocations and Declarations
9424: -------------------------------------------------------------------------- */
9425: subraster /**rastack(),*/ *newlsp=NULL; /* subraster for both lines */
9426: subraster /**rasterize(),*/ *rightsp=NULL; /*rasterize right half of expresn*/
9427: char    /**texsubexpr(),*/ spacexpr[129]/*, *xptr=spacexpr*/; /*for\\[vspace]*/
9428: int     /*evalterm(),*/ evalue=0;       /* evaluate [arg], {arg} */
9429: int     vspace = size+2;                /* #pixels between lines */
9430: /* -------------------------------------------------------------------------
9431: obtain optional [vspace] argument immediately following \\ command
9432: -------------------------------------------------------------------------- */
9433: /* --- check if [vspace] given --- */
9434: if ( *(*expression) == '[' )            /*have [vspace] if leading char is [*/
9435:   {
9436:   /* ---parse [vspace] and bump expression past it, interpret as double--- */
9437:   *expression = texsubexpr(*expression,spacexpr,127,"[","]",0,0);
9438:   if ( *spacexpr == '\000' ) goto end_of_job; /* couldn't get [vspace] */
9439:   evalue = evalterm(mimestore,spacexpr); /* evaluate [space] arg */
9440:   vspace = iround(unitlength*((double)evalue)); /* vspace in pixels */
9441:   } /* --- end-of-if(*(*expression)=='[') --- */
9442: if ( leftexpression == NULL ) goto end_of_job; /* nothing preceding \\ */
9443: /* -------------------------------------------------------------------------
9444: rasterize right half of expression and stack left half above it
9445: -------------------------------------------------------------------------- */
9446: /* --- rasterize right half --- */
9447: if ( (rightsp=rasterize(*expression,size)) /* rasterize right half */
9448: == NULL ) goto end_of_job;              /* quit if failed */
9449: /* --- stack left half above it --- */
9450: /*newlsp = rastack(rightsp,leftexpression,1,vspace,0,3);*//*right under left*/
9451: newlsp = rastack(rightsp,leftexpression,1,vspace,0,1); /*right under left*/
9452: /* --- back to caller --- */
9453: end_of_job:
9454:   if ( newlsp != NULL )                 /* returning entire expression */
9455:     { int newht = (newlsp->image)->height; /* height of returned subraster */
9456:       newlsp->baseline = min2(newht-1,newht/2+5); /* guess new baseline */
9457:       isreplaceleft = 1;                /* so set flag to replace left half*/
9458:       *expression += strlen(*expression); } /* and push to terminating null*/
9459:   return ( newlsp );                    /* 1st line over 2nd, or null=error*/
9460: } /* --- end-of-function rastnewline() --- */
9461: 
9462: 
9463: /* ==========================================================================
9464:  * Function:    rastarrow ( expression, size, basesp,  drctn, isBig, arg3 )
9465:  * Purpose:     returns left/right arrow subraster (e.g., for \longrightarrow)
9466:  * --------------------------------------------------------------------------
9467:  * Arguments:   expression (I)  char **  to first char of null-terminated
9468:  *                              LaTeX expression (unused/unchanged)
9469:  *              size (I)        int containing base font size (not used,
9470:  *                              just stored in subraster)
9471:  *              basesp (I)      subraster *  to character (or subexpression)
9472:  *                              immediately preceding space, whose baseline
9473:  *                              and height params are transferred to space
9474:  *              drctn (I)       int containing +1 for right, -1 for left,
9475:  *                              or 0 for leftright
9476:  *              isBig (I)       int containing 0 for ---> or 1 for ===>
9477:  *              arg3 (I)        int unused
9478:  * --------------------------------------------------------------------------
9479:  * Returns:     ( subraster * ) ptr to left/right arrow subraster
9480:  *                              or NULL for any error
9481:  * --------------------------------------------------------------------------
9482:  * Notes:     o An optional argument [width] may *immediately* follow
9483:  *              the \longxxx to explicitly set the arrow's width in pixels.
9484:  *              For example, \longrightarrow calculates a default width
9485:  *              (as usual in LaTeX), whereas \longrightarrow[50] explicitly
9486:  *              draws a 50-pixel long arrow.  This can be used, e.g.,
9487:  *              to draw commutative diagrams in conjunction with
9488:  *              \array (and maybe with \stackrel and/or \relstack, too).
9489:  *            o In case you really want to render, say, [f]---->[g], just
9490:  *              use an intervening space, i.e., [f]\longrightarrow~[g].
9491:  *              In text mode use two spaces {\rm~[f]\longrightarrow~~[g]}.
9492:  * ======================================================================= */
9493: /* --- entry point --- */
9494: FUNCSCOPE subraster *rastarrow(char **expression, int size, subraster *basesp,
9495:                                int drctn, int isBig, int arg3)
9496: {
9497: /* -------------------------------------------------------------------------
9498: Allocations and Declarations
9499: -------------------------------------------------------------------------- */
9500: subraster /**arrow_subraster(),*/ *arrowsp=NULL; /* subraster for arrow */
9501: char    /**texsubexpr(),*/ widtharg[256]; /* parse for optional [width] */
9502: char    /**texscripts(),*/ sub[1024],super[1024];/*and _^limits after [width]*/
9503: subraster /**rasterize(),*/ *subsp=NULL,*supsp=NULL; /*rasterize limits*/
9504: subraster /**new_subraster(), *rastack(),*/ *spacesp=NULL;/*space below arrow*/
9505: /*int   delete_subraster();*/           /*free work areas in case of error*/
9506: /*int   evalterm();*/                   /* evaluate [arg], {arg} */
9507: int     width = 10 + 8*size,  height;   /* width, height for \longxxxarrow */
9508: int     maxwidth = 1024;                /* max arrow width in pixels */
9509: int     islimits = 1;                   /*true to handle limits internally*/
9510: int     limsize = size-1;               /* font size for limits */
9511: int     vspace = 1;                     /* #empty rows below arrow */
9512: int     pixsz = 1;                      /*default #bits per pixel, 1=bitmap*/
9513: /* -------------------------------------------------------------------------
9514: construct longleft/rightarrow subraster, with limits, and return it to caller
9515: -------------------------------------------------------------------------- */
9516: /* --- check for optional width arg and replace default width --- */
9517: if ( *(*expression) == '[' )            /*check for []-enclosed optional arg*/
9518:   { int widthval;                       /* test [width] before using it */
9519:     *expression = texsubexpr(*expression,widtharg,255,"[","]",0,0);
9520:     widthval =                          /* convert [width] to integer */
9521:         (int)((unitlength*((double)evalterm(mimestore,widtharg)))+0.5);
9522:     if ( widthval>=2 && widthval<=maxwidth )    /* sanity check */
9523:       width = widthval; }               /* replace deafault width */
9524: /* --- now parse for limits, and bump expression past it(them) --- */
9525: if ( islimits )                         /* handling limits internally */
9526:   { *expression = texscripts(*expression,sub,super,3); /* parse for limits */
9527:     if ( *sub != '\000' )               /*have a subscript following arrow*/
9528:       subsp = rasterize(sub,limsize);   /* so try to rasterize subscript */
9529:     if ( *super != '\000' )             /*have superscript following arrow*/
9530:       supsp = rasterize(super,limsize); } /*so try to rasterize superscript*/
9531: /* --- set height based on width --- */
9532: height = min2(17,max2(9,(width+2)/6));  /* height based on width */
9533: height = 1 + (height/2)*2;              /* always force odd height */
9534: /* --- generate arrow subraster --- */
9535: if ( (arrowsp=arrow_subraster(width,height,pixsz,drctn,isBig)) /*build arrow*/
9536: ==   NULL ) goto end_of_job;            /* and quit if we failed */
9537: /* --- add space below arrow --- */
9538: if ( vspace > 0 )                       /* if we have space below arrow */
9539:   if ( (spacesp=new_subraster(width,vspace,pixsz)) /*allocate required space*/
9540:   !=   NULL )                           /* and if we succeeded */
9541:     if ( (arrowsp = rastack(spacesp,arrowsp,2,0,1,3)) /* space below arrow */
9542:     ==   NULL ) goto end_of_job;        /* and quit if we failed */
9543: /* --- init arrow subraster parameters --- */
9544: arrowsp->size = size;                   /*propagate base font size forward*/
9545: arrowsp->baseline = height+vspace-1;    /* set baseline at bottom of arrow */
9546: /* --- add limits above/below arrow, as necessary --- */
9547: if ( subsp != NULL )                    /* stack subscript below arrow */
9548:   if ( (arrowsp = rastack(subsp,arrowsp,2,0,1,3)) /* subscript below arrow */
9549:   ==   NULL ) goto end_of_job;          /* quit if failed */
9550: if ( supsp != NULL )                    /* stack superscript above arrow */
9551:   if ( (arrowsp = rastack(arrowsp,supsp,1,vspace,1,3)) /*supsc above arrow*/
9552:   ==   NULL ) goto end_of_job;          /* quit if failed */
9553: /* --- return arrow (or NULL) to caller --- */
9554: end_of_job:
9555:   return ( arrowsp );
9556: } /* --- end-of-function rastarrow() --- */
9557: 
9558: 
9559: /* ==========================================================================
9560:  * Function:    rastuparrow ( expression, size, basesp,  drctn, isBig, arg3 )
9561:  * Purpose:     returns an up/down arrow subraster (e.g., for \longuparrow)
9562:  * --------------------------------------------------------------------------
9563:  * Arguments:   expression (I)  char **  to first char of null-terminated
9564:  *                              LaTeX expression (unused/unchanged)
9565:  *              size (I)        int containing base font size (not used,
9566:  *                              just stored in subraster)
9567:  *              basesp (I)      subraster *  to character (or subexpression)
9568:  *                              immediately preceding space, whose baseline
9569:  *                              and height params are transferred to space
9570:  *              drctn (I)       int containing +1 for up, -1 for down,
9571:  *                              or 0 for updown
9572:  *              isBig (I)       int containing 0 for ---> or 1 for ===>
9573:  *              arg3 (I)        int unused
9574:  * --------------------------------------------------------------------------
9575:  * Returns:     ( subraster * ) ptr to up/down arrow subraster
9576:  *                              or NULL for any error
9577:  * --------------------------------------------------------------------------
9578:  * Notes:     o An optional argument [height] may *immediately* follow
9579:  *              the \longxxx to explicitly set the arrow's height in pixels.
9580:  *              For example, \longuparrow calculates a default height
9581:  *              (as usual in LaTeX), whereas \longuparrow[25] explicitly
9582:  *              draws a 25-pixel high arrow.  This can be used, e.g.,
9583:  *              to draw commutative diagrams in conjunction with
9584:  *              \array (and maybe with \stackrel and/or \relstack, too).
9585:  *            o In case you really want to render, say, [f]---->[g], just
9586:  *              use an intervening space, i.e., [f]\longuparrow~[g].
9587:  *              In text use two spaces {\rm~[f]\longuparrow~~[g]}.
9588:  * ======================================================================= */
9589: /* --- entry point --- */
9590: FUNCSCOPE subraster *rastuparrow ( char **expression, int size,
9591:                           subraster *basesp, int drctn, int isBig, int arg3 )
9592: {
9593: /* -------------------------------------------------------------------------
9594: Allocations and Declarations
9595: -------------------------------------------------------------------------- */
9596: subraster /**uparrow_subraster(),*/ *arrowsp=NULL; /* subraster for arrow */
9597: char    /**texsubexpr(),*/ heightarg[256]; /* parse for optional [height] */
9598: char    /**texscripts(),*/ sub[1024],super[1024]; /*and _^limits after[width]*/
9599: subraster /**rasterize(),*/ *subsp=NULL,*supsp=NULL; /*rasterize limits*/
9600: /*subraster *rastcat();*/               /* cat superscript left, sub right */
9601: /*int   evalterm();*/                   /* evaluate [arg], {arg} */
9602: int     height = 8 + 2*size,  width;    /* height, width for \longxxxarrow */
9603: int     maxheight = 800;                /* max arrow height in pixels */
9604: int     islimits = 1;                   /*true to handle limits internally*/
9605: int     limsize = size-1;               /* font size for limits */
9606: int     pixsz = 1;                      /*default #bits per pixel, 1=bitmap*/
9607: /* -------------------------------------------------------------------------
9608: construct blank subraster, and return it to caller
9609: -------------------------------------------------------------------------- */
9610: /* --- check for optional height arg and replace default height --- */
9611: if ( *(*expression) == '[' )            /*check for []-enclosed optional arg*/
9612:   { int heightval;                      /* test height before using it */
9613:     *expression = texsubexpr(*expression,heightarg,255,"[","]",0,0);
9614:     heightval =                         /* convert [height] to integer */
9615:         (int)((unitlength*((double)evalterm(mimestore,heightarg)))+0.5);
9616:     if ( heightval>=2 && heightval<=maxheight ) /* sanity check */
9617:       height = heightval; }             /* replace deafault height */
9618: /* --- now parse for limits, and bump expression past it(them) --- */
9619: if ( islimits )                         /* handling limits internally */
9620:   { *expression = texscripts(*expression,sub,super,3); /* parse for limits */
9621:     if ( *sub != '\000' )               /*have a subscript following arrow*/
9622:       subsp = rasterize(sub,limsize);   /* so try to rasterize subscript */
9623:     if ( *super != '\000' )             /*have superscript following arrow*/
9624:       supsp = rasterize(super,limsize); } /*so try to rasterize superscript*/
9625: /* --- set width based on height --- */
9626: width = min2(17,max2(9,(height+2)/4));  /* width based on height */
9627: width = 1 + (width/2)*2;                /* always force odd width */
9628: /* --- generate arrow subraster --- */
9629: if ( (arrowsp=uparrow_subraster(width,height,pixsz,drctn,isBig)) /*build arr*/
9630: ==   NULL ) goto end_of_job;            /* and quit if we failed */
9631: /* --- init arrow subraster parameters --- */
9632: arrowsp->size = size;                   /*propagate base font size forward*/
9633: arrowsp->baseline = height-1;           /* set baseline at bottom of arrow */
9634: /* --- add limits above/below arrow, as necessary --- */
9635: if ( supsp != NULL )                    /* cat superscript to left of arrow*/
9636:   { int supht = (supsp->image)->height, /* superscript height */
9637:         deltab = (1+abs(height-supht))/2; /* baseline difference to center */
9638:   supsp->baseline = supht-1;            /* force script baseline to bottom */
9639:   if ( supht <= height )                /* arrow usually taller than script*/
9640:         arrowsp->baseline -= deltab;    /* so bottom of script goes here */
9641:   else  supsp->baseline -= deltab;      /* else bottom of arrow goes here */
9642:   if ( (arrowsp = rastcat(supsp,arrowsp,3)) /* superscript left of arrow */
9643:     ==   NULL ) goto end_of_job; }      /* quit if failed */
9644: if ( subsp != NULL )                    /* cat subscript to right of arrow */
9645:   { int subht = (subsp->image)->height, /* subscript height */
9646:         deltab = (1+abs(height-subht))/2; /* baseline difference to center */
9647:   arrowsp->baseline = height-1;         /* reset arrow baseline to bottom */
9648:   subsp->baseline = subht-1;            /* force script baseline to bottom */
9649:   if ( subht <= height )                /* arrow usually taller than script*/
9650:         arrowsp->baseline -= deltab;    /* so bottom of script goes here */
9651:   else  subsp->baseline -= deltab;      /* else bottom of arrow goes here */
9652:   if ( (arrowsp = rastcat(arrowsp,subsp,3)) /* subscript right of arrow */
9653:     ==   NULL ) goto end_of_job; }      /* quit if failed */
9654: /* --- return arrow (or NULL) to caller --- */
9655: end_of_job:
9656:   arrowsp->baseline = height-1;         /* reset arrow baseline to bottom */
9657:   return ( arrowsp );
9658: } /* --- end-of-function rastuparrow() --- */
9659: 
9660: 
9661: /* ==========================================================================
9662:  * Function:    rastoverlay (expression, size, basesp, overlay, offset2, arg3)
9663:  * Purpose:     overlays one raster on another
9664:  * --------------------------------------------------------------------------
9665:  * Arguments:   expression (I/O) char **  to first char of null-terminated
9666:  *                              string immediately following overlay \cmd to
9667:  *                              be rasterized, and returning ptr immediately
9668:  *                              following last character processed.
9669:  *              size (I)        int containing 0-7 default font size
9670:  *              basesp (I)      subraster *  to character (or subexpression)
9671:  *                              immediately preceding overlay \cmd
9672:  *                              (unused, but passed for consistency)
9673:  *              overlay (I)     int containing 1 to overlay / (e.g., \not)
9674:  *                              or NOVALUE to pick up 2nd arg from expression
9675:  *              offset2 (I)     int containing #pixels to horizontally offset
9676:  *                              overlay relative to underlying symbol,
9677:  *                              positive(right) or negative or 0,
9678:  *                              or NOVALUE to pick up optional [offset] arg
9679:  *              arg3 (I)        int unused
9680:  * --------------------------------------------------------------------------
9681:  * Returns:     ( subraster * ) ptr to subraster corresponding to composite,
9682:  *                              or NULL for any parsing error
9683:  * --------------------------------------------------------------------------
9684:  * Notes:     o
9685:  * ======================================================================= */
9686: /* --- entry point --- */
9687: FUNCSCOPE subraster *rastoverlay ( char **expression, int size,
9688:                       subraster *basesp, int overlay, int offset2, int arg3 )
9689: {
9690: /* -------------------------------------------------------------------------
9691: Allocations and Declarations
9692: -------------------------------------------------------------------------- */
9693: char    /**texsubexpr(),*/              /*parse expression for base,overlay*/
9694:         expr1[512], expr2[512];         /* base, overlay */
9695: subraster /**rasterize(),*/ *sp1=NULL, *sp2=NULL /*rasterize 1=base,2=overlay*/
9696:         /*,*new_subraster()*/;          /*explicitly alloc sp2 if necessary*/
9697: subraster /**rastcompose(),*/ *overlaysp=NULL;/*subrast for composite overlay*/
9698: int     isalign = 0;                    /* true to align baselines */
9699: /*int   line_raster();*/                /* draw diagonal for \Not */
9700: /*int   evalterm();*/                   /* evaluate [arg], {arg} */
9701: /* -------------------------------------------------------------------------
9702: Obtain base, and maybe overlay, and rasterize them
9703: -------------------------------------------------------------------------- */
9704: /* --- check for optional offset2 arg  --- */
9705: if ( offset2 == NOVALUE )               /* only if not explicitly specified*/
9706:  if ( *(*expression) == '[' )           /*check for []-enclosed optional arg*/
9707:   { int offsetval;                      /* test before using it */
9708:     *expression = texsubexpr(*expression,expr2,511,"[","]",0,0);
9709:     offsetval =                         /* convert [offset2] to int */
9710:         (int)(((double)evalterm(mimestore,expr2))+0.5);
9711:     if ( abs(offsetval) <= 25 )         /* sanity check */
9712:       offset2 = offsetval; }            /* replace deafault */
9713: if ( offset2 == NOVALUE ) offset2 = 0;  /* novalue means no offset */
9714: /* --- parse for base, bump expression past it, and rasterize it --- */
9715: *expression = texsubexpr(*expression,expr1,511,"{","}",0,0);
9716: if ( isempty(expr1) ) goto end_of_job;  /* nothing to overlay, so quit */
9717: rastlift1 = rastlift = 0;               /* reset all raisebox() lifts */
9718: if ( strstr(expr1,"\\raise") != NULL )  /* have a \raisebox */
9719:   isalign = 2;                          /* so align baselines */
9720: if ( (sp1=rasterize(expr1,size))        /* rasterize base expression */
9721: ==   NULL ) goto end_of_job;            /* quit if failed to rasterize */
9722: overlaysp = sp1;                        /*in case we return with no overlay*/
9723: rastlift1 = rastlift;                   /* save lift for base expression */
9724: /* --- get overlay expression, and rasterize it --- */
9725: if ( overlay == NOVALUE )               /* get overlay from input stream */
9726:   { *expression = texsubexpr(*expression,expr2,511,"{","}",0,0);
9727:     if ( !isempty(expr2) ) {            /* have an overlay */
9728:       if ( strstr(expr2,"\\raise") != NULL ) /* have a \raisebox */
9729:         isalign = 2;                    /* so align baselines */
9730:       sp2 = rasterize(expr2,size); } }  /* rasterize overlay expression */
9731: else                                    /* use specific built-in overlay */
9732:   switch ( overlay )
9733:     {
9734:     default: break;
9735:     case 1:                             /* e.g., \not overlays slash */
9736:       sp2 = rasterize("/",size+1);      /* rasterize overlay expression */
9737:       isalign = 0;                      /* automatically handled correctly */
9738:       offset2 = max2(1,size-3);         /* push / right a bit */
9739:       offset2 = 0;
9740:       break;
9741:     case 2:                             /* e.g., \Not draws diagonal */
9742:       sp2 = NULL;                       /* no overlay required */
9743:       isalign = 0;                      /* automatically handled correctly */
9744:       if ( overlaysp != NULL )          /* check that we have raster */
9745:         { raster *rp = overlaysp->image; /* raster to be \Not-ed */
9746:           int width=rp->width, height=rp->height; /* raster dimensions */
9747:           if ( 0 )                      /* diagonal within bounding box */
9748:            line_raster(rp,0,width-1,height-1,0,1); /* just draw diagonal */
9749:           else                          /* construct "wide" diagonal */
9750:            { int margin=3;              /* desired extra margin width */
9751:              sp2 = new_subraster(width+margin,height+margin,1); /*alloc it*/
9752:              if ( sp2 != NULL )         /* allocated successfully */
9753:               line_raster(sp2->image,0,width+margin-1,height+margin-1,0,1);}}
9754:       break;
9755:     case 3:                             /* e.g., \sout for strikeout */
9756:       sp2 = NULL;                       /* no overlay required */
9757:       if ( overlaysp != NULL )          /* check that we have raster */
9758:         { raster *rp = overlaysp->image; /* raster to be \sout-ed */
9759:           int width=rp->width, height=rp->height; /* raster dimensions */
9760:           int baseline = (overlaysp->baseline)-rastlift; /*skip descenders*/
9761:           int midrow = max2(0,min2(height-1,offset2+((baseline+1)/2)));
9762:           if ( 1 )                      /* strikeout within bounding box */
9763:             line_raster(rp,midrow,0,midrow,width-1,1); } /*draw strikeout*/
9764:       break;
9765:     } /* --- end-of-switch(overlay) --- */
9766: if ( sp2 == NULL ) goto end_of_job;     /*return sp1 if failed to rasterize*/
9767: /* -------------------------------------------------------------------------
9768: construct composite overlay
9769: -------------------------------------------------------------------------- */
9770: overlaysp = rastcompose(sp1,sp2,offset2,isalign,3);
9771: end_of_job:
9772:   return ( overlaysp );
9773: } /* --- end-of-function rastoverlay() --- */
9774: 
9775: 
9776: /* ==========================================================================
9777:  * Function:    rastfrac ( expression, size, basesp,  isfrac, arg2, arg3 )
9778:  * Purpose:     \frac,\atop handler, returns a subraster corresponding to
9779:  *              expression (immediately following \frac,\atop) at font size
9780:  * --------------------------------------------------------------------------
9781:  * Arguments:   expression (I/O) char **  to first char of null-terminated
9782:  *                              string immediately following \frac to be
9783:  *                              rasterized, and returning ptr immediately
9784:  *                              following last character processed.
9785:  *              size (I)        int containing 0-7 default font size
9786:  *              basesp (I)      subraster *  to character (or subexpression)
9787:  *                              immediately preceding \frac
9788:  *                              (unused, but passed for consistency)
9789:  *              isfrac (I)      int containing true to draw horizontal line
9790:  *                              between numerator and denominator,
9791:  *                              or false not to draw it (for \atop).
9792:  *              arg2 (I)        int unused
9793:  *              arg3 (I)        int unused
9794:  * --------------------------------------------------------------------------
9795:  * Returns:     ( subraster * ) ptr to subraster corresponding to fraction,
9796:  *                              or NULL for any parsing error
9797:  * --------------------------------------------------------------------------
9798:  * Notes:     o
9799:  * ======================================================================= */
9800: /* --- entry point --- */
9801: FUNCSCOPE subraster *rastfrac(char **expression, int size, subraster *basesp,
9802:                               int isfrac, int arg2, int arg3)
9803: {
9804: /* -------------------------------------------------------------------------
9805: Allocations and Declarations
9806: -------------------------------------------------------------------------- */
9807: char    /**texsubexpr(),*/              /*parse expression for numer,denom*/
9808:         numer[MAXSUBXSZ+1], denom[MAXSUBXSZ+1]; /* parsed numer, denom */
9809: subraster /**rasterize(),*/ *numsp=NULL, *densp=NULL;/*rasterize numer,denom*/
9810: subraster /**rastack(),*/ *fracsp=NULL; /* subraster for numer/denom */
9811: /*subraster *new_subraster(), *spacesp=NULL;*/ /* space for num or den */
9812: int     width=0,                        /* width of constructed raster */
9813:         numheight=0;                    /* height of numerator */
9814: int     baseht=0, baseln=0;             /* height,baseline of base symbol */
9815: /*int   istweak = 1;*/                  /*true to tweak baseline alignment*/
9816: int     /*rule_raster(),*/              /* draw horizontal line for frac */
9817:         lineheight = (isfrac?1:0);      /* thickness of fraction line */
9818: int     vspace = (size>2?2:1);          /*vertical space between components*/
9819: /*int   delete_subraster();*/           /*free work areas in case of error*/
9820: /*int   type_raster();*/                /* display debugging output */
9821: /* -------------------------------------------------------------------------
9822: Obtain numerator and denominator, and rasterize them
9823: -------------------------------------------------------------------------- */
9824: /* --- first parse out optional \frac[] argument(s) --- */
9825: if ( *(*expression) == '[' ) {          /* check for []-enclosed arg(s) */
9826:   char optarg[512];                     /* buffer for optional \frac[arg] */
9827:   *expression = texsubexpr(*expression,optarg,511,"[","]",0,0);
9828:   if ( !isempty(optarg) ) {             /* got optional arg */
9829:     char *semi = strchr(optarg,';');    /* optionally followed by ; */
9830:     if ( semi != NULL ) {               /* found further optional ; */
9831:       char *comma = strchr(semi+1,','); /* further optionally followed by , */
9832:       *semi = '\000';                   /* null-terminate optarg at ; */
9833:       if ( comma != NULL ) *comma = '\000'; /* and that arg at (first) , */
9834:       /* nextarg=atoi(semi+1)); */      /* convert argument after ; */
9835:       isfrac = atoi(semi+1);            /* kludge for \frac[;0] = \atop */
9836:       lineheight = (isfrac?1:0);        /* and reset lineheight */
9837:       if ( comma != NULL ) {            /* have , and arg */
9838:         /* nextarg=atoi(comma+1); */    /* convert argument after , */
9839:         } /* --- end-of-if(comma!=NULL) --- */
9840:       } /* --- end-of-if(semi!=NULL) --- */
9841:     trimwhite(optarg);                  /* remove leading/trailing whitespace*/
9842:     if ( !isempty(optarg) )             /* wasn't just ";other,args" */
9843:       vspace = atoi(optarg);            /* convert optarg to vspace int */
9844:     if ( vspace<0 || vspace>999 )       /* sanity check (revert to default) */
9845:       vspace = (size>2?2:1);            /* back to original default */
9846:     } /* --- end-of-if(!isempty(optarg)) --- */
9847:   } /* --- end-of-if(**expression=='[') --- */
9848: /* ---then parse for numerator,denominator and bump expression past them--- */
9849: *expression = texsubexpr(*expression,numer,0,"{","}",0,0);
9850: *expression = texsubexpr(*expression,denom,0,"{","}",0,0);
9851: if ( *numer=='\000' && *denom=='\000' ) /* missing both components of frac */
9852:   goto end_of_job;                      /* nothing to do, so quit */
9853: /* --- rasterize numerator, denominator --- */
9854: if ( *numer != '\000' )                 /* have a numerator */
9855:  if ( (numsp = rasterize(numer,size-1)) /* so rasterize numer at size-1 */
9856:  ==   NULL ) goto end_of_job;           /* and quit if failed */
9857: if ( *denom != '\000' )                 /* have a denominator */
9858:  if ( (densp = rasterize(denom,size-1)) /* so rasterize denom at size-1 */
9859:  ==   NULL )                            /* failed */
9860:   { if ( numsp != NULL )                /* already rasterized numerator */
9861:       delete_subraster(numsp);          /* so free now-unneeded numerator */
9862:     goto end_of_job; }                  /* and quit */
9863: /* --- if one component missing, use a blank space for it --- */
9864: if ( numsp == NULL )                    /* no numerator given */
9865:   numsp = rasterize("[?]",size-1);      /* missing numerator */
9866: if ( densp == NULL )                    /* no denominator given */
9867:   densp = rasterize("[?]",size-1);      /* missing denominator */
9868: /* --- check that we got both components --- */
9869: if ( numsp==NULL || densp==NULL )       /* some problem */
9870:   { delete_subraster(numsp);            /*delete numerator (if it existed)*/
9871:     delete_subraster(densp);            /*delete denominator (if it existed)*/
9872:     goto end_of_job; }                  /* and quit */
9873: /* --- get height of numerator (to determine where line belongs) --- */
9874: numheight = (numsp->image)->height;     /* get numerator's height */
9875: /* -------------------------------------------------------------------------
9876: construct raster with numerator stacked over denominator
9877: -------------------------------------------------------------------------- */
9878: /* --- construct raster with numer/denom --- */
9879: if ( (fracsp = rastack(densp,numsp,0,(2*vspace)+lineheight,1,3))/*numer/denom*/
9880: ==  NULL )                              /* failed to construct numer/denom */
9881:   { delete_subraster(numsp);            /* so free now-unneeded numerator */
9882:     delete_subraster(densp);            /* and now-unneeded denominator */
9883:     goto end_of_job; }                  /* and then quit */
9884: /* --- determine width of constructed raster --- */
9885: width = (fracsp->image)->width;         /*just get width of embedded image*/
9886: /* --- initialize subraster parameters --- */
9887: fracsp->size = size;                    /* propagate font size forward */
9888: fracsp->baseline = (numheight+vspace+lineheight)+(size+2);/*default baseline*/
9889: fracsp->type = FRACRASTER;              /* signal \frac image */
9890: if ( basesp != (subraster *)NULL )      /* we have base symbol for frac */
9891:   { baseht = (basesp->image)->height;   /* height of base symbol */
9892:     baseln =  basesp->baseline;         /* and its baseline */
9893:   } /* --- end-of-if(basesp!=NULL) --- */
9894: /* -------------------------------------------------------------------------
9895: draw horizontal line between numerator and denominator
9896: -------------------------------------------------------------------------- */
9897: fraccenterline = numheight+vspace;      /* signal that we have a \frac */
9898: if ( isfrac )                           /*line for \frac, but not for \atop*/
9899:   rule_raster(fracsp->image,fraccenterline,0,width,lineheight,0);
9900: /* -------------------------------------------------------------------------
9901: return final result to caller
9902: -------------------------------------------------------------------------- */
9903: end_of_job:
9904:   if ( msgfp!=NULL && msglevel>=99 )
9905:     { fprintf(msgfp,"rastfrac> returning %s\n",(fracsp==NULL?"null":"..."));
9906:       if ( fracsp != NULL )             /* have a constructed raster */
9907:         type_raster(fracsp->image,msgfp); } /* display constructed raster */
9908:   return ( fracsp );
9909: } /* --- end-of-function rastfrac() --- */
9910: 
9911: 
9912: /* ==========================================================================
9913:  * Function:    rastackrel ( expression, size, basesp,  base, arg2, arg3 )
9914:  * Purpose:     \stackrel handler, returns a subraster corresponding to
9915:  *              stacked relation
9916:  * --------------------------------------------------------------------------
9917:  * Arguments:   expression (I/O) char **  to first char of null-terminated
9918:  *                              string immediately following \stackrel to be
9919:  *                              rasterized, and returning ptr immediately
9920:  *                              following last character processed.
9921:  *              size (I)        int containing 0-7 default font size
9922:  *              basesp (I)      subraster *  to character (or subexpression)
9923:  *                              immediately preceding \stackrel
9924:  *                              (unused, but passed for consistency)
9925:  *              base (I)        int containing 1 if upper/first subexpression
9926:  *                              is base relation, or 2 if lower/second is
9927:  *              arg2 (I)        int unused
9928:  *              arg3 (I)        int unused
9929:  * --------------------------------------------------------------------------
9930:  * Returns:     ( subraster * ) ptr to subraster corresponding to stacked
9931:  *                              relation, or NULL for any parsing error
9932:  * --------------------------------------------------------------------------
9933:  * Notes:     o
9934:  * ======================================================================= */
9935: /* --- entry point --- */
9936: FUNCSCOPE subraster *rastackrel ( char **expression, int size,
9937:                             subraster *basesp, int base, int arg2, int arg3 )
9938: {
9939: /* -------------------------------------------------------------------------
9940: Allocations and Declarations
9941: -------------------------------------------------------------------------- */
9942: char    /**texsubexpr(),*/              /*parse expression for upper,lower*/
9943:         upper[MAXSUBXSZ+1], lower[MAXSUBXSZ+1]; /* parsed upper, lower */
9944: subraster /**rasterize(),*/ *upsp=NULL, *lowsp=NULL; /*rasterize upper,lower*/
9945: subraster /**rastack(),*/ *relsp=NULL;  /* subraster for upper/lower */
9946: int     upsize  = (base==1? size:size-1), /* font size for upper component */
9947:         lowsize = (base==2? size:size-1); /* font size for lower component */
9948: int     vspace = 1;                     /*vertical space between components*/
9949: /*int   delete_subraster();*/           /*free work areas in case of error*/
9950: /* -------------------------------------------------------------------------
9951: Obtain numerator and denominator, and rasterize them
9952: -------------------------------------------------------------------------- */
9953: /* --- parse for numerator,denominator and bump expression past them --- */
9954: *expression = texsubexpr(*expression,upper,0,"{","}",0,0);
9955: *expression = texsubexpr(*expression,lower,0,"{","}",0,0);
9956: if ( *upper=='\000' || *lower=='\000' ) /* missing either component */
9957:   goto end_of_job;                      /* nothing to do, so quit */
9958: /* --- rasterize upper, lower --- */
9959: if ( *upper != '\000' )                 /* have upper component */
9960:  if ( (upsp = rasterize(upper,upsize))  /* so rasterize upper component */
9961:  ==   NULL ) goto end_of_job;           /* and quit if failed */
9962: if ( *lower != '\000' )                 /* have lower component */
9963:  if ( (lowsp = rasterize(lower,lowsize)) /* so rasterize lower component */
9964:  ==   NULL )                            /* failed */
9965:   { if ( upsp != NULL )                 /* already rasterized upper */
9966:       delete_subraster(upsp);           /* so free now-unneeded upper */
9967:     goto end_of_job; }                  /* and quit */
9968: /* -------------------------------------------------------------------------
9969: construct stacked relation raster
9970: -------------------------------------------------------------------------- */
9971: /* --- construct stacked relation --- */
9972: if ( (relsp = rastack(lowsp,upsp,3-base,vspace,1,3)) /* stacked relation */
9973: ==   NULL ) goto end_of_job;            /* quit if failed */
9974: /* --- initialize subraster parameters --- */
9975: relsp->size = size;                     /* propagate font size forward */
9976: /* -------------------------------------------------------------------------
9977: return final result to caller
9978: -------------------------------------------------------------------------- */
9979: end_of_job:
9980:   return ( relsp );
9981: } /* --- end-of-function rastackrel() --- */
9982: 
9983: 
9984: /* ==========================================================================
9985:  * Function:    rastmathfunc ( expression, size, basesp,  base, arg2, arg3 )
9986:  * Purpose:     \log, \lim, etc handler, returns a subraster corresponding
9987:  *              to math functions
9988:  * --------------------------------------------------------------------------
9989:  * Arguments:   expression (I/O) char **  to first char of null-terminated
9990:  *                              string immediately following \mathfunc to be
9991:  *                              rasterized, and returning ptr immediately
9992:  *                              following last character processed.
9993:  *              size (I)        int containing 0-7 default font size
9994:  *              basesp (I)      subraster *  to character (or subexpression)
9995:  *                              immediately preceding \mathfunc
9996:  *                              (unused, but passed for consistency)
9997:  *              mathfunc (I)    int containing 1=arccos, 2=arcsin, etc.
9998:  *              islimits (I)    int containing 1 if function may have
9999:  *                              limits underneath, e.g., \lim_{n\to\infty}
10000:  *              arg3 (I)        int unused
10001:  * --------------------------------------------------------------------------
10002:  * Returns:     ( subraster * ) ptr to subraster corresponding to mathfunc,
10003:  *                              or NULL for any parsing error
10004:  * --------------------------------------------------------------------------
10005:  * Notes:     o
10006:  * ======================================================================= */
10007: /* --- entry point --- */
10008: FUNCSCOPE subraster *rastmathfunc ( char **expression, int size,
10009:                     subraster *basesp, int mathfunc, int islimits, int arg3 )
10010: {
10011: /* -------------------------------------------------------------------------
10012: Allocations and Declarations
10013: -------------------------------------------------------------------------- */
10014: char    /**texscripts(),*/              /* parse expression for _limits */
10015:         func[MAXTOKNSZ+1], limits[MAXSUBXSZ+1]; /*func as {\rm func}, limits*/
10016: char    /**texsubexpr(),*/              /* parse expression for arg */
10017:         funcarg[MAXTOKNSZ+1];           /* optional func arg */
10018: subraster /**rasterize(),*/ *funcsp=NULL, *limsp=NULL;/*rasterize func,limits*/
10019: subraster /**rastack(),*/ *mathfuncsp=NULL; /*subraster for mathfunc/limits*/
10020: int     limsize = size-1;               /* font size for limits */
10021: int     vspace = 1;                     /*vertical space between components*/
10022: /*int   delete_subraster();*/           /*free work areas in case of error*/
10023: /* --- table of function names by mathfunc number --- */
10024: static  int  numnames = 34;             /* number of names in table */
10025: static  char *funcnames[] = {
10026:         "error",                        /*  0 index is illegal/error bucket*/
10027:         "arccos",  "arcsin",  "arctan", /*  1 -  3 */
10028:         "arg",     "cos",     "cosh",   /*  4 -  6 */
10029:         "cot",     "coth",    "csc",    /*  7 -  9 */
10030:         "deg",     "det",     "dim",    /* 10 - 12 */
10031:         "exp",     "gcd",     "hom",    /* 13 - 15 */
10032:         "inf",     "ker",     "lg",     /* 16 - 18 */
10033:         "lim",     "liminf",  "limsup", /* 19 - 21 */
10034:         "ln",      "log",     "max",    /* 22 - 24 */
10035:         "min",     "Pr",      "sec",    /* 25 - 27 */
10036:         "sin",     "sinh",    "sup",    /* 28 - 30 */
10037:         "tan",     "tanh",              /* 31 - 32 */
10038:         /* --- extra mimetex funcnames --- */
10039:         "tr",                           /* 33 */
10040:         "pmod"                          /* 34 */
10041:         } ;
10042: /* -------------------------------------------------------------------------
10043: set up and rasterize function name in \rm
10044: -------------------------------------------------------------------------- */
10045: if ( mathfunc<0 || mathfunc>numnames ) mathfunc=0; /* check index bounds */
10046: switch ( mathfunc )                     /* check for special processing */
10047:   {
10048:   default:                              /* no special processing */
10049:     strcpy(func,"{\\rm~");              /* init string with {\rm~ */
10050:     strcat(func,funcnames[mathfunc]);   /* concat function name */
10051:     strcat(func,"}");                   /* and add terminating } */
10052:     break;
10053:   case 34:                              /* \pmod{x} --> (mod x) */
10054:     /* --- parse for \pmod{arg} argument --- */
10055:     *expression = texsubexpr(*expression,funcarg,2047,"{","}",0,0);
10056:     strcpy(func,"{\\({\\rm~mod}");      /* init with {\left({\rm~mod} */
10057:     strcat(func,"\\hspace2");           /* concat space */
10058:     strcat(func,funcarg);               /* and \pmodargument */
10059:     strcat(func,"\\)}");                /* and add terminating \right)} */
10060:     break;
10061:   } /* --- end-of-switch(mathfunc) --- */
10062: if ( (funcsp = rasterize(func,size))    /* rasterize function name */
10063: ==   NULL ) goto end_of_job;            /* and quit if failed */
10064: mathfuncsp = funcsp;                    /* just return funcsp if no limits */
10065: if ( !islimits ) goto end_of_job;       /* treat any subscript normally */
10066: /* -------------------------------------------------------------------------
10067: Obtain limits, if permitted and if provided, and rasterize them
10068: -------------------------------------------------------------------------- */
10069: /* --- parse for subscript limits, and bump expression past it(them) --- */
10070: *expression = texscripts(*expression,limits,limits,1);
10071: if ( *limits=='\000') goto end_of_job;  /* no limits, nothing to do, quit */
10072: /* --- rasterize limits --- */
10073: if ( (limsp = rasterize(limits,limsize)) /* rasterize limits */
10074: ==   NULL ) goto end_of_job;            /* and quit if failed */
10075: /* -------------------------------------------------------------------------
10076: construct func atop limits
10077: -------------------------------------------------------------------------- */
10078: /* --- construct func atop limits --- */
10079: if ( (mathfuncsp = rastack(limsp,funcsp,2,vspace,1,3)) /* func atop limits */
10080: ==   NULL ) goto end_of_job;            /* quit if failed */
10081: /* --- initialize subraster parameters --- */
10082: mathfuncsp->size = size;                /* propagate font size forward */
10083: /* -------------------------------------------------------------------------
10084: return final result to caller
10085: -------------------------------------------------------------------------- */
10086: end_of_job:
10087:   return ( mathfuncsp );
10088: } /* --- end-of-function rastmathfunc() --- */
10089: 
10090: 
10091: /* ==========================================================================
10092:  * Function:    rastsqrt ( expression, size, basesp,  arg1, arg2, arg3 )
10093:  * Purpose:     \sqrt handler, returns a subraster corresponding to
10094:  *              expression (immediately following \sqrt) at font size
10095:  * --------------------------------------------------------------------------
10096:  * Arguments:   expression (I/O) char **  to first char of null-terminated
10097:  *                              string immediately following \sqrt to be
10098:  *                              rasterized, and returning ptr immediately
10099:  *                              following last character processed.
10100:  *              size (I)        int containing 0-7 default font size
10101:  *              basesp (I)      subraster *  to character (or subexpression)
10102:  *                              immediately preceding \accent
10103:  *                              (unused, but passed for consistency)
10104:  *              arg1 (I)        int unused
10105:  *              arg2 (I)        int unused
10106:  *              arg3 (I)        int unused
10107:  * --------------------------------------------------------------------------
10108:  * Returns:     ( subraster * ) ptr to subraster corresponding to expression,
10109:  *                              or NULL for any parsing error
10110:  *                              (expression ptr unchanged if error occurs)
10111:  * --------------------------------------------------------------------------
10112:  * Notes:     o
10113:  * ======================================================================= */
10114: /* --- entry point --- */
10115: FUNCSCOPE subraster *rastsqrt(char **expression, int size, subraster *basesp,
10116:                               int arg1, int arg2, int arg3)
10117: {
10118: /* -------------------------------------------------------------------------
10119: Allocations and Declarations
10120: -------------------------------------------------------------------------- */
10121: char    /**texsubexpr(),*/ subexpr[MAXSUBXSZ+1],/*parse subexpr to be sqrt-ed*/
10122:         rootarg[MAXSUBXSZ+1];           /* optional \sqrt[rootarg]{...} */
10123: subraster /**rasterize(),*/ *subsp=NULL; /* rasterize subexpr */
10124: subraster /**accent_subraster(),*/ *sqrtsp=NULL, /* subraster with the sqrt */
10125:         /**new_subraster(),*/ *rootsp=NULL;/*optionally preceded by [rootarg]*/
10126: int     sqrtheight=0, sqrtwidth=0, surdwidth=0, /* height,width of sqrt */
10127:         rootheight=0, rootwidth=0,      /* height,width of rootarg raster */
10128:         subheight=0, subwidth=0, pixsz=0; /* height,width,pixsz of subexpr */
10129: /*int   rastput();*/                    /* put subexpr in constructed sqrt */
10130: int     overspace = 2;                  /*space between subexpr and overbar*/
10131: /*int   delete_subraster();*/           /* free work areas */
10132: /* -------------------------------------------------------------------------
10133: Obtain subexpression to be sqrt-ed, and rasterize it
10134: -------------------------------------------------------------------------- */
10135: /* --- first check for optional \sqrt[rootarg]{...} --- */
10136: if ( *(*expression) == '[' )            /*check for []-enclosed optional arg*/
10137:   { *expression = texsubexpr(*expression,rootarg,0,"[","]",0,0);
10138:     if ( *rootarg != '\000' )           /* got rootarg */
10139:      if ( (rootsp=rasterize(rootarg,size-1)) /*rasterize it at smaller size*/
10140:      != NULL )                          /* rasterized successfully */
10141:       { rootheight = (rootsp->image)->height;  /* get height of rootarg */
10142:         rootwidth  = (rootsp->image)->width; } /* and its width */
10143:   } /* --- end-of-if(**expression=='[') --- */
10144: /* --- parse for subexpr to be sqrt-ed, and bump expression past it --- */
10145: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
10146: if ( *subexpr == '\000' )               /* couldn't get subexpression */
10147:   goto end_of_job;                      /* nothing to do, so quit */
10148: /* --- rasterize subexpression to be accented --- */
10149: if ( (subsp = rasterize(subexpr,size))  /*rasterize subexpr at original size*/
10150: ==   NULL ) goto end_of_job;            /* quit if failed */
10151: /* -------------------------------------------------------------------------
10152: determine height and width of sqrt raster to be constructed
10153: -------------------------------------------------------------------------- */
10154: /* --- first get height and width of subexpr --- */
10155: subheight = (subsp->image)->height;     /* height of subexpr */
10156: subwidth  = (subsp->image)->width;      /* and its width */
10157: pixsz     = (subsp->image)->pixsz;      /* pixsz remains constant */
10158: /* --- determine height and width of sqrt to contain subexpr --- */
10159: sqrtheight = subheight + overspace;     /* subexpr + blank line + overbar */
10160: surdwidth  = SQRTWIDTH(sqrtheight,(rootheight<1?2:1)); /* width of surd */
10161: sqrtwidth  = subwidth + surdwidth + 1;  /* total width */
10162: /* -------------------------------------------------------------------------
10163: construct sqrt (with room to move in subexpr) and embed subexpr in it
10164: -------------------------------------------------------------------------- */
10165: /* --- construct sqrt --- */
10166: if ( (sqrtsp=accent_subraster(SQRTACCENT,
10167: (rootheight<1?sqrtwidth:(-sqrtwidth)),sqrtheight,0,pixsz))
10168: ==   NULL ) goto end_of_job;            /* quit if failed to build sqrt */
10169: /* --- embed subexpr in sqrt at lower-right corner--- */
10170: rastput(sqrtsp->image,subsp->image,overspace,sqrtwidth-subwidth,1);
10171: sqrtsp->baseline = subsp->baseline + overspace; /* adjust baseline */
10172: /* --- "embed" rootarg at upper-left --- */
10173: if ( rootsp != NULL )                   /*have optional \sqrt[rootarg]{...}*/
10174:   {
10175:   /* --- allocate full raster to contain sqrtsp and rootsp --- */
10176:   int fullwidth = sqrtwidth +rootwidth - min2(rootwidth,max2(0,surdwidth-4)),
10177:       fullheight= sqrtheight+rootheight- min2(rootheight,3+size);
10178:   subraster *fullsp = new_subraster(fullwidth,fullheight,pixsz);
10179:   if ( fullsp != NULL )                 /* allocated successfully */
10180:     { /* --- embed sqrtsp exactly at lower-right corner --- */
10181:       rastput(fullsp->image,sqrtsp->image, /* exactly at lower-right corner*/
10182:         fullheight-sqrtheight,fullwidth-sqrtwidth,1);
10183:       /* --- embed rootsp near upper-left, nestled above leading surd --- */
10184:       rastput(fullsp->image,rootsp->image,
10185:         0,max2(0,surdwidth-rootwidth-2-size),0);
10186:       /* --- replace sqrtsp with fullsp --- */
10187:       delete_subraster(sqrtsp);         /* free original sqrtsp */
10188:       sqrtsp = fullsp;                  /* and repoint it to fullsp instead*/
10189:       sqrtsp->baseline = fullheight - (subheight - subsp->baseline); }
10190:   } /* --- end-of-if(rootsp!=NULL) --- */
10191: /* --- initialize subraster parameters --- */
10192: sqrtsp->size = size;                    /* propagate font size forward */
10193: /* -------------------------------------------------------------------------
10194: free unneeded component subrasters and return final result to caller
10195: -------------------------------------------------------------------------- */
10196: end_of_job:
10197:   if ( subsp != NULL ) delete_subraster(subsp); /* free unneeded subexpr */
10198:   return ( sqrtsp );
10199: } /* --- end-of-function rastsqrt() --- */
10200: 
10201: 
10202: /* ==========================================================================
10203:  * Function:    rastaccent (expression,size,basesp,accent,isabove,isscript)
10204:  * Purpose:     \hat, \vec, \etc handler, returns a subraster corresponding
10205:  *              to expression (immediately following \accent) at font size
10206:  * --------------------------------------------------------------------------
10207:  * Arguments:   expression (I/O) char **  to first char of null-terminated
10208:  *                              string immediately following \accent to be
10209:  *                              rasterized, and returning ptr immediately
10210:  *                              following last character processed.
10211:  *              size (I)        int containing 0-7 default font size
10212:  *              basesp (I)      subraster *  to character (or subexpression)
10213:  *                              immediately preceding \accent
10214:  *                              (unused, but passed for consistency)
10215:  *              accent (I)      int containing HATACCENT or VECACCENT, etc,
10216:  *                              between numerator and denominator,
10217:  *                              or false not to draw it (for \over).
10218:  *              isabove (I)     int containing true if accent is above
10219:  *                              expression to be accented, or false
10220:  *                              if accent is below (e.g., underbrace)
10221:  *              isscript (I)    int containing true if sub/superscripts
10222:  *                              allowed (for under/overbrace), or 0 if not.
10223:  * --------------------------------------------------------------------------
10224:  * Returns:     ( subraster * ) ptr to subraster corresponding to expression,
10225:  *                              or NULL for any parsing error
10226:  *                              (expression ptr unchanged if error occurs)
10227:  * --------------------------------------------------------------------------
10228:  * Notes:     o Also handles \overbrace{}^{} and \underbrace{}_{} by way
10229:  *              of isabove and isscript args.
10230:  * ======================================================================= */
10231: /* --- entry point --- */
10232: FUNCSCOPE subraster *rastaccent ( char **expression, int size,
10233:                    subraster *basesp, int accent, int isabove, int isscript )
10234: {
10235: /* -------------------------------------------------------------------------
10236: Allocations and Declarations
10237: -------------------------------------------------------------------------- */
10238: char    /**texsubexpr(),*/ subexpr[MAXSUBXSZ+1];/*parse subexp to be accented*/
10239: char    /**texscripts(),*/ *script=NULL, /* \under,overbrace allow scripts */
10240:         subscript[MAXTOKNSZ+1], supscript[MAXTOKNSZ+1]; /* parsed scripts */
10241: subraster /**rasterize(),*/ *subsp=NULL,*scrsp=NULL;/*rasterize subexp,script*/
10242: subraster /**rastack(),*/ *accsubsp=NULL; /* stack accent, subexpr, script */
10243: subraster /**accent_subraster(),*/ *accsp=NULL;/*raster for the accent itself*/
10244: int     accheight=0, accwidth=0, accdir=0,/*accent height, width, direction*/
10245:         subheight=0, subwidth=0, pixsz=0; /* height,width,pixsz of subexpr */
10246: /*int   delete_subraster();*/           /*free work areas in case of error*/
10247: int     vspace = 0;                     /*vertical space between accent,sub*/
10248: /* -------------------------------------------------------------------------
10249: Obtain subexpression to be accented, and rasterize it
10250: -------------------------------------------------------------------------- */
10251: /* --- parse for subexpr to be accented, and bump expression past it --- */
10252: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
10253: if ( *subexpr=='\000' )                 /* couldn't get subexpression */
10254:   goto end_of_job;                      /* nothing to do, so quit */
10255: /* --- rasterize subexpression to be accented --- */
10256: if ( (subsp = rasterize(subexpr,size))  /*rasterize subexpr at original size*/
10257: ==   NULL ) goto end_of_job;            /* quit if failed */
10258: /* -------------------------------------------------------------------------
10259: determine desired accent width and height
10260: -------------------------------------------------------------------------- */
10261: /* --- first get height and width of subexpr --- */
10262: subheight = (subsp->image)->height;     /* height of subexpr */
10263: subwidth  = (subsp->image)->width;      /* and its width is overall width */
10264: pixsz     = (subsp->image)->pixsz;      /* original pixsz remains constant */
10265: /* --- determine desired width, height of accent --- */
10266: accwidth = subwidth;                    /* same width as subexpr */
10267: accheight = 4;                          /* default for bars */
10268: switch ( accent )
10269:   { default: break;                     /* default okay */
10270:   case DOTACCENT: case DDOTACCENT:
10271:     accheight = (size<4? 3:4);          /* default for dots */
10272:     break;
10273:   case VECACCENT:
10274:     vspace = 1;                         /* set 1-pixel vertical space */
10275:     accdir = isscript;                  /* +1=right,-1=left,0=lr; +10for==>*/
10276:     isscript = 0;                       /* >>don't<< signal sub/supscript */
10277:   case HATACCENT:
10278:     accheight = 7;                      /* default */
10279:     if ( subwidth < 10 ) accheight = 5; /* unless small width */
10280:       else if ( subwidth > 25 ) accheight = 9; /* or large */
10281:     break;
10282:   } /* --- end-of-switch(accent) --- */
10283: accheight = min2(accheight,subheight);  /*never higher than accented subexpr*/
10284: /* -------------------------------------------------------------------------
10285: construct accent, and construct subraster with accent over (or under) subexpr
10286: -------------------------------------------------------------------------- */
10287: /* --- first construct accent --- */
10288: if ( (accsp = accent_subraster(accent,accwidth,accheight,accdir,pixsz))
10289: ==   NULL ) goto end_of_job;            /* quit if failed to build accent */
10290: /* --- now stack accent above (or below) subexpr, and free both args --- */
10291: accsubsp = (isabove? rastack(subsp,accsp,1,vspace,1,3)/*accent above subexpr*/
10292:            : rastack(accsp,subsp,2,vspace,1,3));      /*accent below subexpr*/
10293: if ( accsubsp == NULL )                 /* failed to stack accent */
10294:   { delete_subraster(subsp);            /* free unneeded subsp */
10295:     delete_subraster(accsp);            /* and unneeded accsp */
10296:     goto end_of_job; }                  /* and quit */
10297: /* -------------------------------------------------------------------------
10298: look for super/subscript (annotation for over/underbrace)
10299: -------------------------------------------------------------------------- */
10300: /* --- first check whether accent permits accompanying annotations --- */
10301: if ( !isscript ) goto end_of_job;       /* no annotations for this accent */
10302: /* --- now get scripts if there actually are any --- */
10303: *expression = texscripts(*expression,subscript,supscript,(isabove?2:1));
10304: script = (isabove? supscript : subscript); /*select above^ or below_ script*/
10305: if ( *script == '\000' ) goto end_of_job; /* no accompanying script */
10306: /* --- rasterize script annotation at size-2 --- */
10307: if ( (scrsp = rasterize(script,size-2)) /* rasterize script at size-2 */
10308: ==   NULL ) goto end_of_job;            /* quit if failed */
10309: /* --- stack annotation above (or below) accent, and free both args --- */
10310: accsubsp = (isabove? rastack(accsubsp,scrsp,1,0,1,3) /* accent above base */
10311:            : rastack(scrsp,accsubsp,2,0,1,3));       /* accent below base */
10312: /* -------------------------------------------------------------------------
10313: return final result to caller
10314: -------------------------------------------------------------------------- */
10315: end_of_job:
10316:   if ( accsubsp != NULL )               /* initialize subraster parameters */
10317:     accsubsp->size = size;              /* propagate font size forward */
10318:   return ( accsubsp );
10319: } /* --- end-of-function rastaccent() --- */
10320: 
10321: 
10322: /* ==========================================================================
10323:  * Function:    rastfont (expression,size,basesp,ifontnum,arg2,arg3)
10324:  * Purpose:     \cal{}, \scr{}, \etc handler, returns subraster corresponding
10325:  *              to char(s) within {}'s rendered at size
10326:  * --------------------------------------------------------------------------
10327:  * Arguments:   expression (I/O) char **  to first char of null-terminated
10328:  *                              string immediately following \font to be
10329:  *                              rasterized, and returning ptr immediately
10330:  *                              following last character processed.
10331:  *              size (I)        int containing 0-7 default font size
10332:  *              basesp (I)      subraster *  to character (or subexpression)
10333:  *                              immediately preceding \accent
10334:  *                              (unused, but passed for consistency)
10335:  *              ifontnum (I)    int containing 1 for \cal{}, 2 for \scr{}
10336:  *              arg2 (I)        int unused
10337:  *              arg3 (I)        int unused
10338:  * --------------------------------------------------------------------------
10339:  * Returns:     ( subraster * ) ptr to subraster corresponding to chars
10340:  *                              between {}'s, or NULL for any parsing error
10341:  * --------------------------------------------------------------------------
10342:  * Notes:     o
10343:  * ======================================================================= */
10344: /* --- entry point --- */
10345: FUNCSCOPE subraster *rastfont(char **expression, int size, subraster *basesp,
10346:                               int ifontnum, int arg2, int arg3)
10347: {
10348: /* -------------------------------------------------------------------------
10349: Allocations and Declarations
10350: -------------------------------------------------------------------------- */
10351: char    /**texsubexpr(),*/ fontchars[MAXSUBXSZ+1], /*chars to render in font*/
10352:         subexpr[MAXSUBXSZ+1];           /* turn \cal{AB} into \calA\calB */
10353: char    *pfchars=fontchars, fchar='\0'; /* run thru fontchars one at a time*/
10354: char    *name = NULL;                   /* fontinfo[ifontnum].name */
10355: int     family = 0,                     /* fontinfo[ifontnum].family */
10356:         istext = 0,                     /* fontinfo[ifontnum].istext */
10357:         class = 0;                      /* fontinfo[ifontnum].class */
10358: subraster /**rasterize(),*/ *fontsp=NULL /* rasterize chars in font */
10359:         /*,*rastflags()*/;              /* or just set flag to switch font */
10360: int     oldsmashmargin = smashmargin;   /* turn off smash in text mode */
10361: #if 0
10362: /* --- fonts recognized by rastfont --- */
10363: static  int  nfonts = 11;               /* legal font #'s are 1...nfonts */
10364: static  struct {char *name; int class;}
10365:   fonts[] =
10366:     { /* --- name  class 1=upper,2=alpha,3=alnum,4=lower,5=digit,9=all --- */
10367:         { "\\math",     0 },
10368:         { "\\mathcal",  1 },            /*(1) calligraphic, uppercase */
10369:         { "\\mathscr",  1 },            /*(2) rsfs/script, uppercase */
10370:         { "\\textrm",   -1 },           /*(3) \rm,\text{abc} --> {\rm~abc} */
10371:         { "\\textit",   -1 },           /*(4) \it,\textit{abc}-->{\it~abc} */
10372:         { "\\mathbb",   -1 },           /*(5) \bb,\mathbb{abc}-->{\bb~abc} */
10373:         { "\\mathbf",   -1 },           /*(6) \bf,\mathbf{abc}-->{\bf~abc} */
10374:         { "\\mathrm",   -1 },           /*(7) \mathrm */
10375:         { "\\cyr",      -1 },           /*(8) \cyr */
10376:         { "\\textgreek",-1 },           /*(9) \textgreek */
10377:         { "\\textbfgreek",CMMI10BGR,1,-1 },/*(10) \textbfgreek{ab} */
10378:         { "\\textbbgreek",BBOLD10GR,1,-1 },/*(11) \textbbgreek{ab} */
10379:         { NULL,         0 }
10380:     } ; /* --- end-of-fonts[] --- */
10381: #endif
10382: /* -------------------------------------------------------------------------
10383: first get font name and class to determine type of conversion desired
10384: -------------------------------------------------------------------------- */
10385: if (ifontnum<=0 || ifontnum>nfontinfo) ifontnum=0; /*math if out-of-bounds*/
10386: name   = fontinfo[ifontnum].name;       /* font name */
10387: family = fontinfo[ifontnum].family;     /* font family */
10388: istext = fontinfo[ifontnum].istext;     /*true in text mode (respect space)*/
10389: class  = fontinfo[ifontnum].class;      /* font class */
10390: if ( istext )                           /* text (respect blanks) */
10391:  { mathsmashmargin = smashmargin;       /* needed for \text{if $n-m$ even} */
10392:    smashmargin = 0; }                   /* don't smash internal blanks */
10393: /* -------------------------------------------------------------------------
10394: now convert \font{abc} --> {\font~abc}, or convert ABC to \calA\calB\calC
10395: -------------------------------------------------------------------------- */
10396: if ( 1 || class<0 )                     /* not character-by-character */
10397:  {
10398:  /* ---
10399:  if \font not immediately followed by { then it has no arg, so just set flag
10400:  ------------------------------------------------------------------------ */
10401:  if ( *(*expression) != '{' )           /* no \font arg, so just set flag */
10402:     {
10403:     if ( msgfp!=NULL && msglevel>=99 )
10404:      fprintf(msgfp,"rastfont> \\%s rastflags() for font#%d\n",name,ifontnum);
10405:     fontsp = rastflags(expression,size,basesp,ISFONTFAM,ifontnum,arg3);
10406:     goto end_of_job;
10407:     } /* --- end-of-if(*(*expression)!='{') --- */
10408:  /* ---
10409:  convert \font{abc} --> {\font~abc}
10410:  ---------------------------------- */
10411:  /* --- parse for {fontchars} arg, and bump expression past it --- */
10412:  *expression = texsubexpr(*expression,fontchars,0,"{","}",0,0);
10413:  if ( msgfp!=NULL && msglevel>=99 )
10414:   fprintf(msgfp,"rastfont> \\%s fontchars=\"%s\"\n",name,fontchars);
10415:  /* --- convert all fontchars at the same time --- */
10416:  strcpy(subexpr,"{");                   /* start off with opening { */
10417:  strcat(subexpr,name);                  /* followed by font name */
10418:  strcat(subexpr,"~");                   /* followed by whitespace */
10419:  strcat(subexpr,fontchars);             /* followed by all the chars */
10420:  strcat(subexpr,"}");                   /* terminate with closing } */
10421:  } /* --- end-of-if(class<0) --- */
10422: else                                    /* character-by-character */
10423:  {
10424:  /* ---
10425:  convert ABC to \calA\calB\calC
10426:  ------------------------------ */
10427:  int    isprevchar=0;                   /* true if prev char converted */
10428:  /* --- parse for {fontchars} arg, and bump expression past it --- */
10429:  *expression = texsubexpr(*expression,fontchars,0,"{","}",0,0);
10430:  if ( msgfp!=NULL && msglevel>=99 )
10431:   fprintf(msgfp,"rastfont> \\%s fontchars=\"%s\"\n",name,fontchars);
10432:  /* --- convert fontchars one at a time --- */
10433:  strcpy(subexpr,"{\\rm~");              /* start off with opening {\rm */
10434:  strcpy(subexpr,"{");                   /* nope, just start off with { */
10435:  for ( pfchars=fontchars; (fchar= *pfchars)!='\000'; pfchars++ )
10436:   {
10437:   if ( isthischar(fchar,WHITEMATH) )    /* some whitespace */
10438:     { if ( 0 || istext )                /* and we're in a text mode font */
10439:         strcat(subexpr,"\\;"); }        /* so respect whitespace */
10440:   else                                  /* char to be displayed in font */
10441:     { int exprlen = 0;                  /* #chars in subexpr before fchar */
10442:       int isinclass = 0;                /* set true if fchar in font class */
10443:       /* --- class: 1=upper, 2=alpha, 3=alnum, 4=lower, 5=digit, 9=all --- */
10444:       switch ( class )                  /* check if fchar is in font class */
10445:         { default: break;               /* no chars in unrecognized class */
10446:           case 1: if ( isupper((int)fchar) ) isinclass=1; break;
10447:           case 2: if ( isalpha((int)fchar) ) isinclass=1; break;
10448:           case 3: if ( isalnum((int)fchar) ) isinclass=1; break;
10449:           case 4: if ( islower((int)fchar) ) isinclass=1; break;
10450:           case 5: if ( isdigit((int)fchar) ) isinclass=1; break;
10451:           case 9: isinclass=1; break; }
10452:       if ( isinclass )                  /* convert current char to \font */
10453:         { strcat(subexpr,name);         /* by prefixing it with font name */
10454:           isprevchar = 1; }             /* and set flag to signal separator*/
10455:       else                              /* current char not in \font */
10456:         { if ( isprevchar )             /* extra separator only after \font*/
10457:            if ( isalpha(fchar) )        /* separator only before alpha */
10458:             strcat(subexpr,"~");        /* need separator after \font */
10459:           isprevchar = 0; }             /* reset flag for next char */
10460:       exprlen = strlen(subexpr);        /* #chars so far */
10461:       subexpr[exprlen] = fchar;         /*fchar immediately after \fontname*/
10462:       subexpr[exprlen+1] = '\000'; }    /* replace terminating '\0' */
10463:   } /* --- end-of-for(pfchars) --- */
10464:  strcat(subexpr,"}");                   /* add closing } */
10465:  } /* --- end-of-if/else(class<0) --- */
10466: /* -------------------------------------------------------------------------
10467: rasterize subexpression containing chars to be rendered at font
10468: -------------------------------------------------------------------------- */
10469: if ( msgfp!=NULL && msglevel>=99 )
10470:   fprintf(msgfp,"rastfont> subexpr=\"%s\"\n",subexpr);
10471: if ( (fontsp = rasterize(subexpr,size)) /* rasterize chars in font */
10472: ==   NULL ) goto end_of_job;            /* and quit if failed */
10473: /* -------------------------------------------------------------------------
10474: back to caller with chars rendered in font
10475: -------------------------------------------------------------------------- */
10476: end_of_job:
10477:   smashmargin = oldsmashmargin;         /* restore smash */
10478:   mathsmashmargin = SMASHMARGIN;        /* this one probably not necessary */
10479:   if ( istext && fontsp!=NULL )         /* raster contains text mode font */
10480:     fontsp->type = blanksignal;         /* signal nosmash */
10481:   return ( fontsp );                    /* chars rendered in font */
10482: } /* --- end-of-function rastfont() --- */
10483: 
10484: 
10485: /* ==========================================================================
10486:  * Function:    rastbegin ( expression, size, basesp, arg1, arg2, arg3 )
10487:  * Purpose:     \begin{}...\end{}  handler, returns a subraster corresponding
10488:  *              to array expression within environment, i.e., rewrites
10489:  *              \begin{}...\end{} as mimeTeX equivalent, and rasterizes that.
10490:  * --------------------------------------------------------------------------
10491:  * Arguments:   expression (I/O) char **  to first char of null-terminated
10492:  *                              string immediately following \begin to be
10493:  *                              rasterized, and returning ptr immediately
10494:  *                              following last character processed.
10495:  *              size (I)        int containing 0-7 default font size
10496:  *              basesp (I)      subraster *  to character (or subexpression)
10497:  *                              immediately preceding \begin
10498:  *                              (unused, but passed for consistency)
10499:  *              arg1 (I)        int unused
10500:  *              arg2 (I)        int unused
10501:  *              arg3 (I)        int unused
10502:  * --------------------------------------------------------------------------
10503:  * Returns:     ( subraster * ) ptr to subraster corresponding to array
10504:  *                              expression, or NULL for any parsing error
10505:  * --------------------------------------------------------------------------
10506:  * Notes:     o
10507:  * ======================================================================= */
10508: /* --- entry point --- */
10509: FUNCSCOPE subraster *rastbegin(char **expression, int size, subraster *basesp,
10510:                                int arg1, int arg2, int arg3)
10511: {
10512: /* -------------------------------------------------------------------------
10513: Allocations and Declarations
10514: -------------------------------------------------------------------------- */
10515: char    /**texsubexpr(),*/ subexpr[MAXSUBXSZ+1],/*\begin{} environment params*/
10516:         *exprptr=NULL,*begptr=NULL,*endptr=NULL,*braceptr=NULL; /* ptrs */
10517: char    *begtoken="\\begin{", *endtoken="\\end{"; /*tokens we're looking for*/
10518: /*int   strreplace();*/                 /* replace substring in string */
10519: /*char  *strchange();*/                 /*\begin...\end --> {\begin...\end}*/
10520: char    *delims = (char *)NULL;         /* mdelims[ienviron] */
10521: subraster /**rasterize(),*/ *sp=NULL;   /* rasterize environment */
10522: int     ienviron = 0;                   /* environs[] index */
10523: int     nbegins = 0;                    /* #\begins nested beneath this one*/
10524: int     envlen=0, sublen=0;             /* #chars in environ, subexpr */
10525: static  int blevel = 0;                 /* \begin...\end nesting level */
10526: static  char *mdelims[] = { NULL, NULL, NULL, NULL,
10527:         "()","[]","{}","||","==",       /* for pbBvVmatrix */
10528:         NULL, NULL, NULL, NULL, "{.", NULL, NULL, NULL, NULL, NULL, NULL,
10529:         NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
10530: static  char *environs[] = {            /* types of environments we process*/
10531:         "eqnarray",                     /* 0 eqnarray environment */
10532:         "array",                        /* 1 array environment */
10533:         "matrix",                       /* 2 array environment */
10534:         "tabular",                      /* 3 array environment */
10535:         "pmatrix",                      /* 4 ( ) */
10536:         "bmatrix",                      /* 5 [ ] */
10537:         "Bmatrix",                      /* 6 { } */
10538:         "vmatrix",                      /* 7 | | */
10539:         "Vmatrix",                      /* 8 || || */
10540:         "gather",                       /* 9 gather environment */
10541:         "align",                        /* 10 align environment */
10542:         "verbatim",                     /* 11 verbatim environment */
10543:         "picture",                      /* 12 picture environment */
10544:         "cases",                        /* 13 cases environment */
10545:         "equation",                     /* 14 for \begin{equation} */
10546:         NULL };                         /* trailer */
10547: /* -------------------------------------------------------------------------
10548: determine type of environment we're beginning
10549: -------------------------------------------------------------------------- */
10550: /* --- first bump nesting level --- */
10551: blevel++;                               /* count \begin...\begin...'s */
10552: /* --- \begin must be followed by {type_of_environment} --- */
10553: exprptr = texsubexpr(*expression,subexpr,0,"{","}",0,0);
10554: if ( *subexpr == '\000' ) goto end_of_job; /* no environment given */
10555: while ( (delims=strchr(subexpr,'*')) != NULL ) /* have environment* */
10556:   {strsqueeze(delims,1);}               /* treat it as environment */
10557: /* --- look up environment in our table --- */
10558: for ( ienviron=0; ;ienviron++ )         /* search table till NULL */
10559:   if ( environs[ienviron] == NULL )     /* found NULL before match */
10560:     goto end_of_job;                    /* so quit */
10561:   else                                  /* see if we have an exact match */
10562:     if ( memcmp(environs[ienviron],subexpr,strlen(subexpr)) == 0 ) /*match*/
10563:       break;                            /* leave loop with ienviron index */
10564: /* --- accumulate any additional params for this environment --- */
10565: *subexpr = '\000';                      /* reset subexpr to empty string */
10566: delims = mdelims[ienviron];             /* mdelims[] string for ienviron */
10567: if ( delims != NULL )                   /* add appropriate opening delim */
10568:   { strcpy(subexpr,"\\");               /* start with \ for (,[,{,|,= */
10569:     strcat(subexpr,delims);             /* then add opening delim */
10570:     subexpr[2] = '\000'; }              /* remove extraneous closing delim */
10571: switch ( ienviron )
10572:   {
10573:   default: goto end_of_job;             /* environ not implemented yet */
10574:   case 0:                               /* \begin{eqnarray} */
10575:     strcpy(subexpr,"\\array{rcl$");     /* set default rcl for eqnarray */
10576:     break;
10577:   case 1:  case 2:  case 3:             /* \begin{array} followed by {lcr} */
10578:     strcpy(subexpr,"\\array{");         /*start with mimeTeX \array{ command*/
10579:     skipwhite(exprptr);                 /* bump to next non-white char */
10580:     if ( *exprptr == '{' )              /* assume we have {lcr} argument */
10581:       { exprptr = texsubexpr(exprptr,subexpr+7,0,"{","}",0,0); /*add on lcr*/
10582:         if ( *(subexpr+7) == '\000' ) goto end_of_job; /* quit if no lcr */
10583:         strcat(subexpr,"$"); }          /* add terminating $ to lcr */
10584:     break;
10585:   case 4:  case 5:  case 6:             /* \begin{pmatrix} or b,B,v,Vmatrix */
10586:   case 7:  case 8:
10587:     strcat(subexpr,"\\array{");         /*start with mimeTeX \array{ command*/
10588:     break;
10589:   case 9:                               /* gather */
10590:     strcat(subexpr,"\\array{c$");       /* center equations */
10591:     break;
10592:   case 10:                              /* align */
10593:     strcat(subexpr,"\\array{rclrclrclrclrclrcl$"); /* a&=b & c&=d & etc */
10594:     break;
10595:   case 11:                              /* verbatim */
10596:     strcat(subexpr,"{\\rm ");           /* {\rm ...} */
10597:     /*strcat(subexpr,"\\\\{\\rm ");*/   /* \\{\rm } doesn't work in context */
10598:     break;
10599:   case 12:                              /* picture */
10600:     strcat(subexpr,"\\picture");        /* picture environment */
10601:     skipwhite(exprptr);                 /* bump to next non-white char */
10602:     if ( *exprptr == '(' )              /*assume we have (width,height) arg*/
10603:       { exprptr = texsubexpr(exprptr,subexpr+8,0,"(",")",0,1); /*add on arg*/
10604:         if ( *(subexpr+8) == '\000' ) goto end_of_job; } /* quit if no arg */
10605:     strcat(subexpr,"{");                /* opening {  after (width,height) */
10606:     break;
10607:   case 13:                              /* cases */
10608:     strcat(subexpr,"\\array{ll$");      /* a&b \\ c&d etc */
10609:     break;
10610:   case 14:                              /* \begin{equation} */
10611:     strcat(subexpr,"{");                /* just enclose expression in {}'s */
10612:     break;
10613:   } /* --- end-of-switch(ienviron) --- */
10614: /* -------------------------------------------------------------------------
10615: locate matching \end{...}
10616: -------------------------------------------------------------------------- */
10617: /* --- first \end following \begin --- */
10618: if ( (endptr=strstr(exprptr,endtoken))  /* find 1st \end following \begin */
10619: ==   NULL ) goto end_of_job;            /* and quit if no \end found */
10620: /* --- find matching endptr by pushing past any nested \begin's --- */
10621: begptr = exprptr;                       /* start after first \begin{...} */
10622: while ( 1 )                             /*break when we find matching \end*/
10623:   {
10624:   /* --- first, set ptr to closing } terminating current \end{...} --- */
10625:   if ( (braceptr=strchr(endptr+1,'}'))  /* find 1st } following \end{ */
10626:   ==   NULL ) goto end_of_job;          /* and quit if no } found */
10627:   /* -- locate next nested \begin --- */
10628:   if ( (begptr=strstr(begptr,begtoken)) /* find next \begin{...} */
10629:   ==   NULL ) break;                    /*no more, so we have matching \end*/
10630:   begptr += strlen(begtoken);           /* push ptr past token */
10631:   if ( begptr >= endptr ) break;        /* past endptr, so not nested */
10632:   /* --- have nested \begin, so push forward to next \end --- */
10633:   nbegins++;                            /* count another nested \begin */
10634:   if ( (endptr=strstr(endptr+strlen(endtoken),endtoken)) /* find next \end */
10635:   ==   NULL ) goto end_of_job;          /* and quit if none found */
10636:   } /* --- end-of-while(1) --- */
10637: /* --- push expression past closing } of \end{} --- */
10638: *expression = braceptr+1;               /* resume processing after } */
10639: /* -------------------------------------------------------------------------
10640: add on everything (i.e., the ...'s) between \begin{}[{}] ... \end{}
10641: -------------------------------------------------------------------------- */
10642: /* --- add on everything, completing subexpr for \begin{}...\end{} --- */
10643: sublen = strlen(subexpr);               /* #chars in "preamble" */
10644: envlen = (int)(endptr-exprptr);         /* #chars between \begin{}{}...\end */
10645: memcpy(subexpr+sublen,exprptr,envlen);  /*concatanate environ after subexpr*/
10646: subexpr[sublen+envlen] = '\000';        /* and null-terminate */
10647: if ( 2 > 1 )                            /* always... */
10648:   strcat(subexpr,"}");                  /* ...followed by terminating } */
10649: /* --- add terminating \right), etc, if necessary --- */
10650: if ( delims != (char *)NULL )           /* need closing delim */
10651:  { strcat(subexpr,"\\");                /* start with \ for ),],},|,= */
10652:    strcat(subexpr,delims+1); }          /* add appropriate closing delim */
10653: /* -------------------------------------------------------------------------
10654: change nested \begin...\end to {\begin...\end} so \array{} can handle them
10655: -------------------------------------------------------------------------- */
10656: if ( nbegins > 0 )                      /* have nested begins */
10657:  if ( blevel < 2 )                      /* only need to do this once */
10658:   {
10659:   begptr = subexpr;                     /* start at beginning of subexpr */
10660:   while( (begptr=strstr(begptr,begtoken)) != NULL ) /* have \begin{...} */
10661:     { strchange(0,begptr,"{");          /* \begin --> {\begin */
10662:       begptr += strlen(begtoken); }     /* continue past {\begin */
10663:   endptr = subexpr;                     /* start at beginning of subexpr */
10664:   while( (endptr=strstr(endptr,endtoken)) != NULL ) /* have \end{...} */
10665:     if ( (braceptr=strchr(endptr+1,'}')) /* find 1st } following \end{ */
10666:     ==   NULL ) goto end_of_job;        /* and quit if no } found */
10667:     else                                /* found terminating } */
10668:      { strchange(0,braceptr,"}");       /* \end{...} --> \end{...}} */
10669:        endptr = braceptr+1; }           /* continue past \end{...} */
10670:   } /* --- end-of-if(nbegins>0) --- */
10671: /* -------------------------------------------------------------------------
10672: post process as necessary
10673: -------------------------------------------------------------------------- */
10674: switch ( ienviron )
10675:   {
10676:   default: break;                       /* no post-processing required */
10677:   case 10:                              /* align */
10678:     strreplace(subexpr,"&=","#*@*#=",0); /* tag all &='s */
10679:     strreplace(subexpr,"&<","#*@*#<",0); /* tag all &<'s */
10680:     strreplace(subexpr,"&\\lt","#*@*#<",0); /* tag all &\lt's */
10681:     strreplace(subexpr,"&\\leq","#*@*#\\leq",0); /* tag all &\leq's */
10682:     strreplace(subexpr,"&>","#*@*#>",0); /* tag all &>'s */
10683:     strreplace(subexpr,"&\\gt","#*@*#>",0); /* tag all &\gt's */
10684:     strreplace(subexpr,"&\\geq","#*@*#\\geq",0); /* tag all &\geq's */
10685:     if ( nbegins < 1 )                  /* don't modify nested arrays */
10686:       strreplace(subexpr,"&","\\hspace{10}&\\hspace{10}",0); /* add space */
10687:     strreplace(subexpr,"#*@*#=","& = &",0); /*restore and xlate tagged &='s*/
10688:     strreplace(subexpr,"#*@*#<","& \\lt &",0); /*restore, xlate tagged &<'s*/
10689:     strreplace(subexpr,"#*@*#\\leq","& \\leq &",0); /*xlate tagged &\leq's*/
10690:     strreplace(subexpr,"#*@*#>","& \\gt &",0); /*restore, xlate tagged &>'s*/
10691:     strreplace(subexpr,"#*@*#\\geq","& \\geq &",0); /*xlate tagged &\geq's*/
10692:     break;
10693:   case 11:                              /* verbatim */
10694:     strreplace(subexpr,"\n","\\\\",0);  /* xlate \n newline to latex \\ */
10695:     /*strcat(subexpr,"\\\\");*/         /* add final latex \\ newline */
10696:     break;
10697:   case 12:                              /* picture */
10698:     strreplace(subexpr,"\\put "," ",0); /*remove \put's (not really needed)*/
10699:     strreplace(subexpr,"\\put(","(",0); /*remove \put's (not really needed)*/
10700:     strreplace(subexpr,"\\oval","\\circle",0); /* actually an ellipse */
10701:     break;
10702:   } /* --- end-of-switch(ienviron) --- */
10703: /* -------------------------------------------------------------------------
10704: return rasterized mimeTeX equivalent of \begin{}...\end{} environment
10705: -------------------------------------------------------------------------- */
10706: /* --- debugging output --- */
10707: if ( msgfp!=NULL && msglevel>=99 )
10708:   fprintf(msgfp,"rastbegin> subexpr=%s\n",subexpr);
10709: /* --- rasterize mimeTeX equivalent of \begin{}...\end{} environment --- */
10710: sp = rasterize(subexpr,size);           /* rasterize subexpr */
10711: end_of_job:
10712:   blevel--;                             /* decrement \begin nesting level */
10713:   return ( sp );                        /* back to caller with sp or NULL */
10714: } /* --- end-of-function rastbegin() --- */
10715: 
10716: 
10717: /* ==========================================================================
10718:  * Function:    rastarray ( expression, size, basesp, arg1, arg2, arg3 )
10719:  * Purpose:     \array handler, returns a subraster corresponding to array
10720:  *              expression (immediately following \array) at font size
10721:  * --------------------------------------------------------------------------
10722:  * Arguments:   expression (I/O) char **  to first char of null-terminated
10723:  *                              string immediately following \array to be
10724:  *                              rasterized, and returning ptr immediately
10725:  *                              following last character processed.
10726:  *              size (I)        int containing 0-7 default font size
10727:  *              basesp (I)      subraster *  to character (or subexpression)
10728:  *                              immediately preceding \array
10729:  *                              (unused, but passed for consistency)
10730:  *              arg1 (I)        int unused
10731:  *              arg2 (I)        int unused
10732:  *              arg3 (I)        int unused
10733:  * --------------------------------------------------------------------------
10734:  * Returns:     ( subraster * ) ptr to subraster corresponding to array
10735:  *                              expression, or NULL for any parsing error
10736:  * --------------------------------------------------------------------------
10737:  * Notes:     o Summary of syntax...
10738:  *                      \array{3,lcrBC$a&b&c\\d&e&f\\etc}
10739:  *            o The 3,lcrBC$ part is an optional "preamble".  The lcr means
10740:  *              what you think, i.e., "horizontal" left,center,right
10741:  *              justification down corresponding column.  The new BC means
10742:  *              "vertical" baseline,center justification across corresponding
10743:  *              row.  The leading 3 specifies the font size 0-4 to be used.
10744:  *              You may also specify +1,-1,+2,-2, etc, which is used as an
10745:  *              increment to the current font size, e.g., -1,lcr$ uses
10746:  *              one font size smaller than current.  Without a leading
10747:  *              + or -, the font size is "absolute".
10748:  *            o The preamble can also be just lcrBC$ without a leading
10749:  *              size-part, or just 3$ without a trailing lcrBC-part.
10750:  *              The default size is whatever is current, and the
10751:  *              default justification is c(entered) and B(aseline).
10752:  * ======================================================================= */
10753: /* --- entry point --- */
10754: FUNCSCOPE subraster *rastarray(char **expression, int size, subraster *basesp,
10755:                                int arg1, int arg2, int arg3)
10756: {
10757: /* -------------------------------------------------------------------------
10758: Allocations and Declarations
10759: -------------------------------------------------------------------------- */
10760: char    /**texsubexpr(),*/subexpr[MAXSUBXSZ+1],*exprptr,/*parse array subexpr*/
10761:          subtok[MAXTOKNSZ+1], *subptr=subtok, /* &,\\ inside { } not a delim*/
10762:          token[MAXTOKNSZ+1],  *tokptr=token, /* subexpr token to rasterize */
10763:         /**preamble(),*/ *preptr=token; /*process optional size,lcr preamble*/
10764: char    *coldelim="&", *rowdelim="\\";  /* need escaped rowdelim */
10765: int     maxarraysz = 63;                /* max #rows, cols */
10766: int     justify[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* -1,0,+1 = l,c,r */
10767:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10768:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10769:           hline[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* hline above row? */
10770:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10771:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10772:           vline[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*vline left of col?*/
10773:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10774:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10775:        colwidth[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*widest tokn in col*/
10776:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10777:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10778:       rowheight[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* "highest" in row */
10779:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10780:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10781:      fixcolsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed col width*/
10782:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10783:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10784:      fixrowsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed row height*/
10785:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10786:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10787:       rowbaseln[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* baseline for row */
10788:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10789:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10790:       vrowspace[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*extra //[len]space*/
10791:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10792:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10793:       rowcenter[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*true = vcenter row*/
10794:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10795:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
10796: static int /* --- propagate global values across arrays --- */
10797:        gjustify[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* -1,0,+1 = l,c,r */
10798:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10799:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10800:       gcolwidth[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*widest tokn in col*/
10801:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10802:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10803:      growheight[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* "highest" in row */
10804:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10805:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10806:     gfixcolsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed col width*/
10807:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10808:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10809:     gfixrowsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed row height*/
10810:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10811:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10812:      growcenter[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*true = vcenter row*/
10813:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10814:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
10815: int     rowglobal=0, colglobal=0,       /* true to set global values */
10816:         rowpropagate=0, colpropagate=0; /* true if propagating values */
10817: int     irow,nrows=0, icol,ncols[65],   /*#rows in array, #cols in each row*/
10818:         maxcols=0;                      /* max# cols in any single row */
10819: int     itoken, ntokens=0,              /* index, total #tokens in array */
10820:         subtoklen=0,                    /* strlen of {...} subtoken */
10821:         istokwhite=1,                   /* true if token all whitespace */
10822:         nnonwhite=0;                    /* #non-white tokens */
10823: int     isescape=0,wasescape=0,         /* current,prev chars escape? */
10824:         ischarescaped=0,                /* is current char escaped? */
10825:         nescapes=0;                     /* #consecutive escapes */
10826: subraster /**rasterize(),*/ *toksp[1025], /* rasterize tokens */
10827:         /**new_subraster(),*/ *arraysp=NULL; /* subraster for entire array */
10828: raster  *arrayrp=NULL;                  /* raster for entire array */
10829: /*int   delete_subraster();*/           /* free toksp[] workspace at eoj */
10830: int     rowspace=2, colspace=4,         /* blank space between rows, cols */
10831:         hspace=1, vspace=1;             /*space to accommodate hline,vline*/
10832: int     width=0, height=0,              /* width,height of array */
10833:         leftcol=0, toprow=0;            /*upper-left corner for cell in it*/
10834: int     rastput();                      /* embed tokens/cells in array */
10835: /*int   rule_raster();*/                /* draw hlines and vlines in array */
10836: char    *hlchar="\\hline", *hdchar="\\hdash"; /* token signals hline */
10837: char    /**texchar(),*/ hltoken[1025];  /* extract \hline from token */
10838: int     ishonly=0, hltoklen, minhltoklen=3; /*flag, token must be \hl or \hd*/
10839: int     isnewrow=1;                     /* true for new row */
10840: int     pixsz = 1;                      /*default #bits per pixel, 1=bitmap*/
10841: int     /*evalterm(),*/ evalue=0;       /* evaluate [arg], {arg} */
10842: static  int mydaemonlevel = 0;          /* check against global daemonlevel*/
10843: /* -------------------------------------------------------------------------
10844: Macros to determine extra raster space required for vline/hline
10845: -------------------------------------------------------------------------- */
10846: #define vlinespace(icol) \
10847:         ( vline[icol] == 0?  0 :        /* no vline so no space needed */   \
10848:           ( icol<1 || icol>=maxcols? vspace+(colspace+1)/2 : vspace ) )
10849: #define hlinespace(irow) \
10850:         ( hline[irow] == 0?  0 :        /* no hline so no space needed */   \
10851:           ( irow<1 || irow>=nrows? hspace+(rowspace+1)/2 : hspace ) )
10852: /* -------------------------------------------------------------------------
10853: Obtain array subexpression
10854: -------------------------------------------------------------------------- */
10855: /* --- parse for array subexpression, and bump expression past it --- */
10856: subexpr[1] = *subexpr = ' ';            /* set two leading blanks */
10857: *expression = texsubexpr(*expression,subexpr+2,0,"{","}",0,0);
10858: if ( msglevel>=29 && msgfp!=NULL )      /* debugging, display array */
10859:   fprintf(msgfp,"rastarray> %.256s\n",subexpr+2);
10860: if ( *(subexpr+2)=='\000' )             /* couldn't get subexpression */
10861:   goto end_of_job;                      /* nothing to do, so quit */
10862: /* -------------------------------------------------------------------------
10863: reset static arrays if main re-entered as daemon (or dll)
10864: -------------------------------------------------------------------------- */
10865: if ( mydaemonlevel != daemonlevel ) {   /* main re-entered */
10866:   for ( icol=0; icol<=maxarraysz; icol++ ) /* for each array[] index */
10867:     gjustify[icol]    = gcolwidth[icol]   = growheight[icol] =
10868:     gfixcolsize[icol] = gfixrowsize[icol] = growcenter[icol] = 0;
10869:   mydaemonlevel = daemonlevel; }        /* update mydaemonlevel */
10870: /* -------------------------------------------------------------------------
10871: process optional size,lcr preamble if present
10872: -------------------------------------------------------------------------- */
10873: /* --- reset size, get lcr's, and push exprptr past preamble --- */
10874: exprptr = preamble(subexpr+2,&size,preptr); /* reset size and get lcr's */
10875: /* --- init with global values --- */
10876: for(icol=0; icol<=maxarraysz; icol++) { /* propagate global values... */
10877:   justify[icol] = gjustify[icol];       /* -1,0,+1 = l,c,r */
10878:   colwidth[icol] = gcolwidth[icol];     /* column width */
10879:   rowheight[icol] = growheight[icol];   /* row height */
10880:   fixcolsize[icol] = gfixcolsize[icol]; /* 1=fixed col width */
10881:   fixrowsize[icol] = gfixrowsize[icol]; /* 1=fixed row height */
10882:   rowcenter[icol] = growcenter[icol]; } /* true = vcenter row */
10883: /* --- process lcr's, etc in preamble --- */
10884: itoken = 0;                             /* debugging flag */
10885: if ( msglevel>=29 && msgfp!=NULL )      /* debugging, display preamble */
10886:  if ( *preptr != '\000' )               /* if we have one */
10887:   fprintf(msgfp,"rastarray> preamble= \"%.256s\"\nrastarray> preamble: ",
10888:   preptr);
10889: irow = icol = 0;                        /* init lcr counts */
10890: while (  *preptr != '\000' )            /* check preamble text for lcr */
10891:   {
10892:   char  prepchar = *preptr;             /* current preamble character */
10893:   int   prepcase = (islower(prepchar)?1:(isupper(prepchar)?2:0)); /*1,2,or 0*/
10894:   if ( irow<maxarraysz && icol<maxarraysz )
10895:    switch ( /*tolower*/(prepchar) )
10896:     {  default: break;                  /* just flush unrecognized chars */
10897:       case 'l': justify[icol] = (-1);           /*left-justify this column*/
10898:                 if (colglobal) gjustify[irow] = justify[irow]; break;
10899:       case 'c': justify[icol] = (0);            /* center this column */
10900:                 if (colglobal) gjustify[irow] = justify[irow]; break;
10901:       case 'r': justify[icol] = (+1);           /* right-justify this col */
10902:                 if (colglobal) gjustify[irow] = justify[irow]; break;
10903:       case '|': vline[icol] += 1;   break;      /* solid vline left of col */
10904:       case '.': vline[icol] = (-1); break;      /*dashed vline left of col */
10905:       case 'b': prepchar='B'; prepcase=2;       /* alias for B */
10906:       case 'B': break;                          /* baseline-justify row */
10907:       case 'v': prepchar='C'; prepcase=2;       /* alias for C */
10908:       case 'C': rowcenter[irow] = 1;            /* vertically center row */
10909:                 if (rowglobal) growcenter[irow] = rowcenter[irow]; break;
10910:       case 'g': colglobal=1; prepcase=0; break; /* set global col values */
10911:       case 'G': rowglobal=1; prepcase=0; break; /* set global row values */
10912:       case '#': colglobal=rowglobal=1; break; } /* set global col,row vals */
10913:   if ( msglevel>=29 && msgfp!=NULL )    /* debugging */
10914:     fprintf(msgfp," %c[%d]",prepchar,
10915:     (prepcase==1?icol+1:(prepcase==2?irow+1:0)));
10916:   preptr++;                             /* check next char for lcr */
10917:   itoken++;                             /* #lcr's processed (debugging only)*/
10918:   /* --- check for number or +number specifying colwidth or rowheight --- */
10919:   if ( prepcase != 0 )                  /* only check upper,lowercase */
10920:    {
10921:    int  ispropagate = (*preptr=='+'?1:0); /* leading + propagates width/ht */
10922:    if ( ispropagate ) {                 /* set row or col propagation */
10923:      if ( prepcase == 1 ) colpropagate = 1; /* propagating col values */
10924:      else if ( prepcase == 2 ) rowpropagate = 1; } /*propagating row values*/
10925:    if ( !colpropagate && prepcase == 1 )
10926:       { colwidth[icol] = 0;             /* reset colwidth */
10927:         fixcolsize[icol] = 0; }         /* reset width flag */
10928:    if ( !rowpropagate && prepcase == 2 )
10929:       { rowheight[irow] = 0;            /* reset row height */
10930:         fixrowsize[irow] = 0; }         /* reset height flag */
10931:    if ( ispropagate ) preptr++;         /* bump past leading + */
10932:    if ( isdigit(*preptr) )              /* digit follows character */
10933:      { char *endptr = NULL;             /* preptr set to 1st char after num*/
10934:        int size = (int)(strtol(preptr,&endptr,10)); /* interpret number */
10935:        char *whchars="?wh";             /* debugging width/height labels */
10936:        preptr = endptr;                 /* skip over all digits */
10937:        if ( size==0 || (size>=3&&size<=500) ) { /* sanity check */
10938:         int index;                      /* icol,irow...maxarraysz index */
10939:         if ( prepcase == 1 )            /* lowercase signifies colwidth */
10940:          for(index=icol; index<=maxarraysz; index++) { /*propagate col size*/
10941:           colwidth[index] = size;       /* set colwidth to fixed size */
10942:           fixcolsize[index] = (size>0?1:0); /* set fixed width flag */
10943:           justify[index] = justify[icol]; /* and propagate justification */
10944:           if ( colglobal ) {            /* set global values */
10945:             gcolwidth[index] = colwidth[index]; /* set global col width */
10946:             gfixcolsize[index] = fixcolsize[index]; /*set global width flag*/
10947:             gjustify[index] = justify[icol]; } /* set global col justify */
10948:           if ( !ispropagate ) break; }  /* don't propagate */
10949:         else                            /* uppercase signifies rowheight */
10950:          for(index=irow; index<=maxarraysz; index++) { /*propagate row size*/
10951:           rowheight[index] = size;      /* set rowheight to size */
10952:           fixrowsize[index] = (size>0?1:0); /* set fixed height flag */
10953:           rowcenter[index] = rowcenter[irow]; /* and propagate row center */
10954:           if ( rowglobal ) {            /* set global values */
10955:             growheight[index] = rowheight[index]; /* set global row height */
10956:             gfixrowsize[index] = fixrowsize[index]; /*set global height flag*/
10957:             growcenter[index] = rowcenter[irow]; } /*set global row center*/
10958:           if ( !ispropagate ) break; }  /* don't propagate */
10959:         } /* --- end-of-if(size>=3&&size<=500) --- */
10960:        if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
10961:          fprintf(msgfp,":%c=%d/fix#%d",whchars[prepcase],
10962:          (prepcase==1?colwidth[icol]:rowheight[irow]),
10963:          (prepcase==1?fixcolsize[icol]:fixrowsize[irow]));
10964:      } /* --- end-of-if(isdigit()) --- */
10965:    } /* --- end-of-if(prepcase!=0) --- */
10966:   if ( prepcase == 1 ) icol++;          /* bump col if lowercase lcr */
10967:     else if ( prepcase == 2 ) irow++;   /* bump row if uppercase BC */
10968:   } /* --- end-of-while(*preptr!='\000') --- */
10969: if ( msglevel>=29 && msgfp!=NULL )      /* debugging, emit final newline */
10970:  if ( itoken > 0 )                      /* if we have preamble */
10971:   fprintf(msgfp,"\n");
10972: /* -------------------------------------------------------------------------
10973: tokenize and rasterize components  a & b \\ c & d \\ etc  of subexpr
10974: -------------------------------------------------------------------------- */
10975: /* --- rasterize tokens one at a time, and maintain row,col counts --- */
10976: nrows = 0;                              /* start with top row */
10977: ncols[nrows] = 0;                       /* no tokens/cols in top row yet */
10978: while ( 1 )                             /* scan chars till end */
10979:   {
10980:   /* --- local control flags --- */
10981:   int   iseox = (*exprptr == '\000'),   /* null signals end-of-expression */
10982:         iseor = iseox,                  /* \\ or eox signals end-of-row */
10983:         iseoc = iseor;                  /* & or eor signals end-of-col */
10984:   /* --- check for escapes --- */
10985:   isescape = isthischar(*exprptr,ESCAPE); /* is current char escape? */
10986:   wasescape= (!isnewrow&&isthischar(*(exprptr-1),ESCAPE)); /*prev char esc?*/
10987:   nescapes = (wasescape?nescapes+1:0);  /* # preceding consecutive escapes */
10988:   ischarescaped = (nescapes%2==0?0:1);  /* is current char escaped? */
10989:   /* -----------------------------------------------------------------------
10990:   check for {...} subexpression starting from where we are now
10991:   ------------------------------------------------------------------------ */
10992:   if ( *exprptr == '{'                  /* start of {...} subexpression */
10993:   &&   !ischarescaped )                 /* if not escaped \{ */
10994:     {
10995:     subptr = texsubexpr(exprptr,subtok,4095,"{","}",1,1); /*entire subexpr*/
10996:     subtoklen = strlen(subtok);         /* #chars in {...} */
10997:     memcpy(tokptr,exprptr,subtoklen);   /* copy {...} to accumulated token */
10998:     tokptr  += subtoklen;               /* bump tokptr to end of token */
10999:     exprptr += subtoklen;               /* and bump exprptr past {...} */
11000:     istokwhite = 0;                     /* signal non-empty token */
11001:     continue;                           /* continue with char after {...} */
11002:     } /* --- end-of-if(*exprptr=='{') --- */
11003:   /* -----------------------------------------------------------------------
11004:   check for end-of-row(\\) and/or end-of-col(&)
11005:   ------------------------------------------------------------------------ */
11006:   /* --- check for (escaped) end-of-row delimiter --- */
11007:   if ( isescape && !ischarescaped )     /* current char is escaped */
11008:     if ( isthischar(*(exprptr+1),rowdelim) /* next char is rowdelim */
11009:     ||   *(exprptr+1) == '\000' )       /* or a pathological null */
11010:       { iseor = 1;                      /* so set end-of-row flag */
11011:         wasescape=isescape=nescapes = 0; } /* reset flags for new row */
11012:   /* --- check for end-of-col delimiter --- */
11013:   if (iseor                             /* end-of-row signals end-of-col */
11014:   ||  (!ischarescaped&&isthischar(*exprptr,coldelim))) /*or unescaped coldel*/
11015:       iseoc = 1;                        /* so set end-of-col flag */
11016:   /* -----------------------------------------------------------------------
11017:   rasterize completed token
11018:   ------------------------------------------------------------------------ */
11019:   if ( iseoc )                          /* we have a completed token */
11020:     {
11021:     *tokptr = '\000';                   /* first, null-terminate token */
11022:     /* --- check first token in row for [len] and/or \hline or \hdash --- */
11023:     ishonly = 0;                        /*init for token not only an \hline*/
11024:     if ( ncols[nrows] == 0 )            /*\hline must be first token in row*/
11025:       {
11026:       tokptr=token; skipwhite(tokptr);  /* skip whitespace after // */
11027:       /* --- first check for optional [len] --- */
11028:       if ( *tokptr == '[' ) {           /* have [len] if leading char is [ */
11029:         /* ---parse [len] and bump tokptr past it, interpret as double--- */
11030:         char lenexpr[128];  int len;    /* chars between [...] as int */
11031:         tokptr = texsubexpr(tokptr,lenexpr,127,"[","]",0,0);
11032:         if ( *lenexpr != '\000' ) {     /* got [len] expression */
11033:           evalue = evalterm(mimestore,lenexpr); /* evaluate len expression */
11034:           len = iround(unitlength*((double)evalue)); /* len in pixels */
11035:           if ( len>=(-63) && len<=255 ) { /* sanity check */
11036:             vrowspace[nrows] = len;     /* extra vspace before this row */
11037:             strsqueezep(token,tokptr);  /* flush [len] from token */
11038:             tokptr=token; skipwhite(tokptr); } } /* reset ptr, skip white */
11039:         } /* --- end-of-if(*tokptr=='[') --- */
11040:       /* --- now check for \hline or \hdash --- */
11041:       tokptr = texchar(tokptr,hltoken); /* extract first char from token */
11042:       hltoklen = strlen(hltoken);       /* length of first char */
11043:       if ( hltoklen >= minhltoklen ) {  /*token must be at least \hl or \hd*/
11044:         if ( memcmp(hlchar,hltoken,hltoklen) == 0 ) /* we have an \hline */
11045:            hline[nrows] += 1;           /* bump \hline count for row */
11046:         else if ( memcmp(hdchar,hltoken,hltoklen) == 0 ) /*we have an \hdash*/
11047:            hline[nrows] = (-1); }       /* set \hdash flag for row */
11048:       if ( hline[nrows] != 0 )          /* \hline or \hdash prefixes token */
11049:         { skipwhite(tokptr);            /* flush whitespace after \hline */
11050:           if ( *tokptr == '\000'        /* end-of-expression after \hline */
11051:           ||   isthischar(*tokptr,coldelim) ) /* or unescaped coldelim */
11052:             { istokwhite = 1;           /* so token contains \hline only */
11053:               if ( iseox ) ishonly = 1; } /* ignore entire row at eox */
11054:           else                          /* token contains more than \hline */
11055:             {strsqueezep(token,tokptr);} } /* so flush \hline */
11056:       } /* --- end-of-if(ncols[nrows]==0) --- */
11057:     /* --- rasterize completed token --- */
11058:     toksp[ntokens] = (istokwhite? NULL : /* don't rasterize empty token */
11059:       rasterize(token,size));           /* rasterize non-empty token */
11060:     if ( toksp[ntokens] != NULL )       /* have a rasterized token */
11061:       nnonwhite++;                      /* bump rasterized token count */
11062:     /* --- maintain colwidth[], rowheight[] max, and rowbaseln[] --- */
11063:     if ( toksp[ntokens] != NULL )       /* we have a rasterized token */
11064:       {
11065:       /* --- update max token "height" in current row, and baseline --- */
11066:       int twidth = ((toksp[ntokens])->image)->width,  /* width of token */
11067:         theight = ((toksp[ntokens])->image)->height, /* height of token */
11068:         tbaseln =  (toksp[ntokens])->baseline, /* baseline of token */
11069:         rheight = rowheight[nrows],     /* current max height for row */
11070:         rbaseln = rowbaseln[nrows];     /* current baseline for max height */
11071:       if ( 0 || fixrowsize[nrows]==0 )  /* rowheight not fixed */
11072:        rowheight[nrows] = /*max2( rheight,*/( /* current (max) rowheight */
11073:         max2(rbaseln+1,tbaseln+1)       /* max height above baseline */
11074:         + max2(rheight-rbaseln-1,theight-tbaseln-1) ); /* plus max below */
11075:       rowbaseln[nrows] = max2(rbaseln,tbaseln); /*max space above baseline*/
11076:       /* --- update max token width in current column --- */
11077:       icol = ncols[nrows];              /* current column index */
11078:       if ( 0 || fixcolsize[icol]==0 )   /* colwidth not fixed */
11079:        colwidth[icol] = max2(colwidth[icol],twidth); /*widest token in col*/
11080:       } /* --- end-of-if(toksp[]!=NULL) --- */
11081:     /* --- bump counters --- */
11082:     if ( !ishonly )                     /* don't count only an \hline */
11083:       if ( ncols[nrows] < maxarraysz )  /* don't overflow arrays */
11084:         { ntokens++;                    /* bump total token count */
11085:           ncols[nrows] += 1; }          /* and bump #cols in current row */
11086:     /* --- get ready for next token --- */
11087:     tokptr = token;                     /* reset ptr for next token */
11088:     istokwhite = 1;                     /* next token starts all white */
11089:     } /* --- end-of-if(iseoc) --- */
11090:   /* -----------------------------------------------------------------------
11091:   bump row as necessary
11092:   ------------------------------------------------------------------------ */
11093:   if ( iseor )                          /* we have a completed row */
11094:     {
11095:     maxcols = max2(maxcols,ncols[nrows]); /* max# cols in array */
11096:     if ( ncols[nrows]>0 || hline[nrows]==0 ) /*ignore row with only \hline*/
11097:       if ( nrows < maxarraysz )         /* don't overflow arrays */
11098:         nrows++;                        /* bump row count */
11099:     ncols[nrows] = 0;                   /* no cols in this row yet */
11100:     if ( !iseox )                       /* don't have a null yet */
11101:       { exprptr++;                      /* bump past extra \ in \\ delim */
11102:         iseox = (*exprptr == '\000'); } /* recheck for pathological \null */
11103:     isnewrow = 1;                       /* signal start of new row */
11104:     } /* --- end-of-if(iseor) --- */
11105:   else
11106:     isnewrow = 0;                       /* no longer first col of new row */
11107:   /* -----------------------------------------------------------------------
11108:   quit when done, or accumulate char in token and proceed to next char
11109:   ------------------------------------------------------------------------ */
11110:   /* --- quit when done --- */
11111:   if ( iseox ) break;                   /* null terminator signalled done */
11112:   /* --- accumulate chars in token --- */
11113:   if ( !iseoc )                         /* don't accumulate delimiters */
11114:     { *tokptr++ = *exprptr;             /* accumulate non-delim char */
11115:       if ( !isthischar(*exprptr,WHITESPACE) ) /* this token isn't empty */
11116:         istokwhite = 0; }               /* so reset flag to rasterize it */
11117:   /* --- ready for next char --- */
11118:   exprptr++;                            /* bump ptr */
11119:   } /* --- end-of-while(*exprptr!='\000') --- */
11120: /* --- make sure we got something to do --- */
11121: if ( nnonwhite < 1 )                    /* completely empty array */
11122:   goto end_of_job;                      /* NULL back to caller */
11123: /* -------------------------------------------------------------------------
11124: determine dimensions of array raster and allocate it
11125: -------------------------------------------------------------------------- */
11126: /* --- adjust colspace --- */
11127: colspace = 2 + 2*size;                  /* temp kludge */
11128: /* --- reset propagated sizes at boundaries of array --- */
11129: colwidth[maxcols] = rowheight[nrows] = 0; /* reset explicit 0's at edges */
11130: /* --- determine width of array raster --- */
11131: width = colspace*(maxcols-1);           /* empty space between cols */
11132: if ( msglevel>=29 && msgfp!=NULL )      /* debugging */
11133:   fprintf(msgfp,"rastarray> %d cols,  widths: ",maxcols);
11134: for ( icol=0; icol<=maxcols; icol++ )   /* and for each col */
11135:   { width += colwidth[icol];            /*width of this col (0 for maxcols)*/
11136:     width += vlinespace(icol);          /*plus space for vline, if present*/
11137:     if ( msglevel>=29 && msgfp!=NULL )  /* debugging */
11138:      fprintf(msgfp," %d=%2d+%d",icol+1,colwidth[icol],(vlinespace(icol))); }
11139: /* --- determine height of array raster --- */
11140: height = rowspace*(nrows-1);            /* empty space between rows */
11141: if ( msglevel>=29 && msgfp!=NULL )      /* debugging */
11142:   fprintf(msgfp,"\nrastarray> %d rows, heights: ",nrows);
11143: for ( irow=0; irow<=nrows; irow++ )     /* and for each row */
11144:   { height += rowheight[irow];          /*height of this row (0 for nrows)*/
11145:     height += vrowspace[irow];          /*plus extra //[len], if present*/
11146:     height += hlinespace(irow);         /*plus space for hline, if present*/
11147:     if ( msglevel>=29 && msgfp!=NULL )  /* debugging */
11148:      fprintf(msgfp," %d=%2d+%d",irow+1,rowheight[irow],(hlinespace(irow))); }
11149: /* --- allocate subraster and raster for array --- */
11150: if ( msglevel>=29 && msgfp!=NULL )      /* debugging */
11151:   fprintf(msgfp,"\nrastarray> tot width=%d(colspc=%d) height=%d(rowspc=%d)\n",
11152:   width,colspace, height,rowspace);
11153: if ( (arraysp=new_subraster(width,height,pixsz)) /* allocate new subraster */
11154: ==   NULL )  goto end_of_job;           /* quit if failed */
11155: /* --- initialize subraster parameters --- */
11156: arraysp->type = IMAGERASTER;            /* image */
11157: arraysp->symdef = NULL;                 /* not applicable for image */
11158: arraysp->baseline=min2(height/2+5,height-1); /*is a little above center good?*/
11159: arraysp->size = size;                   /* size (probably unneeded) */
11160: arrayrp = arraysp->image;               /* raster embedded in subraster */
11161: /* -------------------------------------------------------------------------
11162: embed tokens/cells in array
11163: -------------------------------------------------------------------------- */
11164: itoken = 0;                             /* start with first token */
11165: toprow = 0;                             /* start at top row of array */
11166: for ( irow=0; irow<=nrows; irow++ )     /*tokens were accumulated row-wise*/
11167:   {
11168:   /* --- initialization for row --- */
11169:   int   baseline = rowbaseln[irow];     /* baseline for this row */
11170:   if ( hline[irow] != 0 )               /* need hline above this row */
11171:     { int hrow = (irow<1? 0 : toprow - rowspace/2); /* row for hline */
11172:       if ( irow >= nrows ) hrow = height-1; /* row for bottom hline */
11173:       rule_raster(arrayrp,hrow,0,width,1,(hline[irow]<0?1:0)); } /* hline */
11174:   if ( irow >= nrows ) break;           /*just needed \hline for irow=nrows*/
11175:   toprow += vrowspace[irow];            /* extra //[len] space above irow */
11176:   if ( toprow < 0 ) toprow = 0;         /* check for large negative [-len] */
11177:   toprow += hlinespace(irow);           /* space for hline above irow */
11178:   leftcol = 0;                          /* start at leftmost column */
11179:   for ( icol=0; icol<ncols[irow]; icol++ ) /* go through cells in this row */
11180:     {
11181:     subraster *tsp = toksp[itoken];     /* token that belongs in this cell */
11182:     /* --- first adjust leftcol for vline to left of icol, if present ---- */
11183:     leftcol += vlinespace(icol);        /* space for vline to left of col */
11184:     /* --- now rasterize cell ---- */
11185:     if ( tsp != NULL )                  /* have a rasterized cell token */
11186:       {
11187:       /* --- local parameters --- */
11188:       int cwidth = colwidth[icol],      /* total column width */
11189:           twidth = (tsp->image)->width, /* token width */
11190:           theight= (tsp->image)->height, /* token height */
11191:           tokencol = 0,                 /*H offset (init for left justify)*/
11192:           tokenrow = baseline - tsp->baseline;/*V offset (init for baseline)*/
11193:       /* --- adjust leftcol for vline to left of icol, if present ---- */
11194:       /*leftcol += vlinespace(icol);*/  /* space for vline to left of col */
11195:       /* --- reset justification (if not left-justified) --- */
11196:       if ( justify[icol] == 0 )         /* but user wants it centered */
11197:           tokencol = (cwidth-twidth+1)/2; /* so split margin left/right */
11198:       else if ( justify[icol] == 1 )    /* or user wants right-justify */
11199:           tokencol = cwidth-twidth;     /* so put entire margin at left */
11200:       /* --- reset vertical centering (if not baseline-aligned) --- */
11201:       if ( rowcenter[irow] )            /* center cells in row vertically */
11202:           tokenrow = (rowheight[irow]-theight)/2; /* center row */
11203:       /* --- embed token raster at appropriate place in array raster --- */
11204:       rastput(arrayrp,tsp->image,       /* overlay cell token in array */
11205:           toprow+ tokenrow,             /*with aligned baseline or centered*/
11206:           leftcol+tokencol, 1);         /* and justified as requested */
11207:       } /* --- end-of-if(tsp!=NULL) --- */
11208:     itoken++;                           /* bump index for next cell */
11209:     leftcol += colwidth[icol] + colspace /*move leftcol right for next col*/
11210:       /* + vlinespace(icol) */ ; /*don't add space for vline to left of col*/
11211:     } /* --- end-of-for(icol) --- */
11212:   toprow += rowheight[irow] + rowspace; /* move toprow down for next row */
11213:   } /* --- end-of-for(irow) --- */
11214: /* -------------------------------------------------------------------------
11215: draw vlines as necessary
11216: -------------------------------------------------------------------------- */
11217: leftcol = 0;                            /* start at leftmost column */
11218: for ( icol=0; icol<=maxcols; icol++ )   /* check each col for a vline */
11219:   {
11220:   if ( vline[icol] != 0 )               /* need vline to left of this col */
11221:     { int vcol = (icol<1? 0 : leftcol - colspace/2); /* column for vline */
11222:       if ( icol >= maxcols ) vcol = width-1; /*column for right edge vline*/
11223:       rule_raster(arrayrp,0,vcol,1,height,(vline[icol]<0?2:0)); } /* vline */
11224:   leftcol += vlinespace(icol);          /* space for vline to left of col */
11225:   if ( icol < maxcols )                 /* don't address past end of array */
11226:     leftcol += colwidth[icol] + colspace; /*move leftcol right for next col*/
11227:   } /* --- end-of-for(icol) --- */
11228: /* -------------------------------------------------------------------------
11229: free workspace and return final result to caller
11230: -------------------------------------------------------------------------- */
11231: end_of_job:
11232:   /* --- free workspace --- */
11233:   if ( ntokens > 0 )                    /* if we have workspace to free */
11234:     while ( --ntokens >= 0 )            /* free each token subraster */
11235:       if ( toksp[ntokens] != NULL )     /* if we rasterized this cell */
11236:         delete_subraster(toksp[ntokens]); /* then free it */
11237:   /* --- return final result to caller --- */
11238:   return ( arraysp );
11239: } /* --- end-of-function rastarray() --- */
11240: 
11241: 
11242: /* ==========================================================================
11243:  * Function:    rastpicture ( expression, size, basesp, arg1, arg2, arg3 )
11244:  * Purpose:     \picture handler, returns subraster corresponding to picture
11245:  *              expression (immediately following \picture) at font size
11246:  * --------------------------------------------------------------------------
11247:  * Arguments:   expression (I/O) char **  to first char of null-terminated
11248:  *                              string immediately following \picture to be
11249:  *                              rasterized, and returning ptr immediately
11250:  *                              following last character processed.
11251:  *              size (I)        int containing 0-7 default font size
11252:  *              basesp (I)      subraster *  to character (or subexpression)
11253:  *                              immediately preceding \picture
11254:  *                              (unused, but passed for consistency)
11255:  *              arg1 (I)        int unused
11256:  *              arg2 (I)        int unused
11257:  *              arg3 (I)        int unused
11258:  * --------------------------------------------------------------------------
11259:  * Returns:     ( subraster * ) ptr to subraster corresponding to picture
11260:  *                              expression, or NULL for any parsing error
11261:  * --------------------------------------------------------------------------
11262:  * Notes:     o Summary of syntax...
11263:  *                \picture(width,height){(x,y){pic_elem}~(x,y){pic_elem}~etc}
11264:  *            o
11265:  * ======================================================================= */
11266: /* --- entry point --- */
11267: FUNCSCOPE subraster *rastpicture ( char **expression, int size,
11268:                             subraster *basesp, int arg1, int arg2, int arg3 )
11269: {
11270: /* -------------------------------------------------------------------------
11271: Allocations and Declarations
11272: -------------------------------------------------------------------------- */
11273: char    /**texsubexpr(),*/ picexpr[2049], *picptr=picexpr, /*picture {expre}*/
11274:         putexpr[256], *putptr,*multptr, /*[multi]put (x,y[;xinc,yinc;num])*/
11275:         pream[96], *preptr,             /* optional put preamble */
11276:         picelem[1025];                  /* picture element following put */
11277: subraster /**rasterize(),*/ *picelemsp=NULL, /* rasterize picture elements */
11278:         /**new_subraster(),*/ *picturesp=NULL, /*subraster for entire picture*/
11279:         *oldworkingbox = workingbox;    /* save working box on entry */
11280: raster  *picturerp=NULL;                /* raster for entire picture */
11281: /*int   delete_subraster();*/           /* free picelemsp[] workspace */
11282: int     pixsz = 1;                      /* pixels are one bit each */
11283: double  x=0.0,y=0.0,                    /* x,y-coords for put,multiput*/
11284:         xinc=0.0,yinc=0.0;              /* x,y-incrementss for multiput*/
11285: int     width=0,  height=0,             /* #pixels width,height of picture */
11286:         ewidth=0, eheight=0,            /* pic element width,height */
11287:         maxwidth=1600, maxheight=1600,  /* max width,height in pixels */
11288:         ix=0,xpos=0, iy=0,ypos=0,       /* mimeTeX x,y pixel coords */
11289:         num=1, inum;                    /* number reps, index of element */
11290: /*int   evalterm();*/                   /* evaluate [arg] and {arg}'s */
11291: int     iscenter=0;                     /* center or lowerleft put position*/
11292: int     *oldworkingparam = workingparam, /* save working param on entry */
11293:         origin = 0;                     /* x,yinc ++=00 +-=01 -+=10 --=11 */
11294: /*int   rastput();*/                    /* embed elements in picture */
11295: /*int   type_raster();*/                /* display debugging output */
11296: /* -------------------------------------------------------------------------
11297: First obtain (width,height) arguments immediately following \picture command
11298: -------------------------------------------------------------------------- */
11299: /* --- parse for (width,height) arguments, and bump expression past it --- */
11300: *expression = texsubexpr(*expression,putexpr,254,"(",")",0,0);
11301: if ( *putexpr == '\000' ) goto end_of_job; /* couldn't get (width,height) */
11302: /* --- now interpret width,height returned in putexpr --- */
11303: if ( (putptr=strchr(putexpr,',')) != NULL ) /* look for ',' in width,height*/
11304:   *putptr = '\000';                     /* found it, so replace ',' by '\0'*/
11305: width=height = eround(putexpr);         /*width pixels*/
11306: if ( putptr != NULL )                   /* 2nd arg, if present, is height */
11307:   height = eround(putptr+1);            /*in pixels*/
11308: /* -------------------------------------------------------------------------
11309: Then obtain entire picture {...} subexpression following (width,height)
11310: -------------------------------------------------------------------------- */
11311: /* --- parse for picture subexpression, and bump expression past it --- */
11312: *expression = texsubexpr(*expression,picexpr,2047,"{","}",0,0);
11313: if ( *picexpr == '\000' ) goto end_of_job; /* couldn't get {pic_elements} */
11314: /* -------------------------------------------------------------------------
11315: allocate subraster and raster for complete picture
11316: -------------------------------------------------------------------------- */
11317: /* --- sanity check on width,height args --- */
11318: if ( width < 2 ||  width > maxwidth
11319: ||  height < 2 || height > maxheight ) goto end_of_job;
11320: /* --- allocate and initialize subraster for constructed picture --- */
11321: if ( (picturesp=new_subraster(width,height,pixsz)) /*allocate new subraster*/
11322: ==   NULL )  goto end_of_job;           /* quit if failed */
11323: workingbox = picturesp;                 /* set workingbox to our picture */
11324: /* --- initialize picture subraster parameters --- */
11325: picturesp->type = IMAGERASTER;          /* image */
11326: picturesp->symdef = NULL;               /* not applicable for image */
11327: picturesp->baseline = height/2 + 2;     /* is a little above center good? */
11328: picturesp->size = size;                 /* size (probably unneeded) */
11329: picturerp = picturesp->image;           /* raster embedded in subraster */
11330: if ( msgfp!=NULL && msglevel>=29 )      /* debugging */
11331:   fprintf(msgfp,"picture> width,height=%d,%d\n",width,height);
11332: /* -------------------------------------------------------------------------
11333: parse out each picture element, rasterize it, and place it in picture
11334: -------------------------------------------------------------------------- */
11335: while ( *picptr != '\000' )             /* until we run out of pic_elems */
11336:   {
11337:   /* -----------------------------------------------------------------------
11338:   first obtain leading \[multi]put(x,y[;xinc,yinc;num]) args for pic_elem
11339:   ------------------------------------------------------------------------ */
11340:   /* --- init default values in case not explicitly supplied in args --- */
11341:   x=y=0.0;  xinc=yinc=0.0;  num=1;      /* init default values */
11342:   iscenter = origin = 0;                /* center, origin */
11343:   /* --- get (pream$x,y;xinc,yinc;num ) args and bump picptr past it --- */
11344:   while ( *picptr != '\000' )           /* skip invalid chars preceding ( */
11345:     if ( *picptr == '(' ) break;        /* found opening ( */
11346:     else picptr++;                      /* else skip invalid char */
11347:   picptr = texsubexpr(picptr,putexpr,254,"(",")",0,0);
11348:   if ( *putexpr == '\000' ) goto end_of_job; /* couldn't get (x,y) */
11349:   /* --- first look for $-terminated or for any non-digit preamble --- */
11350:   *pream = '\000';                      /* init preamble as empty string */
11351:   if ( (putptr=strchr(putexpr,'$')) != NULL ) /*check for $ pream terminator*/
11352:     { *putptr++ = '\000';               /* replace $ by '\0', bump past $ */
11353:       strninit(pream,putexpr,92); }     /* copy leading preamble from put */
11354:   else                                  /* look for any non-digit preamble */
11355:     { int npream = 0;                   /* #chars in preamble */
11356:       for ( preptr=pream,putptr=putexpr; ; npream++,putptr++ )
11357:         if ( *putptr == '\000'          /* end-of-putdata signalled */
11358:         ||   !isalpha((int)(*putptr))   /* or found non-alpha char */
11359:         ||   npream > 92 ) break;       /* or preamble too long */
11360:         else *preptr++ = *putptr;       /* copy alpha char to preamble */
11361:       *preptr = '\000'; }               /* null-terminate preamble */
11362:   /* --- interpret preamble --- */
11363:   for ( preptr=pream; ; preptr++ )      /* examine each preamble char */
11364:     if ( *preptr == '\000' ) break;     /* end-of-preamble signalled */
11365:     else switch ( tolower(*preptr) )    /* check lowercase preamble char */
11366:       {
11367:       default: break;                   /* unrecognized flag */
11368:       case 'c': iscenter=1; break;      /* center pic_elem at x,y coords */
11369:       } /* --- end-of-switch --- */
11370:   /* --- interpret x,y;xinc,yinc;num following preamble --- */
11371:   if ( *putptr != '\000' )              /*check for put data after preamble*/
11372:    {
11373:    /* --- first squeeze preamble out of put expression --- */
11374:    if ( *pream != '\000' )              /* have preamble */
11375:      {strsqueezep(putexpr,putptr);}     /* squeeze it out */
11376:    /* --- interpret x,y --- */
11377:    if ( (multptr=strchr(putexpr,';')) != NULL ) /*semicolon signals multiput*/
11378:      *multptr = '\000';                 /* replace semicolon by '\0' */
11379:    if ( (putptr=strchr(putexpr,',')) != NULL ) /* comma separates x,y */
11380:      *putptr = '\000';                  /* replace comma by '\0'  */
11381:    if ( *putexpr != '\000' )            /* leading , may be placeholder */
11382:      x = (double)(eround(putexpr));     /* x coord in pixels*/
11383:    if ( putptr != NULL )                /* 2nd arg, if present, is y coord */
11384:      y = (double)(eround(putptr+1));    /* in pixels */
11385:    /* --- interpret xinc,yinc,num if we have a multiput --- */
11386:    if ( multptr != NULL )               /* found ';' signalling multiput */
11387:      {
11388:      if ( (preptr=strchr(multptr+1,';')) != NULL ) /* ';' preceding num arg*/
11389:        *preptr = '\000';                /* replace ';' by '\0' */
11390:      if ( (putptr=strchr(multptr+1,',')) != NULL ) /* ',' between xinc,yinc*/
11391:        *putptr = '\000';                /* replace ',' by '\0' */
11392:      if ( *(multptr+1) != '\000' )      /* leading , may be placeholder */
11393:        xinc = (double)(eround(multptr+1)); /* xinc in pixels */
11394:      if ( putptr != NULL )              /* 2nd arg, if present, is yinc */
11395:        yinc = (double)(eround(putptr+1)); /* in user pixels */
11396:      num = (preptr==NULL? 999 : atoi(preptr+1)); /*explicit num val or 999*/
11397:      } /* --- end-of-if(multptr!=NULL) --- */
11398:    } /* --- end-of-if(*preptr!='\000') --- */
11399:   if ( msgfp!=NULL && msglevel>=29 )    /* debugging */
11400:     fprintf(msgfp,
11401:     "picture> pream;x,y;xinc,yinc;num=\"%s\";%.2f,%.2f;%.2f,%.2f;%d\n",
11402:     pream,x,y,xinc,yinc,num);
11403:   /* -----------------------------------------------------------------------
11404:   now obtain {...} picture element following [multi]put, and rasterize it
11405:   ------------------------------------------------------------------------ */
11406:   /* --- parse for {...} picture element and bump picptr past it --- */
11407:   picptr = texsubexpr(picptr,picelem,1023,"{","}",0,0);
11408:   if ( *picelem == '\000' ) goto end_of_job; /* couldn't get {pic_elem} */
11409:   if ( msgfp!=NULL && msglevel>=29 )    /* debugging */
11410:     fprintf(msgfp, "picture> picelem=\"%.50s\"\n",picelem);
11411:   /* --- rasterize picture element --- */
11412:   origin = 0;                           /* init origin as working param */
11413:   workingparam = &origin;               /* and point working param to it */
11414:   picelemsp = rasterize(picelem,size);  /* rasterize picture element */
11415:   if ( picelemsp == NULL ) continue;    /* failed to rasterize, skip elem */
11416:   ewidth  = (picelemsp->image)->width;  /* width of element, in pixels */
11417:   eheight = (picelemsp->image)->height; /* height of element, in pixels */
11418:   if ( origin == 55 ) iscenter = 1;     /* origin set to (.5,.5) for center*/
11419:   if ( msgfp!=NULL && msglevel>=29 )    /* debugging */
11420:     { fprintf(msgfp, "picture> ewidth,eheight,origin,num=%d,%d,%d,%d\n",
11421:       ewidth,eheight,origin,num);
11422:       if ( msglevel >= 999 ) type_raster(picelemsp->image,msgfp); }
11423:   /* -----------------------------------------------------------------------
11424:   embed element in picture (once, or multiple times if requested)
11425:   ------------------------------------------------------------------------ */
11426:   for ( inum=0; inum<num; inum++ )      /* once, or for num repetitions */
11427:     {
11428:     /* --- set x,y-coords for this iteration --- */
11429:     ix = iround(x);  iy = iround(y);    /* round x,y to nearest integer */
11430:     if ( iscenter )                     /* place center of element at x,y */
11431:       { xpos = ix - ewidth/2;           /* x picture coord to center elem */
11432:         ypos = height - iy - eheight/2; } /* y pixel coord to center elem */
11433:     else                                /* default places lower-left at x,y*/
11434:       { xpos = ix;                      /* set x pixel coord for left */
11435:         if ( origin==10 || origin==11 ) /* x,yinc's are -+ or -- */
11436:           xpos = ix - ewidth;           /* so set for right instead */
11437:         ypos = height - iy - eheight;   /* set y pixel coord for lower */
11438:         if ( origin==1 || origin==11 )  /* x,yinc's are +- or -- */
11439:           ypos = height - iy; }         /* so set for upper instead */
11440:     if ( msgfp!=NULL && msglevel>=29 )  /* debugging */
11441:       fprintf(msgfp,
11442:       "picture> inum,x,y,ix,iy,xpos,ypos=%d,%.2f,%.2f,%d,%d,%d,%d\n",
11443:       inum,x,y,ix,iy,xpos,ypos);
11444:     /* --- embed token raster at xpos,ypos, and quit if out-of-bounds --- */
11445:     if ( !rastput(picturerp,picelemsp->image,ypos,xpos,0) ) break;
11446:     /* --- apply increment --- */
11447:     if ( xinc==0. && yinc==0. ) break;  /* quit if both increments zero */
11448:     x += xinc;  y += yinc;              /* increment coords for next iter */
11449:     } /* --- end-of-for(inum) --- */
11450:   /* --- free picture element subraster after embedding it in picture --- */
11451:   delete_subraster(picelemsp);          /* done with subraster, so free it */
11452:   } /* --- end-of-while(*picptr!=0) --- */
11453: /* -------------------------------------------------------------------------
11454: return picture constructed from pic_elements to caller
11455: -------------------------------------------------------------------------- */
11456: end_of_job:
11457:   workingbox = oldworkingbox;           /* restore original working box */
11458:   workingparam = oldworkingparam;       /* restore original working param */
11459:   return ( picturesp );                 /* return our picture to caller */
11460: } /* --- end-of-function rastpicture() --- */
11461: 
11462: 
11463: /* ==========================================================================
11464:  * Function:    rastline ( expression, size, basesp, arg1, arg2, arg3 )
11465:  * Purpose:     \line handler, returns subraster corresponding to line
11466:  *              parameters (xinc,yinc){xlen}
11467:  * --------------------------------------------------------------------------
11468:  * Arguments:   expression (I/O) char **  to first char of null-terminated
11469:  *                              string immediately following \line to be
11470:  *                              rasterized, and returning ptr immediately
11471:  *                              following last character processed.
11472:  *              size (I)        int containing 0-7 default font size
11473:  *              basesp (I)      subraster *  to character (or subexpression)
11474:  *                              immediately preceding \line
11475:  *                              (unused, but passed for consistency)
11476:  *              arg1 (I)        int unused
11477:  *              arg2 (I)        int unused
11478:  *              arg3 (I)        int unused
11479:  * --------------------------------------------------------------------------
11480:  * Returns:     ( subraster * ) ptr to subraster corresponding to line
11481:  *                              requested, or NULL for any parsing error
11482:  * --------------------------------------------------------------------------
11483:  * Notes:     o Summary of syntax...
11484:  *                \line(xinc,yinc){xlen}
11485:  *            o if {xlen} not given, then it's assumed xlen = |xinc|
11486:  * ======================================================================= */
11487: /* --- entry point --- */
11488: FUNCSCOPE subraster *rastline(char **expression, int size, subraster *basesp,
11489:                               int arg1, int arg2, int arg3)
11490: {
11491: /* -------------------------------------------------------------------------
11492: Allocations and Declarations
11493: -------------------------------------------------------------------------- */
11494: char    /**texsubexpr(),*/linexpr[257], *xptr=linexpr;/*line(xinc,yinc){xlen}*/
11495: subraster /**new_subraster(),*/ *linesp=NULL; /* subraster for line */
11496: /*char  *origexpression = *expression;*/ /*original expression after \line*/
11497: int     pixsz = 1;                      /* pixels are one bit each */
11498: int     thickness = 1;                  /* line thickness */
11499: double  xinc=0.0, yinc=0.0,             /* x,y-increments for line, */
11500:         xlen=0.0, ylen=0.0;             /* x,y lengths for line */
11501: int     width=0,  height=0,             /* #pixels width,height of line */
11502:         maxwidth=1600, maxheight=1600,  /* max width,height in pixels */
11503:         rwidth=0, rheight=0;            /*alloc width,height plus thickness*/
11504: /*int   evalterm();*/                   /* evaluate [arg] and {arg}'s */
11505: int     istop=0,  isright=0,            /* origin at bot-left if x,yinc>=0 */
11506:         origin = 0;                     /* x,yinc: ++=00 +-=01 -+=10 --=11 */
11507: /*int   line_raster();*/                /* draw line in linesp->image */
11508: /* -------------------------------------------------------------------------
11509: obtain (xinc,yinc) arguments immediately following \line command
11510: -------------------------------------------------------------------------- */
11511: /* --- parse for (xinc,yinc) arguments, and bump expression past it --- */
11512: *expression = texsubexpr(*expression,linexpr,253,"(",")",0,0);
11513: if ( *linexpr == '\000' ) goto end_of_job; /* couldn't get (xinc,yinc) */
11514: /* --- now interpret xinc,yinc;thickness returned in linexpr --- */
11515: if ( (xptr=strchr(linexpr,';')) != NULL ) /* look for ';' after xinc,yinc */
11516:   { *xptr = '\000';                     /* terminate linexpr at ; */
11517:     thickness = evalterm(mimestore,xptr+1); } /* get int thickness */
11518: if ( (xptr=strchr(linexpr,',')) != NULL ) /* look for ',' in xinc,yinc */
11519:   *xptr = '\000';                       /* found it, so replace ',' by '\0'*/
11520: if ( *linexpr != '\000' )               /* check against missing 1st arg */
11521:   xinc = xlen = (double)evalterm(mimestore,linexpr); /* xinc in user units */
11522: if ( xptr != NULL )                     /* 2nd arg, if present, is yinc */
11523:   yinc = ylen = (double)evalterm(mimestore,xptr+1); /* in user units */
11524: /* -------------------------------------------------------------------------
11525: obtain optional {xlen} following (xinc,yinc), and calculate ylen
11526: -------------------------------------------------------------------------- */
11527: /* --- check if {xlen} given --- */
11528: if ( *(*expression) == '{' )            /*have {xlen} if leading char is { */
11529:   {
11530:   /* --- parse {xlen} and bump expression past it, interpret as double --- */
11531:   *expression = texsubexpr(*expression,linexpr,253,"{","}",0,0);
11532:   if ( *linexpr == '\000' ) goto end_of_job; /* couldn't get {xlen} */
11533:   xlen = (double)evalterm(mimestore,linexpr); /* xlen in user units */
11534:   /* --- set other values accordingly --- */
11535:   if ( xlen  < 0.0 ) xinc = -xinc;      /* if xlen negative, flip xinc sign*/
11536:   if ( xinc != 0.0 ) ylen = xlen*yinc/xinc; /* set ylen from xlen and slope*/
11537:   else xlen  = 0.0;                     /* can't have xlen if xinc=0 */
11538:   } /* --- end-of-if(*(*expression)=='{') --- */
11539: /* -------------------------------------------------------------------------
11540: calculate width,height, etc, based on xlen,ylen, etc
11541: -------------------------------------------------------------------------- */
11542: /* --- force lengths positive --- */
11543: xlen = absval(xlen);                    /* force xlen positive */
11544: ylen = absval(ylen);                    /* force ylen positive */
11545: /* --- calculate corresponding lengths in pixels --- */
11546: width   = max2(1,iround(unitlength*xlen)); /*scale by unitlength and round,*/
11547: height  = max2(1,iround(unitlength*ylen)); /* and must be at least 1 pixel */
11548: rwidth  = width  + (ylen<0.001?0:max2(0,thickness-1));
11549: rheight = height + (xlen<0.001?0:max2(0,thickness-1));
11550: /* --- set origin corner, x,yinc's: ++=0=(0,0) +-=1=(0,1) -+=10=(1,0) --- */
11551: if ( xinc < 0.0 ) isright = 1;          /*negative xinc, so corner is (1,?)*/
11552: if ( yinc < 0.0 ) istop = 1;            /*negative yinc, so corner is (?,1)*/
11553: origin = isright*10 + istop;            /* interpret 0=(0,0), 11=(1,1), etc*/
11554: if ( msgfp!=NULL && msglevel>=29 )      /* debugging */
11555:   fprintf(msgfp,"rastline> width,height,origin;x,yinc=%d,%d,%d;%g,%g\n",
11556:   width,height,origin,xinc,yinc);
11557: /* -------------------------------------------------------------------------
11558: allocate subraster and raster for line
11559: -------------------------------------------------------------------------- */
11560: /* --- sanity check on width,height,thickness args --- */
11561: if ( width < 1 ||  width > maxwidth
11562: ||  height < 1 || height > maxheight
11563: ||  thickness<1||thickness>25 ) goto end_of_job;
11564: /* --- allocate and initialize subraster for constructed line --- */
11565: if ( (linesp=new_subraster(rwidth,rheight,pixsz)) /* alloc new subraster */
11566: ==   NULL )  goto end_of_job;           /* quit if failed */
11567: /* --- initialize line subraster parameters --- */
11568: linesp->type = IMAGERASTER;             /* image */
11569: linesp->symdef = NULL;                  /* not applicable for image */
11570: linesp->baseline = height/2 + 2         /* is a little above center good? */
11571:         + (rheight-height)/2;           /* account for line thickness too */
11572: linesp->size = size;                    /* size (probably unneeded) */
11573: /* -------------------------------------------------------------------------
11574: draw the line
11575: -------------------------------------------------------------------------- */
11576: line_raster ( linesp->image,            /* embedded raster image */
11577:         (istop?   0 : height-1),        /* row0, from bottom or top */
11578:         (isright?  width-1 : 0),        /* col0, from left or right */
11579:         (istop?   height-1 : 0),        /* row1, to top or bottom */
11580:         (isright? 0 :  width-1),        /* col1, to right or left */
11581:         thickness );                    /* line thickness (usually 1 pixel)*/
11582: /* -------------------------------------------------------------------------
11583: return constructed line to caller
11584: -------------------------------------------------------------------------- */
11585: end_of_job:
11586:   if ( workingparam != NULL )           /* caller wants origin */
11587:     *workingparam = origin;             /* return origin corner to caller */
11588:   return ( linesp );                    /* return line to caller */
11589: } /* --- end-of-function rastline() --- */
11590: 
11591: 
11592: /* ==========================================================================
11593:  * Function:    rastrule ( expression, size, basesp, arg1, arg2, arg3 )
11594:  * Purpose:     \rule handler, returns subraster corresponding to rule
11595:  *              parameters [lift]{width}{height}
11596:  * --------------------------------------------------------------------------
11597:  * Arguments:   expression (I/O) char **  to first char of null-terminated
11598:  *                              string immediately following \rule to be
11599:  *                              rasterized, and returning ptr immediately
11600:  *                              following last character processed.
11601:  *              size (I)        int containing 0-7 default font size
11602:  *              basesp (I)      subraster *  to character (or subexpression)
11603:  *                              immediately preceding \rule
11604:  *                              (unused, but passed for consistency)
11605:  *              arg1 (I)        int unused
11606:  *              arg2 (I)        int unused
11607:  *              arg3 (I)        int unused
11608:  * --------------------------------------------------------------------------
11609:  * Returns:     ( subraster * ) ptr to subraster corresponding to rule
11610:  *                              requested, or NULL for any parsing error
11611:  * --------------------------------------------------------------------------
11612:  * Notes:     o Summary of syntax...
11613:  *                \rule[lift]{width}{height}
11614:  *            o if [lift] not given, then bottom of rule on baseline
11615:  *            o if width=0 then you get an invisible strut 1 (one) pixel wide
11616:  * ======================================================================= */
11617: /* --- entry point --- */
11618: FUNCSCOPE subraster *rastrule(char **expression, int size, subraster *basesp,
11619:                               int arg1, int arg2, int arg3)
11620: {
11621: /* -------------------------------------------------------------------------
11622: Allocations and Declarations
11623: -------------------------------------------------------------------------- */
11624: char    /**texsubexpr(),*/ rulexpr[257]; /* rule[lift]{wdth}{hgt} */
11625: subraster /**new_subraster(),*/ *rulesp=NULL; /* subraster for rule */
11626: int     pixsz = 1;                      /* pixels are one bit each */
11627: int     lift=0, width=0, height=0;      /* default rule parameters */
11628: double  dval;                           /* convert ascii params to doubles */
11629: int     rwidth=0, rheight=0,            /* alloc width, height plus lift */
11630:         maxwidth=1600, maxheight=1600;  /* max width,height in pixels */
11631: /*int   rule_raster();*/                /* draw rule in rulesp->image */
11632: /*int   evalterm();*/                   /* evaluate args */
11633: /* -------------------------------------------------------------------------
11634: Obtain lift,width,height
11635: -------------------------------------------------------------------------- */
11636: /* --- check for optional lift arg  --- */
11637: if ( *(*expression) == '[' )            /*check for []-enclosed optional arg*/
11638:   { *expression = texsubexpr(*expression,rulexpr,255,"[","]",0,0);
11639:     dval = evalterm(mimestore,rulexpr); /* convert [lift] to int */
11640:     if ( dval <= 99 && dval >= (-99) )  /* sanity check */
11641:       lift = iround(unitlength*dval); } /* scale by unitlength and round */
11642: /* --- parse for width --- */
11643: *expression = texsubexpr(*expression,rulexpr,255,"{","}",0,0);
11644: if ( *rulexpr == '\000' ) goto end_of_job; /* quit if args missing */
11645: dval = evalterm(mimestore,rulexpr);     /* convert {width} to int */
11646: if ( dval <= 500 && dval >= 0 )         /* sanity check */
11647:   width = max2(0,iround(unitlength*dval)); /* scale by unitlength and round*/
11648: /* --- parse for height --- */
11649: *expression = texsubexpr(*expression,rulexpr,255,"{","}",0,0);
11650: if ( *rulexpr == '\000' ) goto end_of_job; /* quit if args missing */
11651: dval = evalterm(mimestore,rulexpr);     /* convert {height} to int */
11652: if ( dval <= 500 && dval > 0 )          /* sanity check */
11653:   height= max2(1,iround(unitlength*dval)); /* scale by unitlength and round*/
11654: /* --- raster width,height in pixels --- */
11655: rwidth  = max2(1,width);                /* raster must be at least 1 pixel*/
11656: rheight = height + (lift>=0?lift:       /* raster height plus lift */
11657:   (-lift<height?0:-lift-height+1));     /* may need empty space above rule */
11658: /* -------------------------------------------------------------------------
11659: allocate subraster and raster for rule
11660: -------------------------------------------------------------------------- */
11661: /* --- sanity check on width,height,thickness args --- */
11662: if ( rwidth < 1 ||  rwidth > maxwidth
11663: ||  rheight < 1 || rheight > maxheight ) goto end_of_job;
11664: /* --- allocate and initialize subraster for constructed rule --- */
11665: if ( (rulesp=new_subraster(rwidth,rheight,pixsz)) /* alloc new subraster */
11666: ==   NULL )  goto end_of_job;           /* quit if failed */
11667: /* --- initialize line subraster parameters --- */
11668: rulesp->type = IMAGERASTER;             /* image */
11669: rulesp->symdef = NULL;                  /* not applicable for image */
11670: rulesp->baseline = rheight-1 + (lift>=0?0:lift); /*adjust baseline for lift*/
11671: rulesp->size = size;                    /* size (probably unneeded) */
11672: /* -------------------------------------------------------------------------
11673: draw the rule
11674: -------------------------------------------------------------------------- */
11675: rule_raster ( rulesp->image,            /* embedded raster image */
11676:         (-lift<height?0:rheight-height), /* topmost row for top-left corner*/
11677:         0,                              /* leftmost col for top-left corner*/
11678:         width,                          /* rule width */
11679:         height,                         /* rule height */
11680:         ( width>0? 0:4 ) );             /* rule type */
11681: /* -------------------------------------------------------------------------
11682: return constructed rule to caller
11683: -------------------------------------------------------------------------- */
11684: end_of_job:
11685:   return ( rulesp );                    /* return rule to caller */
11686: } /* --- end-of-function rastrule() --- */
11687: 
11688: 
11689: /* ==========================================================================
11690:  * Function:    rastcircle ( expression, size, basesp, arg1, arg2, arg3 )
11691:  * Purpose:     \circle handler, returns subraster corresponding to ellipse
11692:  *              parameters (xdiam[,ydiam])
11693:  * --------------------------------------------------------------------------
11694:  * Arguments:   expression (I/O) char **  to first char of null-terminated
11695:  *                              string immediately following \circle to be
11696:  *                              rasterized, and returning ptr immediately
11697:  *                              following last character processed.
11698:  *              size (I)        int containing 0-7 default font size
11699:  *              basesp (I)      subraster *  to character (or subexpression)
11700:  *                              immediately preceding \circle
11701:  *                              (unused, but passed for consistency)
11702:  *              arg1 (I)        int unused
11703:  *              arg2 (I)        int unused
11704:  *              arg3 (I)        int unused
11705:  * --------------------------------------------------------------------------
11706:  * Returns:     ( subraster * ) ptr to subraster corresponding to ellipse
11707:  *                              requested, or NULL for any parsing error
11708:  * --------------------------------------------------------------------------
11709:  * Notes:     o Summary of syntax...
11710:  *                \circle(xdiam[,ydiam])
11711:  *            o
11712:  * ======================================================================= */
11713: /* --- entry point --- */
11714: FUNCSCOPE subraster *rastcircle ( char **expression, int size,
11715:                             subraster *basesp, int arg1, int arg2, int arg3 )
11716: {
11717: /* -------------------------------------------------------------------------
11718: Allocations and Declarations
11719: -------------------------------------------------------------------------- */
11720: char    /**texsubexpr(),*/circexpr[512],*xptr=circexpr;/*circle(xdiam[,ydiam])*/
11721: char    *qptr=NULL, quads[256]="1234";  /* default to draw all quadrants */
11722: double  theta0=0.0, theta1=0.0;         /* ;theta0,theta1 instead of ;quads*/
11723: subraster /**new_subraster(),*/ *circsp=NULL; /* subraster for ellipse */
11724: int     pixsz = 1;                      /* pixels are one bit each */
11725: double  xdiam=0.0, ydiam=0.0;           /* x,y major/minor axes/diameters */
11726: int     width=0,  height=0,             /* #pixels width,height of ellipse */
11727:         maxwidth=1600, maxheight=1600;  /* max width,height in pixels */
11728: int     thickness = 1;                  /* drawn lines are one pixel thick */
11729: /*int   evalterm();*/                   /* evaluate [arg],{arg} expressions*/
11730: int     origin = 55;                    /* force origin centered */
11731: /*int   circle_raster(),*/              /* draw ellipse in circsp->image */
11732: /*      circle_recurse();*/             /* for theta0,theta1 args */
11733: /* -------------------------------------------------------------------------
11734: obtain (xdiam[,ydiam]) arguments immediately following \circle command
11735: -------------------------------------------------------------------------- */
11736: /* --- parse for (xdiam[,ydiam]) args, and bump expression past it --- */
11737: *expression = texsubexpr(*expression,circexpr,500,"(",")",0,0);
11738: if ( *circexpr == '\000' ) goto end_of_job; /* couldn't get (xdiam[,ydiam])*/
11739: /* --- now interpret xdiam[,ydiam] returned in circexpr --- */
11740: if ( (qptr=strchr(circexpr,';')) != NULL ) /* semicolon signals quads data */
11741:   { *qptr = '\000';                     /* replace semicolon by '\0' */
11742:     strninit(quads,qptr+1,128);         /* save user-requested quads */
11743:     if ( (qptr=strchr(quads,',')) != NULL ) /* have theta0,theta1 instead */
11744:       { *qptr = '\000';                 /* replace , with null */
11745:         theta0 = (double)evalterm(mimestore,quads);  /* theta0 precedes , */
11746:         theta1 = (double)evalterm(mimestore,qptr+1); /* theta1 follows , */
11747:         qptr = NULL; }                  /* signal thetas instead of quads */
11748:     else
11749:         qptr = quads; }                 /* set qptr arg for circle_raster()*/
11750: else                                    /* no ;quads at all */
11751:   qptr = quads;                         /* default to all 4 quadrants */
11752: if ( (xptr=strchr(circexpr,',')) != NULL ) /* look for ',' in xdiam[,ydiam]*/
11753:   *xptr = '\000';                       /* found it, so replace ',' by '\0'*/
11754: xdiam = ydiam =                         /* xdiam=ydiam in user units */
11755:   (double)evalterm(mimestore,circexpr); /* evaluate expression */
11756: if ( xptr != NULL )                     /* 2nd arg, if present, is ydiam */
11757:   ydiam = (double)evalterm(mimestore,xptr+1); /* in user units */
11758: /* -------------------------------------------------------------------------
11759: calculate width,height, etc
11760: -------------------------------------------------------------------------- */
11761: /* --- calculate width,height in pixels --- */
11762: width  = max2(1,iround(unitlength*xdiam)); /*scale by unitlength and round,*/
11763: height = max2(1,iround(unitlength*ydiam)); /* and must be at least 1 pixel */
11764: if ( msgfp!=NULL && msglevel>=29 )      /* debugging */
11765:   fprintf(msgfp,"rastcircle> width,height;quads=%d,%d,%s\n",
11766:   width,height,(qptr==NULL?"default":qptr));
11767: /* -------------------------------------------------------------------------
11768: allocate subraster and raster for complete picture
11769: -------------------------------------------------------------------------- */
11770: /* --- sanity check on width,height args --- */
11771: if ( width < 1 ||  width > maxwidth
11772: ||  height < 1 || height > maxheight ) goto end_of_job;
11773: /* --- allocate and initialize subraster for constructed ellipse --- */
11774: if ( (circsp=new_subraster(width,height,pixsz)) /* allocate new subraster */
11775: ==   NULL )  goto end_of_job;           /* quit if failed */
11776: /* --- initialize ellipse subraster parameters --- */
11777: circsp->type = IMAGERASTER;             /* image */
11778: circsp->symdef = NULL;                  /* not applicable for image */
11779: circsp->baseline = height/2 + 2;        /* is a little above center good? */
11780: circsp->size = size;                    /* size (probably unneeded) */
11781: /* -------------------------------------------------------------------------
11782: draw the ellipse
11783: -------------------------------------------------------------------------- */
11784: if ( qptr != NULL )                     /* have quads */
11785:   circle_raster ( circsp->image,        /* embedded raster image */
11786:         0, 0,                           /* row0,col0 are upper-left corner */
11787:         height-1, width-1,              /* row1,col1 are lower-right */
11788:         thickness,                      /* line thickness is 1 pixel */
11789:         qptr );                         /* "1234" quadrants to be drawn */
11790: else                                    /* have theta0,theta1 */
11791:   circle_recurse ( circsp->image,       /* embedded raster image */
11792:         0, 0,                           /* row0,col0 are upper-left corner */
11793:         height-1, width-1,              /* row1,col1 are lower-right */
11794:         thickness,                      /* line thickness is 1 pixel */
11795:         theta0,theta1 );                /* theta0,theta1 arc to be drawn */
11796: /* -------------------------------------------------------------------------
11797: return constructed ellipse to caller
11798: -------------------------------------------------------------------------- */
11799: end_of_job:
11800:   if ( workingparam != NULL )           /* caller wants origin */
11801:     *workingparam = origin;             /* return center origin to caller */
11802:   return ( circsp );                    /* return ellipse to caller */
11803: } /* --- end-of-function rastcircle() --- */
11804: 
11805: 
11806: /* ==========================================================================
11807:  * Function:    rastbezier ( expression, size, basesp, arg1, arg2, arg3 )
11808:  * Purpose:     \bezier handler, returns subraster corresponding to bezier
11809:  *              parameters (col0,row0)(col1,row1)(colt,rowt)
11810:  * --------------------------------------------------------------------------
11811:  * Arguments:   expression (I/O) char **  to first char of null-terminated
11812:  *                              string immediately following \bezier to be
11813:  *                              rasterized, and returning ptr immediately
11814:  *                              following last character processed.
11815:  *              size (I)        int containing 0-7 default font size
11816:  *              basesp (I)      subraster *  to character (or subexpression)
11817:  *                              immediately preceding \bezier
11818:  *                              (unused, but passed for consistency)
11819:  *              arg1 (I)        int unused
11820:  *              arg2 (I)        int unused
11821:  *              arg3 (I)        int unused
11822:  * --------------------------------------------------------------------------
11823:  * Returns:     ( subraster * ) ptr to subraster corresponding to bezier
11824:  *                              requested, or NULL for any parsing error
11825:  * --------------------------------------------------------------------------
11826:  * Notes:     o Summary of syntax...
11827:  *                \bezier(col1,row1)(colt,rowt)
11828:  *            o col0=0,row0=0 assumed, i.e., given by
11829:  *              \picture(){~(col0,row0){\bezier(col1,row1)(colt,rowt)}~}
11830:  * ======================================================================= */
11831: /* --- entry point --- */
11832: FUNCSCOPE subraster *rastbezier ( char **expression, int size,
11833:                             subraster *basesp, int arg1, int arg2, int arg3 )
11834: {
11835: /* -------------------------------------------------------------------------
11836: Allocations and Declarations
11837: -------------------------------------------------------------------------- */
11838: subraster *new_subraster(), *bezsp=NULL; /* subraster for bezier */
11839: char    /**texsubexpr(),*/bezexpr[129],*xptr=bezexpr;/*\bezier(r,c)(r,c)(r,c)*/
11840: double  r0=0.0,c0=0.0, r1=0.0,c1=0.0, rt=0.0,ct=0.0, /* bezier points */
11841:         rmid=0.0, cmid=0.0,             /* coords at parameterized midpoint*/
11842:         rmin=0.0, cmin=0.0,             /* minimum r,c */
11843:         rmax=0.0, cmax=0.0,             /* maximum r,c */
11844:         rdelta=0.0, cdelta=0.0,         /* rmax-rmin, cmax-cmin */
11845:         r=0.0, c=0.0;                   /* some point */
11846: /*int   evalterm();*/                   /* evaluate [arg],{arg} expressions*/
11847: int     iarg=0;                         /* 0=r0,c0 1=r1,c1 2=rt,ct */
11848: int     width=0, height=0,              /* dimensions of bezier raster */
11849:         maxwidth=1600, maxheight=1600;  /* max width,height in pixels */
11850: int     pixsz = 1;                      /* pixels are one bit each */
11851: /*int   thickness = 1;*/                /* drawn lines are one pixel thick */
11852: int     origin = 0;                     /*c's,r's reset to lower-left origin*/
11853: /*int   bezier_raster();*/              /* draw bezier in bezsp->image */
11854: /* -------------------------------------------------------------------------
11855: obtain (c1,r1)(ct,rt) args immediately following \bezier command
11856: -------------------------------------------------------------------------- */
11857: for ( iarg=1; iarg<=2; iarg++ )         /* 0=c0,r0 1=c1,r1 2=ct,rt */
11858:   {
11859:   /* --- parse for (r,c) args, and bump expression past them all --- */
11860:   *expression = texsubexpr(*expression,bezexpr,127,"(",")",0,0);
11861:   if ( *bezexpr == '\000' ) goto end_of_job; /* couldn't get (r,c)*/
11862:   /* --- now interpret (r,c) returned in bezexpr --- */
11863:   c = r = 0.0;                          /* init x-coord=col, y-coord=row */
11864:   if ( (xptr=strchr(bezexpr,',')) != NULL ) /* comma separates row,col */
11865:     { *xptr = '\000';                   /* found it, so replace ',' by '\0'*/
11866:       /* --- row=y-coord in pixels --- */
11867:       r = unitlength*((double)evalterm(mimestore,xptr+1)); }
11868:   /* --- col=x-coord in pixels --- */
11869:   c = unitlength*((double)evalterm(mimestore,bezexpr));
11870:   /* --- store r,c --- */
11871:   switch ( iarg )
11872:     { case 0: r0=r; c0=c; break;
11873:       case 1: r1=r; c1=c; break;
11874:       case 2: rt=r; ct=c; break; }
11875:   } /* --- end-of-for(iarg) --- */
11876: /* --- determine midpoint and maximum,minimum points --- */
11877: rmid = 0.5*(rt + 0.5*(r0+r1));          /* y-coord at middle of bezier */
11878: cmid = 0.5*(ct + 0.5*(c0+c1));          /* x-coord at middle of bezier */
11879: rmin = min3(r0,r1,rmid);                /* lowest row */
11880: cmin = min3(c0,c1,cmid);                /* leftmost col */
11881: rmax = max3(r0,r1,rmid);                /* highest row */
11882: cmax = max3(c0,c1,cmid);                /* rightmost col */
11883: rdelta = rmax-rmin;                     /* height */
11884: cdelta = cmax-cmin;                     /* width */
11885: /* --- rescale coords so we start at 0,0 --- */
11886: r0 -= rmin;  c0 -= cmin;                /* rescale r0,c0 */
11887: r1 -= rmin;  c1 -= cmin;                /* rescale r1,c1 */
11888: rt -= rmin;  ct -= cmin;                /* rescale rt,ct */
11889: /* --- flip rows so 0,0 becomes lower-left corner instead of upper-left--- */
11890: r0 = rdelta - r0 + 1;                   /* map 0-->height-1, height-1-->0 */
11891: r1 = rdelta - r1 + 1;
11892: rt = rdelta - rt + 1;
11893: /* --- determine width,height of raster needed for bezier --- */
11894: width  = (int)(cdelta + 0.9999) + 1;    /* round width up */
11895: height = (int)(rdelta + 0.9999) + 1;    /* round height up */
11896: if ( msgfp!=NULL && msglevel>=29 )      /* debugging */
11897:   fprintf(msgfp,"rastbezier> width,height,origin=%d,%d,%d; c0,r0=%g,%g; "
11898:   "c1,r1=%g,%g\n rmin,mid,max=%g,%g,%g; cmin,mid,max=%g,%g,%g\n",
11899:   width,height,origin, c0,r0, c1,r1, rmin,rmid,rmax, cmin,cmid,cmax);
11900: /* -------------------------------------------------------------------------
11901: allocate raster
11902: -------------------------------------------------------------------------- */
11903: /* --- sanity check on width,height args --- */
11904: if ( width < 1 ||  width > maxwidth
11905: ||  height < 1 || height > maxheight ) goto end_of_job;
11906: /* --- allocate and initialize subraster for constructed bezier --- */
11907: if ( (bezsp=new_subraster(width,height,pixsz)) /* allocate new subraster */
11908: ==   NULL )  goto end_of_job;           /* quit if failed */
11909: /* --- initialize bezier subraster parameters --- */
11910: bezsp->type = IMAGERASTER;              /* image */
11911: bezsp->symdef = NULL;                   /* not applicable for image */
11912: bezsp->baseline = height/2 + 2;         /* is a little above center good? */
11913: bezsp->size = size;                     /* size (probably unneeded) */
11914: /* -------------------------------------------------------------------------
11915: draw the bezier
11916: -------------------------------------------------------------------------- */
11917: bezier_raster ( bezsp->image,           /* embedded raster image */
11918:         r0, c0,                         /* row0,col0 are lower-left corner */
11919:         r1, c1,                         /* row1,col1 are upper-right */
11920:         rt, ct );                       /* bezier tangent point */
11921: /* -------------------------------------------------------------------------
11922: return constructed bezier to caller
11923: -------------------------------------------------------------------------- */
11924: end_of_job:
11925:   if ( workingparam != NULL )           /* caller wants origin */
11926:     *workingparam = origin;             /* return center origin to caller */
11927:   return ( bezsp );                     /* return bezier to caller */
11928: } /* --- end-of-function rastbezier() --- */
11929: 
11930: 
11931: /* ==========================================================================
11932:  * Function:    rastraise ( expression, size, basesp, arg1, arg2, arg3 )
11933:  * Purpose:     \raisebox{lift}{subexpression} handler, returns subraster
11934:  *              containing subexpression with its baseline "lifted" by lift
11935:  *              pixels, scaled by \unitlength, or "lowered" if lift arg
11936:  *              negative
11937:  * --------------------------------------------------------------------------
11938:  * Arguments:   expression (I/O) char **  to first char of null-terminated
11939:  *                              string immediately following \raisebox to be
11940:  *                              rasterized, and returning ptr immediately
11941:  *                              following last character processed.
11942:  *              size (I)        int containing 0-7 default font size
11943:  *              basesp (I)      subraster *  to character (or subexpression)
11944:  *                              immediately preceding \raisebox
11945:  *                              (unused, but passed for consistency)
11946:  *              arg1 (I)        int unused
11947:  *              arg2 (I)        int unused
11948:  *              arg3 (I)        int unused
11949:  * --------------------------------------------------------------------------
11950:  * Returns:     ( subraster * ) ptr to subraster corresponding to \raisebox
11951:  *                              requested, or NULL for any parsing error
11952:  * --------------------------------------------------------------------------
11953:  * Notes:     o Summary of syntax...
11954:  *                \raisebox{lift}{subexpression}
11955:  *            o
11956:  * ======================================================================= */
11957: /* --- entry point --- */
11958: FUNCSCOPE subraster *rastraise(char **expression, int size, subraster *basesp,
11959:                                int arg1, int arg2, int arg3)
11960: {
11961: /* -------------------------------------------------------------------------
11962: Allocations and Declarations
11963: -------------------------------------------------------------------------- */
11964: char    /**texsubexpr(),*/ subexpr[MAXSUBXSZ+1], *liftexpr=subexpr; /*args*/
11965: subraster /**rasterize(),*/ *raisesp=NULL; /*rasterize subexpr to be raised*/
11966: int     lift=0;                         /* amount to raise/lower baseline */
11967: /*int   evalterm();*/                   /* evaluate [arg],{arg} expressions*/
11968: /* -------------------------------------------------------------------------
11969: obtain {lift} argument immediately following \raisebox command
11970: -------------------------------------------------------------------------- */
11971: rastlift = 0;                           /* reset global lift adjustment */
11972: /* --- parse for {lift} arg, and bump expression past it --- */
11973: *expression = texsubexpr(*expression,liftexpr,0,"{","}",0,0);
11974: if ( *liftexpr == '\000' ) goto end_of_job; /* couldn't get {lift} */
11975: lift = eround(liftexpr);                /* {lift} to integer */
11976: if ( abs(lift) > 200 ) lift=0;          /* sanity check */
11977: /* -------------------------------------------------------------------------
11978: obtain {subexpr} argument after {lift}, and rasterize it
11979: -------------------------------------------------------------------------- */
11980: /* --- parse for {subexpr} arg, and bump expression past it --- */
11981: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
11982: /* --- rasterize subexpression to be raised/lowered --- */
11983: if ( (raisesp = rasterize(subexpr,size)) /* rasterize subexpression */
11984: ==   NULL ) goto end_of_job;            /* and quit if failed */
11985: /* -------------------------------------------------------------------------
11986: raise/lower baseline and return it to caller
11987: -------------------------------------------------------------------------- */
11988: /* --- raise/lower baseline --- */
11989: raisesp->baseline += lift;              /* new baseline (no height checks) */
11990: rastlift = lift;                        /* set global to signal adjustment */
11991: /* --- return raised subexpr to caller --- */
11992: end_of_job:
11993:   return ( raisesp );                   /* return raised subexpr to caller */
11994: } /* --- end-of-function rastraise() --- */
11995: 
11996: 
11997: /* ==========================================================================
11998:  * Function:    rastrotate ( expression, size, basesp, arg1, arg2, arg3 )
11999:  * Purpose:     \rotatebox{degrees}{subexpression} handler, returns subraster
12000:  *              containing subexpression rotated by degrees (counterclockwise
12001:  *              if degrees positive)
12002:  * --------------------------------------------------------------------------
12003:  * Arguments:   expression (I/O) char **  to first char of null-terminated
12004:  *                              string immediately following \rotatebox to be
12005:  *                              rasterized, and returning ptr immediately
12006:  *                              following last character processed.
12007:  *              size (I)        int containing 0-7 default font size
12008:  *              basesp (I)      subraster *  to character (or subexpression)
12009:  *                              immediately preceding \rotatebox
12010:  *                              (unused, but passed for consistency)
12011:  *              arg1 (I)        int unused
12012:  *              arg2 (I)        int unused
12013:  *              arg3 (I)        int unused
12014:  * --------------------------------------------------------------------------
12015:  * Returns:     ( subraster * ) ptr to subraster corresponding to \rotatebox
12016:  *                              requested, or NULL for any parsing error
12017:  * --------------------------------------------------------------------------
12018:  * Notes:     o Summary of syntax...
12019:  *                \rotatebox{degrees}{subexpression}
12020:  *            o
12021:  * ======================================================================= */
12022: /* --- entry point --- */
12023: FUNCSCOPE subraster *rastrotate ( char **expression, int size,
12024:                             subraster *basesp, int arg1, int arg2, int arg3 )
12025: {
12026: /* -------------------------------------------------------------------------
12027: Allocations and Declarations
12028: -------------------------------------------------------------------------- */
12029: char    /**texsubexpr(),*/ subexpr[MAXSUBXSZ+1], *degexpr=subexpr; /*args*/
12030: subraster /**rasterize(),*/ *rotsp=NULL; /* subraster for rotated subexpr */
12031: raster  /**rastrot(),*/ *rotrp=NULL;    /* rotate subraster->image 90 degs */
12032: /*int   delete_raster();*/              /* delete intermediate rasters */
12033: int     baseline=0;                     /* baseline of rasterized image */
12034: double  degrees=0.0, ipart,fpart;       /* degrees to be rotated */
12035: int     idegrees=0, isneg=0;            /* positive ipart, isneg=1 if neg */
12036: int     n90=0, isn90=1;                 /* degrees is n90 multiples of 90 */
12037: /*int   evalterm();*/                   /* evaluate [arg],{arg} expressions*/
12038: /* -------------------------------------------------------------------------
12039: obtain {degrees} argument immediately following \rotatebox command
12040: -------------------------------------------------------------------------- */
12041: /* --- parse for {degrees} arg, and bump expression past it --- */
12042: *expression = texsubexpr(*expression,degexpr,0,"{","}",0,0);
12043: if ( *degexpr == '\000' ) goto end_of_job; /* couldn't get {degrees} */
12044: degrees = (double)evalterm(mimestore,degexpr); /* degrees to be rotated */
12045: if ( degrees < 0.0 )                    /* clockwise rotation desired */
12046:   { degrees = -degrees;                 /* flip sign so degrees positive */
12047:     isneg = 1; }                        /* and set flag to indicate flip */
12048: fpart = modf(degrees,&ipart);           /* integer and fractional parts */
12049: ipart = (double)(((int)degrees)%360);   /* degrees mod 360 */
12050: degrees = ipart + fpart;                /* restore fractional part */
12051: if ( isneg )                            /* if clockwise rotation requested */
12052:   degrees = 360.0 - degrees;            /* do equivalent counterclockwise */
12053: idegrees = (int)(degrees+0.5);          /* integer degrees */
12054: n90 = idegrees/90;                      /* degrees is n90 multiples of 90 */
12055: isn90 = (90*n90==idegrees);             /*true if degrees is multiple of 90*/
12056: isn90 = 1;                              /* forced true for time being */
12057: /* -------------------------------------------------------------------------
12058: obtain {subexpr} argument after {degrees}, and rasterize it
12059: -------------------------------------------------------------------------- */
12060: /* --- parse for {subexpr} arg, and bump expression past it --- */
12061: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
12062: /* --- rasterize subexpression to be rotated --- */
12063: if ( (rotsp = rasterize(subexpr,size))  /* rasterize subexpression */
12064: ==   NULL ) goto end_of_job;            /* and quit if failed */
12065: /* --- return unmodified image if no rotation requested --- */
12066: if ( abs(idegrees) < 2 ) goto end_of_job; /* don't bother rotating image */
12067: /* --- extract params for image to be rotated --- */
12068: rotrp = rotsp->image;                   /* unrotated rasterized image */
12069: baseline = rotsp->baseline;             /* and baseline of that image */
12070: /* -------------------------------------------------------------------------
12071: rotate by multiples of 90 degrees
12072: -------------------------------------------------------------------------- */
12073: if ( isn90 )                            /* rotation by multiples of 90 */
12074:  if ( n90 > 0 )                         /* do nothing for 0 degrees */
12075:   {
12076:   n90 = 4-n90;                          /* rasrot() rotates clockwise */
12077:   while ( n90 > 0 )                     /* still have remaining rotations */
12078:     { raster *nextrp = rastrot(rotrp);  /* rotate raster image */
12079:       if ( nextrp == NULL ) break;      /* something's terribly wrong */
12080:       delete_raster(rotrp);             /* free previous raster image */
12081:       rotrp = nextrp;                   /* and replace it with rotated one */
12082:       n90--; }                          /* decrement remaining count */
12083:   } /* --- end-of-if(isn90) --- */
12084: /* -------------------------------------------------------------------------
12085: requested rotation not multiple of 90 degrees
12086: -------------------------------------------------------------------------- */
12087: if ( !isn90 )                           /* explicitly construct rotation */
12088:   { ; }                                 /* not yet implemented */
12089: /* -------------------------------------------------------------------------
12090: re-populate subraster envelope with rotated image
12091: -------------------------------------------------------------------------- */
12092: /* --- re-init various subraster parameters, embedding raster in it --- */
12093: if ( rotrp != NULL )                    /* rotated raster constructed okay */
12094:  { rotsp->type = IMAGERASTER;           /* signal constructed image */
12095:    rotsp->image = rotrp;                /* raster we just constructed */
12096:    /* --- now try to guess pleasing baseline --- */
12097:    if ( idegrees > 2 ) {                /* leave unchanged if unrotated */
12098:     if ( strlen(subexpr) < 3            /* we rotated a short expression */
12099:     ||   abs(idegrees-180) < 3 )        /* or just turned it upside-down */
12100:       baseline = rotrp->height - 1;     /* so set with nothing descending */
12101:     else                                /* rotated a long expression */
12102:       baseline = (65*(rotrp->height-1))/100; } /* roughly center long expr */
12103:    rotsp->baseline = baseline; }        /* set baseline as calculated above*/
12104: /* --- return rotated subexpr to caller --- */
12105: end_of_job:
12106:   return ( rotsp );                     /*return rotated subexpr to caller*/
12107: } /* --- end-of-function rastrotate() --- */
12108: 
12109: 
12110: /* ==========================================================================
12111:  * Function:    rastmagnify ( expression, size, basesp, arg1, arg2, arg3 )
12112:  * Purpose:     \magnify{magstep}{subexpression} handler, returns subraster
12113:  *              containing magnified subexpression
12114:  * --------------------------------------------------------------------------
12115:  * Arguments:   expression (I/O) char **  to first char of null-terminated
12116:  *                              string immediately following \reflectbox to
12117:  *                              be rasterized, and returning ptr immediately
12118:  *                              following last character processed.
12119:  *              size (I)        int containing 0-7 default font size
12120:  *              basesp (I)      subraster *  to character (or subexpression)
12121:  *                              immediately preceding \reflectbox
12122:  *                              (unused, but passed for consistency)
12123:  *              arg1 (I)        int unused
12124:  *              arg2 (I)        int unused
12125:  *              arg3 (I)        int unused
12126:  * --------------------------------------------------------------------------
12127:  * Returns:     ( subraster * ) ptr to subraster corresponding to \magnify
12128:  *                              requested, or NULL for any parsing error
12129:  * --------------------------------------------------------------------------
12130:  * Notes:     o Summary of syntax...
12131:  *                \magnify{magstep}{subexpression}
12132:  *            o
12133:  * ======================================================================= */
12134: /* --- entry point --- */
12135: FUNCSCOPE subraster *rastmagnify ( char **expression, int size,
12136:                             subraster *basesp, int arg1, int arg2, int arg3 )
12137: {
12138: /* -------------------------------------------------------------------------
12139: Allocations and Declarations
12140: -------------------------------------------------------------------------- */
12141: char    /**texsubexpr(),*/ subexpr[MAXSUBXSZ+1], *magexpr=subexpr; /* args */
12142: subraster /**rasterize(),*/ *magsp=NULL; /* subraster for magnified subexpr */
12143: raster  /**rastmag(),*/ *magrp=NULL;    /* magnify subraster->image */
12144: int     magstep = 1;                    /* default magnification */
12145: /*int   delete_raster();*/              /* delete intermediate raster */
12146: int     baseline=0;                     /* baseline of rasterized image */
12147: /* -------------------------------------------------------------------------
12148: obtain {magstep} argument immediately following \magnify command
12149: -------------------------------------------------------------------------- */
12150: /* --- parse for {magstep} arg, and bump expression past it --- */
12151: *expression = texsubexpr(*expression,magexpr,255,"{","}",0,0);
12152: magstep = atoi(magexpr);                /* convert {magstep} to int */
12153: if ( magstep<1 || magstep>10 )          /* check magstep input */
12154:   magstep = 1;                          /* back to default if illegal */
12155: /* -------------------------------------------------------------------------
12156: obtain {subexpr} argument after {magstep}, and rasterize it
12157: -------------------------------------------------------------------------- */
12158: /* --- parse for {subexpr} arg, and bump expression past it --- */
12159: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
12160: /* --- rasterize subexpression to be reflected --- */
12161: if ( (magsp = rasterize(subexpr,size))  /* rasterize subexpression */
12162: ==   NULL ) goto end_of_job;            /* and quit if failed */
12163: /* --- return unmodified image if no magnification requested --- */
12164: if ( magstep<=1 ) goto end_of_job;      /* don't bother magnifying image */
12165: /* --- extract params for image to be magnified --- */
12166: magrp = magsp->image;                   /* unmagnified rasterized image */
12167: baseline = magsp->baseline;             /* and baseline of that image */
12168: /* -------------------------------------------------------------------------
12169: magnify image and adjust its parameters
12170: -------------------------------------------------------------------------- */
12171: /* --- magnify image --- */
12172: magrp = rastmag(magsp->image,magstep);  /* magnify raster image */
12173: if ( magrp == NULL ) goto end_of_job;   /* failed to magnify image */
12174: delete_raster(magsp->image);            /* free original raster image */
12175: magsp->image = magrp;                   /*and replace it with magnified one*/
12176: /* --- adjust parameters --- */
12177: baseline *= magstep;                    /* scale baseline */
12178: if ( baseline > 0 ) baseline += 1;      /* adjust for no descenders */
12179: magsp->baseline = baseline;             /*reset baseline of magnified image*/
12180: /* --- return magnified subexpr to caller --- */
12181: end_of_job:
12182:   return ( magsp );                     /*back to caller with magnified expr*/
12183: } /* --- end-of-function rastmagnify() --- */
12184: 
12185: 
12186: /* ==========================================================================
12187:  * Function:    rastreflect ( expression, size, basesp, arg1, arg2, arg3 )
12188:  * Purpose:     \reflectbox[axis]{subexpression} handler, returns subraster
12189:  *              containing subexpression reflected horizontally (i.e., around
12190:  *              vertical axis, |_ becomes _|) if [axis] not given or axis=1,
12191:  *              or reflected vertically if axis=2 given.
12192:  * --------------------------------------------------------------------------
12193:  * Arguments:   expression (I/O) char **  to first char of null-terminated
12194:  *                              string immediately following \reflectbox to
12195:  *                              be rasterized, and returning ptr immediately
12196:  *                              following last character processed.
12197:  *              size (I)        int containing 0-7 default font size
12198:  *              basesp (I)      subraster *  to character (or subexpression)
12199:  *                              immediately preceding \reflectbox
12200:  *                              (unused, but passed for consistency)
12201:  *              arg1 (I)        int unused
12202:  *              arg2 (I)        int unused
12203:  *              arg3 (I)        int unused
12204:  * --------------------------------------------------------------------------
12205:  * Returns:     ( subraster * ) ptr to subraster corresponding to \reflectbox
12206:  *                              requested, or NULL for any parsing error
12207:  * --------------------------------------------------------------------------
12208:  * Notes:     o Summary of syntax...
12209:  *                \reflectbox[axis]{subexpression}
12210:  *            o
12211:  * ======================================================================= */
12212: /* --- entry point --- */
12213: FUNCSCOPE subraster *rastreflect ( char **expression, int size,
12214:                             subraster *basesp, int arg1, int arg2, int arg3 )
12215: {
12216: /* -------------------------------------------------------------------------
12217: Allocations and Declarations
12218: -------------------------------------------------------------------------- */
12219: char    /**texsubexpr(),*/ subexpr[MAXSUBXSZ+1], *axisexpr=subexpr; /* args */
12220: subraster /**rasterize(),*/ *refsp=NULL; /* subraster for reflected subexpr */
12221: raster  /**rastref(),*/ *refrp=NULL;    /* reflect subraster->image */
12222: int     axis = 1;                       /* default horizontal reflection */
12223: /*int   delete_raster();*/              /* delete intermediate raster */
12224: int     baseline=0;                     /* baseline of rasterized image */
12225: /* -------------------------------------------------------------------------
12226: obtain [axis] argument immediately following \reflectbox command, if given
12227: -------------------------------------------------------------------------- */
12228: /* --- check for optional [axis] arg  --- */
12229: if ( *(*expression) == '[' )            /*check for []-enclosed optional arg*/
12230:   { *expression = texsubexpr(*expression,axisexpr,255,"[","]",0,0);
12231:     axis = atoi(axisexpr);              /* convert [axis] to int */
12232:     if ( axis<1 || axis>2 )             /* check axis input */
12233:       axis = 1; }                       /* back to default if illegal */
12234: /* -------------------------------------------------------------------------
12235: obtain {subexpr} argument after optional [axis], and rasterize it
12236: -------------------------------------------------------------------------- */
12237: /* --- parse for {subexpr} arg, and bump expression past it --- */
12238: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
12239: /* --- rasterize subexpression to be reflected --- */
12240: if ( (refsp = rasterize(subexpr,size))  /* rasterize subexpression */
12241: ==   NULL ) goto end_of_job;            /* and quit if failed */
12242: /* --- return unmodified image if no reflection requested --- */
12243: if ( axis<1 || axis>2 ) goto end_of_job; /* don't bother reflecting image */
12244: /* --- extract params for image to be reflected --- */
12245: refrp = refsp->image;                   /* unreflected rasterized image */
12246: baseline = refsp->baseline;             /* and baseline of that image */
12247: /* -------------------------------------------------------------------------
12248: reflect image and adjust its parameters
12249: -------------------------------------------------------------------------- */
12250: /* --- reflect image --- */
12251: refrp = rastref(refsp->image,axis);     /* reflect raster image */
12252: if ( refrp == NULL ) goto end_of_job;   /* failed to reflect image */
12253: delete_raster(refsp->image);            /* free original raster image */
12254: refsp->image = refrp;                   /*and replace it with reflected one*/
12255: /* --- adjust parameters --- */
12256: if ( axis == 2 )                        /* for vertical reflection */
12257:   baseline = refrp->height - 1;         /* set with nothing descending */
12258: refsp->baseline = baseline;             /* reset baseline of reflected image*/
12259: /* --- return reflected subexpr to caller --- */
12260: end_of_job:
12261:   return ( refsp );                     /*back to caller with reflected expr*/
12262: } /* --- end-of-function rastreflect() --- */
12263: 
12264: 
12265: /* ==========================================================================
12266:  * Function:    rastfbox ( expression, size, basesp, arg1, arg2, arg3 )
12267:  * Purpose:     \fbox{subexpression} handler, returns subraster
12268:  *              containing subexpression with frame box drawn around it
12269:  * --------------------------------------------------------------------------
12270:  * Arguments:   expression (I/O) char **  to first char of null-terminated
12271:  *                              string immediately following \fbox to be
12272:  *                              rasterized, and returning ptr immediately
12273:  *                              following last character processed.
12274:  *              size (I)        int containing 0-7 default font size
12275:  *              basesp (I)      subraster *  to character (or subexpression)
12276:  *                              immediately preceding \fbox
12277:  *                              (unused, but passed for consistency)
12278:  *              arg1 (I)        int unused
12279:  *              arg2 (I)        int unused
12280:  *              arg3 (I)        int unused
12281:  * --------------------------------------------------------------------------
12282:  * Returns:     ( subraster * ) ptr to subraster corresponding to \fbox
12283:  *                              requested, or NULL for any parsing error
12284:  * --------------------------------------------------------------------------
12285:  * Notes:     o Summary of syntax...
12286:  *                \fbox[width][height]{subexpression}
12287:  *            o
12288:  * ======================================================================= */
12289: /* --- entry point --- */
12290: FUNCSCOPE subraster *rastfbox(char **expression, int size, subraster *basesp,
12291:                               int arg1, int arg2, int arg3)
12292: {
12293: /* -------------------------------------------------------------------------
12294: Allocations and Declarations
12295: -------------------------------------------------------------------------- */
12296: char    /**texsubexpr(),*/ subexpr[MAXSUBXSZ+1], widtharg[512]; /* args */
12297: subraster /**rasterize(),*/ *framesp=NULL; /*rasterize subexpr to be framed*/
12298: raster  /**border_raster(),*/ *bp=NULL; /* framed image raster */
12299: int     /*evalterm(),*/ evalue=0;       /* interpret [width][height] */
12300: int     fwidth=6, fthick=1,             /*extra frame width, line thickness*/
12301:         fsides=0;               /* frame sides: 1=left,2=top,4=right,8=bot */
12302: int     width=(-1), height=(-1),        /* optional [width][height] args */
12303:         iscompose = 0;                  /* set true if optional args given */
12304: /* -------------------------------------------------------------------------
12305: obtain optional [width][height] arguments immediately following \fbox
12306: -------------------------------------------------------------------------- */
12307: /* --- first check for optional \fbox[width] --- */
12308: if ( *(*expression) == '[' ) {          /* check for []-enclosed width arg */
12309:   *expression = texsubexpr(*expression,widtharg,511,"[","]",0,0);
12310:   if ( !isempty(widtharg) ) {           /* got widtharg */
12311:     char *comma = strchr(widtharg,','); /* look for [width,sides] */
12312:     if ( comma == (char *)NULL )        /* no comma */
12313:       comma = strchr(widtharg,';');     /* permit semicolon [width;sides] */
12314:     if ( comma != (char *)NULL ) {      /* optional [width,fsides] found */
12315:       fsides = atoi(comma+1);           /* interpret fsides after comma */
12316:       if ( size < 5 )                   /* for smaller fonts */
12317:         { fwidth = 2;  fthick = 1; }    /* tighten frame, thinner accent */
12318:       else { fwidth = 3;  fthick = 2; } /* loosen frame, thicken accent */
12319:       *comma = '\000';                  /* null-terminate width at comma */
12320:       trimwhite(widtharg); }            /*remove leading/trailing whitespace*/
12321:     if ( comma==(char *)NULL || !isempty(widtharg) ) { /* have a width */
12322:       height = 1;                       /* default explicit height, too */
12323:       if ( fsides == 0 ) {              /* a normal framebox */
12324:         evalue = eround(widtharg);      /* interpret and scale width */
12325:         width = max2(1,evalue);         /* must be >0 */
12326:         fwidth = 2; iscompose = 1; }
12327:       else                              /* absolute pixels for "accents" */
12328:         width = evalterm(mimestore,widtharg); }
12329:     } /* --- end-of-if(!isempty(widtharg)) --- */
12330:   } /* --- end-of-if(**expression=='[') --- */
12331: if ( width > 0 || fsides > 0)           /* found leading [width], so... */
12332:  if ( *(*expression) == '[' )           /* check for []-enclosed height arg */
12333:   { *expression = texsubexpr(*expression,widtharg,511,"[","]",0,0);
12334:     if ( !isempty(widtharg) ) {         /* got widtharg */
12335:       if ( fsides == 0 ) {              /* a normal framebox */
12336:         evalue = eround(widtharg);      /* interpret and scale height */
12337:         height = max2(1,evalue);        /* must be >0 */
12338:         fwidth = 0; }                   /* no extra border */
12339:       else                              /* absolute pixels for "accents" */
12340:         height = evalterm(mimestore,widtharg); }
12341:   } /* --- end-of-if(**expression=='[') --- */
12342: /* -------------------------------------------------------------------------
12343: obtain {subexpr} argument
12344: -------------------------------------------------------------------------- */
12345: /* --- parse for {subexpr} arg, and bump expression past it --- */
12346: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
12347: /* --- rasterize subexpression to be framed --- */
12348: if ( width<0 || height<0 )              /* no explicit dimensions given */
12349:   { if ( (framesp = rasterize(subexpr,size)) /* rasterize subexpression */
12350:     ==   NULL ) goto end_of_job; }      /* and quit if failed */
12351: else
12352:   { char composexpr[8192];              /* compose subexpr with empty box */
12353:     sprintf(composexpr,"\\compose{\\hspace{%d}\\vspace{%d}}{%.8000s}",
12354:     width,height,subexpr);
12355:     if ( (framesp = rasterize(composexpr,size)) /* rasterize subexpression */
12356:     ==   NULL ) goto end_of_job; }      /* and quit if failed */
12357: /* -------------------------------------------------------------------------
12358: draw frame, reset params, and return it to caller
12359: -------------------------------------------------------------------------- */
12360: /* --- draw border --- */
12361: if ( fsides > 0 ) fthick += (100*fsides); /* embed fsides in fthick arg */
12362: if ( (bp = border_raster(framesp->image,-fwidth,-fwidth,fthick,1))
12363: ==   NULL ) goto end_of_job;            /* draw border and quit if failed */
12364: /* --- replace original image and raise baseline to accommodate frame --- */
12365: framesp->image = bp;                    /* replace image with framed one */
12366: if ( !iscompose )                       /* simple border around subexpr */
12367:   framesp->baseline += fwidth;          /* so just raise baseline */
12368: else
12369:   framesp->baseline = (framesp->image)->height - 1; /* set at bottom */
12370: /* --- return framed subexpr to caller --- */
12371: end_of_job:
12372:   return ( framesp );                   /* return framed subexpr to caller */
12373: } /* --- end-of-function rastfbox() --- */
12374: 
12375: 
12376: /* ==========================================================================
12377:  * Function:    rastovalbox ( expression, size, basesp, arg1, arg2, arg3 )
12378:  * Purpose:     \ovalbox{subexpression} handler, returns subraster
12379:  *              containing subexpression with ellipse drawn around it
12380:  * --------------------------------------------------------------------------
12381:  * Arguments:   expression (I/O) char **  to first char of null-terminated
12382:  *                              string immediately following \ovalbox to be
12383:  *                              rasterized, and returning ptr immediately
12384:  *                              following last character processed.
12385:  *              size (I)        int containing 0-7 default font size
12386:  *              basesp (I)      subraster *  to character (or subexpression)
12387:  *                              immediately preceding \ovalbox
12388:  *                              (unused, but passed for consistency)
12389:  *              arg1 (I)        int unused
12390:  *              arg2 (I)        int unused
12391:  *              arg3 (I)        int unused
12392:  * --------------------------------------------------------------------------
12393:  * Returns:     ( subraster * ) ptr to subraster corresponding to \ovalbox
12394:  *                              requested, or NULL for any parsing error
12395:  * --------------------------------------------------------------------------
12396:  * Notes:     o Summary of syntax...
12397:  *                \ovalbox[n;#ovals,wdelta,hdelta]{subexpression}
12398:  *            o Originally copied from rastfbox(), above,
12399:  *              but I've modified the [] optional argument parsing
12400:  *            o Thanks to answers from Jean-Marie Becker and "Narasimham" at
12401:  *                 http://math.stackexchange.com/questions/2149677/
12402:  *              for providing info about ellipse circumscribing a rectangle
12403:  * ======================================================================= */
12404: /* --- entry point --- */
12405: FUNCSCOPE subraster *rastovalbox ( char **expression, int size,
12406:                             subraster *basesp, int arg1, int arg2, int arg3 )
12407: {
12408: /* -------------------------------------------------------------------------
12409: Allocations and Declarations
12410: -------------------------------------------------------------------------- */
12411: char    /**texsubexpr(),*/ subexpr[MAXSUBXSZ+1], narg[512], /* args */
12412:         composexpr[MAXSUBXSZ+2048],     /* compose subexpr[] with ellipse(s)*/
12413:         ovalexpr[2048];                 /* nested \compose{}{}'s */
12414: subraster /**rasterize(),*/ *framesp=NULL; /*rasterize subexpr to be oval'ed*/
12415: /*int   delete_subraster();*/           /* just need width,height */
12416: int     fwidth = 3+size/999;            /* extra frame width */
12417: int     width=(-1), height=(-1),        /* width,height of subexpr[] */
12418:         origheight = (-1),              /* original subexpr[] height */
12419:         baseline = (-1);                /* and its original baseline */
12420: /*double sqrt2 = 1.414213562;*/         /*you think that's accurate enough?*/
12421: double  x0=0.0, y0=0.0,                 /*rectangle half-width, half-height*/
12422:         a=0.0,  b=0.0;                  /*ellipse semi-major, semi-minor axis*/
12423: double  n = 2.0;                        /*width/height=(semi-major/minor)^n*/
12424: int     ioval=0, novals=1,              /* #concentric ovals to draw */
12425:         wdelta=(3), hdelta=(-3);        /*width,height deltas for each oval*/
12426: /* -------------------------------------------------------------------------
12427: obtain {subexpr} argument
12428: -------------------------------------------------------------------------- */
12429: /* --- first check for optional \ovalbox[n] and bump expression past it--- */
12430: if ( *(*expression) == '[' ) {          /* check for []-enclosed n arg */
12431:   *expression = texsubexpr(*expression,narg,511,"[","]",0,0);
12432:   if ( !isempty(narg) ) {               /* got n */
12433:     char *semi = strchr(narg,';');      /* optionally followed by ;#ovals */
12434:     if ( semi != NULL ) {               /* found optional ;#ovals */
12435:       char *comma = strchr(semi+1,','); /* optionally followed by ,w,hdelta */
12436:       *semi = '\000';                   /* null-terminate 'n' argument at ; */
12437:       if ( comma != NULL ) *comma = '\000'; /* and #ovals arg at (first) , */
12438:       if ( (novals=atoi(semi+1))        /* convert #ovals argument after ; */
12439:       < 1 ) novals = 1;                 /* and make sure it's >0 */
12440:       if ( comma != NULL ) {            /*have ,wdelta and maybe ,hdelta args*/
12441:         char *comma2 = strchr(comma+1,','); /* ,wdelta and maybe ,hdelta */
12442:         if ( comma2 != NULL ) *comma2 = '\000'; /* null-terminate wdelta */
12443:         hdelta = wdelta = atoi(comma+1); /* init both wdelta,hdelta */
12444:         if ( comma2 != NULL )           /* but we have separate ,hdelta */
12445:           hdelta = atoi(comma2+1);      /* so set hdelta separately */
12446:         } /* --- end-of-if(comma!=NULL) --- */
12447:       } /* --- end-of-if(semi!=NULL) --- */
12448:     if ( (n = atof(narg))               /* convert narg to double */
12449:     <=   .001 ) n = 2.0;                /* sanity check (revert to default) */
12450:     } /* --- end-of-if(!isempty(narg)) --- */
12451:   } /* --- end-of-if(**expression=='[') --- */
12452: /* --- parse for {subexpr} arg, and bump expression past it --- */
12453: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
12454: /* -------------------------------------------------------------------------
12455: rasterize subexpression to be ellipse-framed
12456: -------------------------------------------------------------------------- */
12457: if ( (framesp = rasterize(subexpr,size)) /* rasterize subexpression */
12458: ==   NULL ) goto end_of_job;            /* and quit if failed */
12459: if ( framesp->image == NULL ) goto end_of_job; /* or quit if no image */
12460: /* --- pull out info we need --- */
12461: width  = (framesp->image)->width;       /* width  of subexpr image */
12462: height = (framesp->image)->height;      /* height of subexpr image */
12463: baseline = framesp->baseline;           /* and original baseline */
12464: delete_subraster(framesp);              /* just needed width,height,baseline */
12465: origheight = height;                    /* original "rectangle" height */
12466: /* --- old formulas for circumscribing ellipse (corresponds to n=1) --- */
12467: /*width  = (int)(0.5+sqrt2*(double)(width +fwidth));*/ /*major ellipse axis*/
12468: /*height = (int)(0.5+sqrt2*(double)(height+fwidth));*/ /*minor ellipse axis*/
12469: /* --- use general formulas for circumscribing ellipse --- */
12470: x0 = ((double)width)/2.0;  y0 = ((double)height)/2.0; /* half-width,height */
12471: a = sqrt(x0*x0 + pow(x0,2.0/n)*pow(y0,2.0-2.0/n));
12472: b = sqrt(y0*y0 + pow(y0,2.0/n)*pow(x0,2.0-2.0/n));
12473: width  = (int)(2.0*a + 0.5) + fwidth;
12474: height = (int)(2.0*b + 0.5) + fwidth;
12475: baseline += ((height-origheight)+1)/2;  /* baseline of subexpr[] chars */
12476: /* -------------------------------------------------------------------------
12477: construct nested \compose{\compose{\circle(,)}{\circle(,)}}{\circle(,)}
12478: -------------------------------------------------------------------------- */
12479: *ovalexpr = '\000';                     /* init as empty string */
12480: for ( ioval=0; ioval<novals; ioval++ ) { /* nest each oval */
12481:   char thisoval[2048],                  /* oval to be nested with ovalexpr */
12482:        ovalbuff[2048];                  /* temp ovalexpr buffer */
12483:   int thiswidth  =  width + (ioval*wdelta), /*  width for this oval */
12484:       thisheight = height + (ioval*hdelta); /* height for this oval */
12485:   if ( strlen(ovalexpr) > 1024 ) break; /* sanity check */
12486:   strcpy(ovalbuff,ovalexpr);            /* save current ovalexpr */
12487:   sprintf(thisoval,"\\circle(%d,%d)",thiswidth,thisheight); /*draw this oval*/
12488:   if ( ioval == 0 ) {                   /* initial oval */
12489:     strcpy(ovalexpr,thisoval); }        /* just copy \circle(,) directive */
12490:   else {                                /* nest with \compose{}{}'s */
12491:     sprintf(ovalexpr,"\\compose{%s}{%s}",ovalbuff,thisoval); }
12492:   } /* --- end-of-for(ioval) --- */
12493: /* -------------------------------------------------------------------------
12494: compose with circumscribed ellipse(s), reset params, and return it to caller
12495: -------------------------------------------------------------------------- */
12496: /* --- construct expression to be rasterized
12497:        (note: too bad we have to re-rasterize original subexpr[]) --- */
12498:       /* sprintf(composexpr,"\\compose{\\circle(%d,%d)}{%.8000s}",
12499:          width,height,subexpr); */
12500: sprintf(composexpr,"\\compose{%s}{%.8000s}", ovalexpr,subexpr);
12501: if ( (framesp = rasterize(composexpr,size)) /*rasterize ellipse with subexpr*/
12502: ==   NULL ) goto end_of_job;            /* and quit if failed */
12503: framesp->baseline = baseline;           /* reset baseline (I hope) */
12504: /* --- return ellipse-framed subexpr to caller --- */
12505: end_of_job:
12506:   return ( framesp );                   /* return framed subexpr to caller */
12507: } /* --- end-of-function rastovalbox() --- */
12508: 
12509: 
12510: /* ==========================================================================
12511:  * Function:    rastinput ( expression, size, basesp, arg1, arg2, arg3 )
12512:  * Purpose:     \input{filename} handler, reads filename and returns
12513:  *              subraster containing image of expression read from filename
12514:  * --------------------------------------------------------------------------
12515:  * Arguments:   expression (I/O) char **  to first char of null-terminated
12516:  *                              string immediately following \input to be
12517:  *                              rasterized, and returning ptr immediately
12518:  *                              following last character processed.
12519:  *              size (I)        int containing 0-7 default font size
12520:  *              basesp (I)      subraster *  to character (or subexpression)
12521:  *                              immediately preceding \input
12522:  *                              (unused, but passed for consistency)
12523:  *              arg1 (I)        int unused
12524:  *              arg2 (I)        int unused
12525:  *              arg3 (I)        int unused
12526:  * --------------------------------------------------------------------------
12527:  * Returns:     ( subraster * ) ptr to subraster corresponding to expression
12528:  *                              in filename, or NULL for any parsing error
12529:  * --------------------------------------------------------------------------
12530:  * Notes:     o Summary of syntax...
12531:  *                \input{filename}     reads entire file named filename
12532:  *                \input{filename:tag} reads filename, but returns only
12533:  *                those characters between <tag>...</tag> in that file.
12534:  *            o
12535:  * ======================================================================= */
12536: /* --- entry point --- */
12537: FUNCSCOPE subraster *rastinput(char **expression, int size, subraster *basesp,
12538:                                int arg1, int arg2, int arg3)
12539: {
12540: /* -------------------------------------------------------------------------
12541: Allocations and Declarations
12542: -------------------------------------------------------------------------- */
12543: char    /**texsubexpr(),*/ tag[1024]="\000", filename[1024]="\000"; /*args*/
12544: subraster /**rasterize(),*/ *inputsp=NULL /* rasterized input image */
12545:         /*,*read_pbm()*/;       /* read .pbm file and make subraster image */
12546: int     status /*, rastreadfile()*/; /* read input file */
12547: FILE    *fp=NULL /*, *rastopenfile()*/; /* reading .pbm files locally */
12548: int     format=0, npts=0,       /* don't reformat (numerical) input */
12549:         ispbm = 0;              /* set true for \input[pbm]{filename.pbm} */
12550: double  sf = 0.0;               /* shrink factor (0 or 1 means don't shrink)*/
12551: int     isinput = (seclevel<=inputseclevel?1:0); /*true if \input permitted*/
12552: /*int   evalterm();*/           /* evaluate expressions */
12553: char    *inputpath = INPUTPATH; /* permitted \input{} paths for any user */
12554: /*int   isstrstr();*/           /* search for valid inputpath in filename */
12555: char    subexpr[MAXFILESZ+1] = "\000", /*concatanated lines from input file*/
12556:         /**mimeprep(),*/        /* preprocess inputted data */
12557:         /**dbltoa(),*/ *reformat=NULL; /* reformat numerical input */
12558: /* -------------------------------------------------------------------------
12559: obtain [tag]{filename} argument
12560: -------------------------------------------------------------------------- */
12561: /* --- parse for optional [tag] or [fmt] arg, bump expression past it --- */
12562: if ( *(*expression) == '[' )            /* check for []-enclosed value */
12563:   { char argfld[MAXTOKNSZ+1];           /* optional argument field */
12564:     *expression = texsubexpr(*expression,argfld,MAXTOKNSZ-1,"[","]",0,0);
12565:     if ( (reformat=strstr(argfld,"dtoa")) != NULL ) /*dtoa/dbltoa requested*/
12566:       { format = 1;                     /* signal dtoa()/dbltoa() format */
12567:         if ( (reformat=strchr(reformat,'=')) != NULL ) /* have dtoa= */
12568:           npts = (int)strtol(reformat+1,NULL,0); } /* so set npts */
12569:     if ( (reformat=strstr(argfld,"pbm")) != NULL ) /*input a .pbm image file*/
12570:       { ispbm = 1;                      /* set ispbm flag */
12571:         if ( (reformat=strchr(reformat,',')) != NULL ) /* have pbm, */
12572:           sf = strtod(reformat+1,NULL); } /* so set sf(shrinkfactor) */
12573:     if ( format==0 && !ispbm ) {        /* not reformat request, and not pbm */
12574:       strninit(tag,argfld,1020); } }    /* so interpret arg as tag */
12575: /* --- parse for {filename} arg, and bump expression past it --- */
12576: *expression = texsubexpr(*expression,filename,1020,"{","}",0,0);
12577: /* --- check for alternate filename:tag --- */
12578: if ( !isempty(filename)                 /* got filename */
12579: /*&& isempty(tag)*/ )                   /* but no [tag] */
12580:  { char *delim = strchr(filename,':');  /* look for : in filename:tag */
12581:    if ( delim != (char *)NULL )         /* found it */
12582:     { *delim = '\000';                  /* null-terminate filename at : */
12583:       strninit(tag,delim+1,1020); } }   /* and stuff after : is tag */
12584: /* --- check filename for an inputpath valid for all users --- */
12585: if ( !isinput                           /* if this user can't \input{} */
12586: &&   !isempty(filename)                 /* and we got a filename */
12587: &&   !isempty(inputpath) )              /* and an inputpath */
12588:   if ( isstrstr(filename,inputpath,0) ) /* filename has allowed inputpath */
12589:     isinput = 1;                        /* okay to \input{} this filename */
12590: /* --- guard against recursive runaway (e.g., file \input's itself) --- */
12591: if ( ++ninputcmds > 8 )                 /* max \input's per expression */
12592:   isinput = 0;                          /* flip flag off after the max */
12593: /* --------------------------------------------------------------------------
12594: Read file (and convert to numeric if [dtoa] option was given)
12595: -------------------------------------------------------------------------- */
12596: if ( isinput ) {                        /* user permitted to use \input{} */
12597:   if ( ispbm ) {                        /* special case: input a .pbm file */
12598:     fp = rastopenfile(filename,"r");    /* open the .pbm file for "r"ead */
12599:     if ( fp != NULL ) {                 /* opened .pbm file successfully */
12600:       if(0) printf("rastinput> sf=%.3f to read_pbm(%s,sf)\n",sf,filename);
12601:       inputsp = read_pbm(fp,sf);        /* create subraster image from .pbm */
12602:       fclose(fp); }                     /* close file after reading */
12603:     goto end_of_job;                    /* all done, return inputsp to caller*/
12604:     } /* --- end-of-if(ispbm) --- */
12605:   status = rastreadfile(filename,0,tag,subexpr); /* read file */
12606:   if ( *subexpr == '\000' ) goto end_of_job;   /* quit if problem */
12607:   /* --- rasterize input subexpression  --- */
12608:   mimeprep(subexpr);                    /* preprocess subexpression */
12609:   if ( format == 1 ) {                  /* dtoa()/dbltoa() */
12610:     double d = strtod(subexpr,NULL);    /* interpret subexpr as double */
12611:     if ( d != 0.0 )                     /* conversion to double successful */
12612:       if ( (reformat=dbltoa(d,npts)) != NULL ) /* reformat successful */
12613:         strcpy(subexpr,reformat); }     /*replace subexpr with reformatted*/
12614:   } /* --- end-of-if(isinput) --- */
12615: /* --------------------------------------------------------------------------
12616: emit error message for unauthorized users trying to use \input{}
12617: -------------------------------------------------------------------------- */
12618: else {                                  /* inputseclevel > seclevel */
12619:   sprintf(subexpr,
12620:   "\\ \\text{[\\backslash input\\lbrace %.128s\\rbrace\\ not permitted]}\\ ",
12621:   (isempty(filename)?"???":filename));
12622:   } /* --- end-of-if/else(isinput) --- */
12623: /* --------------------------------------------------------------------------
12624: Rasterize constructed subexpression
12625: -------------------------------------------------------------------------- */
12626: inputsp = rasterize(subexpr,size);      /* rasterize subexpression */
12627: /* --- return input image to caller --- */
12628: end_of_job:
12629:   return ( inputsp );                   /* return input image to caller */
12630: } /* --- end-of-function rastinput() --- */
12631: 
12632: 
12633: /* ==========================================================================
12634:  * Function:    rastcounter ( expression, size, basesp, arg1, arg2, arg3 )
12635:  * Purpose:     \counter[value]{filename} handler, returns subraster
12636:  *              containing image of counter value read from filename
12637:  *              (or optional [value]), and increments counter
12638:  * --------------------------------------------------------------------------
12639:  * Arguments:   expression (I/O) char **  to first char of null-terminated
12640:  *                              string immediately following \counter to be
12641:  *                              rasterized, and returning ptr immediately
12642:  *                              following last character processed.
12643:  *              size (I)        int containing 0-7 default font size
12644:  *              basesp (I)      subraster *  to character (or subexpression)
12645:  *                              immediately preceding \counter
12646:  *                              (unused, but passed for consistency)
12647:  *              arg1 (I)        int unused
12648:  *              arg2 (I)        int unused
12649:  *              arg3 (I)        int unused
12650:  * --------------------------------------------------------------------------
12651:  * Returns:     ( subraster * ) ptr to subraster corresponding to \counter
12652:  *                              requested, or NULL for any parsing error
12653:  * --------------------------------------------------------------------------
12654:  * Notes:     o Summary of syntax...
12655:  *                \counter[value][logfile]{filename:tag}
12656:  *            o :tag is optional
12657:  * ======================================================================= */
12658: /* --- entry point --- */
12659: FUNCSCOPE subraster *rastcounter ( char **expression, int size,
12660:                             subraster *basesp, int arg1, int arg2, int arg3 )
12661: {
12662: /* -------------------------------------------------------------------------
12663: Allocations and Declarations
12664: -------------------------------------------------------------------------- */
12665: char    /**texsubexpr(),*/ filename[1024]="\000", /* counter file */
12666:         logfile[1024]="\000", tag[1024]="\000"; /*optional log file and tag*/
12667: subraster /**rasterize(),*/ *countersp=NULL; /* rasterized counter image */
12668: FILE    /* *fp=NULL,*/ *logfp=NULL; /* counter and log file pointers */
12669: int     status=0,/*rastreadfile(),rastwritefile(),*//*read,write counter file*/
12670:         iscounter = (seclevel<=counterseclevel?1:0), /*is \counter permitted*/
12671:         isstrict = 1;           /* true to only write to existing files */
12672: char    text[MAXFILESZ] = "1_", /* only line in counter file without tags */
12673:         *delim = NULL,          /* delimiter in text */
12674:         utext[128] = "1_",      /* default delimiter */
12675:         *udelim = utext+1;      /* underscore delimiter */
12676: /*char  *rasteditfilename(),*/  /* edit log file name */
12677: /*      *timestamp(),*/         /* timestamp for logging */
12678: /*      *dbltoa();*/            /* double to comma-separated ascii */
12679: int     counter = 1,            /* atoi(text) (after _ removed, if present) */
12680:         value = 1,              /* optional [value] argument */
12681:         gotvalue = 0,           /* set true if [value] supplied */
12682:         isdelta = 0,            /* set true if [+value] or [-value] is delta*/
12683:         ordindex = (-1);        /* ordinal[] index to append ordinal suffix */
12684: /*--- ordinal suffixes based on units digit of counter ---*/
12685: static  char *ordinal[]={"th","st","nd","rd","th","th","th","th","th","th"};
12686: static  char *logvars[]={"REMOTE_ADDR","HTTP_REFERER",NULL}; /* log vars*/
12687: static  int  commentvar = 1;    /* logvars[commentvar] replaced by comment */
12688: /* -------------------------------------------------------------------------
12689: first obtain optional [value][logfile] args immediately following \counter
12690: -------------------------------------------------------------------------- */
12691: /* --- first check for optional \counter[value] --- */
12692: if ( *(*expression) == '[' )            /* check for []-enclosed value */
12693:   { *expression = texsubexpr(*expression,text,1023,"[","]",0,0);
12694:     if ( *text != '\000' )              /* got counter value (or logfile) */
12695:      if ( strlen(text) >= 1 ) {         /* and it's not an empty string */
12696:       if ( isthischar(*text,"+-0123456789") ) /* check for leading +-digit */
12697:         gotvalue = 1;                   /* signal we got optional value */
12698:       else                              /* not +-digit, so must be logfile */
12699:         strcpy(logfile,text); }         /* so just copy it */
12700:   } /* --- end-of-if(**expression=='[') --- */
12701: /* --- next check for optional \counter[][logfile] --- */
12702: if ( *(*expression) == '[' )            /* check for []-enclosed logfile */
12703:   { *expression = texsubexpr(*expression,filename,1023,"[","]",0,0);
12704:     if ( *filename != '\000' )          /* got logfile (or counter value) */
12705:      if ( strlen(filename) >= 1 ) {     /* and it's not an empty string */
12706:       if ( !(isthischar(*text,"+-0123456789")) /* not a leading +-digit */
12707:       ||   gotvalue )                   /* or we already got counter value */
12708:         strcpy(logfile,filename);       /* so just copy it */
12709:       else                              /* leading +-digit must be value */
12710:         { strcpy(text,filename);        /* copy value to text line */
12711:           gotvalue = 1; } }             /* and signal we got optional value*/
12712:   } /* --- end-of-if(**expression=='[') --- */
12713: /* --- evaluate [value] if present --- */
12714: if ( gotvalue ) {                       /*leading +-digit should be in text*/
12715:  if ( *text == '+' ) isdelta = (+1);    /* signal adding */
12716:  if ( *text == '-' ) isdelta = (-1);    /* signal subtracting */
12717:  value = (int)(strtod((isdelta==0?text:text+1),&udelim)+0.1); /*abs(value)*/
12718:  if ( isdelta == (-1) ) value = (-value); /* set negative value if needed */
12719:  counter = value;                       /* re-init counter */
12720:  } /* --- end-of-if(gotvalue) --- */
12721: /* -------------------------------------------------------------------------
12722: obtain counter {filename} argument
12723: -------------------------------------------------------------------------- */
12724: /* --- parse for {filename} arg, and bump expression past it --- */
12725: *expression = texsubexpr(*expression,filename,1023,"{","}",0,0);
12726: /* --- check for counter filename:tag --- */
12727: if ( *filename != '\000' )              /* got filename */
12728:  if ( (delim=strchr(filename,':'))      /* look for : in filename:tag */
12729:  !=   (char *)NULL )                    /* found it */
12730:   { *delim = '\000';                    /* null-terminate filename at : */
12731:     strcpy(tag,delim+1); }              /* and stuff after : is tag */
12732: /* --------------------------------------------------------------------------
12733: emit error message for unauthorized users trying to use \counter{}
12734: -------------------------------------------------------------------------- */
12735: if ( !iscounter ) {                     /* counterseclevel > seclevel */
12736:  sprintf(text,
12737:  "\\ \\text{[\\backslash counter\\lbrace %.128s\\rbrace\\ not permitted]}\\ ",
12738:  (isempty(filename)?"???":filename));
12739:  goto rasterize_counter;                /* rasterize error message */
12740:  } /* --- end-of-if(!iscounter) --- */
12741: /* --------------------------------------------------------------------------
12742: Read and parse file, increment and rewrite counter (with optional underscore)
12743: -------------------------------------------------------------------------- */
12744: if ( strlen(filename) > 1 )             /* make sure we got {filename} arg */
12745:   {
12746:   /* --- read and interpret first (and only) line from counter file --- */
12747:   if ( !gotvalue || (isdelta!=0) )      /*if no [count] arg or if delta arg*/
12748:    if ( (status=rastreadfile(filename,1,tag,text)) > 0 ) /*try reading file*/
12749:     { char *vdelim = NULL;              /* underscore delim from file */
12750:       double fileval  = strtod(text,&vdelim); /* value and delim from file */
12751:       counter = (int)(fileval<0.0?fileval-0.1:fileval+0.1); /* integerized */
12752:       counter += value;                 /* bump count by 1 or add/sub delta*/
12753:       if ( !gotvalue ) udelim=vdelim; } /* default to file's current delim */
12754:   /* --- check for ordinal suffix --- */
12755:   if ( udelim != (char *)NULL )         /* have some delim after value */
12756:    if ( *udelim == '_' )                /* underscore signals ordinal */
12757:     { int abscount = (counter>=0?counter:(-counter)); /* abs(counter) */
12758:       ordindex = abscount%10;           /* least significant digit */
12759:       if ( abscount >= 10 )             /* counter is 10 or greater */
12760:        if ( (abscount/10)%10 == 1 )     /* and the last two are 10-19 */
12761:         ordindex = 0; }         /* use th for 11,12,13 rather than st,nd,rd */
12762:   /* --- rewrite counter file --- */
12763:   if ( status >= 0 )                    /* file was read okay */
12764:    { sprintf(text,"%d",counter);        /*build image of incremented counter*/
12765:      if ( ordindex >= 0 ) strcat(text,"_"); /* tack on _ */
12766:      if ( *tag == '\000' ) strcat(text,"\n"); /* and newline */
12767:      status = rastwritefile(filename,tag,text,isstrict); } /*rewrite counter*/
12768:   } /* --- end-of-if(strlen(filename)>1) --- */
12769: /* --------------------------------------------------------------------------
12770: log counter request
12771: -------------------------------------------------------------------------- */
12772: if ( strlen(logfile) > 1 )              /* optional [logfile] given */
12773:  {
12774:  char   comment[1024] = "\000",         /* embedded comment, logfile:comment*/
12775:         *commptr = strchr(logfile,':'); /* check for : signalling comment */
12776:  int    islogokay = 1;                  /* logfile must exist if isstrict */
12777:  if ( commptr != NULL )                 /* have embedded comment */
12778:   { strcpy(comment,commptr+1);          /* comment follows : */
12779:     *commptr = '\000'; }                /* null-terminate actual logfile */
12780:  strcpy(logfile,rasteditfilename(logfile)); /* edit log file name */
12781:  if ( *logfile == '\000' ) islogokay = 0; /* given an invalid file name */
12782:  else if ( isstrict ) {                 /*okay, but only write if it exists*/
12783:   if ( (logfp=fopen(logfile,"r")) == (FILE *)NULL ) /*doesn't already exist*/
12784:     islogokay = 0;                      /* so don't write log file */
12785:   else fclose(logfp); }                 /* close file opened for test read */
12786:  if ( islogokay )                       /* okay to write logfile */
12787:   if ( (logfp = fopen(logfile,"a"))     /* open logfile */
12788:   != (FILE *)NULL ) {                   /* opened successfully for append */
12789:    int  ilog=0;                         /* logvars[] index */
12790:    fprintf(logfp,"%s  ",timestamp(TZDELTA,0)); /* first emit timestamp */
12791:    if (*tag=='\000') fprintf(logfp,"%s",filename); /* emit counter filename */
12792:    else fprintf(logfp,"<%s>",tag);      /* or tag if we have one */
12793:    fprintf(logfp,"=%d",counter);        /* emit counter value */
12794:    if ( status < 1 )                    /* read or re-write failed */
12795:     fprintf(logfp,"(%s %d)","error status",status); /* emit error */
12796:    for ( ilog=0; logvars[ilog] != NULL; ilog++ ) /* log till end-of-table */
12797:     if ( ilog == commentvar             /* replace with comment... */
12798:     &&   commptr != NULL )              /* ...if available */
12799:      fprintf(logfp,"  %.256s",comment); /* log embedded comment */
12800:     else
12801:      { char *logval = getenv(logvars[ilog]); /*getenv(variable) to be logged*/
12802:        fprintf(logfp,"  %.64s",         /* log variable */
12803:         (logval!=NULL?logval:"<unknown>")); } /* emit value or <unknown> */
12804:    fprintf(logfp,"\n");                 /* terminating newline */
12805:    fclose(logfp);                       /* close logfile */
12806:    } /* --- end-of-if(islogokay&&logfp!=NULL) --- */
12807:  } /* --- end-of-if(strlen(logfile)>1) --- */
12808: /* --------------------------------------------------------------------------
12809: construct counter expression and rasterize it
12810: -------------------------------------------------------------------------- */
12811: /* --- construct expression --- */
12812: /*sprintf(text,"%d",counter);*/         /* start with counter */
12813: strcpy(text,dbltoa(((double)counter),0)); /* comma-separated counter value */
12814: if ( ordindex >= 0 )                    /* need to tack on ordinal suffix */
12815:   { strcat(text,"^{\\underline{\\rm~"); /* start with ^ and {\underline{\rm */
12816:     strcat(text,ordinal[ordindex]);     /* then st,nd,rd, or th */
12817:     strcat(text,"}}"); }                /* finish with }} */
12818: /* --- rasterize it --- */
12819: rasterize_counter:
12820:   countersp = rasterize(text,size);     /* rasterize counter subexpression */
12821: /* --- return counter image to caller --- */
12822: /*end_of_job:*/
12823:   return ( countersp );                 /* return counter image to caller */
12824: } /* --- end-of-function rastcounter() --- */
12825: 
12826: 
12827: /* ==========================================================================
12828:  * Function:    rasteval ( expression, size, basesp, arg1, arg2, arg3 )
12829:  * Purpose:     handle \eval
12830:  * --------------------------------------------------------------------------
12831:  * Arguments:   expression (I/O) char **  to first char of null-terminated
12832:  *                              string immediately following \eval,
12833:  *                              and returning ptr immediately
12834:  *                              following last character processed.
12835:  *              size (I)        int containing 0-7 default font size
12836:  *              basesp (I)      subraster *  to character (or subexpression)
12837:  *                              immediately preceding \eval
12838:  *                              (unused, but passed for consistency)
12839:  *              arg1 (I)        int unused
12840:  *              arg2 (I)        int unused
12841:  *              arg3 (I)        int unused
12842:  * --------------------------------------------------------------------------
12843:  * Returns:     ( subraster * ) subraster ptr to image of eval-uated
12844:  *                              expression
12845:  * --------------------------------------------------------------------------
12846:  * Notes:     o
12847:  * ======================================================================= */
12848: /* --- entry point --- */
12849: FUNCSCOPE subraster *rasteval(char **expression, int size, subraster *basesp,
12850:                               int arg1, int arg2, int arg3 )
12851: {
12852: /* -------------------------------------------------------------------------
12853: Allocations and Declarations
12854: -------------------------------------------------------------------------- */
12855: char    /**texsubexpr(),*/ subexpr[MAXSUBXSZ]; /* arg to be evaluated */
12856: subraster /**rasterize(),*/ *evalsp=NULL; /* rasterize evaluated expression */
12857: int     /*evalterm(),*/ value=0;        /* evaluate expression */
12858: /* -------------------------------------------------------------------------
12859: Parse for subexpr to be \eval-uated, and bump expression past it
12860: -------------------------------------------------------------------------- */
12861: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
12862: if ( *subexpr == '\000' )               /* couldn't get subexpression */
12863:   goto end_of_job;                      /* nothing to do, so quit */
12864: /* -------------------------------------------------------------------------
12865: Evaluate expression, ascii-ize integer result, rasterize it
12866: -------------------------------------------------------------------------- */
12867: /* --- evaluate expression --- */
12868: value = evalterm(mimestore,subexpr);    /* evaluate expression */
12869: /* --- ascii-ize it --- */
12870: sprintf(subexpr,"%d",value);            /* ascii version of value */
12871: /* --- rasterize ascii-ized expression value --- */
12872: evalsp = rasterize(subexpr,size);       /* rasterize evaluated expression */
12873: /* --- return evaluated expression raster to caller --- */
12874: end_of_job:
12875:   return ( evalsp );                    /* return evaluated expr to caller */
12876: } /* --- end-of-function rasteval() --- */
12877: 
12878: 
12879: /* ==========================================================================
12880:  * Function:    rastmathtex ( expression, size, basesp, arg1, arg2, arg3 )
12881:  * Purpose:     handle \mathtex...
12882:  *                ...obtain pixel image corresponding to expression
12883:  *              by "calling" mathtex, using wget,
12884:  *              for pbm-style bitmap of expression,
12885:  *              and then pixelizing it, and wrapping it in a subraster.
12886:  * --------------------------------------------------------------------------
12887:  * Arguments:   expression (I/O) char **  to first char of null-terminated
12888:  *                              string immediately following \mathtex,
12889:  *                              and returning ptr immediately
12890:  *                              following last character processed.
12891:  *              size (I)        int containing 0-11 default font size
12892:  *                              (unused, but passed for consistency)
12893:  *              basesp (I)      subraster *  to character (or subexpression)
12894:  *                              immediately preceding \mathtex
12895:  *                              (unused, but passed for consistency)
12896:  *              arg1 (I)        int unused
12897:  *              arg2 (I)        int unused
12898:  *              arg3 (I)        int unused
12899:  * --------------------------------------------------------------------------
12900:  * Returns:     ( subraster * ) subraster ptr to image of pixelized
12901:  *                              pbm bitmap of expression returned by mathtex
12902:  * --------------------------------------------------------------------------
12903:  * Notes:     o Adapted from function
12904:  *              BYTE *plainmimetext(char *expression, int *width, int *height)
12905:  *              in gifsave89.c
12906:  * ======================================================================= */
12907: /* ---
12908:  * entry point
12909:  * -------------- */
12910: FUNCSCOPE subraster *rastmathtex ( char **expression, int size,
12911:                             subraster *basesp, int arg1, int arg2, int arg3 )
12912: {
12913: /* ---
12914:  * allocations and declarations
12915:  * ------------------------------- */
12916: /* --- for mimetex --- */
12917: char    /**texsubexpr(),*/ subexpr[MAXSUBXSZ]; /* arg to be evaluated */
12918: subraster /**read_pbm(),*/ *mathtexsp=NULL; /*rasterize mathtex'ed expression*/
12919: /* --- for wget() pipe to mathtex --- */
12920: char    command[2048],                  /* complete popen(command,"r") */
12921:         execwget[1024];                 /* wget either mimetex or mathtex */
12922: FILE    *wget  = NULL;                  /* wget's stdout */
12923: char    *localhost="localhost://", *plocalhost=NULL; /*check if running local*/
12924: char    *mathtexpwd = MATHTEXPWD;       /* mathTeX \password{} */
12925: /* ---
12926:  * initialization
12927:  * ----------------- */
12928: /* --- Parse for subexpr for mathtex, and bump expression past it ---*/
12929: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
12930: if ( *subexpr == '\000' )               /* couldn't get subexpression */
12931:   goto end_of_job;                      /* nothing to do, so quit */
12932: /* ---
12933:  * set up wget command for webservice
12934:  * ------------------------------------- */
12935: strcpy( execwget,                       /* --- mathTeX webservice --- */
12936:         WGET                            /*...begins with /path/to/wget */
12937:         " -q -O - "                     /*...followed by wget options */
12938:         "\"" MATHTEX "?\\pbm" );        /*..."mathtex.cgi?\pbm\dpi{400} */
12939: if ( !isempty(mathtexpwd) ) {           /* need \password{} ??? */
12940:   strcat(execwget,"\\password{");       /* start with \password{ */
12941:   strcat(execwget,mathtexpwd);          /* then actual mathtex password */
12942:   strcat(execwget,"}"); }               /* and closing } */
12943: strcat(execwget," ");                   /* blank space if message needs it */
12944: /* ---
12945:  * open pipe to wget (or run locally)
12946:  * ------------------------------------- */
12947: /* --- first construct popen() command using execwget --- */
12948: strcpy(command,execwget);               /* shell command plus mimetex url */
12949: strcat(command,subexpr);                /* followed by \mathtex{subexpr} */
12950: strcat(command,"\"");                   /* and closing " */
12951: /* --- then check for local mathTeX executable --- */
12952: if ( (plocalhost=strstr(MATHTEX,localhost)) /* does url have localhost://? */
12953: !=   NULL ) {                           /* if so, replace wget */
12954:   strcpy(command,plocalhost+strlen(localhost)); /*path follows localhost://*/
12955:   strcat(command," \"\\pbm\\stdout ");  /* start with "\pbm\stdout */
12956:   strcat(command,subexpr);              /* followed by \mathtex{subexpr} */
12957:   strcat(command,"\"");                 /* and closing " */
12958:   } /* --- end-of-if(plocalhost!=NULL) --- */
12959: /* --- issue wget command and capture its stdout --- */
12960: if ( (wget = popen(command,"r"))        /* issue command and capture stdout*/
12961: == NULL ) goto end_of_job;              /* or quit if failed */
12962: /* ---
12963:  * create subraster for pixelized image
12964:  * --------------------------------------- */
12965: mathtexsp = read_pbm(wget,0.0);
12966: /* ---
12967:  * end-of-job
12968:  * ------------- */
12969: end_of_job:
12970:   if ( wget != NULL ) pclose(wget);     /* close pipe (if open) */
12971:   return ( mathtexsp );                 /* back with mathtex subraster */
12972: } /* --- end-of-function rastmathtex() --- */
12973: 
12974: 
12975: /* ==========================================================================
12976:  * Function:    rasttoday ( expression, size, basesp, arg1, arg2, arg3 )
12977:  * Purpose:     handle \today
12978:  * --------------------------------------------------------------------------
12979:  * Arguments:   expression (I/O) char **  to first char of null-terminated
12980:  *                              string immediately following \today,
12981:  *                              and returning ptr immediately
12982:  *                              following last character processed.
12983:  *              size (I)        int containing 0-7 default font size
12984:  *              basesp (I)      subraster *  to character (or subexpression)
12985:  *                              immediately preceding \today
12986:  *                              (unused, but passed for consistency)
12987:  *              arg1 (I)        int unused
12988:  *              arg2 (I)        int unused
12989:  *              arg3 (I)        int unused
12990:  * --------------------------------------------------------------------------
12991:  * Returns:     ( subraster * ) subraster ptr to date stamp
12992:  * --------------------------------------------------------------------------
12993:  * Notes:     o
12994:  * ======================================================================= */
12995: /* --- entry point --- */
12996: FUNCSCOPE subraster *rasttoday(char **expression, int size, subraster *basesp,
12997:                                int arg1, int arg2, int arg3)
12998: {
12999: /* -------------------------------------------------------------------------
13000: Allocations and Declarations
13001: -------------------------------------------------------------------------- */
13002: char    /**texsubexpr(),*/ optarg[2050]; /* optional [+/-tzdelta,ifmt] args */
13003: char    /**timestamp(),*/ *today=optarg; /* timestamp to be rasterized */
13004: subraster /**rasterize(),*/ *todaysp=NULL; /* rasterize timestamp */
13005: int     ifmt=1, tzdelta=0;              /* default timestamp() args */
13006: /* -------------------------------------------------------------------------
13007: Get optional args \today[+/-tzdelta,ifmt]
13008: -------------------------------------------------------------------------- */
13009: /* --- check for optional \today[+/-tzdelta,ifmt] --- */
13010: if ( *(*expression) == '[' )            /* check for []-enclosed value */
13011:   { *expression = texsubexpr(*expression,optarg,2047,"[","]",0,0);
13012:     if ( *optarg != '\000' )            /* got optional arg */
13013:      { char *comma = strchr(optarg,','); /* comma between +/-tzdelta,ifmt */
13014:        int iarg, nargs=(comma==NULL?1:2); /* #optional args between []'s */
13015:        if ( comma != NULL ) *comma = '\000'; /* null-terminate first arg */
13016:        for ( iarg=1; iarg<=nargs; iarg++ ) /* process one or both args */
13017:         { char *arg = (iarg==1?optarg:comma+1); /* choose 1st or 2nd arg */
13018:           if ( isthischar(*arg,"+-") )  /* leading +/- signals tzdelta */
13019:             tzdelta = atoi(arg);        /* so interpret arg as tzdelta */
13020:           else ifmt = atoi(arg); }      /* else interpret args as ifmt */
13021:      } /* --- end-of-if(*optarg!='\0') --- */
13022:   } /* --- end-of-if(**expression=='[') --- */
13023: /* -------------------------------------------------------------------------
13024: Get timestamp and rasterize it
13025: -------------------------------------------------------------------------- */
13026: strcpy(today,"\\text{");                /* rasterize timestamp as text */
13027: strcat(today,timestamp(tzdelta,ifmt));  /* get timestamp */
13028: strcat(today,"}");                      /* terminate \text{} braces */
13029: todaysp = rasterize(today,size);        /* rasterize timestamp */
13030: /* --- return timestamp raster to caller --- */
13031: /*end_of_job:*/
13032:   return ( todaysp );                   /* return timestamp to caller */
13033: } /* --- end-of-function rasttoday() --- */
13034: 
13035: 
13036: /* ==========================================================================
13037:  * Function:    rastcalendar ( expression, size, basesp, arg1, arg2, arg3 )
13038:  * Purpose:     handle \calendar
13039:  * --------------------------------------------------------------------------
13040:  * Arguments:   expression (I/O) char **  to first char of null-terminated
13041:  *                              string immediately following \calendar
13042:  *                              and returning ptr immediately
13043:  *                              following last character processed.
13044:  *              size (I)        int containing 0-7 default font size
13045:  *              basesp (I)      subraster *  to character (or subexpression)
13046:  *                              immediately preceding \calendar
13047:  *                              (unused, but passed for consistency)
13048:  *              arg1 (I)        int unused
13049:  *              arg2 (I)        int unused
13050:  *              arg3 (I)        int unused
13051:  * --------------------------------------------------------------------------
13052:  * Returns:     ( subraster * ) subraster ptr to rendered one-month calendar
13053:  * --------------------------------------------------------------------------
13054:  * Notes:     o
13055:  * ======================================================================= */
13056: /* --- entry point --- */
13057: FUNCSCOPE subraster *rastcalendar ( char **expression, int size,
13058:                             subraster *basesp, int arg1, int arg2, int arg3 )
13059: {
13060: /* -------------------------------------------------------------------------
13061: Allocations and Declarations
13062: -------------------------------------------------------------------------- */
13063: char    /**texsubexpr(),*/ optarg[2050]; /* optional [year,month] args */
13064: char    /**calendar(),*/ *calstr=NULL;  /* calendar to be rasterized */
13065: subraster /**rasterize(),*/ *calendarsp=NULL; /* rasterize calendar string */
13066: int     year=0,month=0,day=0, argval=0; /* default calendar() args */
13067: /* -------------------------------------------------------------------------
13068: Get optional args \today[+/-tzdelta,ifmt]
13069: -------------------------------------------------------------------------- */
13070: /* --- check for optional \calendar[year,month] --- */
13071: if ( *(*expression) == '[' )            /* check for []-enclosed value */
13072:   { *expression = texsubexpr(*expression,optarg,2047,"[","]",0,0);
13073:     if ( *optarg != '\000' )            /* got optional arg */
13074:      { char *comma = strchr(optarg,','), /* comma between year,month */
13075:        *comma2 = NULL;                  /* second comma before day */
13076:        int iarg, nargs=(comma==NULL?1:2); /* #optional args between []'s */
13077:        if ( comma != NULL ) { *comma = '\000'; /*null-terminate first arg*/
13078:         if ( (comma2=strchr(comma+1,',')) != NULL ) /* have third arg */
13079:          { *comma2 = '\000'; nargs++; } } /* null-term 2nd arg, bump count */
13080:        for ( iarg=1; iarg<=nargs; iarg++ ) /* process one or both args */
13081:         { char *arg= (iarg==1?optarg:(iarg==2?comma+1:comma2+1)); /*get arg*/
13082:           argval = atoi(arg);           /* interpret arg as integer */
13083:           if ( iarg < 3 )               /* first two args are month,year */
13084:            {if ( argval>1972 && argval<2100 ) year = argval; /* year value */
13085:             else if ( argval>=1 && argval<=12 ) month = argval;} /*or month*/
13086:           else                          /* only 3rd arg can be day */
13087:            if ( argval>=1 && argval<=31 ) day = argval; } /* day value */
13088:      } /* --- end-of-if(*optarg!='\0') --- */
13089:   } /* --- end-of-if(**expression=='[') --- */
13090: /* -------------------------------------------------------------------------
13091: Get calendar string and rasterize it
13092: -------------------------------------------------------------------------- */
13093: if ( msgfp!= NULL && msglevel>=9 )
13094:   fprintf(msgfp,"rastcalendar> year=%d, month=%d, day=%d\n",
13095:   year,month,day);
13096: calstr = calendar(year,month,day);              /* get calendar string */
13097: calendarsp = rasterize(calstr,size);    /* rasterize calendar string */
13098: /* --- return calendar raster to caller --- */
13099: /*end_of_job:*/
13100:   return ( calendarsp );                /* return calendar to caller */
13101: } /* --- end-of-function rastcalendar() --- */
13102: 
13103: 
13104: /* ==========================================================================
13105:  * Function:    rastenviron ( expression, size, basesp, arg1, arg2, arg3 )
13106:  * Purpose:     handle \environment
13107:  * --------------------------------------------------------------------------
13108:  * Arguments:   expression (I/O) char **  to first char of null-terminated
13109:  *                              string immediately following \environment
13110:  *                              and returning ptr immediately
13111:  *                              following last character processed (in this
13112:  *                              case, \environment takes no arguments, so
13113:  *                              expression is returned unchanged).
13114:  *              size (I)        int containing 0-7 default font size
13115:  *              basesp (I)      subraster *  to character (or subexpression)
13116:  *                              immediately preceding \environment
13117:  *                              (unused, but passed for consistency)
13118:  *              arg1 (I)        int unused
13119:  *              arg2 (I)        int unused
13120:  *              arg3 (I)        int unused
13121:  * --------------------------------------------------------------------------
13122:  * Returns:     ( subraster * ) subraster ptr to rendered environment image
13123:  * --------------------------------------------------------------------------
13124:  * Notes:     o
13125:  * ======================================================================= */
13126: /* --- entry point --- */
13127: FUNCSCOPE subraster *rastenviron ( char **expression, int size,
13128:                             subraster *basesp, int arg1, int arg2, int arg3 )
13129: {
13130: /* -------------------------------------------------------------------------
13131: Allocations and Declarations
13132: -------------------------------------------------------------------------- */
13133: char    /**texsubexpr(),*/ optarg[255]; /* optional [...] args (for future)*/
13134: char    environstr[8192] = "\000",      /* string for all environment vars */
13135:         environvar[1024] = "\000";      /* one environment variable */
13136: char    /**strwrap(),*/                 /* wrap long lines */
13137:         /**strdetex(),*/                /* removes/replaces any math chars */
13138:         *environptr = NULL;             /* ptr to preprocessed environvar */
13139: /*char  *mimeprep();*/                  /* preprocess environvar string */
13140: /*int   unescape_url();*/               /* convert all %xx's to chars */
13141: int     isenviron = (seclevel<=environseclevel?1:0); /*is \environ permitted*/
13142: int     maxvarlen = 512,                /* max chars in environment var */
13143:         maxenvlen = 6400,               /* max chars in entire string */
13144:         wraplen = 48;                   /* strwrap() wrap lines at 48 chars*/
13145: int     ienv = 0;                       /* environ[] index */
13146: subraster /**rasterize(),*/ *environsp=NULL; /*rasterize environment string*/
13147: /* -------------------------------------------------------------------------
13148: Get args
13149: -------------------------------------------------------------------------- */
13150: /* --- check for optional \environment args --- */
13151: if ( 1 )                                /* there aren't any args (yet) */
13152:  if ( *(*expression) == '[' ) {         /* check for []-enclosed value */
13153:    *expression = texsubexpr(*expression,optarg,250,"[","]",0,0);
13154:    if ( *optarg != '\000' ) { ;         /* got optional arg, so process it */
13155:      wraplen = atoi(optarg);            /* interpret \environment[wraplen] */
13156:      if ( wraplen < 1 ) wraplen = 8;    /* set minimum */
13157:      } /* --- end-of-if(*optarg!='\0') --- */
13158:    } /* --- end-of-if(**expression=='[') --- */
13159: /* --------------------------------------------------------------------------
13160: emit error message for unauthorized users trying to use \environ
13161: -------------------------------------------------------------------------- */
13162: if ( !isenviron ) {                     /* environseclevel > seclevel */
13163:   sprintf(environstr,
13164:   "\\ \\text{[\\backslash environment\\ not permitted]}\\ ");
13165:   goto rasterize_environ;               /* rasterize error message */
13166:   } /* --- end-of-if(!isenviron) --- */
13167: /* -------------------------------------------------------------------------
13168: Accumulate environment variables and rasterize string containing them
13169: -------------------------------------------------------------------------- */
13170: *environstr = '\000';                   /* reset environment string */
13171: strcat(environstr,"\\nocaching\\fbox{\\normalsize\\text{"); /*init string*/
13172: for ( ienv=0; ; ienv++ ) {              /* loop over environ[] strings */
13173:   if ( environ[ienv] == (char *)NULL ) break; /* null terminates list */
13174:   if ( *(environ[ienv]) == '\000' ) break; /* double-check empty string */
13175:   strninit(environvar,environ[ienv],maxvarlen); /* max length displayed */
13176:   if ( strlen(environ[ienv]) > maxvarlen ) /* we truncated the variable */
13177:     strcat(environvar,"...");           /* so add an ellipsis */
13178:   unescape_url(environvar,0);           /* convert all %xx's to chars */
13179:   environptr = strdetex(environvar,1);  /* remove/replace any math chars */
13180:   strninit(environvar,environptr,maxvarlen); /*de-tex'ed/nomath environvar*/
13181:   environptr = strwrap(environvar,wraplen,-6); /* wrap long lines */
13182:   strninit(environvar,environptr,maxvarlen); /* line-wrapped environvar */
13183:   mimeprep(environvar);                 /* preprocess environvar string */
13184:   if ( strlen(environstr) + strlen(environvar) > maxenvlen ) break;
13185:   sprintf(environstr+strlen(environstr), /* display environment string */
13186:   " %2d. %s\\\\\n", ienv+1,environvar);
13187:   if ( msgfp!= NULL && msglevel>=9 )
13188:     fprintf(msgfp,"rastenviron> %2d. %.256s\n",
13189:     ienv+1,/*environ[ienv]*/environvar);
13190:   if ( strlen(environstr) >= 7200 ) break; /* don't overflow buffer */
13191:   } /* --- end-of-for(ienv) --- */
13192: strcat(environstr,"}}");                /* end {\text{...}} mode */
13193: rasterize_environ:
13194:   environsp = rasterize(environstr,size); /* rasterize environment string */
13195: /* --- return environment raster to caller --- */
13196: /*end_of_job:*/
13197:   return ( environsp );                 /* return environment to caller */
13198: } /* --- end-of-function rastenviron() --- */
13199: 
13200: 
13201: /* ==========================================================================
13202:  * Function:    rastmessage ( expression, size, basesp, arg1, arg2, arg3 )
13203:  * Purpose:     handle \message
13204:  * --------------------------------------------------------------------------
13205:  * Arguments:   expression (I/O) char **  to first char of null-terminated
13206:  *                              string immediately following \message
13207:  *                              and returning ptr immediately
13208:  *                              following last character processed.
13209:  *              size (I)        int containing 0-7 default font size
13210:  *              basesp (I)      subraster *  to character (or subexpression)
13211:  *                              immediately preceding \mesasge
13212:  *                              (unused, but passed for consistency)
13213:  *              arg1 (I)        int unused
13214:  *              arg2 (I)        int unused
13215:  *              arg3 (I)        int unused
13216:  * --------------------------------------------------------------------------
13217:  * Returns:     ( subraster * ) subraster ptr to rendered message image
13218:  * --------------------------------------------------------------------------
13219:  * Notes:     o
13220:  * ======================================================================= */
13221: /* --- entry point --- */
13222: FUNCSCOPE subraster *rastmessage ( char **expression, int size,
13223:                             subraster *basesp, int arg1, int arg2, int arg3 )
13224: {
13225: /* -------------------------------------------------------------------------
13226: Allocations and Declarations
13227: -------------------------------------------------------------------------- */
13228: char    /**texsubexpr(),*/ amsg[256]="\000"; /* message number text */
13229: int     imsg = 0;                       /* default message number */
13230: char    msg[4096];
13231: subraster /**rasterize(),*/ *messagesp=NULL; /* rasterize requested message */
13232: /*int   strreplace();*/                 /*replace SERVER_NAME in refmsgnum*/
13233: int     reflevels = REFLEVELS;          /* #topmost levels to match */
13234: /*char  *urlprune();*/                  /*prune referer_match in refmsgnum*/
13235: /*char  *strdetex();*/                  /* remove math chars from messages */
13236: char    *http_host    = getenv("HTTP_HOST"), /* http host for mimeTeX */
13237:         *server_name  = getenv("SERVER_NAME"), /* server hosting mimeTeX */
13238:         *referer_match = (!isempty(http_host)?http_host: /*match http_host*/
13239:           (!isempty(server_name)?server_name:(NULL))); /* or server_name */
13240: /* -------------------------------------------------------------------------
13241: obtain message {amsg} argument
13242: -------------------------------------------------------------------------- */
13243: /* --- parse for {amsg} arg, and bump expression past it --- */
13244: *expression = texsubexpr(*expression,amsg,255,"{","}",0,0);
13245: /* --- interpret argument --- */
13246: if ( *amsg != '\000' ) {                /* got amsg arg */
13247:   imsg = atoi(amsg);                    /* interpret as an int */
13248:   if ( imsg < 0                         /* if too small */
13249:   ||   imsg > maxmsgnum )               /* or too big */
13250:     imsg = 0; }                         /* default to first message */
13251: /* --- retrieve requested message --- */
13252: strninit(msg,msgtable[imsg],4095);      /* local copy of message */
13253: /* --- process as necessary --- */
13254: if ( imsg == refmsgnum) {               /* urlncmp() failed to validate */
13255:   if ( reflevels > 0 )                  /* have #levels to validate */
13256:    strreplace(msg,"SERVER_NAME",        /* replace SERVER_NAME */
13257:     strdetex(urlprune(referer_match,reflevels),1),0); /*with referer_match*/
13258:   } /* --- end-of-switch(imsg) --- */
13259: /* --- rasterize requested message --- */
13260: messagesp = rasterize(msg,size);        /* rasterize message string */
13261: /* --- return message raster to caller --- */
13262: /*end_of_job:*/
13263:   return ( messagesp );                 /* return message to caller */
13264: } /* --- end-of-function rastmessage() --- */
13265: 
13266: 
13267: /* ==========================================================================
13268:  * Function:    rastnoop ( expression, size, basesp, nargs, arg2, arg3 )
13269:  * Purpose:     no op -- flush \escape without error
13270:  * --------------------------------------------------------------------------
13271:  * Arguments:   expression (I/O) char **  to first char of null-terminated
13272:  *                              string immediately following \escape to be
13273:  *                              flushed, and returning ptr immediately
13274:  *                              following last character processed.
13275:  *              size (I)        int containing 0-5 default font size
13276:  *              basesp (I)      subraster *  to character (or subexpression)
13277:  *                              immediately preceding \escape
13278:  *                              (unused, but passed for consistency)
13279:  *              nargs (I)       int containing number of {}-args after
13280:  *                              \escape to be flushed along with it
13281:  *              arg2 (I)        int unused
13282:  *              arg3 (I)        int unused
13283:  * --------------------------------------------------------------------------
13284:  * Returns:     ( subraster * ) NULL subraster ptr
13285:  * --------------------------------------------------------------------------
13286:  * Notes:     o
13287:  * ======================================================================= */
13288: /* --- entry point --- */
13289: FUNCSCOPE subraster *rastnoop(char **expression, int size, subraster *basesp,
13290:                               int nargs, int arg2, int arg3)
13291: {
13292: /* -------------------------------------------------------------------------
13293: Allocations and Declarations
13294: -------------------------------------------------------------------------- */
13295: char    /**texsubexpr(),*/subexpr[MAXSUBXSZ+1];/*dummy args eaten by \escape*/
13296: subraster /**rasterize(),*/ *noopsp=NULL; /* rasterize subexpr */
13297: /* --- flush accompanying args if necessary --- */
13298: if ( nargs != NOVALUE                   /* not unspecified */
13299: &&   nargs > 0 )                        /* and args to be flushed */
13300:   while ( --nargs >= 0 )                /* count down */
13301:     *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0); /*flush arg*/
13302: /* --- return null ptr to caller --- */
13303: /*end_of_job:*/
13304:   return ( noopsp );                    /* return NULL ptr to caller */
13305: } /* --- end-of-function rastnoop() --- */
13306: 
13307: 
13308: /* ==========================================================================
13309:  * Function:    rastopenfile ( filename, mode )
13310:  * Purpose:     Opens filename[.tex] in mode, returning FILE *
13311:  * --------------------------------------------------------------------------
13312:  * Arguments:   filename (I/O)  char * to null-terminated string containing
13313:  *                              name of file to open (preceded by path
13314:  *                              relative to mimetex executable)
13315:  *                              If fopen() fails, .tex appeneded,
13316:  *                              and returned if that fopen() succeeds
13317:  *              mode (I)        char * to null-terminated string containing
13318:  *                              fopen() mode
13319:  * --------------------------------------------------------------------------
13320:  * Returns:     ( FILE * )      pointer to opened file, or NULL if error
13321:  * --------------------------------------------------------------------------
13322:  * Notes:     o
13323:  * ======================================================================= */
13324: /* --- entry point --- */
13325: FUNCSCOPE FILE *rastopenfile ( char *filename, char *mode )
13326: {
13327: /* -------------------------------------------------------------------------
13328: Allocations and Declarations
13329: -------------------------------------------------------------------------- */
13330: FILE    *fp = (FILE *)NULL /*,*fopen()*/; /*file pointer to opened filename*/
13331: char    texfile[2050] = "\000",         /* local, edited copy of filename */
13332:         /**rasteditfilename(),*/        /* prepend pathprefix if necessary */
13333:         amode[512] = "r";               /* test open mode if arg mode=NULL */
13334: int     ismode = 0;                     /* true of mode!=NULL */
13335: /* --------------------------------------------------------------------------
13336: Check mode and open file
13337: -------------------------------------------------------------------------- */
13338: /* --- edit filename --- */
13339: strncpy(texfile,rasteditfilename(filename),2047); /*edited copy of filename*/
13340: texfile[2047] = '\000';                 /* make sure it's null terminated */
13341: /* --- check mode --- */
13342: if ( mode != (char *)NULL )             /* caller passed mode arg */
13343:  if ( *mode != '\000' )                 /* and it's not an empty string */
13344:   { ismode = 1;                         /* so flip mode flag true */
13345:     strncpy(amode,mode,254);            /* and replace "r" with caller's */
13346:     amode[254] = '\000';                /* make sure it's null terminated */
13347:     compress(amode,' '); }              /* remove embedded blanks */
13348: /* --- open filename or filename.tex --- */
13349: if ( strlen(texfile) > 1 )              /* make sure we got actual filename*/
13350:   if ( (fp = fopen(texfile,amode))      /* try opening given filename */
13351:   ==   NULL )                           /* failed to open given filename */
13352:   { strcpy(filename,texfile);           /* signal possible filename error */
13353:     strcat(texfile,".tex");             /* but first try adding .tex */
13354:     if ( (fp = fopen(texfile,amode))    /* now try opening filename.tex */
13355:     !=   NULL )                         /* filename.tex succeeded */
13356:       strcpy(filename,texfile); }       /* replace caller's filename */
13357: /* --- close file if only opened to check name --- */
13358: if ( !ismode && fp!=NULL )              /* no mode, so just checking */
13359:   fclose(fp);                           /* close file, fp signals success */
13360: /* --- return fp or NULL to caller --- */
13361: /*end_of_job:*/
13362:   if ( msglevel>=9 && msgfp!=NULL )     /* debuging */
13363:     { fprintf(msgfp,"rastopenfile> returning fopen(%s,%s) = %s\n",
13364:       filename,amode,(fp==NULL?"NULL":"Okay")); fflush(msgfp); }
13365:   return ( fp );                        /* return fp or NULL to caller */
13366: } /* --- end-of-function rastopenfile() --- */
13367: 
13368: 
13369: /* ==========================================================================
13370:  * Function:    rasteditfilename ( filename )
13371:  * Purpose:     edits filename to remove security problems,
13372:  *              e.g., removes all ../'s and ..\'s.
13373:  * --------------------------------------------------------------------------
13374:  * Arguments:   filename (I)    char * to null-terminated string containing
13375:  *                              name of file to be edited
13376:  * --------------------------------------------------------------------------
13377:  * Returns:     ( char * )      pointer to edited filename,
13378:  *                              or empty string "\000" if any problem
13379:  * --------------------------------------------------------------------------
13380:  * Notes:     o
13381:  * ======================================================================= */
13382: /* --- entry point --- */
13383: FUNCSCOPE char *rasteditfilename ( char *filename )
13384: {
13385: /* -------------------------------------------------------------------------
13386: Allocations and Declarations
13387: -------------------------------------------------------------------------- */
13388: static  char editname[2050];            /*edited filename returned to caller*/
13389: /*char  *strchange();*/                 /* prepend pathprefix if necessary */
13390: int     /*strreplace(),*/               /* remove ../'s and ..\'s */
13391:         isprefix = (*pathprefix=='\000'?0:1); /* true if paths have prefix */
13392: /* --------------------------------------------------------------------------
13393: edit filename
13394: -------------------------------------------------------------------------- */
13395: /* --- first check filename arg --- */
13396: *editname = '\000';                     /* init edited name as empty string*/
13397: if ( filename == (char *)NULL ) goto end_of_job; /* no filename arg */
13398: if ( *filename == '\000' ) goto end_of_job; /* filename is an empty string */
13399: /* --- init edited filename --- */
13400: strcpy(editname,filename);              /* init edited name as input name */
13401: compress(editname,' ');                 /* remove embedded blanks */
13402: /* --- remove leading or embedded ....'s --- */
13403: while ( strreplace(editname,"....",NULL,0) > 0 ) ;  /* squeeze out ....'s */
13404: /* --- remove leading / and \ and dots (and blanks) --- */
13405: if ( *editname != '\000' )              /* still have chars in filename */
13406:  while ( isthischar(*editname," ./\\") ) /* absolute paths invalid */
13407:    {strsqueeze(editname,1);}            /* so flush leading / or \ (or ' ')*/
13408: if ( *editname == '\000' ) goto end_of_job; /* no chars left in filename */
13409: /* --- remove leading or embedded ../'s and ..\'s --- */
13410: while ( strreplace(editname,"../",NULL,0) > 0 ) ;  /* squeeze out ../'s */
13411: while ( strreplace(editname,"..\\",NULL,0) > 0 ) ; /* and ..\'s */
13412: while ( strreplace(editname,"../",NULL,0) > 0 ) ;  /* and ../'s again */
13413: /* --- prepend path prefix (if compiled with -DPATHPREFIX) --- */
13414: if ( isprefix && *editname!='\000' )    /* filename is preceded by prefix */
13415:   strchange(0,editname,pathprefix);     /* so prepend prefix */
13416: end_of_job:
13417:   return ( editname );                  /* back with edited filename */
13418: } /* --- end-of-function rasteditfilename() --- */
13419: 
13420: 
13421: /* ==========================================================================
13422:  * Function:    rastreadfile ( filename, islock, tag, value )
13423:  * Purpose:     Read filename, returning value as string
13424:  *              between <tag>...</tag> or entire file if tag=NULL passed.
13425:  * --------------------------------------------------------------------------
13426:  * Arguments:   filename (I)    char * to null-terminated string containing
13427:  *                              name of file to read (preceded by path
13428:  *                              relative to mimetex executable)
13429:  *              islock (I)      int containing 1 to lock file while reading
13430:  *                              (hopefully done by opening in "r+" mode)
13431:  *              tag (I)         char * to null-terminated string containing
13432:  *                              html-like tagname.  File contents between
13433:  *                              <tag> and </tag> will be returned, or
13434:  *                              entire file if tag=NULL passed.
13435:  *              value (O)       char * returning value between <tag>...</tag>
13436:  *                              or entire file if tag=NULL.
13437:  * --------------------------------------------------------------------------
13438:  * Returns:     ( int )         1=okay, 0=some error
13439:  * --------------------------------------------------------------------------
13440:  * Notes:     o
13441:  * ======================================================================= */
13442: /* --- entry point --- */
13443: FUNCSCOPE int rastreadfile(char *filename, int islock, char *tag, char *value)
13444: {
13445: /* -------------------------------------------------------------------------
13446: Allocations and Declarations
13447: -------------------------------------------------------------------------- */
13448: FILE    *fp = (FILE *)NULL /*, *rastopenfile()*/; /*ptr to opened filename*/
13449: char    texfile[1024] = "\000",         /* local copy of input filename */
13450:         text[MAXLINESZ+1];              /* line from input file */
13451: char    *tagp, tag1[1024], tag2[1024];  /* left <tag> and right <tag/> */
13452: int     vallen=0, maxvallen=MAXFILESZ;  /* #chars in value, max allowed */
13453: int     status = (-1);                  /* status returned, 1=okay */
13454: int     tagnum = 0;                     /* tag we're looking for */
13455: /*int   islock = 1;*/                   /* true to lock file */
13456: /* --------------------------------------------------------------------------
13457: Open file
13458: -------------------------------------------------------------------------- */
13459: /* --- first check output arg --- */
13460: if ( value == (char *)NULL ) goto end_of_job; /* no output buffer supplied */
13461: *value = '\000';                        /* init buffer with empty string */
13462: /* --- open filename or filename.tex --- */
13463: if ( filename != (char *)NULL )         /* make sure we got filename arg */
13464:   { strncpy(texfile,filename,1023);     /* local copy of filename */
13465:     texfile[1023] = '\000';             /* make sure it's null terminated */
13466:     fp = rastopenfile(texfile,(islock?"r+":"r")); } /* try opening it */
13467: /* --- check that file opened --- */
13468: if ( fp == (FILE *)NULL )               /* failed to open file */
13469:   { sprintf(value,"{\\normalsize\\rm[file %s?]}",texfile);
13470:     goto end_of_job; }                  /* return error message to caller */
13471: status = 0;                             /* file opened successfully */
13472: if ( islock ) rewind(fp);               /* start at beginning of file */
13473: /* --------------------------------------------------------------------------
13474: construct <tag>'s
13475: -------------------------------------------------------------------------- */
13476: if ( tag != (char *)NULL )              /* caller passed tag arg */
13477:  if ( *tag != '\000' )                  /* and it's not an empty string */
13478:   { strcpy(tag1,"<"); strcpy(tag2,"</"); /* begin with < and </ */
13479:     strcat(tag1,tag); strcat(tag2,tag); /* followed by caller's tag */
13480:     strcat(tag1,">"); strcat(tag2,">"); /* ending both tags with > */
13481:     compress(tag1,' '); compress(tag2,' '); /* remove embedded blanks */
13482:     tagnum = 1; }                       /* signal that we have tag */
13483: /* --------------------------------------------------------------------------
13484: Read file, concatnate lines
13485: -------------------------------------------------------------------------- */
13486: while ( fgets(text,MAXLINESZ-1,fp) != (char *)NULL ) { /*read input till eof*/
13487:   switch ( tagnum ) {                   /* look for left- or right-tag */
13488:     case 0: status = 1; break;          /* no tag to look for */
13489:     case 1:                             /* looking for opening left <tag> */
13490:       if ( (tagp=strstr(text,tag1)) == NULL ) break; /*haven't found it yet*/
13491:       tagp += strlen(tag1);             /* first char past tag */
13492:       strsqueezep(text,tagp);           /*shift out preceding text and tag*/
13493:       tagnum = 2;                       /*now looking for closing right tag*/
13494:     case 2:                             /* looking for closing right </tag> */
13495:       if ( (tagp=strstr(text,tag2)) == NULL ) break; /*haven't found it yet*/
13496:       *tagp = '\000';                   /* terminate line at tag */
13497:       tagnum = 3;                       /* done after this line */
13498:       status = 1;                       /* successfully read tag */
13499:       break;
13500:     } /* ---end-of-switch(tagnum) --- */
13501:   if ( tagnum != 1 ) {                  /* no tag or left tag already found*/
13502:     int textlen = strlen(text);         /* #chars in current line */
13503:     if ( vallen+textlen > maxvallen ) break; /* quit before overflow */
13504:     strcat(value,text);                 /* concat line to end of value */
13505:     vallen += textlen;                  /* bump length */
13506:     if ( tagnum > 2 ) break; }          /* found right tag, so we're done */
13507:   } /* --- end-of-while(fgets()!=NULL) --- */
13508: if ( tagnum<1 || tagnum>2 ) status=1;   /* okay if no tag or we found tag */
13509: fclose ( fp );                          /* close input file after reading */
13510: /* --- return value and status to caller --- */
13511: end_of_job:
13512:   return ( status );                    /* return status to caller */
13513: } /* --- end-of-function rastreadfile() --- */
13514: 
13515: 
13516: /* ==========================================================================
13517:  * Function:    rastwritefile ( filename, tag, value, isstrict )
13518:  * Purpose:     Re/writes filename, replacing string between <tag>...</tag>
13519:  *              with value, or writing entire file as value if tag=NULL.
13520:  * --------------------------------------------------------------------------
13521:  * Arguments:   filename (I)    char * to null-terminated string containing
13522:  *                              name of file to write (preceded by path
13523:  *                              relative to mimetex executable)
13524:  *              tag (I)         char * to null-terminated string containing
13525:  *                              html-like tagname.  File contents between
13526:  *                              <tag> and </tag> will be replaced, or
13527:  *                              entire file written if tag=NULL passed.
13528:  *              value (I)       char * containing string replacing value
13529:  *                              between <tag>...</tag> or replacing entire
13530:  *                              file if tag=NULL.
13531:  *              isstrict (I)    int containing 1 to only rewrite existing
13532:  *                              files, or 0 to create new file if necessary.
13533:  * --------------------------------------------------------------------------
13534:  * Returns:     ( int )         1=okay, 0=some error
13535:  * --------------------------------------------------------------------------
13536:  * Notes:     o
13537:  * ======================================================================= */
13538: /* --- entry point --- */
13539: FUNCSCOPE int rastwritefile(char *filename,char *tag,char *value,int isstrict)
13540: {
13541: /* -------------------------------------------------------------------------
13542: Allocations and Declarations
13543: -------------------------------------------------------------------------- */
13544: FILE    *fp = (FILE *)NULL /*, *rastopenfile()*/; /*ptr to opened filename*/
13545: char    texfile[1024] = "\000",         /* local copy of input filename */
13546:         filebuff[MAXFILESZ+1] = "\000", /* entire contents of file */
13547:         tag1[1024], tag2[1024]          /* left <tag> and right <tag/> */
13548:         /*,*strchange(),*/              /* put value between <tag>...</tag>*/
13549:         /**timestamp()*/;               /* log modification time */
13550: int     istag=0, /*rastreadfile(),*/    /* read file if tag!=NULL */
13551:         /*isstrict = (seclevel>5? 1:0),*/ /*true only writes existing files*/
13552:         isnewfile = 0,                  /* true if writing new file */
13553:         status = 0;                     /* status returned, 1=okay */
13554: int     istimestamp = 0;                /* true to update <timestamp> tag */
13555: /* --------------------------------------------------------------------------
13556: check args
13557: -------------------------------------------------------------------------- */
13558: /* --- check filename and value --- */
13559: if ( filename == (char *)NULL           /* quit if no filename arg supplied*/
13560: ||   value == (char *)NULL ) goto end_of_job; /* or no value arg supplied */
13561: if ( strlen(filename) < 2               /* quit if unreasonable filename */
13562: ||   *value == '\000' ) goto end_of_job; /* or empty value string supplied */
13563: /* --- establish filename[.tex] --- */
13564: strncpy(texfile,filename,1023);         /* local copy of input filename */
13565: texfile[1023] = '\000';                 /* make sure it's null terminated */
13566: if ( rastopenfile(texfile,NULL)         /* unchanged or .tex appended */
13567: ==   (FILE *)NULL )                     /* can't open, so write new file */
13568:   { if ( isstrict ) goto end_of_job;    /* fail if new files not permitted */
13569:     isnewfile = 1; }                    /* signal we're writing new file */
13570: /* --- check whether tag supplied by caller --- */
13571: if ( tag != (char *)NULL )              /* caller passed tag argument */
13572:  if ( *tag != '\000' )                  /* and it's not an empty string */
13573:   { istag = 1;                          /* so flip tag flag true */
13574:     strcpy(tag1,"<"); strcpy(tag2,"</");  /* begin tags with < and </ */
13575:     strcat(tag1,tag); strcat(tag2,tag);   /* followed by caller's tag */
13576:     strcat(tag1,">"); strcat(tag2,">"); /* ending both tags with > */
13577:     compress(tag1,' '); compress(tag2,' '); } /* remove embedded blanks */
13578: /* --------------------------------------------------------------------------
13579: read existing file if just rewriting a single tag
13580: -------------------------------------------------------------------------- */
13581: /* --- read original file if only replacing a tag within it --- */
13582: *filebuff = '\000';                     /* init as empty file */
13583: if ( !isnewfile )                       /* if file already exists */
13584:  if ( istag )                           /* and just rewriting one tag */
13585:   if ( rastreadfile(texfile,1,NULL,filebuff) /* read entire existing file */
13586:   <=   0 ) goto end_of_job;             /* signal error if failed to read */
13587: /* --------------------------------------------------------------------------
13588: construct new file data if needed (entire file replaced by value if no tag)
13589: -------------------------------------------------------------------------- */
13590: if ( istag )                            /* only replacing tag in file */
13591:  {
13592:  /* --- find <tag> and </tag> in file --- */
13593:  int    tlen1=strlen(tag1),  tlen2=strlen(tag2), flen;  /*tag,buff lengths*/
13594:  char   *tagp1 = (isnewfile? NULL:strstr(filebuff,tag1)), /* <tag> in file*/
13595:         *tagp2 = (isnewfile? NULL:strstr(filebuff,tag2)); /*</tag> in file*/
13596:  /* --- if adding new <tag> just concatanate at end of file --- */
13597:  if ( tagp1 == (char *)NULL )           /* add new tag to file */
13598:   {
13599:   /* --- preprocess filebuff --- */
13600:   if ( tagp2 != (char *)NULL )          /* apparently have ...</tag> */
13601:     {strsqueezep(filebuff,tagp2+tlen2);} /* remove ...</tag> */
13602:   if ( (flen = strlen(filebuff))        /* #chars currently in buffer */
13603:   > 0 )                                 /* we have non-empty buffer */
13604:    if (!isthischar(*(filebuff+flen-1),"\n\r")) /*no newline at end of file*/
13605:      if(0)strcat(filebuff,"\n");        /* so add one before new tag */
13606:   /* --- add new tag --- */
13607:   strcat(filebuff,tag1);                /* add opening <tag> */
13608:   strcat(filebuff,value);               /* then value */
13609:   strcat(filebuff,tag2);                /* finally closing </tag> */
13610:   strcat(filebuff,"\n");                /* newline at end of file */
13611:   } /* --- end-of-if(tagp1==NULL) --- */
13612:  else                                   /* found existing opening <tag> */
13613:   {
13614:   if ( tagp2 == NULL )                  /* apparently have <tag>... */
13615:     { *(tagp1+tlen1) = '\000';          /* so get rid of trailing ... */
13616:       strcat(filebuff,value);           /* then concatanate value */
13617:       strcat(filebuff,tag2); }          /* and finally closing </tag> */
13618:   else                                  /* else have <tag>...<tag/> */
13619:    if ( (flen=((int)(tagp2-tagp1))-tlen1) /* len of .'s in <tag>...</tag> */
13620:    >=   0 )                             /* usually <tag> precedes </tag> */
13621:     strchange(flen,tagp1+tlen1,value);  /* change ...'s to value */
13622:    else                                 /* weirdly, </tag> precedes <tag> */
13623:     { char fbuff[4096];                 /* field buff for <tag>value</tag> */
13624:       if ( (flen = ((int)(tagp1-tagp2))+tlen1) /* strlen(</tag>...<tag>) */
13625:       <=   0 ) goto end_of_job;         /* must be internal error */
13626:       strcpy(fbuff,tag1);               /* set opening <tag> */
13627:       strcat(fbuff,value);              /* then value */
13628:       strcat(fbuff,tag2);               /* finally closing </tag> */
13629:       strchange(flen,tagp2,fbuff); }    /* replace original </tag>...<tag> */
13630:   } /* --- end-of-if/else(tagp1==NULL) --- */
13631:  } /* --- end-of-if(istag) --- */
13632: /* --------------------------------------------------------------------------
13633: rewrite file and return to caller
13634: -------------------------------------------------------------------------- */
13635: /* --- first open file for write --- */
13636: if ( (fp=rastopenfile(texfile,"w"))     /* open for write */
13637: ==   (FILE *)NULL ) goto end_of_job;    /* signal error if can't open */
13638: /* --- rewrite and close file --- */
13639: if ( fputs((istag?filebuff:value),fp)   /* write filebuff or value */
13640: !=  EOF ) status = 1;                   /* signal success if succeeded */
13641: fclose ( fp );                          /* close output file after writing */
13642: /* --- modify timestamp --- */
13643: if ( status > 0 )                       /*forget timestamp if write failed*/
13644:  if ( istimestamp )                     /* if we're updating timestamp */
13645:   if ( istag )                          /* only log time in tagged file */
13646:    if ( strstr(tag,"timestamp") == (char *)NULL ) /* but avoid recursion */
13647:     { char fbuff[2048];                 /* field buff <timestamp> value */
13648:       strcpy(fbuff,tag);                /* tag modified */
13649:       strcat(fbuff," modified at ");    /* spacer */
13650:       strcat(fbuff,timestamp(TZDELTA,0)); /* start with timestamp */
13651:       status = rastwritefile(filename,"timestamp",fbuff,1); }
13652: /* --- return status to caller --- */
13653: end_of_job:
13654:   return ( status );                    /* return status to caller */
13655: } /* --- end-of-function rastwritefile() --- */
13656: 
13657: 
13658: /* ==========================================================================
13659:  * Function:    calendar ( year, month, day )
13660:  * Purpose:     returns null-terminated character string containing
13661:  *              \begin{array}...\end{array} for the one-month calendar
13662:  *              specified by year=1973...2099 and month=1...12.
13663:  *              If either arg out-of-range, today's value is used.
13664:  * --------------------------------------------------------------------------
13665:  * Arguments:   year (I)        int containing 1973...2099 or 0 for current
13666:  *                              year
13667:  *              month (I)       int containing 1...12 or 0 for current month
13668:  *              day (I)         int containing day to emphasize or 0
13669:  * --------------------------------------------------------------------------
13670:  * Returns:     ( char * )      char ptr to null-terminated buffer
13671:  *                              containing \begin{array}...\end{array}
13672:  *                              string that will render calendar for
13673:  *                              requested month, or NULL for any error.
13674:  * --------------------------------------------------------------------------
13675:  * Notes:     o
13676:  * ======================================================================= */
13677: /* --- entry point --- */
13678: FUNCSCOPE char *calendar( int year, int month, int day )
13679: {
13680: /* -------------------------------------------------------------------------
13681: Allocations and Declarations
13682: -------------------------------------------------------------------------- */
13683: static char calbuff[4096];              /* calendar returned to caller */
13684: time_t  time_val = (time_t)(0);         /* binary value returned by time() */
13685: struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */
13686: int     yy=0, mm=0, dd=0;               /* today (emphasize today's dd) */
13687: int     idd=1, iday=0, daynumber();     /* day-of-week for idd=1...31 */
13688: char    aval[64];                       /* ascii day or 4-digit year */
13689: /* --- calendar data --- */
13690: static  char *monthnames[] = { "?", "January", "February", "March", "April",
13691:          "May", "June", "July", "August", "September", "October",
13692:         "November", "December", "?" } ;
13693: static  int modays[] =
13694:         { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0 };
13695: /* -------------------------------------------------------------------------
13696: initialization
13697: -------------------------------------------------------------------------- */
13698: /* --- get current date/time --- */
13699: time((time_t *)(&time_val));            /* get date and time */
13700: tmstruct = localtime((time_t *)(&time_val)); /* interpret time_val */
13701: yy  =  1900 + (int)(tmstruct->tm_year); /* current four-digit year */
13702: mm  =  1 + (int)(tmstruct->tm_mon);     /* current month, 1-12 */
13703: dd  =  (int)(tmstruct->tm_mday);        /* current day, 1-31 */
13704: /* --- check args --- */
13705: if ( year<1973 || year>2099 ) year  = yy; /* current year if out-of-bounds */
13706: if ( month<1 || month>12 ) month = mm;  /* current month if out-of-bounds */
13707: if ( month==mm && year==yy && day==0 )  /* current month and default day */
13708:   day = dd;                             /* emphasize current day */
13709: modays[2] = (year%4==0?29:28);          /* Feb has 29 days in leap years */
13710: /* --- initialize calendar string --- */
13711: strcpy(calbuff,"{\\begin{gather}");     /* center `month year` above cal */
13712: strcat(calbuff,"\\small\\text{");       /* month set in roman */
13713: strcat(calbuff,monthnames[month]);      /* insert month name */
13714: strcat(calbuff," }");                   /* add a space */
13715: sprintf(aval,"%d",year);                /* convert year to ascii */
13716: strcat(calbuff,aval);                   /* add year */
13717: strcat(calbuff,"\\\\");                 /* end top row */
13718: strcat(calbuff,                         /* now begin calendar arrayr */
13719:         "\\begin{array}{|c|c|c|c|c|c|c|CCCCCC} \\hline"
13720:         "\\tiny\\text{Sun} & \\tiny\\text{Mon} & \\tiny\\text{Tue} &"
13721:         "\\tiny\\text{Wed} & \\tiny\\text{Thu} & \\tiny\\text{Fri} &"
13722:         "\\tiny\\text{Sat} \\\\ \\hline " );
13723: /* -------------------------------------------------------------------------
13724: generate calendar
13725: -------------------------------------------------------------------------- */
13726: for ( idd=1; idd<=modays[month]; idd++ ) /* run through days of month */
13727:   {
13728:   /* --- get day-of-week for this day --- */
13729:   iday = 1 + (daynumber(year,month,idd)%7); /* 1=Monday...7=Sunday */
13730:   if ( iday == 7 ) iday = 0;            /* now 0=Sunday...6=Saturday */
13731:   /* --- may need empty cells at beginning of month --- */
13732:   if ( idd == 1 )                       /* first day of month */
13733:    if ( iday > 0 )                      /* need to skip cells */
13734:     { strcpy(aval,"\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\"); /*cells to skip*/
13735:       aval[3*iday] = '\000';            /*skip cells preceding 1st of month*/
13736:       strcat(calbuff,aval); }           /* add skip string to buffer */
13737:   /* --- add idd to current cell --- */
13738:   sprintf(aval,"%d",idd);               /* convert idd to ascii */
13739:   if ( idd == day                       /* emphasize today's date */
13740:   /*&&   month==mm && year==yy*/ )      /* only if this month's calendar */
13741:    { strcat(calbuff,"{\\fs{-1}\\left\\langle "); /*emphasize, 1 size smaller*/
13742:      strcat(calbuff,aval);              /* put in idd */
13743:      strcat(calbuff,"\\right\\rangle}"); } /* finish emphasis */
13744:   else                                  /* not today's date */
13745:     strcat(calbuff,aval);               /* so just put in idd */
13746:   /* --- terminate cell --- */
13747:   if ( idd < modays[month] ) {          /* not yet end-of-month */
13748:    if ( iday < 6 )                      /* still have days left in week */
13749:     strcat(calbuff,"&");                /* new cell in same week */
13750:    else                                 /* reached end-of-week */
13751:     strcat(calbuff,"\\\\ \\hline"); }   /* so start new week */
13752:   } /* --- end-of-for(idd) --- */
13753: strcat(calbuff,"\\\\ \\hline");         /* final underline at end-of-month */
13754: /* --- return calendar to caller --- */
13755: strcat(calbuff,"\\end{array}\\end{gather}}"); /* terminate array */
13756: return ( calbuff );                     /* back to caller with calendar */
13757: } /* --- end-of-function calendar() --- */
13758: 
13759: 
13760: /* ==========================================================================
13761:  * Function:    timestamp ( tzdelta, ifmt )
13762:  * Purpose:     returns null-terminated character string containing
13763:  *              current date:time stamp as ccyy-mm-dd:hh:mm:ss{am,pm}
13764:  * --------------------------------------------------------------------------
13765:  * Arguments:   tzdelta (I)     integer, positive or negative, containing
13766:  *                              containing number of hours to be added or
13767:  *                              subtracted from system time (to accommodate
13768:  *                              your desired time zone).
13769:  *              ifmt (I)        integer containing 0 for default format
13770:  * --------------------------------------------------------------------------
13771:  * Returns:     ( char * )      ptr to null-terminated buffer
13772:  *                              containing current date:time stamp
13773:  * --------------------------------------------------------------------------
13774:  * Notes:     o
13775:  * ======================================================================= */
13776: /* --- entry point --- */
13777: FUNCSCOPE char *timestamp( int tzdelta, int ifmt )
13778: {
13779: /* -------------------------------------------------------------------------
13780: Allocations and Declarations
13781: -------------------------------------------------------------------------- */
13782: static  char timebuff[256];             /* date:time buffer back to caller */
13783: /*long  time_val = 0L;*/                /* binary value returned by time() */
13784: time_t  time_val = (time_t)(0);         /* binary value returned by time() */
13785: struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */
13786: int     year=0, hour=0,ispm=1,          /* adjust year, and set am/pm hour */
13787:         month=0, day=0,                 /* adjust day and month for delta  */
13788:         minute=0,second=0;              /* minute and second not adjusted  */
13789: /*int   tzadjust();*/                   /* time zone adjustment function */
13790: /*int   daynumber();*/                  /* #days since Jan 1, 1973 */
13791: static  char *daynames[] = { "Monday", "Tuesday", "Wednesday",
13792:          "Thursday", "Friday", "Saturday", "Sunday" } ;
13793: static  char *monthnames[] = { "?", "January", "February", "March", "April",
13794:          "May", "June", "July", "August", "September", "October",
13795:         "November", "December", "?" } ;
13796: /* -------------------------------------------------------------------------
13797: get current date:time, adjust values, and and format stamp
13798: -------------------------------------------------------------------------- */
13799: /* --- first init returned timebuff in case of any error --- */
13800: *timebuff = '\000';
13801: /* --- get current date:time --- */
13802: time((time_t *)(&time_val));            /* get date and time */
13803: tmstruct = localtime((time_t *)(&time_val)); /* interpret time_val */
13804: /* --- extract fields --- */
13805: year  = (int)(tmstruct->tm_year);       /* local copy of year,  0=1900 */
13806: month = (int)(tmstruct->tm_mon) + 1;    /* local copy of month, 1-12 */
13807: day   = (int)(tmstruct->tm_mday);       /* local copy of day,   1-31 */
13808: hour  = (int)(tmstruct->tm_hour);       /* local copy of hour,  0-23 */
13809: minute= (int)(tmstruct->tm_min);        /* local copy of minute,0-59 */
13810: second= (int)(tmstruct->tm_sec);        /* local copy of second,0-59 */
13811: /* --- adjust year --- */
13812: year += 1900;                           /* set century in year */
13813: /* --- adjust for timezone --- */
13814: tzadjust(tzdelta,&year,&month,&day,&hour);
13815: /* --- check params --- */
13816: if ( hour<0  || hour>23
13817: ||   day<1   || day>31
13818: ||   month<1 || month>12
13819: ||   year<1973 ) goto end_of_job;
13820: /* --- adjust hour for am/pm --- */
13821: switch ( ifmt )
13822:   {
13823:   default:
13824:   case 0:
13825:     if ( hour < 12 )                    /* am check */
13826:      { ispm=0;                          /* reset pm flag */
13827:        if ( hour == 0 ) hour = 12; }    /* set 00hrs = 12am */
13828:     if ( hour > 12 ) hour -= 12;        /* pm check sets 13hrs to 1pm, etc */
13829:     break;
13830:   } /* --- end-of-switch(ifmt) --- */
13831: /* --- format date:time stamp --- */
13832: switch ( ifmt )
13833:   {
13834:   default:
13835:   case 0:  /* --- 2005-03-05:11:49:59am --- */
13836:     sprintf(timebuff,"%04d-%02d-%02d:%02d:%02d:%02d%s", year,month,day,
13837:     hour,minute,second,((ispm)?"pm":"am"));
13838:     break;
13839:   case 1:  /* --- Saturday, March 5, 2005 --- */
13840:     sprintf(timebuff,"%s, %s %d, %d",
13841:     daynames[daynumber(year,month,day)%7],monthnames[month],day,year);
13842:     break;
13843:   case 2: /* --- Saturday, March 5, 2005, 11:49:59am --- */
13844:     sprintf(timebuff,"%s, %s %d, %d, %d:%02d:%02d%s",
13845:     daynames[daynumber(year,month,day)%7],monthnames[month],day,year,
13846:     hour,minute,second,((ispm)?"pm":"am"));
13847:     break;
13848:   case 3: /* --- 11:49:59am --- */
13849:     sprintf(timebuff,"%d:%02d:%02d%s",
13850:     hour,minute,second,((ispm)?"pm":"am"));
13851:     break;
13852:   case 4: /* --- 1231235959 (mmddhhmmss time as integer) --- */
13853:     sprintf(timebuff,"%d%02d%02d%02d%02d",
13854:     month,day,hour,minute,second);
13855:     break;
13856:   } /* --- end-of-switch(ifmt) --- */
13857: end_of_job:
13858:   return ( timebuff );                  /* return stamp to caller */
13859: } /* --- end-of-function timestamp() --- */
13860: 
13861: 
13862: /* ==========================================================================
13863:  * Function:    tzadjust ( tzdelta, year, month, day, hour )
13864:  * Purpose:     Adjusts hour, and day,month,year if necessary,
13865:  *              by delta increment to accommodate your time zone.
13866:  * --------------------------------------------------------------------------
13867:  * Arguments:   tzdelta (I)     integer, positive or negative, containing
13868:  *                              containing number of hours to be added or
13869:  *                              subtracted from given time (to accommodate
13870:  *                              your desired time zone).
13871:  *              year (I)        addr of int containing        4-digit year
13872:  *              month (I)       addr of int containing month  1=Jan - 12=Dec.
13873:  *              day (I)         addr of int containing day    1-31 for Jan.
13874:  *              hour (I)        addr of int containing hour   0-23
13875:  * Returns:     ( int )         1 for success, or 0 for error
13876:  * --------------------------------------------------------------------------
13877:  * Notes:     o
13878:  * ======================================================================= */
13879: /* --- entry point --- */
13880: FUNCSCOPE int tzadjust(int tzdelta, int *year, int *month, int *day, int *hour)
13881: {
13882: /* --------------------------------------------------------------------------
13883: Allocations and Declarations
13884: -------------------------------------------------------------------------- */
13885: int     yy = *year, mm = *month, dd = *day, hh = *hour; /*dereference args*/
13886: /* --- calendar data --- */
13887: static  int modays[] =
13888:         { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0 };
13889: /* --------------------------------------------------------------------------
13890: check args
13891: -------------------------------------------------------------------------- */
13892: if ( mm<1 || mm>12 ) return(-1);        /* bad month */
13893: if ( dd<1 || dd>modays[mm] ) return(-1); /* bad day */
13894: if ( hh<0 || hh>23 ) return(-1);        /* bad hour */
13895: if ( tzdelta>23 || tzdelta<(-23) ) return(-1); /* bad tzdelta */
13896: /* --------------------------------------------------------------------------
13897: make adjustments
13898: -------------------------------------------------------------------------- */
13899: /* --- adjust hour --- */
13900: hh += tzdelta;                          /* apply caller's delta */
13901: /* --- adjust for feb 29 --- */
13902: modays[2] = (yy%4==0?29:28);            /* Feb has 29 days in leap years */
13903: /* --- adjust day --- */
13904: if ( hh < 0 )                           /* went to preceding day */
13905:   { dd--;  hh += 24; }
13906: if ( hh > 23 )                          /* went to next day */
13907:   { dd++;  hh -= 24; }
13908: /* --- adjust month --- */
13909: if ( dd < 1 )                           /* went to preceding month */
13910:   { mm--;  dd = modays[mm]; }
13911: if ( dd > modays[mm] )                  /* went to next month */
13912:   { mm++;  dd = 1; }
13913: /* --- adjust year --- */
13914: if ( mm < 1 )                           /* went to preceding year */
13915:   { yy--;  mm = 12;  dd = modays[mm]; }
13916: if ( mm > 12 )                          /* went to next year */
13917:   { yy++;  mm = 1;   dd = 1; }
13918: /* --- back to caller --- */
13919: *year=yy; *month=mm; *day=dd; *hour=hh; /* reset adjusted args */
13920: return ( 1 );
13921: } /* --- end-of-function tzadjust() --- */
13922: 
13923: 
13924: /* ==========================================================================
13925:  * Function:    daynumber ( year, month, day )
13926:  * Purpose:     Returns number of actual calendar days from Jan 1, 1973
13927:  *              to the given date (e.g., bvdaynumber(1974,1,1)=365).
13928:  * --------------------------------------------------------------------------
13929:  * Arguments:   year (I)        int containing year -- may be either 1995 or
13930:  *                              95, or may be either 2010 or 110 for those
13931:  *                              years.
13932:  *              month (I)       int containing month, 1=Jan thru 12=Dec.
13933:  *              day (I)         int containing day of month, 1-31 for Jan, etc.
13934:  * Returns:     ( int )         Number of days from Jan 1, 1973 to given date,
13935:  *                              or -1 for error (e.g., year<1973).
13936:  * --------------------------------------------------------------------------
13937:  * Notes:     o
13938:  * ======================================================================= */
13939: /* --- entry point --- */
13940: FUNCSCOPE int daynumber ( int year, int month, int day )
13941: {
13942: /* --------------------------------------------------------------------------
13943: Allocations and Declarations
13944: -------------------------------------------------------------------------- */
13945: /* --- returned value (note: returned as a default "int") --- */
13946: int     ndays;                          /* #days since jan 1, year0 */
13947: /* --- initial conditions --- */
13948: static  int year0 = 73,                 /* jan 1 was a monday, 72 was a leap */
13949:         days4yrs = 1461,                /* #days in 4 yrs = 365*4 + 1 */
13950:         days1yr  = 365;
13951: /* --- table of accumulated days per month (last index not used) --- */
13952: static  int modays[] =
13953:         { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
13954: /* --- variables for #days since day0 --- */
13955: int     nyears, nfouryrs;               /*#years, #4-yr periods since year0*/
13956: /* --------------------------------------------------------------------------
13957: Check input
13958: -------------------------------------------------------------------------- */
13959: if ( month < 1 || month > 12 )          /*month used as index, so must be ok*/
13960:         return ( -1 );                  /* otherwise, forget it */
13961: if ( year >= 1900 ) year -= 1900;       /*use two-digit years (3 after 2000)*/
13962: /* --------------------------------------------------------------------------
13963: Find #days since jan 1, 1973
13964: -------------------------------------------------------------------------- */
13965: /* --- figure #complete 4-year periods and #remaining yrs till current --- */
13966: nyears = year - year0;                  /* #years since year0 */
13967: if ( nyears < 0 ) return ( -1 );        /* we're not working backwards */
13968: nfouryrs = nyears/4;                    /* #complete four-year periods */
13969: nyears -= (4*nfouryrs);                 /* remainder excluding current year*/
13970: /* --- #days from jan 1, year0 till jan 1, this year --- */
13971: ndays = (days4yrs*nfouryrs)             /* #days in 4-yr periods */
13972:       +  (days1yr*nyears);              /* +remaining days */
13973: /*if ( year > 100 ) ndays--;*/          /* subtract leap year for 2000AD */
13974: /* --- add #days within current year --- */
13975: ndays += (modays[month-1] + (day-1));
13976: /* --- may need an extra day if current year is a leap year --- */
13977: if ( nyears == 3 )                      /*three preceding yrs so this is 4th*/
13978:     { if ( month > 2 )                  /* past feb so need an extra day */
13979:         /*if ( year != 100 )*/          /* unless it's 2000AD */
13980:           ndays++; }                    /* so add it in */
13981: return ( (int)(ndays) );                /* #days back to caller */
13982: } /* --- end-of-function daynumber() --- */
13983: 
13984: 
13985: /* ==========================================================================
13986:  * Function:    strwrap ( s, linelen, tablen )
13987:  * Purpose:     Inserts \n's and spaces in (a copy of) s to wrap lines
13988:  *              at linelen and indent them by tablen.
13989:  * --------------------------------------------------------------------------
13990:  * Arguments:   s (I)           char * to null-terminated string
13991:  *                              to be wrapped.
13992:  *              linelen (I)     int containing maximum linelen
13993:  *                              between \\'s.
13994:  *              tablen (I)      int containing number of spaces to indent
13995:  *                              lines.  0=no indent.  Positive means
13996:  *                              only indent first line and not others.
13997:  *                              Negative means indent all lines except first.
13998:  * --------------------------------------------------------------------------
13999:  * Returns:     ( char * )      ptr to "line-wrapped" copy of s
14000:  *                              or "" (empty string) for any error.
14001:  * --------------------------------------------------------------------------
14002:  * Notes:     o The returned copy of s has embedded \\'s as necessary
14003:  *              to wrap lines at linelen.  Any \\'s in the input copy
14004:  *              are removed first.  If (and only if) the input s contains
14005:  *              a terminating \\ then so does the returned copy.
14006:  *            o The returned pointer addresses a static buffer,
14007:  *              so don't call strwrap() again until you're finished
14008:  *              with output from the preceding call.
14009:  *            o Modified for mimetex from original version written
14010:  *              for mathtex (where \n in verbatim mode instead of \\
14011:  *              produced linebreaks).
14012:  * ======================================================================= */
14013: /* --- entry point --- */
14014: FUNCSCOPE char *strwrap ( char *s, int linelen, int tablen )
14015: {
14016: /* -------------------------------------------------------------------------
14017: Allocations and Declarations
14018: -------------------------------------------------------------------------- */
14019: static  char sbuff[4096];               /* line-wrapped copy of s */
14020: char    *sol = sbuff;                   /* ptr to start of current line*/
14021: char    tab[32] = "                 ";  /* tab string */
14022: /*int   strreplace();*/                 /* remove \n's */
14023: /*char  *strchange();*/                 /* add \n's and indent space */
14024: int     finalnewline = (lastchar(s)=='\n'?1:0); /*newline at end of string?*/
14025: int     istab = (tablen>0?1:0),         /* init true to indent first line */
14026:         iswhite = 0;                    /* true if line break on whitespace*/
14027: int     rhslen  = 0,                    /* remaining right hand side length*/
14028:         thislen = 0,                    /* length of current line segment */
14029:         thistab = 0,                    /* length of tab on current line */
14030:         wordlen = 0;                    /* length to next whitespace char */
14031: /* -------------------------------------------------------------------------
14032: Make a clean copy of s
14033: -------------------------------------------------------------------------- */
14034: /* --- check input --- */
14035: *sbuff = '\000';                        /* initialize in case of error */
14036: if ( isempty(s) ) goto end_of_job;      /* no input */
14037: if ( tablen < 0 ) tablen = (-tablen);   /* set positive tablen */
14038: if ( tablen >= linelen ) tablen = linelen-1; /* tab was longer than line */
14039: tab[min2(tablen,16)] = '\000';          /* null-terminate tab string */
14040: tablen = strlen(tab);                   /* reset to actual tab length */
14041: finalnewline = 0;                       /* turned off for mimetex version */
14042: /* --- start with copy of s --- */
14043: strninit(sbuff,s,3000);                 /* leave room for \n's and tabs */
14044: if ( linelen < 1 ) goto end_of_job;     /* can't do anything */
14045: trimwhite(sbuff);                       /*remove leading/trailing whitespace*/
14046: strreplace(sbuff,"\n"," ",0);           /* remove any original \n's */
14047: strreplace(sbuff,"\r"," ",0);           /* remove any original \r's */
14048: strreplace(sbuff,"\t"," ",0);           /* remove any original \t's */
14049: strreplace(sbuff,"\f"," ",0);           /* remove any original \f's */
14050: strreplace(sbuff,"\v"," ",0);           /* remove any original \v's */
14051: strreplace(sbuff,"\\\\"," ",0);         /* remove any original \\'s */
14052: /* -------------------------------------------------------------------------
14053: Insert \\'s and spaces as needed
14054: -------------------------------------------------------------------------- */
14055: while ( 1 ) {                           /* till end-of-line */
14056:   /* --- init --- */
14057:   trimwhite(sol);                       /*remove leading/trailing whitespace*/
14058:   thislen = thistab = 0;                /* no chars in current line yet */
14059:   if ( istab && tablen>0 ) {            /* need to indent this line */
14060:     strchange(0,sol,tab);               /* insert indent at start of line */
14061:     thistab = tablen; }                 /* line starts with whitespace tab */
14062:   if ( sol == sbuff ) istab = 1-istab;  /* flip tab flag after first line */
14063:   sol += thistab;                       /* skip tab */
14064:   rhslen = strlen(sol);                 /* remaining right hand side chars */
14065:   if ( rhslen+thistab <= linelen ) break; /* no more \\'s needed */
14066:   if ( 0 && msgfp!=NULL && msglevel >= 99 ) {
14067:     fprintf(msgfp,"strwrap> rhslen=%d, sol=\"\"%s\"\"\n",rhslen,sol);
14068:     fflush(msgfp); }
14069:   /* --- look for last whitespace preceding linelen --- */
14070:   while ( 1 ) {                         /* till we exceed linelen */
14071:     wordlen = strcspn(sol+thislen," \t\n\r\f\v :;.,"); /*ptr next white/break*/
14072:     if ( sol[thislen+wordlen] == '\000' ) /* no more whitespace in string */
14073:       goto end_of_job;                  /* so nothing more we can do */
14074:     if ( thislen+thistab+wordlen >= linelen ) /* next word won't fit */
14075:       if ( thislen > 0 ) break;         /* but make sure line has one word */
14076:     thislen += (wordlen+1); }           /* ptr past next whitespace char */
14077:   if ( thislen < 1 ) break;             /* line will have one too-long word*/
14078:   /*sol[thislen-1] = '\n';*/            /* replace last space with newline */
14079:   /*sol += thislen;*/                   /* next line starts after newline */
14080:   iswhite = (isthischar(sol[thislen-1],":;.,")?0:1); /*linebreak on space?*/
14081:   strchange(iswhite,sol+thislen-iswhite,"\\\\"); /* put \\ at end of line */
14082:   sol += (thislen+2-iswhite);           /* next line starts after \\ */
14083:   } /* --- end-of-while(1) --- */
14084: end_of_job:
14085:   if ( finalnewline ) strcat(sbuff,"\\\\"); /* replace final newline */
14086:   return ( sbuff );                     /* back with clean copy of s */
14087: } /* --- end-of-function strwrap() --- */
14088: 
14089: 
14090: /* ==========================================================================
14091:  * Function:    strnlower ( s, n )
14092:  * Purpose:     lowercase the first n chars of string s
14093:  * --------------------------------------------------------------------------
14094:  * Arguments:   s (I/O)         (char *)pointer to null-terminated string
14095:  *                              whose chars are to be lowercased
14096:  *              n (I)           int containing max number of chars to be
14097:  *                              lowercased (less than n will be lowercased
14098:  *                              if terminating '\000' found first)
14099:  *                              If n<=0 (or n>=strlen(s)) then the entire
14100:  *                              string s will be lowercased
14101:  * --------------------------------------------------------------------------
14102:  * Returns:     ( char * )      s (always same as input)
14103:  * --------------------------------------------------------------------------
14104:  * Notes:     o
14105:  * ======================================================================= */
14106: /* --- entry point --- */
14107: FUNCSCOPE char *strnlower ( char *s, int n )
14108: {
14109: /* -------------------------------------------------------------------------
14110: lowercase s
14111: -------------------------------------------------------------------------- */
14112: char    *p = s;                         /* save s for return to caller */
14113: if ( !isempty(s) )                      /* check for valid input */
14114:   while ( *p != '\000' ) {              /* lowercase each char till end */
14115:     *p = tolower(*p);                   /* lowercase this char */
14116:     if ( n > 0 )                        /* only lowercase first n chars */
14117:       if ( --n < 1 ) break;             /* quit when we're done */
14118:     p++; }                              /* proceed to next char */
14119: return ( s );                           /* back to caller with s */
14120: } /* --- end-of-function strnlower() --- */
14121: 
14122: 
14123: /* ==========================================================================
14124:  * Function:    urlprune ( url, n )
14125:  * Purpose:     Prune http://abc.def.ghi.com/etc into abc.def.ghi.com
14126:  *              (if n=2 only ghi.com is returned, or if n=-1 only "ghi")
14127:  * --------------------------------------------------------------------------
14128:  * Arguments:   url (I)         char * to null-terminated string
14129:  *                              containing url to be pruned
14130:  *              n (i)           int containing number of levels retained
14131:  *                              in pruned url.  If n<0 its abs() is used,
14132:  *                              but the topmost level (usually .com, .org,
14133:  *                              etc) is omitted.  That is, if n=2 would
14134:  *                              return "ghi.com" then n=-1 returns "ghi".
14135:  *                              n=0 retains all levels.
14136:  * --------------------------------------------------------------------------
14137:  * Returns:     ( char * )      pointer to (static) null-terminated string
14138:  *                              containing pruned url with the first n
14139:  *                              top-level domain, e.g., for n=2,
14140:  *                              http://abc.def.ghi.com/etc returns ghi.com,
14141:  *                              or an empty string "\000" for any error
14142:  * --------------------------------------------------------------------------
14143:  * Notes:     o
14144:  * ======================================================================= */
14145: /* --- entry point --- */
14146: FUNCSCOPE char *urlprune ( char *url, int n )
14147: {
14148: /* -------------------------------------------------------------------------
14149: Allocations and Declarations
14150: -------------------------------------------------------------------------- */
14151: static  char pruned[2048];              /* pruned url returned to caller */
14152: char    *purl = /*NULL*/pruned;         /* ptr to pruned, init for error */
14153: char    *delim = NULL;                  /* delimiter separating components */
14154: /*char  *strnlower();*/                 /* lowercase a string */
14155: int     istruncate = (n<0?1:0);         /*true to truncate .com from pruned*/
14156: int     ndots = 0;                      /* number of dots found in url */
14157: /* -------------------------------------------------------------------------
14158: prune the url
14159: -------------------------------------------------------------------------- */
14160: /* --- first check input --- */
14161: *pruned = '\000';                       /* init for error */
14162: if ( isempty(url) ) goto end_of_job;    /* missing input, so return NULL */
14163: if ( n < 0 ) n = (-n);                  /* flip n positive */
14164: if ( n == 0 ) n = 999;                  /* retain all levels of url */
14165: /* --- preprocess url --- */
14166: strninit(pruned,url,2032);              /* copy url to our static buffer */
14167: strlower(pruned);                       /* lowercase it and... */
14168: trimwhite(pruned);                      /*remove leading/trailing whitespace*/
14169: /* --- first remove leading http:// --- */
14170: if ( (delim=strstr(pruned,"://")) != NULL ) /* found http:// or ftp:// etc */
14171:   if ( ((int)(delim-pruned)) <= 8 ) {   /* make sure it's a prefix */
14172:     strsqueezep(pruned,delim+3);        /* squeeze out leading http:// */
14173:     trimwhite(pruned); }                /*remove leading/trailing whitespace*/
14174: /* --- next remove leading www. --- */
14175: if ( (delim=strstr(pruned,"www.")) != NULL ) /* found www. */
14176:   if ( ((int)(delim-pruned)) == 0 ) {   /* make sure it's the leading chars*/
14177:     strsqueezep(pruned,delim+4);        /* squeeze out leading www. */
14178:     trimwhite(pruned); }                /*remove leading/trailing whitespace*/
14179: /* --- finally remove leading / and everything following it --- */
14180: if ( (delim=strchr(pruned,'/')) != NULL ) /* found first / */
14181:   *delim = '\000';                      /* null-terminate url at first / */
14182: if ( isempty(pruned) ) goto end_of_job; /* nothing left in url */
14183: /* --- count dots from back of url --- */
14184: delim = pruned + strlen(pruned);        /*ptr to '\000' terminating pruned*/
14185: while ( ((int)(delim-pruned)) > 0 ) {   /* don't back up before first char */
14186:   delim--;                              /* ptr to preceding character */
14187:   if ( *delim != '.' ) continue;        /* not a dot, so keep looking */
14188:   ndots++;                              /* count another dot found */
14189:   if ( istruncate ) {                   /* remove trailing .com */
14190:     istruncate = 0;                     /* don't truncate any more dots */
14191:     *delim = '\000';                    /* truncate pruned url */
14192:     ndots = 0; }                        /* and reset dot count */
14193:   if ( ndots >= n ) {                   /* have all requested levels */
14194:     strsqueezep(pruned,delim+1);        /* squeeze out leading levels */
14195:     break; }                            /* and we're done */
14196:   } /* --- end-of-while() --- */
14197: purl = pruned;                          /*completed okay, return pruned url*/
14198: end_of_job:
14199:   return ( purl );                      /* back with pruned url */
14200: } /* --- end-of-function urlprune() --- */
14201: 
14202: 
14203: /* ==========================================================================
14204:  * Function:    urlncmp ( url1, url2, n )
14205:  * Purpose:     Compares the n topmost levels of two urls
14206:  * --------------------------------------------------------------------------
14207:  * Arguments:   url1 (I)        char * to null-terminated string
14208:  *                              containing url to be compared with url2
14209:  *              url2 (I)        char * to null-terminated string
14210:  *                              containing url to be compared with url1
14211:  *              n (I)           int containing number of top levels
14212:  *                              to compare, or 0 to compare them all.
14213:  *                              n<0 compares that many top levels excluding
14214:  *                              the last, i.e., for n=-1, xxx.com and xxx.org
14215:  *                              would be considered a match
14216:  * --------------------------------------------------------------------------
14217:  * Returns:     ( int )         1 if url's match, or
14218:  *                              0 if not.
14219:  * --------------------------------------------------------------------------
14220:  * Notes:     o
14221:  * ======================================================================= */
14222: /* --- entry point --- */
14223: FUNCSCOPE int urlncmp ( char *url1, char *url2, int n )
14224: {
14225: /* -------------------------------------------------------------------------
14226: Allocations and Declarations
14227: -------------------------------------------------------------------------- */
14228: char    /**urlprune(),*/ *prune=NULL,   /* prune url's */
14229:         prune1[4096], prune2[4096];     /* pruned copies of url1,url2 */
14230: int     ismatch = 0;                    /* true if url's match */
14231: /* -------------------------------------------------------------------------
14232: prune url's and compare the pruned results
14233: -------------------------------------------------------------------------- */
14234: /* --- check input --- */
14235: if ( isempty(url1)                      /*make sure both url1,url2 supplied*/
14236: ||   isempty(url2) ) goto end_of_job;   /* missing input, so return 0 */
14237: /* --- prune url's --- */
14238: prune = urlprune(url1,n);               /* ptr to pruned version of url1 */
14239: if ( isempty(prune) ) goto end_of_job;  /* some problem with url1 */
14240: strninit(prune1,prune,4064);            /* local copy of pruned url1 */
14241: prune = urlprune(url2,n);               /* ptr to pruned version of url2 */
14242: if ( isempty(prune) ) goto end_of_job;  /* some problem with url2 */
14243: strninit(prune2,prune,4064);            /* local copy of pruned url2 */
14244: /* --- compare pruned url's --- */
14245: if ( strcmp(prune1,prune2) == 0 )       /* pruned url's are identical */
14246:   ismatch = 1;                          /* signal match to caller */
14247: end_of_job:
14248:   return ( ismatch );                   /*back with #matching url components*/
14249: } /* --- end-of-function urlncmp() --- */
14250: 
14251: 
14252: /* ==========================================================================
14253:  * Function:    dbltoa ( dblval, npts )
14254:  * Purpose:     Converts double to ascii, in financial format
14255:  *              (e.g., comma-separated and negatives enclosed in ()'s).
14256:  * -------------------------------------------------------------------------
14257:  * Arguments:   dblval (I)      double containing value to be converted.
14258:  *              npts (I)        int containing #places after decimal point
14259:  *                              to be displayed in returned string.
14260:  * Returns:     ( char * )      null-terminated string containing
14261:  *                              double converted to financial format.
14262:  * -------------------------------------------------------------------------
14263:  * Notes:     o
14264:  * ======================================================================= */
14265: /* --- entry point --- */
14266: FUNCSCOPE char *dbltoa ( double dblval, int npts )
14267: /* double dblval;
14268:    int  npts; */
14269: {
14270: /* -------------------------------------------------------------------------
14271: Allocations and Declarations
14272: ------------------------------------------------------------------------- */
14273: static  char finval[256];               /* buffer returned to caller */
14274: static  char digittbl[32]="0123456789*"; /* table of ascii decimal digits */
14275: char    *finptr = finval;               /* ptr to next char being converted*/
14276: double  floor();                        /* integer which is glb(double) */
14277: double  dbldigit;                       /* to shift out digits from dblval */
14278: int     digit;                          /* one digit from dblval */
14279: int     isneg = 0;                      /* reset true if dblval negative */
14280: int     ifrac = 0;                      /* npts fractional digits of dblval*/
14281: char    digits[64]; int ndigits=0;      /* all the digits [0]=least signif */
14282: /* -------------------------------------------------------------------------
14283: Check sign
14284: ------------------------------------------------------------------------- */
14285: if ( dblval < 0.0 )                     /* got a negative value to convert */
14286:     { isneg=1; dblval=(-dblval); }      /* set flag and make it positive */
14287: /* -------------------------------------------------------------------------
14288: Get fractional part of dblval if required
14289: ------------------------------------------------------------------------- */
14290: if ( npts > 0 )
14291:     { int ipts = npts;                  /* loop index */
14292:       dbldigit = dblval-floor(dblval);  /* fractional part as double */
14293:       digit = 1;                        /* check if rounded frac > 1 */
14294:       while ( --ipts >= 0 )             /* count down */
14295:         { dbldigit *= 10.0;             /* shift left one digit at a time */
14296:           digit *= 10; }                /* and keep max up-to-date */
14297:       ifrac = (int)(dbldigit + 0.5);    /* store fractional part as integer*/
14298:       if ( ifrac >= digit )             /* round to next whole number */
14299:         { dblval++; ifrac=0; }          /* bump val, reset frac to zero */
14300:     } /* --- end-of-if(npts>0) --- */
14301: else dblval += 0.5;                     /* no frac, round to nearest whole */
14302: /* -------------------------------------------------------------------------
14303: Get whole digits
14304: ------------------------------------------------------------------------- */
14305: dblval = floor(dblval);                 /* get rid of fractional part */
14306: while ( dblval > 0.0 )                  /* still have data digits remaining*/
14307:     { dbldigit = floor(dblval/10.0);    /* shift out next digit */
14308:       digit = (int)(dblval - 10.0*dbldigit + 0.01); /* least signif digit */
14309:       if ( digit<0 || digit>9 ) digit=10; /* index check */
14310:       digits[ndigits++] = digittbl[digit]; /* store ascii digit */
14311:       dblval = dbldigit; }              /* ready for next digit */
14312: if ( ndigits < 1 ) digits[ndigits++] = '0'; /* store a single '0' for 0.0 */
14313: /* -------------------------------------------------------------------------
14314: Format whole part from digits[] array
14315: ------------------------------------------------------------------------- */
14316: if ( isneg ) *finptr++ = '(';           /* leading paren for negative value*/
14317: for ( digit=ndigits-1; digit>=0; digit-- ) /* start with most significant */
14318:     { *finptr++ = digits[digit];        /* store digit */
14319:       if ( digit>0 && digit%3==0 )      /* need a comma */
14320:         *finptr++ = ','; }              /* put in separating comma */
14321: /* -------------------------------------------------------------------------
14322: Format fractional part using ifrac
14323: ------------------------------------------------------------------------- */
14324: if ( npts > 0 )
14325:     { *finptr++ = '.';                  /* start with decimal point */
14326:       sprintf(finptr,"%0*d",npts,ifrac); /* convert to string */
14327:       finptr += npts; }                 /* bump ptr past fractional digits */
14328: /* -------------------------------------------------------------------------
14329: End-of-Job
14330: ------------------------------------------------------------------------- */
14331: if ( isneg ) *finptr++ = ')';           /*trailing paren for negative value*/
14332: *finptr = '\000';                       /* null-terminate converted double */
14333: return ( finval );                      /* converted double back to caller */
14334: } /* --- end-of-function dbltoa() --- */
14335: 
14336: 
14337: /* ==========================================================================
14338:  * Function:    rotmatrix ( axis, theta )
14339:  *              Constructs rotation matrix for theta degrees around axis
14340:  *              (see Notes below for discussion).
14341:  * --------------------------------------------------------------------------
14342:  * Arguments:   axis (I)        point3d * to u=(u_x,u_y,u_z) components
14343:  *                              of rotation axis (u's tail point at origin),
14344:  *                              or NULL ptr returns previous matrix
14345:  *              theta (I)       double containing rotation in degrees
14346:  *                              (positive theta rotation according to
14347:  *                              right-hand screw rule),
14348:  *                              or 0.0 returns previous matrix
14349:  * --------------------------------------------------------------------------
14350:  * Returns:     ( matrix3d * )  pointer to constructed rotation matrix,
14351:  *                              or NULL for any error
14352:  * --------------------------------------------------------------------------
14353:  * Notes:     o For discussion, see
14354:  *              http://wikipedia.org/wiki/Rotation_matrix#In_three_dimensions
14355:  *
14356:  * ======================================================================= */
14357: /* --- entry point --- */
14358: FUNCSCOPE matrix3d *rotmatrix ( point3d *axis, double theta )
14359: {
14360: /* -------------------------------------------------------------------------
14361: Allocations and Declarations
14362: -------------------------------------------------------------------------- */
14363: static  matrix3d rot =                  /* returned rotation matrix */
14364:         { {0.,0.,0,}, {0.,0.,0,}, {0.,0.,0,} }; /* just init as zero's */
14365: matrix3d *prot = &(rot);                /* init returned rot ptr */
14366: double  pi = 3.14159265359;             /* pi */
14367: double  u,ux,uy,uz,                     /* axis length and components */
14368:         tsin,tcos;                      /* sin,cos (theta) */
14369: /* -------------------------------------------------------------------------
14370: Initialization
14371: -------------------------------------------------------------------------- */
14372: if ( axis == NULL                       /*  no rotation axis, or no theta */
14373: ||   absval(theta) < 1.0e-10 ) goto end_of_job;/*just return preceding matrix*/
14374: if ( 1 ) theta *= (pi/180.);            /*convert theta in degrees to radians*/
14375: tsin = sin(theta);  tcos = cos(theta);  /* sin,cos (theta) */
14376: ux = axis->x;  uy = axis->y;  uz = axis->z; /* unnormalized axis components */
14377: u = sqrt((ux*ux)+(uy*uy)+(uz*uz));      /* axis length */
14378: if ( u < 1.0e-10 ) goto end_of_job;     /* zero-vector is an error */
14379: ux /= u;  uy /= u;  uz /= u;            /* normalized axis components */
14380: /* -------------------------------------------------------------------------
14381: rotation matrix components, stored row-wise
14382: -------------------------------------------------------------------------- */
14383: /* --- x-row --- */
14384: rot.xrow.x = tcos + ux*ux*(1.0-tcos);
14385: rot.xrow.y =        ux*uy*(1.0-tcos) - uz*tsin;
14386: rot.xrow.z =        ux*uz*(1.0-tcos) + uy*tsin;
14387: /* --- y-row --- */
14388: rot.yrow.x =        uy*ux*(1.0-tcos) + uz*tsin;
14389: rot.yrow.y = tcos + uy*uy*(1.0-tcos);
14390: rot.yrow.z =        uy*uz*(1.0-tcos) - ux*tsin;
14391: /* --- z-row --- */
14392: rot.zrow.x =        uz*ux*(1.0-tcos) - uy*tsin;
14393: rot.zrow.y =        uz*uy*(1.0-tcos) + ux*tsin;
14394: rot.zrow.z = tcos + uz*uz*(1.0-tcos);
14395: prot = &(rot);                          /* point return value to matrix */
14396: end_of_job:
14397:   return ( prot );                      /*back to caller with rotation matrix*/
14398: } /* --- end-of-function rotmatrix() --- */
14399: 
14400: 
14401: /* ==========================================================================
14402:  * Function:    matmult ( mat, vec )
14403:  *              returns result of mat(rix) x vec(tor)
14404:  * --------------------------------------------------------------------------
14405:  * Arguments:   mat (I)         matrix3d * to matrix
14406:  *              vec (I)         point3d *  to vector
14407:  * --------------------------------------------------------------------------
14408:  * Returns:     ( point3d * )   pointer to result of mat(rix) x vec(tor),
14409:  *                              or NULL for any error
14410:  * --------------------------------------------------------------------------
14411:  * Notes:     o
14412:  * ======================================================================= */
14413: /* --- entry point --- */
14414: FUNCSCOPE point3d *matmult ( matrix3d *mat, point3d *vec )
14415: {
14416: /* -------------------------------------------------------------------------
14417: Allocations and Declarations
14418: -------------------------------------------------------------------------- */
14419: static  point3d mult;                   /* returned matrix x vector product */
14420: point3d *pmult = NULL;                  /* init mult ptr for error */
14421: /* -------------------------------------------------------------------------
14422: Initialization
14423: -------------------------------------------------------------------------- */
14424: if ( mat == NULL                        /* mat and vec required input */
14425: ||   vec == NULL ) goto end_of_job;     /* missing required input */
14426: /* -------------------------------------------------------------------------
14427: matrix multiplication
14428: -------------------------------------------------------------------------- */
14429: /* --- x,y,z-components of mat(rix) x vec(tor) product --- */
14430: mult.x = (mat->xrow.x)*vec->x + (mat->xrow.y)*vec->y + (mat->xrow.z)*vec->z;
14431: mult.y = (mat->yrow.x)*vec->x + (mat->yrow.y)*vec->y + (mat->yrow.z)*vec->z;
14432: mult.z = (mat->zrow.x)*vec->x + (mat->zrow.y)*vec->y + (mat->zrow.z)*vec->z;
14433: pmult = &(mult);                        /* point return value to product */
14434: end_of_job:
14435:   return ( pmult );                     /*back to caller with matrix x vector*/
14436: } /* --- end-of-function matmult() --- */
14437: 
14438: 
14439: /* ==========================================================================
14440:  * Function:    aalowpass ( rp, bytemap, grayscale )
14441:  * Purpose:     calculates a lowpass anti-aliased bytemap
14442:  *              for rp->bitmap, with each byte 0...grayscale-1
14443:  * --------------------------------------------------------------------------
14444:  * Arguments:   rp (I)          raster *  to raster whose bitmap
14445:  *                              is to be anti-aliased
14446:  *              bytemap (O)     intbyte * to bytemap, calculated
14447:  *                              by applying lowpass filter to rp->bitmap,
14448:  *                              and returned (as you'd expect) in 1-to-1
14449:  *                              addressing correspondence with rp->bitmap
14450:  *              grayscale (I)   int containing number of grayscales
14451:  *                              to be calculated, 0...grayscale-1
14452:  *                              (should typically be given as 256)
14453:  * --------------------------------------------------------------------------
14454:  * Returns:     ( int )         1=success, 0=any error
14455:  * --------------------------------------------------------------------------
14456:  * Notes:     o If the center point of the box being averaged is black,
14457:  *              then the entire "average" is forced black (grayscale-1)
14458:  *              regardless of the surrounding points.  This is my attempt
14459:  *              to avoid a "washed-out" appearance of thin (one-pixel-wide)
14460:  *              lines, which would otherwise turn from black to a gray shade.
14461:  *           o  Also, while the weights for neighbor points are fixed,
14462:  *              you may adjust the center point weight on the compile line.
14463:  *              A higher weight sharpens the resulting anti-aliased image;
14464:  *              lower weights blur it out more (but keep the "center" black
14465:  *              as per the preceding note).
14466:  * ======================================================================= */
14467: /* --- entry point --- */
14468: FUNCSCOPE int aalowpass (raster *rp, intbyte *bytemap, int grayscale)
14469: {
14470: /* -------------------------------------------------------------------------
14471: Allocations and Declarations
14472: -------------------------------------------------------------------------- */
14473: int     status = 1;                     /* 1=success, 0=failure to caller */
14474: pixbyte *bitmap= (rp==NULL?NULL:rp->pixmap); /*local rp->pixmap ptr*/
14475: int     irow=0, icol=0;                 /* rp->height, rp->width indexes */
14476: int     weights[9] = { 1,3,1, 3,0,3, 1,3,1 }; /* matrix of weights */
14477: int     adjindex[9]= { 0,1,2, 7,-1,3, 6,5,4 }; /*clockwise from upper-left*/
14478: int     totwts = 0;                     /* sum of all weights in matrix */
14479: int     isforceavg = 1,                 /*force avg black if center black?*/
14480:         isminmaxwts = 1,                /*use wts or #pts for min/max test */
14481:         blackscale = 0; /*(grayscale+1)/4;*/ /*force black if wgted avg>bs */
14482: /* -------------------------------------------------------------------------
14483: Initialization
14484: -------------------------------------------------------------------------- */
14485: /* --- calculate total weights --- */
14486: weights[4]= centerwt;                   /* weight for center point */
14487: weights[1]= weights[3]= weights[5]= weights[7]= adjacentwt; /*adjacent pts*/
14488: totwts = centerwt + 4*(1+adjacentwt);   /* tot is center plus neighbors */
14489: /* -------------------------------------------------------------------------
14490: Calculate bytemap as 9-point weighted average over bitmap
14491: -------------------------------------------------------------------------- */
14492: for ( irow=0; irow<rp->height; irow++ )
14493:  for ( icol=0; icol<rp->width; icol++ )
14494:   {
14495:   int   ipixel = icol + irow*(rp->width); /* center pixel index */
14496:   int   jrow=0, jcol=0,                 /* box around ipixel */
14497:         bitval = 0,                     /* value of bit/pixel at jrow,jcol */
14498:         iscenter = 0,                   /* set true if center pixel black */
14499:         nadjacent=0, wadjacent=0,       /* #adjacent black pixels, their wts*/
14500:         ngaps = 0,                      /* #gaps in 8 pixels around center */
14501:         iwt=(-1), sumwts=0;             /* weights index, sum in-bound wts */
14502:   char  adjmatrix[8];                   /* adjacency "matrix" */
14503:   memset(adjmatrix,0,8);                /* zero out adjacency matrix */
14504:   bytemap[ipixel] = 0;                  /* init pixel white */
14505:   /*--- for ipixel at irow,icol, get weighted average of adjacent pixels ---*/
14506:   for ( jrow=irow-1; jrow<=irow+1; jrow++ )  /* jrow = irow-1...irow+1 */
14507:    for ( jcol=icol-1; jcol<=icol+1; jcol++ ) /* jcol = icol-1...icol+1 */
14508:     {
14509:     int jpixel = jcol + jrow*(rp->width); /* averaging index */
14510:     iwt++;                              /*always bump weight index*/
14511:     if ( jrow<0 || jrow>=rp->height     /* if row out pf bounds */
14512:     ||   jcol<0 || jcol>=rp->width )    /* or col out of bounds */
14513:         continue;                       /* ignore this point */
14514:     bitval = (int)getlongbit(bitmap,jpixel); /* value of bit at jrow,jcol */
14515:     if ( bitval )                       /* this is a black pixel */
14516:       { if ( jrow==irow && jcol==icol ) /* and this is center point */
14517:           iscenter = 1;                 /* set flag for center point black */
14518:         else                            /* adjacent point black */
14519:           { nadjacent++;                /* bump adjacent black count */
14520:             adjmatrix[adjindex[iwt]] = 1; } /*set "bit" in adjacency matrix*/
14521:         wadjacent += weights[iwt]; }    /* sum weights for black pixels */
14522:     sumwts += weights[iwt];             /* and sum weights for all pixels */
14523:     } /* --- end-of-for(jrow,jcol) --- */
14524:   /* --- count gaps --- */
14525:   ngaps = (adjmatrix[7]!=adjmatrix[0]?1:0); /* init count */
14526:   for ( iwt=0; iwt<7; iwt++ )           /* clockwise around adjacency */
14527:     if ( adjmatrix[iwt] != adjmatrix[iwt+1] ) ngaps++; /* black/white flip */
14528:   ngaps /= 2;                           /*each gap has 2 black/white flips*/
14529:   /* --- anti-alias pixel, but leave it black if it was already black --- */
14530:   if ( isforceavg && iscenter )         /* force avg if center point black */
14531:       bytemap[ipixel] = grayscale-1;    /* so force grayscale-1=black */
14532:   else                                  /* center point not black */
14533:    if ( ngaps <= 2 )                    /*don't darken checkerboarded pixel*/
14534:     { bytemap[ipixel] =                 /* 0=white ... grayscale-1=black */
14535:         ((totwts/2 - 1) + (grayscale-1)*wadjacent)/totwts; /* not /sumwts; */
14536:       if ( blackscale > 0               /* blackscale kludge turned on */
14537:       &&   bytemap[ipixel] > blackscale ) /* weighted avg > blackscale */
14538:         bytemap[ipixel] = grayscale-1; } /* so force it entirely black */
14539:   /*--- only anti-alias pixels whose adjacent pixels fall within bounds ---*/
14540:   if ( !iscenter ) {                    /* apply min/maxadjacent test */
14541:    if ( isminmaxwts )                   /* min/max refer to adjacent weights*/
14542:     { if ( wadjacent < minadjacent      /* wts of adjacent points too low */
14543:       ||   wadjacent > maxadjacent )    /* or too high */
14544:         bytemap[ipixel] = 0; }          /* so leave point white */
14545:    else                                 /* min/max refer to #adjacent points*/
14546:     { if ( nadjacent < minadjacent      /* too few adjacent points black */
14547:       ||   nadjacent > maxadjacent )    /* or too many */
14548:         bytemap[ipixel] = 0; } }        /* so leave point white */
14549:   } /* --- end-of-for(irow,icol) --- */
14550: /* -------------------------------------------------------------------------
14551: Back to caller with gray-scale anti-aliased bytemap
14552: -------------------------------------------------------------------------- */
14553: /*end_of_job:*/
14554:   return ( status );
14555: } /* --- end-of-function aalowpass() --- */
14556: 
14557: 
14558: /* ==========================================================================
14559:  * Function:    aapnm ( rp, bytemap, grayscale )
14560:  * Purpose:     calculates a lowpass anti-aliased bytemap
14561:  *              for rp->bitmap, with each byte 0...grayscale-1,
14562:  *              based on the pnmalias.c algorithm
14563:  * --------------------------------------------------------------------------
14564:  * Arguments:   rp (I)          raster *  to raster whose bitmap
14565:  *                              is to be anti-aliased
14566:  *              bytemap (O)     intbyte * to bytemap, calculated
14567:  *                              by applying pnm-based filter to rp->bitmap,
14568:  *                              and returned (as you'd expect) in 1-to-1
14569:  *                              addressing correspondence with rp->bitmap
14570:  *              grayscale (I)   int containing number of grayscales
14571:  *                              to be calculated, 0...grayscale-1
14572:  *                              (should typically be given as 256)
14573:  * --------------------------------------------------------------------------
14574:  * Returns:     ( int )         1=success, 0=any error
14575:  * --------------------------------------------------------------------------
14576:  * Notes:    o  Based on the pnmalias.c algorithm in the netpbm package
14577:  *              on sourceforge.
14578:  * ======================================================================= */
14579: /* --- entry point --- */
14580: FUNCSCOPE int aapnm (raster *rp, intbyte *bytemap, int grayscale)
14581: {
14582: /* -------------------------------------------------------------------------
14583: Allocations and Declarations
14584: -------------------------------------------------------------------------- */
14585: pixbyte *bitmap = rp->pixmap;           /* local rp->pixmap ptr */
14586: int     width=rp->width, height=rp->height, /* width, height of raster */
14587:         icol = 0,        irow = 0,      /* width, height indexes */
14588:         imap = (-1);                    /* pixel index = icol + irow*width */
14589: int     bgbitval=0, fgbitval=1;         /* background, foreground bitval */
14590: int     isfirstaa = 1;                  /*debugging switch signals 1st pixel*/
14591: #if 0
14592: int     totwts=12, wts[9]={1,1,1, 1,4,1, 1,1,1}; /* pnmalias default wts */
14593: int     totwts=16, wts[9]={1,2,1, 2,4,2, 1,2,1}; /* weights */
14594: #endif
14595: int     totwts=18, wts[9]={1,2,1, 2,6,2, 1,2,1}; /* pnmalias default wts */
14596: int     isresetparams = 1,              /* true to set antialiasing params */
14597:         isfgalias  = 1,                 /* true to antialias fg bits */
14598:         isfgonly   = 0,                 /* true to only antialias fg bits */
14599:         isbgalias  = 0,                 /* true to antialias bg bits */
14600:         isbgonly   = 0;                 /* true to only antialias bg bits */
14601: /* -------------------------------------------------------------------------
14602: Initialization
14603: -------------------------------------------------------------------------- */
14604: /* --- check for bold light --- */
14605: if ( 0 )
14606:  { if ( weightnum > 2 ) { isbgalias=1; }        /* simulate bold */
14607:    if ( weightnum < 1 ) { isbgonly=1; isfgalias=0; } } /* simulate light */
14608: /* --- reset wts[], etc, and calculate total weights --- */
14609: if ( isresetparams )                    /* wts[], etc taken from params */
14610:   { int iwt=0;                          /* wts[iwt] index */
14611:     wts[4]= centerwt;                   /* weight for center point */
14612:     wts[1]=wts[3]=wts[5]=wts[7] = adjacentwt; /* and adjacent points */
14613:     wts[0]=wts[2]=wts[6]=wts[8] = cornerwt;   /* and corner points */
14614:     for ( totwts=0,iwt=0; iwt<9; iwt++ ) totwts += wts[iwt]; /* sum wts */
14615:     isfgalias = fgalias;                /* set isfgalias */
14616:     isfgonly = fgonly;                  /* set isfgonly */
14617:     isbgalias = bgalias;                /* set isbgalias */
14618:     isbgonly = bgonly; }                /* set isbgonly */
14619: /* -------------------------------------------------------------------------
14620: Calculate bytemap as 9-point weighted average over bitmap
14621: -------------------------------------------------------------------------- */
14622: for ( irow=0; irow<height; irow++ )
14623:  for ( icol=0; icol<width; icol++ )
14624:   {
14625:   /* --- local allocations and declarations --- */
14626:   int   bitval=0,                       /* value of rp bit at irow,icol */
14627:         nnbitval=0, nebitval=0, eebitval=0, sebitval=0, /*adjacent vals*/
14628:         ssbitval=0, swbitval=0, wwbitval=0, nwbitval=0; /*compass pt names*/
14629:   int   isbgedge=0, isfgedge=0;         /*does pixel border a bg or fg edge*/
14630:   int   aabyteval=0;                    /* antialiased (or unchanged) value*/
14631:   /* --- bump imap index and get center bit value --- */
14632:   imap++;                               /* imap = icol + irow*width */
14633:   bitval = getlongbit(bitmap,imap);     /* value of rp input bit at imap */
14634:   aabyteval = (intbyte)(bitval==bgbitval?0:grayscale-1); /* default aa val */
14635:   bytemap[imap] = (intbyte)(aabyteval); /* init antialiased pixel */
14636:   /* --- check if we're antialiasing this pixel --- */
14637:   if ( (isbgonly && bitval==fgbitval)   /* only antialias background bit */
14638:   ||   (isfgonly && bitval==bgbitval) ) /* only antialias foreground bit */
14639:     continue;                           /* leave default and do next bit */
14640:   /* --- get surrounding bits --- */
14641:   if ( irow > 0 )                       /* nn (north) bit available */
14642:      nnbitval = getlongbit(bitmap,imap-width); /* nn bit value */
14643:   if ( irow < height-1 )                /* ss (south) bit available */
14644:      ssbitval = getlongbit(bitmap,imap+width); /* ss bit value */
14645:   if ( icol > 0 )                       /* ww (west) bit available */
14646:    { wwbitval = getlongbit(bitmap,imap-1); /* ww bit value */
14647:      if ( irow > 0 )                    /* nw bit available */
14648:        nwbitval = getlongbit(bitmap,imap-width-1); /* nw bit value */
14649:      if ( irow < height-1 )             /* sw bit available */
14650:        swbitval = getlongbit(bitmap,imap+width-1); } /* sw bit value */
14651:   if ( icol < width-1 )                 /* ee (east) bit available */
14652:    { eebitval = getlongbit(bitmap,imap+1); /* ee bit value */
14653:      if ( irow > 0 )                    /* ne bit available */
14654:        nebitval = getlongbit(bitmap,imap-width+1); /* ne bit value */
14655:      if ( irow < height-1 )             /* se bit available */
14656:        sebitval = getlongbit(bitmap,imap+width+1); } /* se bit value */
14657:   /* --- check for edges --- */
14658:   isbgedge =                            /* current pixel borders a bg edge */
14659:         (nnbitval==bgbitval && eebitval==bgbitval) ||   /*upper-right edge*/
14660:         (eebitval==bgbitval && ssbitval==bgbitval) ||   /*lower-right edge*/
14661:         (ssbitval==bgbitval && wwbitval==bgbitval) ||   /*lower-left  edge*/
14662:         (wwbitval==bgbitval && nnbitval==bgbitval) ;    /*upper-left  edge*/
14663:   isfgedge =