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)  retruns 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:  *              mimetexsetmsg(newmsglevel,newmsgfp)    set msglevel and msgfp
 232:  *      PART1   ========================== Driver ===========================
 233:  *              main(argc,argv) parses math expression and emits mime xbitmap
 234:  *              CreateGifFromEq(expression,gifFileName)  entry pt for win dll
 235:  *              ismonth(month)          is month current month ("jan"-"dec")?
 236:  *              logger(fp,msglevel,logvars)        logs environment variables
 237:  *              emitcache(cachefile,maxage,valign,isbuffer)    emit cachefile
 238:  *              readcachefile(cachefile,buffer)    read cachefile into buffer
 239:  *              advertisement(expression,mode)  wrap expression in ad message
 240:  *              crc16(s)                               16-bit crc of string s
 241:  *              md5str(instr)                      md5 hash library functions
 242:  *              GetPixel(x,y)           callback function for gifsave library
 243:  *
 244:  * Source:      mimetex.c  (needs mimetex.h and texfonts.h to compile,
 245:  *              and also needs gifsave.c when compiled with -DAA or -DGIF)
 246:  *
 247:  * --------------------------------------------------------------------------
 248:  * Notes      o See individual function entry points for specific comments
 249:  *              about the purpose, calling sequence, side effects, etc
 250:  *              of each mimeTeX function listed above.
 251:  *            o See bottom of file for main() driver (and "friends"),
 252:  *              and compile as
 253:  *                 cc -DAA mimetex.c gifsave.c -lm -o mimetex.cgi
 254:  *              to produce an executable that emits gif images with
 255:  *              anti-aliasing (see Notes below).  You may also compile
 256:  *                 cc -DGIF mimetex.c gifsave.c -lm -o mimetex.cgi
 257:  *              to produce an executable that emits gif images without
 258:  *              anti-aliasing.  Alternatively, compile mimeTeX as
 259:  *                 cc -DXBITMAP mimetex.c -lm -o mimetex.cgi
 260:  *              to produce an executable that just emits mime xbitmaps.
 261:  *              In either case you'll need mimetex.h and texfonts.h,
 262:  *              and with -DAA or -DGIF you'll also need gifsave.c
 263:  *            o The font information in texfonts.h was produced by multiple
 264:  *              runs of gfuntype, one run per struct (i.e., one run per font
 265:  *              family at a particular size).  Compile gfuntype as
 266:  *                 cc gfuntype.c mimetex.c -lm -o gfuntype
 267:  *              See gfuntype.c, and also mimetex.html#fonts, for details.
 268:  *            o For gif images, the gifsave.c library by Sverre H. Huseby
 269:  *              <http://shh.thathost.com> slightly modified by me to allow
 270:  *              (a)sending output to stdout or returning it in memory,
 271:  *              and (b)specifying a transparent background color index,
 272:  *              is included with mimeTeX, and it's documented in
 273:  *              mimetex.html#gifsave
 274:  *            o MimeTeX's principal reusable function is rasterize(),
 275:  *              which takes a string like "f(x)=\int_{-\infty}^xe^{-t^2}dt"
 276:  *              and returns a (sub)raster representing it as a bit or bytemap.
 277:  *              Your application can do anything it likes with this pixel map.
 278:  *              MimeTeX just outputs it, either as a mime xbitmap or as a gif.
 279:  *              See  mimetex.html#makeraster  for further discussion
 280:  *              and examples.
 281:  *            o File mimetex.c also contains library functions implementing
 282:  *              a raster datatype, functions to manipulate rasterized .mf
 283:  *              fonts (see gfuntype.c which rasterizes .mf fonts), functions
 284:  *              to parse LaTeX expressions, etc.  As already mentioned,
 285:  *              a complete list of mimetex.c functions is above.  See their
 286:  *              individual entry points below for further comments.
 287:  *                 As also mentioned, these functions eventually belong in
 288:  *              several different modules, possibly along the lines suggested
 289:  *              by the divisions above.  But until the best decomposition
 290:  *              becomes clear, it seems better to keep mimetex.c
 291:  *              neatly together, avoiding a bad decomposition that
 292:  *              becomes permanent by default.
 293:  *            o Optional compile-line -D defined symbols are documented
 294:  *              in mimetex.html#options .  They include (additional -D
 295:  *              switches are discussed at mimetex.html#options)...
 296:  *              -DAA
 297:  *                  Turns on gif anti-aliasing with default values
 298:  *                  (CENTERWT=32, ADJACENTWT=3, CORNERWT=1)
 299:  *                  for the following anti-aliasing parameters...
 300:  *              -DCENTERWT=n
 301:  *              -DADJACENTWT=j
 302:  *              -DCORNERWT=k
 303:  *                      *** Note: Ignore these three switches because
 304:  *                      *** mimeTeX's current anti-aliasing algorithm
 305:  *                      *** no longer uses them (as of version 1.60).
 306:  *                  MimeTeX currently provides a lowpass filtering
 307:  *                  algorithm for anti-aliasing, which is applied to the
 308:  *                  existing set of bitmap fonts.  This lowpass filter
 309:  *                  applies default weights
 310:  *                              1   2   1
 311:  *                              2   8   2
 312:  *                              1   2   1
 313:  *                  to neighboring pixels. The defaults weights are
 314:  *                  CENTERWT=8, ADJACENTWT=2 and CORNERWT=1,
 315:  *                  which you can adjust to control anti-aliasing.
 316:  *                  Lower CENTERWT values will blur/spread out lines
 317:  *                  while higher values will tend to sharpen lines.
 318:  *                  Experimentation is recommended to determine
 319:  *                  what value works best for you.
 320:  *              -DCACHEPATH=\"path/\"
 321:  *                  This option saves each rendered image to a file
 322:  *                  in directory  path/  which mimeTeX reads rather than
 323:  *                  re-rendering the same image every time it's given
 324:  *                  the same LaTeX expression.  Sometimes mimeTeX disables
 325:  *                  caching, e.g., expressions containing \input{ } are
 326:  *                  re-rendered since the contents of the inputted file
 327:  *                  may have changed.  If compiled without -DCACHEPATH
 328:  *                  mimeTeX always re-renders expressions.  This usually
 329:  *                  isn't too cpu intensive, but if you have unusually
 330:  *                  high hit rates then image caching may be helpful.
 331:  *                      The  path/  is relative to mimetex.cgi, and must
 332:  *                  be writable by it.  Files created under  path/  are
 333:  *                  named filename.gif, where filename is the 32-character
 334:  *                  MD5 hash of the LaTeX expression.
 335:  *              -DDEFAULTSIZE=n
 336:  *                  MimeTeX currently has eight font sizes numbered 0-7,
 337:  *                  and always starts in DEFAULTSIZE whose default value
 338:  *                  is 3 (corresponding to \large). Specify -DDEFAULTSIZE=4
 339:  *                  on the compile line if you prefer mimeTeX to start in
 340:  *                  larger default size 4 (corresponding to \Large), etc.
 341:  *              -DDISPLAYSIZE=n
 342:  *                  By default, operator limits like \int_a^b are rendered
 343:  *                  \textstyle at font sizes \normalsize and smaller,
 344:  *                  and rendered \displaystyle at font sizes \large and
 345:  *                  larger.  This default corresponds to -DDISPLAYSIZE=3,
 346:  *                  which you can adjust; e.g., -DDISPLAYSIZE=0 always
 347:  *                  defaults to \displaystyle, and 99 (or any large number)
 348:  *                  always defaults to \textstyle.  Note that explicit
 349:  *                  \textstyle, \displaystyle, \limits or \nolimits
 350:  *                  directives in an expression always override
 351:  *                  the DISPLAYSIZE default.
 352:  *              -DERRORSTATUS=n
 353:  *                  The default, 0, means mimeTeX always exits with status 0,
 354:  *                  regardless of whether or not it detects error(s) while
 355:  *                  trying to render your expression.  Specify any non-zero
 356:  *                  value (typically -1) if you write a script/plugin for
 357:  *                  mimeTeX that traps non-zero exit statuses.  MimeTeX then
 358:  *                  exits with its own non-zero status when it detects an
 359:  *                  error it can identify, or with your ERRORSTATUS value
 360:  *                  for errors it can't specifically identify.
 361:  *              -DREFERER=\"domain\"   -or-
 362:  *              -DREFERER=\"domain1,domain2,etc\"
 363:  *                  Blocks mimeTeX requests from unauthorized domains that
 364:  *                  may be using your server's mimetex.cgi without permission.
 365:  *                  If REFERER is defined, mimeTeX checks for the environment
 366:  *                  variable HTTP_REFERER and, if it exists, performs a
 367:  *                  case-insensitive test to make sure it contains 'domain'
 368:  *                  as a substring.  If given several 'domain's (second form)
 369:  *                  then HTTP_REFERER must contain either 'domain1' or
 370:  *                  'domain2', etc, as a (case-insensitive) substring.
 371:  *                  If HTTP_REFERER fails to contain a substring matching
 372:  *                  any of these domain(s), mimeTeX emits an error message
 373:  *                  image corresponding to the expression specified by
 374:  *                  the  invalid_referer_msg  string defined in main().
 375:  *                  Note: if HTTP_REFERER is not an environment variable,
 376:  *                  mimeTeX correctly generates the requested expression
 377:  *                  (i.e., no referer error).
 378:  *              -DWARNINGS=n  -or-
 379:  *              -DNOWARNINGS
 380:  *                  If an expression submitted to mimeTeX contains an
 381:  *                  unrecognzied escape sequence, e.g., "y=x+\abc+1", then
 382:  *                  mimeTeX generates a gif image containing an embedded
 383:  *                  warning in the form "y=x+[\abc?]+1".  If you want these
 384:  *                  warnings suppressed, -DWARNINGS=0 or -DNOWARNINGS tells
 385:  *                  mimeTeX to ignore unrecognized symbols, and the rendered
 386:  *                  image is "y=x++1" instead.
 387:  *              -DWHITE
 388:  *                  MimeTeX usually renders black symbols on a white
 389:  *                  background.  This option renders white symbols on
 390:  *                  a black background instead.
 391:  * --------------------------------------------------------------------------
 392:  * Revision History:
 393:  * 09/18/02     J.Forkosh       Installation.
 394:  * 12/11/02     J.Forkosh       Version 1.00 released.
 395:  * 07/04/03     J.Forkosh       Version 1.01 released.
 396:  * 10/17/03     J.Forkosh       Version 1.20 released.
 397:  * 12/21/03     J.Forkosh       Version 1.30 released.
 398:  * 02/01/04     J.Forkosh       Version 1.40 released.
 399:  * 10/02/04     J.Forkosh       Version 1.50 released.
 400:  * 11/30/04     J.Forkosh       Version 1.60 released.
 401:  * 10/11/05     J.Forkosh       Version 1.64 released.
 402:  * 11/30/06     J.Forkosh       Version 1.65 released.
 403:  * 09/06/08     J.Forkosh       Version 1.70 released.
 404:  * 03/23/09     J.Forkosh       Version 1.71 released.
 405:  * 11/18/09     J.Forkosh       Version 1.72 released.
 406:  * 11/15/11     J.Forkosh       Version 1.73 released.
 407:  * 02/15/12     J.Forkosh       Version 1.74 released.
 408:  * 12/28/16     J.Forkosh       Version 1.75 released.
 409:  * 06/10/17     J.Forkosh       Most recent revision (also see REVISIONDATE)
 410:  * See  http://www.forkosh.com/mimetexchangelog.html  for further details.
 411:  *
 412:  ****************************************************************************/
 413: 
 414: /* -------------------------------------------------------------------------
 415: Program id
 416: -------------------------------------------------------------------------- */
 417: #define VERSION "1.75"                  /* mimeTeX version number */
 418: #define REVISIONDATE "10 June 2017"     /* date of most recent revision */
 419: #define COPYRIGHTTEXT "Copyright(c) 2002-2017, John Forkosh Associates, Inc"
 420: 
 421: /* -------------------------------------------------------------------------
 422: header files and macros
 423: -------------------------------------------------------------------------- */
 424: /* --- standard headers --- */
 425: #include <stdio.h>
 426: #include <stdlib.h>
 427: /*#include <unistd.h>*/
 428: #define _GNU_SOURCE                     /* for strcasestr() in string.h */
 429: #include <string.h>
 430: char    *strcasestr();                  /* non-standard extension */
 431: #include <ctype.h>
 432: #include <math.h>
 433: #include <time.h>
 434: extern  char **environ;         /* for \environment directive */
 435: 
 436: /* -------------------------------------------------------------------------
 437: messages (used mostly by main() and also by rastmessage())
 438: -------------------------------------------------------------------------- */
 439: static  char *copyright1 =              /* copyright, gnu/gpl notice */
 440:  "+-----------------------------------------------------------------------+\n"
 441:  "|mimeTeX vers " VERSION ", " COPYRIGHTTEXT                             "|\n"
 442:  "+-----------------------------------------------------------------------+\n"
 443:  "| mimeTeX is free software, licensed to you under terms of the GNU/GPL, |\n"
 444:  "|           and comes with absolutely no warranty whatsoever.           |",
 445: *copyright2 =
 446:  "|          See http://www.forkosh.com/mimetex.html for details.         |\n"
 447:  "+-----------------------------------------------------------------------+";
 448: static  int maxmsgnum = 3,              /* maximum msgtable[] index */
 449:         /* --- keep these message numbers updated if table changes --- */
 450:         invmsgnum = 0,                  /* general invalid message */
 451:         refmsgnum = 3;                  /* urlncmp() failed to validate */
 452: static  char *msgtable[] = {            /* messages referenced by [index] */
 453:  "\\red\\small\\rm\\fbox{\\array{"      /* [0] is invalid_referer_msg */
 454:    "Please~read~www.forkosh.com/mimetex.html\\\\and~install~mimetex.cgi~"
 455:    "on~your~own~server.\\\\Thank~you,~John~Forkosh}}",
 456:  "\\red\\small\\rm\\fbox{\\array{"      /* [1] */
 457:    "Please~provide~your~{\\tiny~HTTP-REFERER}~to~access~the~public\\\\"
 458:    "mimetex~server.~~Or~please~read~~www.forkosh.com/mimetex.html\\\\"
 459:    "and~install~mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}",
 460:  "\\red\\small\\rm\\fbox{\\array{"      /* [2] */
 461:    "The~public~mimetex~server~is~for~testing.~~For~production,\\\\"
 462:    "please~read~~www.forkosh.com/mimetex.html~~and~install\\\\"
 463:    "mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}",
 464:  "\\red\\small\\rm\\fbox{\\array{"      /* [3] */
 465:    "Only~SERVER_NAME~may~use~mimetex~on~this~server.\\\\"
 466:    "Please~read~~www.forkosh.com/mimetex.html~~and~install\\\\"
 467:    "mimetex.cgi~on~your~own~server.~~Thank~you,~John~Forkosh}}",
 468:  NULL } ;                               /* trailer */
 469: 
 470: /* -------------------------------------------------------------------------
 471: additional symbols
 472: -------------------------------------------------------------------------- */
 473: /* ---
 474:  * windows-specific header info
 475:  * ---------------------------- */
 476: #if !defined(WINDOWS)           /* -DWINDOWS not supplied by user */
 477:   #if defined(_WINDOWS) || defined(_WIN32) || defined(WIN32) \
 478:   ||  defined(DJGPP)            /* try to recognize windows compilers */ \
 479:   ||  defined(_USRDLL)          /* must be WINDOWS if compiling for DLL */
 480:     #define WINDOWS             /* signal windows */
 481:   #endif
 482: #endif
 483: #if defined(WINDOWS)            /* Windows opens stdout in char mode, and */
 484:   #include <fcntl.h>            /* precedes every 0x0A with spurious 0x0D.*/
 485:   #include <io.h>               /* So emitcache() issues a Win _setmode() */
 486:                                 /* call to put stdout in binary mode. */
 487:   #if defined(_O_BINARY) && !defined(O_BINARY)  /* only have _O_BINARY */
 488:     #define O_BINARY _O_BINARY  /* make O_BINARY available, etc... */
 489:     #define setmode  _setmode
 490:     #define fileno   _fileno
 491:   #endif
 492:   #if defined(_O_BINARY) || defined(O_BINARY)  /* setmode() now available */
 493:     #define HAVE_SETMODE        /* so we'll use setmode() */
 494:   #endif
 495:   #if defined(_MSC_VER) && defined(_DEBUG) /* MS VC++ in debug mode */
 496:     /* to show source file and line numbers where memory leaks occur... */
 497:     #define _CRTDBG_MAP_ALLOC   /* ...include this debug macro */
 498:     #include <crtdbg.h>         /* and this debug library */
 499:   #endif
 500:   #define ISWINDOWS 1
 501: #else
 502:   #define ISWINDOWS 0
 503: #endif
 504: 
 505: /* ---
 506:  * check for supersampling or low-pass anti-aliasing
 507:  * ------------------------------------------------- */
 508: #if defined(SS)
 509:   #define ISSUPERSAMPLING 1
 510:   #if !defined(AAALGORITHM)
 511:     #define AAALGORITHM 1               /* default supersampling algorithm */
 512:   #endif
 513:   #if !defined(AA)                      /* anti-aliasing not explicitly set */
 514:     #define AA                          /* so define it ourselves */
 515:   #endif
 516:   #if !defined(SSFONTS)                 /* need supersampling fonts */
 517:     #define SSFONTS
 518:   #endif
 519: #else
 520:   #define ISSUPERSAMPLING 0
 521:   #if !defined(AAALGORITHM)
 522:     #define AAALGORITHM 3 /*2*/         /* default lowpass algorithm */
 523:   #endif
 524: #endif
 525: #if !defined(MAXFOLLOW)
 526:   #define MAXFOLLOW 8                   /* aafollowline() maxturn default */
 527: #endif
 528: 
 529: /* ---
 530:  * set aa (and default gif) if any anti-aliasing options specified
 531:  * --------------------------------------------------------------- */
 532: #if defined(AA) || defined(GIF) || defined(PNG) \
 533: ||  defined(CENTERWT) || defined(ADJACENTWT) || defined(CORNERWT) \
 534: ||  defined(MINADJACENT) || defined(MAXADJACENT)
 535:   #if !defined(GIF) && !defined(AA)     /* aa not explicitly specified */
 536:     #define AA                          /* so define it ourselves */
 537:   #endif
 538:   #if !defined(GIF) && !defined(PNG)    /* neither gif nor png specified */
 539:     #define GIF                         /* so default to gif */
 540:   #endif
 541: #endif
 542: /* --- resolve output option inconsistencies --- */
 543: #if defined(XBITMAP)                    /* xbitmap supercedes gif and png */
 544:   #if defined(AA)
 545:     #undef AA
 546:   #endif
 547:   #if defined(GIF)
 548:     #undef GIF
 549:   #endif
 550:   #if defined(PNG)
 551:     #undef PNG
 552:   #endif
 553: #endif
 554: 
 555: /* ---
 556:  * decide whether or not to compile main()
 557:  * --------------------------------------- */
 558: #if defined(XBITMAP) || defined(GIF) || defined(PNG)
 559:   /* --- yes, compile main() --- */
 560:   #define DRIVER                        /* main() driver will be compiled */
 561: #else /* --- main() won't be compiled (e.g., for gfuntype.c) --- */
 562:   #if !defined(TEXFONTS)
 563:     #define NOTEXFONTS                  /* texfonts not required */
 564:   #endif
 565: #endif
 566: 
 567: /* ---
 568:  * application headers
 569:  * ------------------- */
 570: #if !defined(NOTEXFONTS) && !defined(TEXFONTS)
 571:   #define TEXFONTS                      /* to include texfonts.h */
 572: #endif
 573: #include "mimetex.h"
 574: 
 575: /* ---
 576:  * info needed when gif image returned in memory buffer
 577:  * ---------------------------------------------------- */
 578: #if defined(GIF)                        /* compiling along with gifsave.c */
 579:   extern int gifSize;
 580:   extern int maxgifSize;
 581: #else                                   /* or just set dummy values */
 582:   static int gifSize=0, maxgifSize=0;
 583: #endif
 584: /* --- gamma correction --- */
 585: #if !defined(GAMMA)
 586:   #define GAMMA 1.25 /*1.75*/ /*2.2*/
 587: #endif
 588: #if !defined(REVERSEGAMMA)
 589:   #define REVERSEGAMMA 0.5              /* for \reverse white-on-black */
 590: #endif
 591: /* --- opaque background (default to transparent) --- */
 592: #if !defined(OPAQUE)
 593:   #define ISTRANSPARENT 1
 594: #else
 595:   #define ISTRANSPARENT 0
 596: #endif
 597: 
 598: /* ---
 599:  * internal buffer sizes
 600:  * --------------------- */
 601: #if !defined(MAXEXPRSZ)
 602:   #define MAXEXPRSZ (32768-1)           /*max #bytes in input tex expression*/
 603: #endif
 604: #if !defined(MAXSUBXSZ)
 605:   #define MAXSUBXSZ (((MAXEXPRSZ+1)/2)-1)/*max #bytes in input subexpression*/
 606: #endif
 607: #if !defined(MAXTOKNSZ)
 608:   #define MAXTOKNSZ (((MAXSUBXSZ+1)/4)-1) /* max #bytes in input token */
 609: #endif
 610: #if !defined(MAXFILESZ)
 611:   #define MAXFILESZ (65536-1)           /*max #bytes in input (output) file*/
 612: #endif
 613: #if !defined(MAXLINESZ)
 614:   #define MAXLINESZ (4096-1)            /* max #chars in line from file */
 615: #endif
 616: #if !defined(MAXGIFSZ)
 617:   #define MAXGIFSZ 131072               /* max #bytes in output GIF image */
 618: #endif
 619: 
 620: /* ---
 621:  * note: point3d.x,y,z are relative to the origin 0.,0.,0. of an abstract
 622:  * coordinate system, calculated to be at the center pixel of a raster.
 623:  * point3d and matrix3d are used for rotations, as per
 624:  *   https://en.wikipedia.org/wiki/Rotation_matrix#In_three_dimensions
 625:  * in function rotrast3d()
 626:  * ------------------------------------------------------------------------ */
 627: /* --- 3d-point --- */
 628: #define point3d struct point3d_struct   /* "typedef" for point3d_struct */
 629: point3d {
 630:   /* --- x,y,z-coords relative to 0.,0.,0. origin of abstract coord axes --- */
 631:   double x;                             /* x-coord */
 632:   double y;                             /* y-coord */
 633:   double z;                             /* z-coord */
 634:   } ; /* --- end-of-point3d_struct --- */
 635: /* --- 3d-matrix (rotation matrix) stored row-wise --- */
 636: #define matrix3d struct matrix3d_struct /* "typedef" for matrix3d_struct */
 637: matrix3d {
 638:   /* --- 3x3 matrix stored row-wise --- */
 639:   point3d xrow;                         /* x-row */
 640:   point3d yrow;                         /* y-row */
 641:   point3d zrow;                         /* z-row */
 642:   } ; /* --- end-of-matrix3d_struct --- */
 643: /* --- 3d-rotation parameters --- */
 644: #define YAXIS {0.,1.,0.}                /* x-component=0, y=1, z=0 */
 645: #define UTHETA 0.0                      /* no rotation (in degrees) */
 646: GLOBAL(point3d,uaxis,YAXIS);            /* rotate around y-axis */
 647: GLOBAL(double,utheta,UTHETA);           /* rotation amount in degrees */
 648: 
 649: /* -------------------------------------------------------------------------
 650: adjustable default values
 651: -------------------------------------------------------------------------- */
 652: /* ---
 653:  * anti-aliasing parameters
 654:  * ------------------------ */
 655: #if !defined(CENTERWT)
 656:   /*#define CENTERWT 32*/               /* anti-aliasing centerwt default */
 657:   /*#define CENTERWT 10*/               /* anti-aliasing centerwt default */
 658:   #define CENTERWT 8                    /* anti-aliasing centerwt default */
 659: #endif
 660: #if !defined(ADJACENTWT)
 661:   /*#define ADJACENTWT 3*/              /* anti-aliasing adjacentwt default*/
 662:   #define ADJACENTWT 2                  /* anti-aliasing adjacentwt default*/
 663: #endif
 664: #if !defined(CORNERWT)
 665:   #define CORNERWT 1                    /* anti-aliasing cornerwt default*/
 666: #endif
 667: #if !defined(MINADJACENT)
 668:   #define MINADJACENT 6                 /*anti-aliasing minadjacent default*/
 669: #endif
 670: #if !defined(MAXADJACENT)
 671:   #define MAXADJACENT 8                 /*anti-aliasing maxadjacent default*/
 672: #endif
 673: /* --- variables for anti-aliasing parameters --- */
 674: GLOBAL(int,centerwt,CENTERWT);          /*lowpass matrix center pixel wt */
 675: GLOBAL(int,adjacentwt,ADJACENTWT);      /*lowpass matrix adjacent pixel wt*/
 676: GLOBAL(int,cornerwt,CORNERWT);          /*lowpass matrix corner pixel wt */
 677: GLOBAL(int,minadjacent,MINADJACENT);    /* darken if>=adjacent pts black*/
 678: GLOBAL(int,maxadjacent,MAXADJACENT);    /* darken if<=adjacent pts black */
 679: GLOBAL(int,weightnum,1);                /* font wt, */
 680: GLOBAL(int,maxaaparams,4);              /* #entries in table */
 681: /* --- anti-aliasing parameter values by font weight --- */
 682: #define aaparameters struct aaparameters_struct /* typedef */
 683: aaparameters
 684:   { int centerwt;                       /* lowpass matrix center   pixel wt*/
 685:     int adjacentwt;                     /* lowpass matrix adjacent pixel wt*/
 686:     int cornerwt;                       /* lowpass matrix corner   pixel wt*/
 687:     int minadjacent;                    /* darken if >= adjacent pts black */
 688:     int maxadjacent;                    /* darken if <= adjacent pts black */
 689:     int fgalias,fgonly,bgalias,bgonly; } ; /* aapnm() params */
 690: STATIC aaparameters aaparams[]          /* set params by weight */
 691:   #ifdef INITVALS
 692:   =
 693:   { /* ----------------------------------------------------
 694:     centerwt adj corner minadj max  fgalias,only,bgalias,only
 695:     ------------------------------------------------------- */
 696:         { 64,  1,  1,    6,  8,     1,0,0,0 },  /* 0 = light */
 697:         { CENTERWT,ADJACENTWT,CORNERWT,MINADJACENT,MAXADJACENT,1,0,0,0 },
 698:         { 8,   1,  1,    5,  8,     1,0,0,0 },  /* 2 = semibold */
 699:         { 8,   2,  1,    4,  9,     1,0,0,0 }   /* 3 = bold */
 700:   } /* --- end-of-aaparams[] --- */
 701:   #endif
 702:   ;
 703: /* --- anti-aliasing diagnostics (to help improve algorithm) --- */
 704: STATIC int patternnumcount0[99], patternnumcount1[99], /*aalookup() counts*/
 705:         ispatternnumcount = 1;          /* true to accumulate counts */
 706: 
 707: /* -------------------------------------------------------------------------
 708: other variables
 709: -------------------------------------------------------------------------- */
 710: /* --- black on white background (default), or white on black --- */
 711: #if defined(WHITE)
 712:   #define ISBLACKONWHITE 0              /* white on black background */
 713: #else
 714:   #define ISBLACKONWHITE 1              /* black on white background */
 715: #endif
 716: /* --- colors --- */
 717: #define BGRED   (ISBLACKONWHITE?255:0)
 718: #define BGGREEN (ISBLACKONWHITE?255:0)
 719: #define BGBLUE  (ISBLACKONWHITE?255:0)
 720: #if !defined(FGRED)
 721:   #define FGRED   (ISBLACKONWHITE?0:255)
 722: #endif
 723: #if !defined(FGGREEN)
 724:   #define FGGREEN (ISBLACKONWHITE?0:255)
 725: #endif
 726: #if !defined(FGBLUE)
 727:   #define FGBLUE  (ISBLACKONWHITE?0:255)
 728: #endif
 729: /* --- advertisement
 730:    one image in every ADFREQUENCY is wrapped in "advertisement" --- */
 731: #if !defined(ADFREQUENCY)
 732:   #define ADFREQUENCY 0                 /* never show advertisement if 0 */
 733: #endif
 734: #ifndef HOST_SHOWAD
 735:   #define HOST_SHOWAD "\000"            /* show ads on all hosts */
 736: #endif
 737: /* --- mathTeX info for \mathtex{} directive --- */
 738: #if !defined(MATHTEX)                   /* localhost:// or http://url/ */
 739:   #define MATHTEX "localhost://mathtex.cgi"
 740: #endif
 741: #if !defined(MATHTEXPWD)                /* mathTeX \password{} */
 742:   #define MATHTEXPWD "\000"
 743: #endif
 744: #if !defined(WGET)                      /* local /path/to/wget */
 745:   #define WGET "wget"
 746: #endif
 747: /* --- "smash" margin (0 means no smashing) --- */
 748: #if !defined(SMASHMARGIN)
 749:   #if defined(NOSMASH)
 750:     #define SMASHMARGIN 0
 751:   #else
 752:     #define SMASHMARGIN 3
 753:   #endif
 754: #endif
 755: #if !defined(SMASHCHECK)
 756:   #define SMASHCHECK 0
 757: #endif
 758: /* --- textwidth --- */
 759: #if !defined(TEXTWIDTH)
 760:   #define TEXTWIDTH (400)
 761: #endif
 762: /* --- font "combinations" --- */
 763: #define CMSYEX (109)                    /*select CMSY10, CMEX10 or STMARY10*/
 764: /* --- prefix prepended to all expressions --- */
 765: #if !defined(PREFIX)
 766:   #define PREFIX "\000"                 /* default no prepended prefix */
 767: #endif
 768: /* --- skip argv[]'s preceding ARGSIGNAL when parsing command-line args --- */
 769: #if defined(NOARGSIGNAL)
 770:   #define ARGSIGNAL NULL
 771: #endif
 772: #if !defined(ARGSIGNAL)
 773:   #define ARGSIGNAL "++"
 774: #endif
 775: /* --- security and logging (inhibit message logging, etc) --- */
 776: #if !defined(SECURITY)
 777:   #define SECURITY 999                  /* default highest security level */
 778: #endif
 779: #if !defined(LOGFILE)
 780:   #define LOGFILE "mimetex.log"         /* default log file */
 781: #endif
 782: #if !defined(CACHELOG)
 783:   #define CACHELOG "mimetex.log"        /* default caching log file */
 784: #endif
 785: #if !defined(NODUMPENVP) && !defined(DUMPENVP)
 786:   #define DUMPENVP                      /* assume char *envp[] available */
 787: #endif
 788: /* --- max query_string length if no http_referer supplied --- */
 789: #if !defined(NOREFMAXLEN)
 790:   #define NOREFMAXLEN 9999              /* default to any length query */
 791: #endif
 792: #if !defined(NOREFSAFELEN)
 793:   #define NOREFSAFELEN 24               /* too small for hack exploit */
 794: #endif
 795: /* --- check whether or not to perform http_referer check --- */
 796: #if defined(REFERER)                    /* only specified referers allowed */
 797:   #undef NOREFMAXLEN
 798:   #define NOREFMAXLEN NOREFSAFELEN
 799: #else                                   /* all http_referer's allowed */
 800:   #define REFERER NULL
 801: #endif
 802: /* --- check top levels of http_referer against server_name --- */
 803: #if defined(REFLEVELS)                  /* #topmost levels to check */
 804:   #undef NOREFMAXLEN
 805:   #define NOREFMAXLEN NOREFSAFELEN
 806: #else
 807:   #if defined(NOREFCHECK)
 808:     #define REFLEVELS 0                 /* don't match host and referer */
 809:   #else
 810:     #define REFLEVELS 3                 /* default matches abc.def.com */
 811:   #endif
 812: #endif
 813: /* --- password to bypass preceding REFERER checks --- */
 814: #if !defined(PASSWORD)
 815:   #define PASSWORD "\000"               /* \password{PASSWORD} in expression */
 816: #endif
 817: static  char password[128] = "\000";    /* user's \password{password} */
 818: static  int  ispassword = 0;            /* true if password==PASSWORD */
 819: /* --- check whether or not \input, \counter, \environment permitted --- */
 820: #if defined(DEFAULTSECURITY)            /* default security specified */
 821:   #define EXPLICITDEFSECURITY           /* don't override explicit default */
 822: #else                                   /* defualt security not specified */
 823:   #define DEFAULTSECURITY (8)           /* so set default security level */
 824: #endif
 825: #if defined(INPUTREFERER)               /*http_referer's permitted to \input*/
 826:   #if !defined(INPUTSECURITY)           /* so we need to permit \input{} */
 827:     #define INPUTSECURITY (99999)       /* make sure SECURITY<INPUTSECURITY */
 828:   #endif
 829: #else                                   /* no INPUTREFERER list supplied */
 830:   #define INPUTREFERER NULL             /* so init it as NULL pointer */
 831: #endif
 832: #if !defined(INPUTPATH)                 /* \input{} paths permitted for... */
 833:   #define INPUTPATH NULL                /* ...any referer */
 834: #endif
 835: #if !defined(INPUTSECURITY)             /* \input{} security not specified */
 836:   #if defined(INPUTOK)                  /* but INPUTOK flag specified */
 837:     #define INPUTSECURITY (99999)       /* so enable \input{} */
 838:     #if !defined(EXPLICITDEFSECURITY)   /* don't override explicit default */
 839:       #undef  DEFAULTSECURITY           /* but we'll override our default */
 840:       #define DEFAULTSECURITY (99999)   /*let -DINPUTOK enable \counter,etc*/
 841:     #endif
 842:   #else                                 /* else no \input{} specified */
 843:     #define INPUTSECURITY DEFAULTSECURITY /* set default \input security */
 844:   #endif
 845: #endif
 846: #if !defined(COUNTERSECURITY)           /*\counter{} security not specified*/
 847:   #if defined(COUNTEROK)                /* but COUNTEROK flag specified */
 848:     #define COUNTERSECURITY (99999)     /* so enable \counter{} */
 849:   #else                                 /* else no \counter{} specified */
 850:     #define COUNTERSECURITY DEFAULTSECURITY /*set default \counter security*/
 851:   #endif
 852: #endif
 853: #if !defined(ENVIRONSECURITY)           /* \environ security not specified */
 854:   #if defined(ENVIRONOK)                /* but ENVIRONOK flag specified */
 855:     #define ENVIRONSECURITY (99999)     /* so enable \environ */
 856:   #else                                 /* else no \environ specified */
 857:     #define ENVIRONSECURITY DEFAULTSECURITY /*set default \environ security*/
 858:   #endif
 859: #endif
 860: /* --- image caching (cache images if given -DCACHEPATH=\"path\") --- */
 861: #if !defined(CACHEPATH)
 862:   #define ISCACHING 0                   /* no caching */
 863:   #define CACHEPATH "\000"              /* same directory as mimetex.cgi */
 864: #else
 865:   #define ISCACHING 1                   /* caching if -DCACHEPATH="path" */
 866: #endif
 867: /* --- \input paths (prepend prefix if given -DPATHPREFIX=\"prefix\") --- */
 868: #if !defined(PATHPREFIX)
 869:   #define PATHPREFIX "\000"             /* paths relative mimetex.cgi */
 870: #endif
 871: /* --- time zone delta t (in hours) --- */
 872: #if !defined(TZDELTA)
 873:   #define TZDELTA 0
 874: #endif
 875: /* --- treat +'s in query string as blanks? --- */
 876: #if defined(PLUSBLANK)                  /* + always interpreted as blank */
 877:   #define ISPLUSBLANK 1
 878: #else
 879:   #if defined(PLUSNOTBLANK)             /* + never interpreted as blank */
 880:     #define ISPLUSBLANK 0
 881:   #else                                 /* program tries to determine */
 882:     #define ISPLUSBLANK (-1)
 883:   #endif
 884: #endif
 885: 
 886: /* -------------------------------------------------------------------------
 887: debugging and logging / error reporting
 888: -------------------------------------------------------------------------- */
 889: /* --- debugging and error reporting --- */
 890: #if !defined(MSGLEVEL)
 891:   #define MSGLEVEL 1
 892: #endif
 893: #define DBGLEVEL 9                      /* debugging if msglevel>=DBGLEVEL */
 894: #define LOGLEVEL 3                      /* logging if msglevel>=LOGLEVEL */
 895: #if !defined(FORMLEVEL)
 896:   #define FORMLEVEL LOGLEVEL            /*msglevel if called from html form*/
 897: #endif
 898: #if !defined(ERRORSTATUS)               /* exit(ERRORSTATUS) for any error */
 899:   #define ERRORSTATUS 0                 /* default doesn't signal errors */
 900: #endif
 901: GLOBAL(int,seclevel,SECURITY);          /* security level */
 902: GLOBAL(int,inputseclevel,INPUTSECURITY); /* \input{} security level */
 903: GLOBAL(int,counterseclevel,COUNTERSECURITY); /* \counter{} security level */
 904: GLOBAL(int,environseclevel,ENVIRONSECURITY); /* \environ{} security level */
 905: GLOBAL(int,msglevel,MSGLEVEL);          /* message level for verbose/debug */
 906: GLOBAL(int,errorstatus,ERRORSTATUS);    /* exit status if error encountered*/
 907: GLOBAL(int,exitstatus,0);               /* exit status (0=success) */
 908: STATIC  FILE *msgfp;                    /* output in command-line mode */
 909: /* --- logging macros --- */
 910: #define logdump(lvl,msg)  if(msglevel>=(lvl)) {logmsg(msg);}else
 911: #define logmsg(msg) if(1) {logger(msgfp,msglevel,(msg),NULL);}else
 912: /* --- embed warnings in rendered expressions, [\xxx?] if \xxx unknown --- */
 913: #if defined(WARNINGS)
 914:   #define WARNINGLEVEL WARNINGS
 915: #else
 916:   #if defined(NOWARNINGS)
 917:     #define WARNINGLEVEL 0
 918:   #else
 919:     #define WARNINGLEVEL 1
 920:   #endif
 921: #endif
 922: GLOBAL(int,warninglevel,WARNINGLEVEL);  /* warning level */
 923: 
 924: /* -------------------------------------------------------------------------
 925: control flags and values
 926: -------------------------------------------------------------------------- */
 927: GLOBAL(int,isquery,0);                  /* true=cgi?query; false=commandline*/
 928: GLOBAL(int,daemonlevel,0);              /* incremented in main() */
 929: GLOBAL(int,recurlevel,0);               /* inc/decremented in rasterize() */
 930: GLOBAL(int,scriptlevel,0);              /* inc/decremented in rastlimits() */
 931: GLOBAL(int,isstring,0);                 /*pixmap is ascii string, not raster*/
 932: GLOBAL(int,isligature,0);               /* true if ligature found */
 933: GLOBAL(char,*subexprptr,(char *)NULL);  /* ptr within expression to subexpr*/
 934: /*SHARED(int,imageformat,1);*/          /* image is 1=bitmap, 2=.gf-like */
 935: GLOBAL(int,isdisplaystyle,1);           /* displaystyle mode (forced if 2) */
 936: GLOBAL(int,ispreambledollars,0);        /* displaystyle mode set by $$...$$ */
 937: GLOBAL(int,ninputcmds,0);               /* # of \input commands processed */
 938: GLOBAL(int,fontnum,0);                  /* cal=1,scr=2,rm=3,it=4,bb=5,bf=6 */
 939: GLOBAL(int,fontsize,NORMALSIZE);        /* current size */
 940: GLOBAL(int,magstep,1);                  /* magstep (1=no change) */
 941: GLOBAL(int,displaysize,DISPLAYSIZE);    /* use \displaystyle when fontsize>=*/
 942: GLOBAL(int,shrinkfactor,3);             /* shrinkfactors[fontsize] */
 943: GLOBAL(int,rastlift,0);                 /* rastraise() lift parameter */
 944: GLOBAL(int,rastlift1,0);                /* rastraise() lift for base exprssn*/
 945: GLOBAL(double,unitlength,1.0);          /* #pixels per unit (may be <1.0) */
 946: GLOBAL(int,iunitlength,1);              /* #pixels per unit as int for store*/
 947: /*GLOBAL(int,textwidth,TEXTWIDTH);*/    /* #pixels across line */
 948: GLOBAL(int,adfrequency,ADFREQUENCY);    /* advertisement frequency */
 949: GLOBAL(int,isnocatspace,0);             /* >0 to not add space in rastcat()*/
 950: GLOBAL(int,smashmargin,SMASHMARGIN);    /* minimum "smash" margin */
 951: GLOBAL(int,mathsmashmargin,SMASHMARGIN); /* needed for \text{if $n-m$ even}*/
 952: GLOBAL(int,issmashdelta,1);             /* true if smashmargin is a delta */
 953: GLOBAL(int,isexplicitsmash,0);          /* true if \smash explicitly given */
 954: GLOBAL(int,smashcheck,SMASHCHECK);      /* check if terms safe to smash */
 955: GLOBAL(int,isnomath,0);                 /* true to inhibit math mode */
 956: GLOBAL(int,isscripted,0);               /* is (lefthand) term text-scripted*/
 957: GLOBAL(int,isdelimscript,0);            /* is \right delim text-scripted */
 958: GLOBAL(int,issmashokay,0);              /*is leading char okay for smashing*/
 959: #define BLANKSIGNAL (-991234)           /*rastsmash signal right-hand blank*/
 960: GLOBAL(int,blanksignal,BLANKSIGNAL);    /*rastsmash signal right-hand blank*/
 961: GLOBAL(int,blanksymspace,0);            /* extra (or too much) space wanted*/
 962: GLOBAL(int,istransparent,ISTRANSPARENT);/* true sets background transparent*/
 963: GLOBAL(int,fgred,FGRED);
 964:   GLOBAL(int,fggreen,FGGREEN);
 965:   GLOBAL(int,fgblue,FGBLUE);            /* fg r,g,b */
 966: GLOBAL(int,bgred,BGRED);
 967:   GLOBAL(int,bggreen,BGGREEN);
 968:   GLOBAL(int,bgblue,BGBLUE);            /* bg r,g,b */
 969: GLOBAL(double,gammacorrection,GAMMA);   /* gamma correction */
 970: GLOBAL(int,isplusblank,ISPLUSBLANK);    /*interpret +'s in query as blanks?*/
 971: GLOBAL(int,isblackonwhite,ISBLACKONWHITE); /*1=black on white,0=reverse*/
 972: GLOBAL(char,exprprefix[256],PREFIX);    /* prefix prepended to expressions */
 973: GLOBAL(int,aaalgorithm,AAALGORITHM);    /* for lp, 1=aalowpass, 2 =aapnm */
 974: GLOBAL(int,maxfollow,MAXFOLLOW);        /* aafollowline() maxturn parameter*/
 975: GLOBAL(int,fgalias,1);
 976:   GLOBAL(int,fgonly,0);
 977:   GLOBAL(int,bgalias,0);
 978:   GLOBAL(int,bgonly,0);                 /* aapnm() params */
 979: GLOBAL(int,issupersampling,ISSUPERSAMPLING); /*1=supersampling 0=lowpass*/
 980: GLOBAL(int,isss,ISSUPERSAMPLING);       /* supersampling flag for main() */
 981: GLOBAL(int,ispbmpgm,0);                 /* true for pbm/pgm instead of gif */
 982: GLOBAL(int,pbmpgmtype,0);               /* 1=pbm / 2=pgm (else error) */
 983: GLOBAL(int,*workingparam,(int *)NULL);  /* working parameter */
 984: GLOBAL(subraster,*workingbox,(subraster *)NULL); /*working subraster box*/
 985: GLOBAL(int,isreplaceleft,0);            /* true to replace leftexpression */
 986: GLOBAL(subraster,*leftexpression,(subraster *)NULL); /*rasterized so far*/
 987: GLOBAL(mathchardef,*leftsymdef,NULL);   /* mathchardef for preceding symbol*/
 988: GLOBAL(int,fraccenterline,NOVALUE);     /* baseline for punct. after \frac */
 989: /*GLOBAL(int,currentcharclass,NOVALUE);*/ /*primarily to check for PUNCTION*/
 990: GLOBAL(int,iscaching,ISCACHING);        /* true if caching images */
 991: GLOBAL(char,cachepath[256],CACHEPATH);  /* relative path to cached files */
 992: GLOBAL(int,isemitcontenttype,1);        /* true to emit mime content-type */
 993: int     iscachecontenttype = 0;         /* true to cache mime content-type */
 994: char    contenttype[2048] = "\000";     /* content-type:, etc buffer */
 995: GLOBAL(char,pathprefix[256],PATHPREFIX); /*prefix for \input,\counter paths*/
 996: /*GLOBAL(int,iswindows,ISWINDOWS);*/    /* true if compiled for ms windows */
 997: 
 998: /* -------------------------------------------------------------------------
 999: store for evalterm() [n.b., these are stripped-down funcs from nutshell]
1000: -------------------------------------------------------------------------- */
1001: #define STORE struct store_struct       /* "typedef" for store struct */
1002: #define MAXSTORE 100                    /* max 100 identifiers */
1003: STORE {
1004:   char  *identifier;                    /* identifier */
1005:   int   *value;                         /* address of corresponding value */
1006:   } ; /* --- end-of-store_struct --- */
1007: static STORE mimestore[MAXSTORE] = {
1008:     { "fontsize", &fontsize },  { "fs", &fontsize },    /* font size */
1009:     { "fontnum", &fontnum },    { "fn", &fontnum },     /* font number */
1010:     { "unitlength", &iunitlength },                     /* unitlength */
1011:     /*{ "mytestvar", &mytestvar },*/
1012:     { NULL, NULL }                                      /* end-of-store */
1013:   } ; /* --- end-of-mimestore[] --- */
1014: 
1015: /* -------------------------------------------------------------------------
1016: other application global data
1017: -------------------------------------------------------------------------- */
1018: /* --- getdirective() global data --- */
1019: static  int  argformat   = 0;           /* 111... if arg not {}-enclosed */
1020: static  int  optionalpos = 0;           /* # {args} before optional [args] */
1021: static  int  noptional   = 0;           /* # optional [args] found */
1022: static  char optionalargs[10][128] =    /* buffer for optional args */
1023:   { "\000", "\000", "\000", "\000", "\000", "\000", "\000", "\000" };
1024: /* --- **dump/debug** data --- */
1025: int     isdumpgif = 0;                  /* **debug** dump gif image */
1026: #if !defined(DUMPGIF)
1027:   #define DUMPGIF "dumpgif"             /* use .gif_dumpgif extension */
1028: #endif
1029: 
1030: /* -------------------------------------------------------------------------
1031: miscellaneous macros
1032: -------------------------------------------------------------------------- */
1033: #if 0   /* --- these are now #define'd in mimetex.h --- */
1034: #define max2(x,y)  ((x)>(y)? (x):(y))   /* larger of 2 arguments */
1035: #define min2(x,y)  ((x)<(y)? (x):(y))   /* smaller of 2 arguments */
1036: #define max3(x,y,z) max2(max2(x,y),(z)) /* largest of 3 arguments */
1037: #define min3(x,y,z) min2(min2(x,y),(z)) /* smallest of 3 arguments */
1038: #define absval(x)  ((x)>=0?(x):(-(x)))  /* absolute value */
1039: #define iround(x)  ((int)((x)>=0?(x)+0.5:(x)-0.5)) /* round double to int */
1040: #define dmod(x,y)  ((x)-((y)*((double)((int)((x)/(y)))))) /*x%y for doubles*/
1041: #endif
1042: #define compress(s,c) if((s)!=NULL)     /* remove embedded c's from s */ \
1043:         { char *p; while((p=strchr((s),(c)))!=NULL) {strsqueeze(p,1);} } else
1044: #define slower(s)  if ((s)!=NULL)       /* lowercase all chars in s */ \
1045:         { char *p=(s); while(*p!='\000'){*p=tolower(*p); p++;} } else
1046: /*subraster *subrastcpy();*/            /* need global module declaration */
1047: /*#define spnosmash(sp) if (sp->type==CHARASTER) sp=subrastcpy(sp); \ */
1048: /*      sp->type=blanksignal */
1049: /* ---evaluate \directive[arg] or \directive{arg} scaled by unitlength--- */
1050: #define eround(arg) (iround(unitlength*((double)evalterm(mimestore,(arg)))))
1051: /* --- check if a string is empty --- */
1052: #define isempty(s)  ((s)==NULL?1:(*(s)=='\000'?1:0))
1053: /* --- last char of a string --- */
1054: #define lastchar(s) (isempty(s)?'\000':*((s)+(strlen(s)-1)))
1055: /* --- lowercase a string --- */
1056: #define strlower(s) strnlower((s),0)    /* lowercase an entire string */
1057: /* --- strip leading and trailing whitespace (including ~) --- */
1058: #define trimwhite(thisstr) if ( (thisstr) != NULL ) { \
1059:         int thislen = strlen(thisstr); \
1060:         while ( --thislen >= 0 ) \
1061:           if ( isthischar((thisstr)[thislen]," \t\n\r\f\v") ) \
1062:             (thisstr)[thislen] = '\000'; \
1063:           else break; \
1064:         if ( (thislen = strspn((thisstr)," \t\n\r\f\v")) > 0 ) \
1065:           {strsqueeze((thisstr),thislen);} } else
1066: /* --- strncpy() n bytes and make sure it's null-terminated --- */
1067: #define strninit(target,source,n) if( (target)!=NULL && (n)>=0 ) { \
1068:           char *thissource = (source); \
1069:           (target)[0] = '\000'; \
1070:           if ( (n)>0 && thissource!=NULL ) { \
1071:             strncpy((target),thissource,(n)); \
1072:             (target)[(n)] = '\000'; } }
1073: /* --- strcpy(s,s+n) using memmove() (also works for negative n) --- */
1074: #define strsqueeze(s,n) if((n)!=0) { if(!isempty((s))) { \
1075:         int thislen3=strlen(s); \
1076:         if ((n) >= thislen3) *(s) = '\000'; \
1077:         else memmove(s,s+(n),1+thislen3-(n)); }} else/*user supplies final;*/
1078: /* --- strsqueeze(s,t) with two pointers --- */
1079: #define strsqueezep(s,t) if(!isempty((s))&&!isempty((t))) { \
1080:         int sqlen=strlen((s))-strlen((t)); \
1081:         if (sqlen>0 && sqlen<=999) {strsqueeze((s),sqlen);} } else
1082: /* --- skip/find whitespace (from mathtex.c for getdirective()) --- */
1083: #define findwhite(thisstr)  while ( !isempty(thisstr) ) \
1084:         if ( isthischar(*(thisstr),WHITESPACE) ) break; else (thisstr)++
1085:         /* thisstr += strcspn(thisstr,WHITESPACE) */
1086: /* --- skip \command (i.e., find char past last char of \command) --- */
1087: #define skipcommand(thisstr)  while ( !isempty(thisstr) ) \
1088:         if ( !isalpha(*(thisstr)) ) break; else (thisstr)++
1089: 
1090: /* ---
1091:  * PART2
1092:  * ------ */
1093: #if !defined(PARTS) || defined(PART2)
1094: /* ==========================================================================
1095:  * Function:    new_raster ( width, height, pixsz )
1096:  * Purpose:     Allocation and constructor for raster.
1097:  *              mallocs and initializes memory for width*height pixels,
1098:  *              and returns raster struct ptr to caller.
1099:  * --------------------------------------------------------------------------
1100:  * Arguments:   width (I)       int containing width, in bits,
1101:  *                              of raster pixmap to be allocated
1102:  *              height (I)      int containing height, in bits/scans,
1103:  *                              of raster pixmap to be allocated
1104:  *              pixsz (I)       int containing #bits per pixel, 1 or 8
1105:  * --------------------------------------------------------------------------
1106:  * Returns:     ( raster * )    ptr to allocated and initialized
1107:  *                              raster struct, or NULL for any error.
1108:  * --------------------------------------------------------------------------
1109:  * Notes:
1110:  * ======================================================================= */
1111: /* --- entry point --- */
1112: raster  *new_raster ( int width, int height, int pixsz )
1113: {
1114: /* -------------------------------------------------------------------------
1115: Allocations and Declarations
1116: -------------------------------------------------------------------------- */
1117: raster  *rp = (raster *)NULL;           /* raster ptr returned to caller */
1118: pixbyte *pixmap = NULL;                 /* raster pixel map to be malloced */
1119: int     nbytes = pixsz*bitmapsz(width,height); /* #bytes needed for pixmap */
1120: int     filler = (isstring?' ':0);      /* pixmap filler */
1121: int     delete_raster();                /* in case pixmap malloc() fails */
1122: int     npadding = (0&&issupersampling?8+256:0); /* padding bytes */
1123: /* -------------------------------------------------------------------------
1124: allocate and initialize raster struct and embedded bitmap
1125: -------------------------------------------------------------------------- */
1126: if ( msgfp!=NULL && msglevel>=9999 )
1127:   { fprintf(msgfp,"new_raster(%d,%d,%d)> entry point\n",
1128:     width,height,pixsz); fflush(msgfp); }
1129: /* --- allocate and initialize raster struct --- */
1130: rp = (raster *)malloc(sizeof(raster));  /* malloc raster struct */
1131: if ( msgfp!=NULL && msglevel>=9999 )
1132:   { fprintf(msgfp,"new_raster> rp=malloc(%d) returned (%s)\n",
1133:     (int)sizeof(raster),(rp==NULL?"null ptr":"success")); fflush(msgfp); }
1134: if ( rp == (raster *)NULL )             /* malloc failed */
1135:   goto end_of_job;                      /* return error to caller */
1136: rp->width = width;                      /* store width in raster struct */
1137: rp->height = height;                    /* and store height */
1138: rp->format = 1;                         /* initialize as bitmap format */
1139: rp->pixsz = pixsz;                      /* store #bits per pixel */
1140: rp->pixmap = (pixbyte *)NULL;           /* init bitmap as null ptr */
1141: /* --- allocate and initialize bitmap array --- */
1142: if ( msgfp!=NULL && msglevel>=9999 )
1143:   { fprintf(msgfp,"new_raster> calling pixmap=malloc(%d)\n",
1144:     nbytes); fflush(msgfp); }
1145: if ( nbytes>0 && nbytes<=pixsz*maxraster )  /* fail if width*height too big*/
1146:   pixmap = (pixbyte *)malloc(nbytes+npadding); /*bytes for width*height bits*/
1147: if ( msgfp!=NULL && msglevel>=9999 )
1148:   { fprintf(msgfp,"new_raster> pixmap=malloc(%d) returned (%s)\n",
1149:     nbytes,(pixmap==NULL?"null ptr":"success")); fflush(msgfp); }
1150: if ( pixmap == (pixbyte *)NULL )        /* malloc failed */
1151:   { delete_raster(rp);                  /* so free everything */
1152:     rp = (raster *)NULL;                /* reset pointer */
1153:     goto end_of_job; }                  /* and return error to caller */
1154: memset((void *)pixmap,filler,nbytes);   /* init bytes to binary 0's or ' 's*/
1155: *pixmap = (pixbyte)0;                   /* and first byte always 0 */
1156: rp->pixmap = pixmap;                    /* store ptr to malloced memory */
1157: /* -------------------------------------------------------------------------
1158: Back to caller with address of raster struct, or NULL ptr for any error.
1159: -------------------------------------------------------------------------- */
1160: end_of_job:
1161:   if ( msgfp!=NULL && msglevel>=9999 )
1162:     { fprintf(msgfp,"new_raster(%d,%d,%d)> returning (%s)\n",
1163:       width,height,pixsz,(rp==NULL?"null ptr":"success")); fflush(msgfp); }
1164:   return ( rp );                        /* back to caller with raster */
1165: } /* --- end-of-function new_raster() --- */
1166: 
1167: 
1168: /* ==========================================================================
1169:  * Function:    new_subraster ( width, height, pixsz )
1170:  * Purpose:     Allocate a new subraster along with
1171:  *              an embedded raster of width x height.
1172:  * --------------------------------------------------------------------------
1173:  * Arguments:   width (I)       int containing width of embedded raster
1174:  *              height (I)      int containing height of embedded raster
1175:  *              pixsz (I)       int containing #bits per pixel, 1 or 8
1176:  * --------------------------------------------------------------------------
1177:  * Returns:     ( subraster * ) ptr to newly-allocated subraster,
1178:  *                              or NULL for any error.
1179:  * --------------------------------------------------------------------------
1180:  * Notes:     o if width or height <=0, embedded raster not allocated
1181:  * ======================================================================= */
1182: /* --- entry point --- */
1183: subraster *new_subraster ( int width, int height, int pixsz )
1184: {
1185: /* -------------------------------------------------------------------------
1186: Allocations and Declarations
1187: -------------------------------------------------------------------------- */
1188: subraster *sp=NULL;                     /* subraster returned to caller */
1189: raster  *new_raster(), *rp=NULL;        /* image raster embedded in sp */
1190: int     delete_subraster();             /* in case new_raster() fails */
1191: int     size = NORMALSIZE,              /* default size */
1192:         baseline = height-1;            /* and baseline */
1193: /* -------------------------------------------------------------------------
1194: allocate and initialize subraster struct
1195: -------------------------------------------------------------------------- */
1196: if ( msgfp!=NULL && msglevel>=9999 )
1197:   { fprintf(msgfp,"new_subraster(%d,%d,%d)> entry point\n",
1198:     width,height,pixsz); fflush(msgfp); }
1199: /* --- allocate subraster struct --- */
1200: sp = (subraster *)malloc(sizeof(subraster));  /* malloc subraster struct */
1201: if ( sp == (subraster *)NULL )          /* malloc failed */
1202:   goto end_of_job;                      /* return error to caller */
1203: /* --- initialize subraster struct --- */
1204: sp->type = NOVALUE;                     /* character or image raster */
1205: sp->symdef =  (mathchardef *)NULL;      /* mathchardef identifying image */
1206: sp->baseline = baseline;                /*0 if image is entirely descending*/
1207: sp->size = size;                        /* font size 0-4 */
1208: sp->toprow = sp->leftcol = (-1);        /* upper-left corner of subraster */
1209: sp->image = (raster *)NULL;             /*ptr to bitmap image of subraster*/
1210: /* -------------------------------------------------------------------------
1211: allocate raster and embed it in subraster, and return to caller
1212: -------------------------------------------------------------------------- */
1213: /* --- allocate raster struct if desired --- */
1214: if ( width>0 && height>0 && pixsz>0 )   /* caller wants raster */
1215:   { if ( (rp=new_raster(width,height,pixsz)) /* allocate embedded raster */
1216:     !=   NULL )                         /* if allocate succeeded */
1217:         sp->image = rp;                 /* embed raster in subraster */
1218:     else                                /* or if allocate failed */
1219:       { delete_subraster(sp);           /* free non-unneeded subraster */
1220:         sp = NULL; } }                  /* signal error */
1221: /* --- back to caller with new subraster or NULL --- */
1222: end_of_job:
1223:   if ( msgfp!=NULL && msglevel>=9999 )
1224:     { fprintf(msgfp,"new_subraster(%d,%d,%d)> returning (%s)\n",
1225:       width,height,pixsz,(sp==NULL?"null ptr":"success")); fflush(msgfp); }
1226:   return ( sp );
1227: } /* --- end-of-function new_subraster() --- */
1228: 
1229: 
1230: /* ==========================================================================
1231:  * Function:    new_chardef (  )
1232:  * Purpose:     Allocates and initializes a chardef struct,
1233:  *              but _not_ the embedded raster struct.
1234:  * --------------------------------------------------------------------------
1235:  * Arguments:   none
1236:  * --------------------------------------------------------------------------
1237:  * Returns:     ( chardef * )   ptr to allocated and initialized
1238:  *                              chardef struct, or NULL for any error.
1239:  * --------------------------------------------------------------------------
1240:  * Notes:
1241:  * ======================================================================= */
1242: /* --- entry point --- */
1243: chardef *new_chardef (  )
1244: {
1245: /* -------------------------------------------------------------------------
1246: Allocations and Declarations
1247: -------------------------------------------------------------------------- */
1248: chardef *cp = (chardef *)NULL;          /* chardef ptr returned to caller */
1249: /* -------------------------------------------------------------------------
1250: allocate and initialize chardef struct
1251: -------------------------------------------------------------------------- */
1252: cp = (chardef *)malloc(sizeof(chardef)); /* malloc chardef struct */
1253: if ( cp == (chardef *)NULL )            /* malloc failed */
1254:   goto end_of_job;                      /* return error to caller */
1255: cp->charnum = cp->location = 0;         /* init character description */
1256: cp->toprow = cp->topleftcol = 0;        /* init upper-left corner */
1257: cp->botrow = cp->botleftcol = 0;        /* init lower-left corner */
1258: cp->image.width = cp->image.height = 0; /* init raster dimensions */
1259: cp->image.format = 0;                   /* init raster format */
1260: cp->image.pixsz = 0;                    /* and #bits per pixel */
1261: cp->image.pixmap = NULL;                /* init raster pixmap as null */
1262: /* -------------------------------------------------------------------------
1263: Back to caller with address of chardef struct, or NULL ptr for any error.
1264: -------------------------------------------------------------------------- */
1265: end_of_job:
1266:   return ( cp );
1267: } /* --- end-of-function new_chardef() --- */
1268: 
1269: 
1270: /* ==========================================================================
1271:  * Function:    delete_raster ( rp )
1272:  * Purpose:     Destructor for raster.
1273:  *              Frees memory for raster bitmap and struct.
1274:  * --------------------------------------------------------------------------
1275:  * Arguments:   rp (I)          ptr to raster struct to be deleted.
1276:  * --------------------------------------------------------------------------
1277:  * Returns:     ( int )         1 if completed successfully,
1278:  *                              or 0 otherwise (for any error).
1279:  * --------------------------------------------------------------------------
1280:  * Notes:
1281:  * ======================================================================= */
1282: /* --- entry point --- */
1283: int     delete_raster ( raster *rp )
1284: {
1285: /* -------------------------------------------------------------------------
1286: free raster bitmap and struct
1287: -------------------------------------------------------------------------- */
1288: if ( rp != (raster *)NULL )             /* can't free null ptr */
1289:   {
1290:   if ( rp->pixmap != (pixbyte *)NULL )  /* can't free null ptr */
1291:     free((void *)rp->pixmap);           /* free pixmap within raster */
1292:   free((void *)rp);                     /* lastly, free raster struct */
1293:   } /* --- end-of-if(rp!=NULL) --- */
1294: return ( 1 );                           /* back to caller, 1=okay 0=failed */
1295: } /* --- end-of-function delete_raster() --- */
1296: 
1297: 
1298: /* ==========================================================================
1299:  * Function:    delete_subraster ( sp )
1300:  * Purpose:     Deallocates a subraster (and embedded raster)
1301:  * --------------------------------------------------------------------------
1302:  * Arguments:   sp (I)          ptr to subraster struct to be deleted.
1303:  * --------------------------------------------------------------------------
1304:  * Returns:     ( int )         1 if completed successfully,
1305:  *                              or 0 otherwise (for any error).
1306:  * --------------------------------------------------------------------------
1307:  * Notes:
1308:  * ======================================================================= */
1309: /* --- entry point --- */
1310: int     delete_subraster ( subraster *sp )
1311: {
1312: /* -------------------------------------------------------------------------
1313: free subraster struct
1314: -------------------------------------------------------------------------- */
1315: int     delete_raster();                /* to delete embedded raster */
1316: if ( sp != (subraster *)NULL )          /* can't free null ptr */
1317:   {
1318:   if ( sp->type != CHARASTER )          /* not static character data */
1319:     if ( sp->image != NULL )            /*raster allocated within subraster*/
1320:       delete_raster(sp->image);         /* so free embedded raster */
1321:   free((void *)sp);                     /* and free subraster struct itself*/
1322:   } /* --- end-of-if(sp!=NULL) --- */
1323: return ( 1 );                           /* back to caller, 1=okay 0=failed */
1324: } /* --- end-of-function delete_subraster() --- */
1325: 
1326: 
1327: /* ==========================================================================
1328:  * Function:    delete_chardef ( cp )
1329:  * Purpose:     Deallocates a chardef (and bitmap of embedded raster)
1330:  * --------------------------------------------------------------------------
1331:  * Arguments:   cp (I)          ptr to chardef struct to be deleted.
1332:  * --------------------------------------------------------------------------
1333:  * Returns:     ( int )         1 if completed successfully,
1334:  *                              or 0 otherwise (for any error).
1335:  * --------------------------------------------------------------------------
1336:  * Notes:
1337:  * ======================================================================= */
1338: /* --- entry point --- */
1339: int     delete_chardef ( chardef *cp )
1340: {
1341: /* -------------------------------------------------------------------------
1342: free chardef struct
1343: -------------------------------------------------------------------------- */
1344: if ( cp != (chardef *)NULL )            /* can't free null ptr */
1345:   {
1346:   if ( cp->image.pixmap != NULL )       /* pixmap allocated within raster */
1347:     free((void *)cp->image.pixmap);     /* so free embedded pixmap */
1348:   free((void *)cp);                     /* and free chardef struct itself */
1349:   } /* --- end-of-if(cp!=NULL) --- */
1350: /* -------------------------------------------------------------------------
1351: Back to caller with 1=okay, 0=failed.
1352: -------------------------------------------------------------------------- */
1353: return ( 1 );
1354: } /* --- end-of-function delete_chardef() --- */
1355: 
1356: 
1357: /* ==========================================================================
1358:  * Function:    rastcpy ( rp )
1359:  * Purpose:     makes duplicate copy of rp
1360:  * --------------------------------------------------------------------------
1361:  * Arguments:   rp (I)          ptr to raster struct to be copied
1362:  * --------------------------------------------------------------------------
1363:  * Returns:     ( raster * )    ptr to new copy rp,
1364:  *                              or NULL for any error.
1365:  * --------------------------------------------------------------------------
1366:  * Notes:     o
1367:  * ======================================================================= */
1368: /* --- entry point --- */
1369: raster  *rastcpy ( raster *rp )
1370: {
1371: /* -------------------------------------------------------------------------
1372: Allocations and Declarations
1373: -------------------------------------------------------------------------- */
1374: raster  *new_raster(), *newrp=NULL;     /*copied raster returned to caller*/
1375: int     height= (rp==NULL?0:rp->height), /* original and copied height */
1376:         width = (rp==NULL?0:rp->width), /* original and copied width */
1377:         pixsz = (rp==NULL?0:rp->pixsz), /* #bits per pixel */
1378:         nbytes= (rp==NULL?0:(pixmapsz(rp))); /* #bytes in rp's pixmap */
1379: /* -------------------------------------------------------------------------
1380: allocate copied raster and fill it
1381: -------------------------------------------------------------------------- */
1382: /* --- allocate copied raster with same width,height, and copy bitmap --- */
1383: if ( rp != NULL )                       /* nothing to copy if ptr null */
1384:   if ( (newrp = new_raster(width,height,pixsz)) /*same width,height in copy*/
1385:   !=   NULL )                           /* check that allocate succeeded */
1386:     memcpy(newrp->pixmap,rp->pixmap,nbytes); /* fill copied raster pixmap */
1387: return ( newrp );                       /* return copied raster to caller */
1388: } /* --- end-of-function rastcpy() --- */
1389: 
1390: 
1391: /* ==========================================================================
1392:  * Function:    subrastcpy ( sp )
1393:  * Purpose:     makes duplicate copy of sp
1394:  * --------------------------------------------------------------------------
1395:  * Arguments:   sp (I)          ptr to subraster struct to be copied
1396:  * --------------------------------------------------------------------------
1397:  * Returns:     ( subraster * ) ptr to new copy sp,
1398:  *                              or NULL for any error.
1399:  * --------------------------------------------------------------------------
1400:  * Notes:     o
1401:  * ======================================================================= */
1402: /* --- entry point --- */
1403: subraster *subrastcpy ( subraster *sp )
1404: {
1405: /* -------------------------------------------------------------------------
1406: Allocations and Declarations
1407: -------------------------------------------------------------------------- */
1408: subraster *new_subraster(), *newsp=NULL; /* allocate new subraster */
1409: raster  *rastcpy(), *newrp=NULL;        /* and new raster image within it */
1410: int     delete_subraster();             /* dealloc newsp if rastcpy() fails*/
1411: /* -------------------------------------------------------------------------
1412: make copy, and return it to caller
1413: -------------------------------------------------------------------------- */
1414: if ( sp == NULL ) goto end_of_job;      /* nothing to copy */
1415: /* --- allocate new subraster "envelope" for copy --- */
1416: if ( (newsp=new_subraster(0,0,0))       /* allocate subraster "envelope" */
1417: ==   NULL ) goto end_of_job;            /* and quit if we fail to allocate */
1418: /* --- transparently copy original envelope to new one --- */
1419: memcpy((void *)newsp,(void *)sp,sizeof(subraster)); /* copy envelope */
1420: /* --- make a copy of the rasterized image itself, if there is one --- */
1421: if ( sp->image != NULL )                /* there's an image embedded in sp */
1422:   if ( (newrp = rastcpy(sp->image))     /* so copy rasterized image in sp */
1423:   ==   NULL )                           /* failed to copy successfully */
1424:     { delete_subraster(newsp);          /* won't need newsp any more */
1425:       newsp = NULL;                     /* because we're returning error */
1426:       goto end_of_job; }                /* back to caller with error signal*/
1427: /* --- set new params in new envelope --- */
1428: newsp->image = newrp;                   /* new raster image we just copied */
1429: switch ( sp->type )                     /* set new raster image type */
1430:   { case STRINGRASTER: case CHARASTER: newsp->type = STRINGRASTER; break;
1431:     case ASCIISTRING:                  newsp->type = ASCIISTRING;  break;
1432:     case FRACRASTER:                   newsp->type = FRACRASTER;   break;
1433:     case BLANKSIGNAL:                  newsp->type = blanksignal;  break;
1434:     case IMAGERASTER:  default:        newsp->type = IMAGERASTER;  break; }
1435: /* --- return copy of sp to caller --- */
1436: end_of_job:
1437:   return ( newsp );                     /* copy back to caller */
1438: } /* --- end-of-function subrastcpy() --- */
1439: 
1440: 
1441: /* ==========================================================================
1442:  * Function:    rastrot ( rp )
1443:  * Purpose:     rotates rp image 90 degrees right/clockwise
1444:  * --------------------------------------------------------------------------
1445:  * Arguments:   rp (I)          ptr to raster struct to be rotated
1446:  * --------------------------------------------------------------------------
1447:  * Returns:     ( raster * )    ptr to new raster rotated relative to rp,
1448:  *                              or NULL for any error.
1449:  * --------------------------------------------------------------------------
1450:  * Notes:     o An underbrace is } rotated 90 degrees clockwise,
1451:  *              a hat is <, etc.
1452:  * ======================================================================= */
1453: /* --- entry point --- */
1454: raster  *rastrot ( raster *rp )
1455: {
1456: /* -------------------------------------------------------------------------
1457: Allocations and Declarations
1458: -------------------------------------------------------------------------- */
1459: raster  *new_raster(), *rotated=NULL;   /*rotated raster returned to caller*/
1460: int     height = rp->height, irow,      /* original height, row index */
1461:         width = rp->width, icol,        /* original width, column index */
1462:         pixsz = rp->pixsz;              /* #bits per pixel */
1463: /* -------------------------------------------------------------------------
1464: allocate rotated raster and fill it
1465: -------------------------------------------------------------------------- */
1466: /* --- allocate rotated raster with flipped width<-->height --- */
1467: if ( (rotated = new_raster(height,width,pixsz)) /* flip width,height */
1468: !=   NULL )                             /* check that allocation succeeded */
1469:   /* --- fill rotated raster --- */
1470:   for ( irow=0; irow<height; irow++ )   /* for each row of rp */
1471:     for ( icol=0; icol<width; icol++ )  /* and each column of rp */
1472:       { int value = getpixel(rp,irow,icol);
1473:         /* setpixel(rotated,icol,irow,value); } */
1474:         setpixel(rotated,icol,(height-1-irow),value); }
1475: return ( rotated );                     /* return rotated raster to caller */
1476: } /* --- end-of-function rastrot() --- */
1477: 
1478: 
1479: /* ==========================================================================
1480:  * Function:    rastrot3d ( rp, axis, theta )
1481:  * Purpose:     rotates rp around 3d-axis by theta, projecting back onto
1482:  *              x,y-plane
1483:  * --------------------------------------------------------------------------
1484:  * Arguments:   rp (I)          ptr to raster struct with image to be rotated
1485:  *              axis (I)        point3d * specifying rotation axis
1486:  *              theta (I)       double specifying rotation in degrees
1487:  * --------------------------------------------------------------------------
1488:  * Returns:     ( raster * )    ptr to new raster struct with rotated image,
1489:  *                              or NULL for any error.
1490:  * --------------------------------------------------------------------------
1491:  * Notes:     o
1492:  * ======================================================================= */
1493: /* --- entry point --- */
1494: raster  *rastrot3d ( raster *rp, point3d *axis, double theta )
1495: {
1496: /* -------------------------------------------------------------------------
1497: Allocations and Declarations
1498: -------------------------------------------------------------------------- */
1499: raster  *new_raster(), *rotp = NULL;    /* rotated raster back to caller */
1500: int     width = rp->width,  icol = 0,   /* raster width,  column index */
1501:         height= rp->height, irow = 0,   /* raster height, row index */
1502:         pixsz = rp->pixsz;              /* #bits per pixel */
1503: double  midcol = ((double)(width))/2.0, /* x-origin index coord */
1504:         midrow = ((double)(height))/2.0; /* y-origin index coord */
1505: int     bgpixel = 0;                    /* background pixel value */
1506: matrix3d *rotmatrix(), *rotmat=NULL;    /* rotation matrix */
1507: point3d *matmult();                     /* matrix multiplication */
1508: /* -------------------------------------------------------------------------
1509: Initialization
1510: -------------------------------------------------------------------------- */
1511: /* --- check input --- */
1512: if ( rp == NULL ) goto end_of_job;      /* error if no input raster supplied */
1513: /* --- get rotation matrix --- */
1514: if ( (rotmat = rotmatrix(axis,theta))   /*rotate around axis by theta degrees*/
1515: ==   NULL ) goto end_of_job;            /*some problem, likely 0-length axis*/
1516: /* --- allocate rotated raster (with same width,height) --- */
1517: if ( (rotp = new_raster(width,height,pixsz)) /* (same width,height) */
1518: ==   NULL ) goto end_of_job;            /* quit if allocation failed */
1519: /* -------------------------------------------------------------------------
1520: rotate pixel-by-pixel
1521: -------------------------------------------------------------------------- */
1522: for ( irow=0; irow<height; irow++ ) {
1523:   double y = ((double)irow) - midrow;   /* y-coord relative to grid center */
1524:   for ( icol=0; icol<width; icol++ ) {
1525:     int pixval = getpixel(rp,irow,icol); /* pixel value */
1526:     double x = ((double)icol) - midcol; /* x-coord relative to grid center */
1527:     point3d u={x,y,0.}, *urot=NULL;     /* original, rotated coords */
1528:     /* ---
1529:      * rotate pixel
1530:      * --------------- */
1531:     if ( pixval != bgpixel )            /* if not a background pixel */
1532:       if ( (urot = matmult(rotmat,&u)) /* apply rotation matrix */
1533:       !=   NULL ) {                     /* succeeded */
1534:         double dy=(+0.00), dx=(-0.25);  /*ad hoc rounding determined by tests*/
1535:         int jrow = (int)(urot->y + midrow + dy); /* rotated row */
1536:         int jcol = (int)(urot->x + midcol + dx); /* rotated col */
1537:         if ( jrow>=0 && jrow<height     /* check rotated pixel coords */
1538:         &&   jcol>=0 && jcol<width ) {  /* rotated pixel in bounds */
1539:           setpixel(rotp,jrow,jcol,pixval); } /* rotated pixel in place */
1540:         } /* --- end-of-if(urot!=NULL) --- */
1541:     } /* --- end-of-for(icol) --- */
1542:   } /* --- end-of-for(irow) --- */
1543: /* ---
1544:  * end-of-job
1545:  * ------------- */
1546: end_of_job:
1547:   return ( rotp );                      /* return rotated raster to caller */
1548: } /* --- end-of-function rastrot3d() --- */
1549: 
1550: 
1551: /* ==========================================================================
1552:  * Function:    rastmag ( rp, magstep )
1553:  * Purpose:     magnifies rp by integer magstep,
1554:  *              e.g., double-height and double-width if magstep=2
1555:  * --------------------------------------------------------------------------
1556:  * Arguments:   rp (I)          ptr to raster struct to be "magnified"
1557:  *              magstep (I)     int containing magnification scale,
1558:  *                              e.g., 2 to double the width and height of rp
1559:  * --------------------------------------------------------------------------
1560:  * Returns:     ( raster * )    ptr to new raster magnified relative to rp,
1561:  *                              or NULL for any error.
1562:  * --------------------------------------------------------------------------
1563:  * Notes:     o
1564:  * ======================================================================= */
1565: /* --- entry point --- */
1566: raster  *rastmag ( raster *rp, int magstep )
1567: {
1568: /* -------------------------------------------------------------------------
1569: Allocations and Declarations
1570: -------------------------------------------------------------------------- */
1571: raster  *new_raster(), *magnified=NULL; /* magnified raster back to caller */
1572: int     height = rp->height, irow,      /* height, row index */
1573:         width = rp->width, icol,        /* width, column index */
1574:         mrow = 0, mcol = 0,             /* dup pixels magstep*magstep times*/
1575:         pixsz = rp->pixsz;              /* #bits per pixel */
1576: /* -------------------------------------------------------------------------
1577: check args
1578: -------------------------------------------------------------------------- */
1579: if ( rp == NULL ) goto end_of_job;      /* no input raster supplied */
1580: if ( magstep<1 || magstep>10 ) goto end_of_job; /* sanity check */
1581: /* -------------------------------------------------------------------------
1582: allocate magnified raster and fill it
1583: -------------------------------------------------------------------------- */
1584: /* --- allocate magnified raster with magstep*width, magstep*height --- */
1585: if ( (magnified = new_raster(magstep*width,magstep*height,pixsz))/*allocate*/
1586: !=   NULL )                             /* check that allocation succeeded */
1587:   /* --- fill reflected raster --- */
1588:   for ( irow=0; irow<height; irow++ )   /* for each row of rp */
1589:     for ( mrow=0; mrow<magstep; mrow++ ) /* dup row magstep times */
1590:       for ( icol=0; icol<width; icol++ ) /* and for each column of rp */
1591:         for ( mcol=0; mcol<magstep; mcol++ ) { /* dup col magstep times */
1592:          int value = getpixel(rp,irow,icol);
1593:          int row1 = irow*magstep, col1 = icol*magstep;
1594:          setpixel(magnified,(row1+mrow),(col1+mcol),value); }
1595: end_of_job:
1596:   return ( magnified );                 /*return magnified raster to caller*/
1597: } /* --- end-of-function rastmag() --- */
1598: 
1599: 
1600: /* ==========================================================================
1601:  * Function:    bytemapmag ( bytemap, width, height, magstep )
1602:  * Purpose:     magnifies a bytemap by integer magstep,
1603:  *              e.g., double-height and double-width if magstep=2
1604:  * --------------------------------------------------------------------------
1605:  * Arguments:   bytemap (I)     intbyte * ptr to byte map to be "magnified"
1606:  *              width (I)       int containing #cols in original bytemap
1607:  *              height (I)      int containing #rows in original bytemap
1608:  *              magstep (I)     int containing magnification scale,
1609:  *                              e.g., 2 to double the width and height of rp
1610:  * --------------------------------------------------------------------------
1611:  * Returns:     ( intbyte * )   ptr to new bytemap magnified relative to
1612:  *                              original bytemap, or NULL for any error.
1613:  * --------------------------------------------------------------------------
1614:  * Notes:     o Apply EPX/Scale2x/AdvMAME2x  for magstep 2,
1615:  *              and Scale3x/AdvMAME3x  for magstep 3,
1616:  *              as described by http://en.wikipedia.org/wiki/2xSaI
1617:  * ======================================================================= */
1618: /* --- entry point --- */
1619: intbyte *bytemapmag ( intbyte *bytemap, int width, int height, int magstep )
1620: {
1621: /* -------------------------------------------------------------------------
1622: Allocations and Declarations
1623: -------------------------------------------------------------------------- */
1624: intbyte *magnified=NULL;                /* magnified bytemap back to caller*/
1625: int     irow, icol,                     /* original height, width indexes */
1626:         mrow=0, mcol=0;                 /* dup bytes magstep*magstep times */
1627: int     imap = (-1),                    /* original bytemap[] index */
1628:         byteval = 0;                    /* byteval=bytemap[imap] */
1629: int     isAdvMAME = 1;                  /* true to apply AdvMAME2x and 3x */
1630: int     icell[10],                      /* bytemap[] nearest neighbors */
1631:         bmmdiff = 64;                   /* nearest neighbor diff allowed */
1632: #define bmmeq(i,j) ((absval((icell[i]-icell[j]))<=bmmdiff)) /*approx equal*/
1633: /* -------------------------------------------------------------------------
1634: check args
1635: -------------------------------------------------------------------------- */
1636: if ( bytemap == NULL ) goto end_of_job; /* no input bytemap supplied */
1637: if ( width<1 || height<1 ) goto end_of_job; /* invalid bytemap dimensions */
1638: if ( width*height>100000 ) goto end_of_job; /* sanity check */
1639: if ( magstep<1 || magstep>10 ) goto end_of_job; /* sanity check */
1640: /* -------------------------------------------------------------------------
1641: allocate magnified bytemap and fill it
1642: -------------------------------------------------------------------------- */
1643: /* --- allocate bytemap for magstep*width, magstep*height --- */
1644: if ( (magnified = (intbyte *)(malloc(magstep*width*magstep*height)))/*alloc*/
1645: !=   NULL )                             /* check that allocation succeeded */
1646:   /* --- fill reflected raster --- */
1647:   for ( irow=0; irow<height; irow++ )   /* for each row of bytemap */
1648:    for ( icol=0; icol<width; icol++ ) { /* and for each column of bytemap */
1649:     int imag1 = (icol + irow*(width*magstep))*magstep; /*upper-left corner*/
1650:     imap++;                             /* bump bytemap[] index */
1651:     byteval = (int)(bytemap[imap]);     /* grayscale value at this pixel */
1652:     for ( mrow=0; mrow<magstep; mrow++ ) /* dup row magstep times */
1653:      for ( mcol=0; mcol<magstep; mcol++ ) { /* dup col magstep times */
1654:       int idup = mcol + mrow*(width*magstep); /* offset from imag1 */
1655:       int imag = imag1+idup;            /* adjust magnified[imag] */
1656:       magnified[imag] = (intbyte)(byteval);
1657:       /* --- apply AdvMAME2x and 3x (if desired) --- */
1658:       if ( isAdvMAME ) {                /* AdvMAME2x and 3x wanted */
1659:        int mcell = 1 + mcol + magstep*mrow; /*1,2,3,4 or 1,2,3,4,5,6,7,8,9*/
1660:        icell[5]= byteval,               /* center cell of 3x3 bytemap[] */
1661:        icell[4]= (icol>0?(int)(bytemap[imap-1]):byteval), /*left of center*/
1662:        icell[6]= (icol<width?(int)(bytemap[imap+1]):byteval), /*right*/
1663:        icell[2]= (irow>0?(int)(bytemap[imap-width]):byteval),/*above center*/
1664:        icell[8]= (irow<height?(int)(bytemap[imap+width]):byteval), /*below*/
1665:        icell[1]= (irow>0&&icol>0?(int)(bytemap[imap-width-1]):byteval),
1666:        icell[3]= (irow>0&&icol<width?(int)(bytemap[imap-width+1]):byteval),
1667:        icell[7]= (irow<height&&icol>0?(int)(bytemap[imap+width-1]):byteval),
1668:       icell[9]=(irow<height&&icol<width?(int)(bytemap[imap+width+1]):byteval);
1669:        switch ( magstep ) {             /* 2x magstep=2, 3x magstep=3 */
1670:         default: break;                 /* no AdvMAME at other magsteps */
1671:         case 2:                         /* AdvMAME2x */
1672:          if ( mcell == 1 )
1673:            if ( bmmeq(4,2) && !bmmeq(4,8) && !bmmeq(2,6) )
1674:              magnified[imag] = icell[2];
1675:          if ( mcell == 2 )
1676:            if ( bmmeq(2,6) && !bmmeq(2,4) && !bmmeq(6,8) )
1677:              magnified[imag] = icell[6];
1678:          if ( mcell == 4 )
1679:            if ( bmmeq(6,8) && !bmmeq(6,2) && !bmmeq(8,4) )
1680:              magnified[imag] = icell[8];
1681:          if ( mcell == 3 )
1682:            if ( bmmeq(8,4) && !bmmeq(8,6) && !bmmeq(4,2) )
1683:              magnified[imag] = icell[4];
1684:          break;
1685:         case 3:                         /* AdvMAME3x */
1686:          if ( mcell == 1 )
1687:            if ( bmmeq(4,2) && !bmmeq(4,8) && !bmmeq(2,6) )
1688:              magnified[imag] = icell[4];
1689:          if ( mcell == 2 )
1690:            if ( (bmmeq(4,2) && !bmmeq(4,8) && !bmmeq(2,6) && !bmmeq(5,3))
1691:              || (bmmeq(2,6) && !bmmeq(2,4) && !bmmeq(6,8) && !bmmeq(5,1)) )
1692:              magnified[imag] = icell[2];
1693:          if ( mcell == 3 )
1694:            if ( bmmeq(2,6) && !bmmeq(2,4) && !bmmeq(6,8) )
1695:              magnified[imag] = icell[6];
1696:          if ( mcell == 4 )
1697:            if ( (bmmeq(8,4) && !bmmeq(8,6) && !bmmeq(4,2) && !bmmeq(5,1))
1698:              || (bmmeq(4,2) && !bmmeq(4,8) && !bmmeq(2,6) && !bmmeq(5,7)) )
1699:              magnified[imag] = icell[4];
1700:          if ( mcell == 6 )
1701:            if ( (bmmeq(2,6) && !bmmeq(2,4) && !bmmeq(6,8) && !bmmeq(5,9))
1702:              || (bmmeq(6,8) && !bmmeq(6,2) && !bmmeq(8,4) && !bmmeq(5,3)) )
1703:              magnified[imag] = icell[6];
1704:          if ( mcell == 7 )
1705:            if ( bmmeq(8,4) && !bmmeq(8,6) && !bmmeq(4,2) )
1706:              magnified[imag] = icell[4];
1707:          if ( mcell == 8 )
1708:            if ( (bmmeq(6,8) && !bmmeq(6,2) && !bmmeq(8,4) && !bmmeq(5,7))
1709:              || (bmmeq(8,4) && !bmmeq(8,6) && !bmmeq(4,2) && !bmmeq(5,9)) )
1710:              magnified[imag] = icell[8];
1711:          if ( mcell == 9 )
1712:            if ( bmmeq(6,8) && !bmmeq(6,2) && !bmmeq(8,4) )
1713:              magnified[imag] = icell[6];
1714:          break;
1715:         } } /* --- end-of-switch(magstep) --- */
1716:       } /* --- end-of-for(mrow,mcol) --- */
1717:     } /* --- end-of-for(irow,icol) --- */
1718: end_of_job:
1719:   return ( magnified );                 /*return magnified raster to caller*/
1720: } /* --- end-of-function bytemapmag() --- */
1721: 
1722: 
1723: /* ==========================================================================
1724:  * Function:    rastref ( rp, axis )
1725:  * Purpose:     reflects rp, horizontally about y-axis |_ becomes _| if axis=1
1726:  *              or vertically about x-axis M becomes W if axis=2.
1727:  * --------------------------------------------------------------------------
1728:  * Arguments:   rp (I)          ptr to raster struct to be reflected
1729:  *              axis (I)        int containing 1 for horizontal reflection,
1730:  *                              or 2 for vertical
1731:  * --------------------------------------------------------------------------
1732:  * Returns:     ( raster * )    ptr to new raster reflected relative to rp,
1733:  *                              or NULL for any error.
1734:  * --------------------------------------------------------------------------
1735:  * Notes:     o
1736:  * ======================================================================= */
1737: /* --- entry point --- */
1738: raster  *rastref ( raster *rp, int axis )
1739: {
1740: /* -------------------------------------------------------------------------
1741: Allocations and Declarations
1742: -------------------------------------------------------------------------- */
1743: raster  *new_raster(), *reflected=NULL; /* reflected raster back to caller */
1744: int     height = rp->height, irow,      /* height, row index */
1745:         width = rp->width, icol,        /* width, column index */
1746:         pixsz = rp->pixsz;              /* #bits per pixel */
1747: /* -------------------------------------------------------------------------
1748: allocate reflected raster and fill it
1749: -------------------------------------------------------------------------- */
1750: /* --- allocate reflected raster with same width, height --- */
1751: if ( axis==1 || axis==2 )               /* first validate axis arg */
1752:  if ( (reflected = new_raster(width,height,pixsz)) /* same width, height */
1753:  !=   NULL )                            /* check that allocation succeeded */
1754:   /* --- fill reflected raster --- */
1755:   for ( irow=0; irow<height; irow++ )   /* for each row of rp */
1756:     for ( icol=0; icol<width; icol++ ) { /* and each column of rp */
1757:       int value = getpixel(rp,irow,icol);
1758:       if ( axis == 1 ) { setpixel(reflected,irow,width-1-icol,value); }
1759:       if ( axis == 2 ) { setpixel(reflected,height-1-irow,icol,value); } }
1760: return ( reflected );                   /*return reflected raster to caller*/
1761: } /* --- end-of-function rastref() --- */
1762: 
1763: 
1764: /* ==========================================================================
1765:  * Function:    rastput ( target, source, top, left, isopaque )
1766:  * Purpose:     Overlays source onto target,
1767:  *              with the 0,0-bit of source onto the top,left-bit of target.
1768:  * --------------------------------------------------------------------------
1769:  * Arguments:   target (I)      ptr to target raster struct
1770:  *              source (I)      ptr to source raster struct
1771:  *              top (I)         int containing 0 ... target->height - 1
1772:  *              left (I)        int containing 0 ... target->width - 1
1773:  *              isopaque (I)    int containing false (zero) to allow
1774:  *                              original 1-bits of target to "show through"
1775:  *                              0-bits of source.
1776:  * --------------------------------------------------------------------------
1777:  * Returns:     ( int )         1 if completed successfully,
1778:  *                              or 0 otherwise (for any error).
1779:  * --------------------------------------------------------------------------
1780:  * Notes:
1781:  * ======================================================================= */
1782: /* --- entry point --- */
1783: int     rastput ( raster *target, raster *source,
1784:                 int top, int left, int isopaque )
1785: {
1786: /* -------------------------------------------------------------------------
1787: Allocations and Declarations
1788: -------------------------------------------------------------------------- */
1789: int     irow, icol,             /* indexes over source raster */
1790:         twidth=target->width, theight=target->height, /*target width,height*/
1791:         tpix, ntpix = twidth*theight; /* #pixels in target */
1792: int     isfatal = 0,            /* true to abend on out-of-bounds error */
1793:         isstrict = 0/*1*/,      /* true for strict bounds check - no "wrap"*/
1794:         isokay = 1;             /* true if no pixels out-of-bounds */
1795: /* -------------------------------------------------------------------------
1796: superimpose source onto target, one bit at a time
1797: -------------------------------------------------------------------------- */
1798: if ( isstrict && (top<0 || left<0) )            /* args fail strict test */
1799:  isokay = 0;                                    /* so just return error */
1800: else
1801:  for ( irow=0; irow<source->height; irow++ )    /* for each scan line */
1802:   {
1803:   tpix = (top+irow)*target->width + left - 1;   /*first target pixel (-1)*/
1804:   for ( icol=0; icol<source->width; icol++ )    /* each pixel in scan line */
1805:     {
1806:     int svalue = getpixel(source,irow,icol);    /* source pixel value */
1807:     ++tpix;                                     /* bump target pixel */
1808:     if ( msgfp!=NULL && msglevel>=9999 )        /* debugging output */
1809:       { fprintf(msgfp,"rastput> tpix,ntpix=%d,%d top,irow,theight=%d,%d,%d "
1810:         "left,icol,twidth=%d,%d,%d\n", tpix,ntpix, top,irow,theight,
1811:         left,icol,twidth);  fflush(msgfp); }
1812:     if ( tpix >= ntpix                          /* bounds check failed */
1813:     ||   (isstrict && (irow+top>=theight || icol+left>=twidth)) )
1814:       { isokay = 0;                             /* reset okay flag */
1815:         if ( isfatal ) goto end_of_job;         /* abort if error is fatal */
1816:         else break; }                           /*or just go on to next row*/
1817:     if ( tpix >= 0 )                            /* bounds check okay */
1818:      if ( svalue!=0 || isopaque ) {             /*got dark or opaque source*/
1819:       setpixel(target,irow+top,icol+left,svalue); }/*overlay source on targ*/
1820:     } /* --- end-of-for(icol) --- */
1821:   } /* --- end-of-for(irow) --- */
1822: /* -------------------------------------------------------------------------
1823: Back to caller with 1=okay, 0=failed.
1824: -------------------------------------------------------------------------- */
1825: end_of_job:
1826:   return ( isokay /*isfatal? (tpix<ntpix? 1:0) : 1*/ );
1827: } /* --- end-of-function rastput() --- */
1828: 
1829: 
1830: /* ==========================================================================
1831:  * Function:    rastcompose ( sp1, sp2, offset2, isalign, isfree )
1832:  * Purpose:     Overlays sp2 on top of sp1, leaving both unchanged
1833:  *              and returning a newly-allocated composite subraster.
1834:  *              Frees/deletes input sp1 and/or sp2 depending on value
1835:  *              of isfree (0=none, 1=sp1, 2=sp2, 3=both).
1836:  * --------------------------------------------------------------------------
1837:  * Arguments:   sp1 (I)         subraster *  to "underneath" subraster,
1838:  *                              whose baseline is preserved
1839:  *              sp2 (I)         subraster *  to "overlaid" subraster
1840:  *              offset2 (I)     int containing 0 or number of pixels
1841:  *                              to horizontally shift sp2 relative to sp1,
1842:  *                              either positive (right) or negative
1843:  *              isalign (I)     int containing 1 to align baselines,
1844:  *                              or 0 to vertically center sp2 over sp1.
1845:  *                              For isalign=2, images are vertically
1846:  *                              centered, but then adjusted by \raisebox
1847:  *                              lifts, using global variables rastlift1
1848:  *                              for sp1 and rastlift for sp2.
1849:  *              isfree (I)      int containing 1=free sp1 before return,
1850:  *                              2=free sp2, 3=free both, 0=free none.
1851:  * --------------------------------------------------------------------------
1852:  * Returns:     ( subraster * ) pointer to constructed subraster
1853:  *                              or  NULL for any error
1854:  * --------------------------------------------------------------------------
1855:  * Notes:     o The top-left corner of each raster box has coords (0,0),
1856:  *              down to (h-1,w-1) for a box of height h and width w.
1857:  *            o A raster's baseline, b, is typically 0 <= b < h.
1858:  *              But b can actually go out-of-bounds, b>=h or b<0, for
1859:  *              an image additionally lifted (b>=h) or lowered (b<0)
1860:  *              with respect to the surrounding expression.
1861:  *            o Note that b=h-1 means no descenders and the bottom
1862:  *              of the symbol rests exactly on the baseline,
1863:  *              whereas b=0 means the top pixel of the symbol rests
1864:  *              on the baseline, and all other pixels are descenders.
1865:  *            o The composite raster is constructed as follows...
1866:  *              The base image is labelled height h1 and baseline b1,
1867:  *              the overlay h2 and b2, and the composite H and B.
1868:  *                   base       overlay
1869:  *          --- +------------------------+ ---   For the overlay to be
1870:  *           ^  |   ^        +----------+|  ^    vertically centered with
1871:  *           |  |   |        |          ||  |    respect to the base,
1872:  *           |  |   |B-b1    |          ||  |      B - b1 = H-B -(h1-b1), so
1873:  *           |  |   v        |          ||  |      2*B = H-h1 + 2*b1
1874:  *           |  |+----------+|          ||  |      B = b1 + (H-h1)/2
1875:  *           B  ||  ^    ^  ||          ||  |    And when the base image is
1876:  *           |  ||  |    |  ||          ||  |    bigger, H=h1 and B=b1 is
1877:  *           |  ||  b1   |  ||          ||  |    the obvious correct answer.
1878:  *           |  ||  |    h1 ||          || H=h2
1879:  *           v  ||  v    |  ||          ||  |
1880:  *    ----------||-------|--||          ||--|--------
1881:  *    baseline  || h1-b1 v  || overlay  ||  |
1882:  *    for base  |+----------+| baseline ||  |
1883:  *    and com-  |   ^        | ignored  ||  |
1884:  *    posite    |   |H-B-    |----------||  |
1885:  *              |   | (h1-b1)|          ||  |
1886:  *              |   v        +----------+|  v
1887:  *              +------------------------+ ---
1888:  * ======================================================================= */
1889: /* --- entry point --- */
1890: subraster *rastcompose ( subraster *sp1, subraster *sp2, int offset2,
1891:                         int isalign, int isfree )
1892: {
1893: /* -------------------------------------------------------------------------
1894: Allocations and Declarations
1895: -------------------------------------------------------------------------- */
1896: subraster *new_subraster(), *sp=(subraster *)NULL; /* returned subraster */
1897: raster  *rp=(raster *)NULL;             /* new composite raster in sp */
1898: int     delete_subraster();             /* in case isfree non-zero */
1899: int     rastput();                      /*place sp1,sp2 in composite raster*/
1900: int     base1   = sp1->baseline,        /*baseline for underlying subraster*/
1901:         height1 = (sp1->image)->height, /* height for underlying subraster */
1902:         width1  = (sp1->image)->width,  /* width for underlying subraster */
1903:         pixsz1  = (sp1->image)->pixsz,  /* pixsz for underlying subraster */
1904:         base2   = sp2->baseline,        /*baseline for overlaid subraster */
1905:         height2 = (sp2->image)->height, /* height for overlaid subraster */
1906:         width2  = (sp2->image)->width,  /* width for overlaid subraster */
1907:         pixsz2  = (sp2->image)->pixsz;  /* pixsz for overlaid subraster */
1908: int     height  = max2(height1,height2), /*composite height if sp2 centered*/
1909:         base    = base1 + (height-height1)/2, /* and composite baseline */
1910:         tlc2    = (height-height2)/2,   /* top-left corner for overlay */
1911:         width=0, pixsz=0;               /* other params for composite */
1912: int     lift1   = rastlift1,            /* vertical \raisebox lift for sp1 */
1913:         lift2   = rastlift;             /* vertical \raisebox lift for sp2 */
1914: /* -------------------------------------------------------------------------
1915: Initialization
1916: -------------------------------------------------------------------------- */
1917: /* --- determine height, width and baseline of composite raster --- */
1918: switch ( isalign ) {
1919:   default:
1920:   case 0:                               /* centered, baselines not aligned */
1921:     height = max2(height1,height2);     /* max height */
1922:     base   = base1 + (height-height1)/2; /* baseline for sp1 */
1923:     break;
1924:   case 1:                               /* baselines of sp1,sp2 aligned */
1925:     height = max2(base1+1,base2+1)      /* max height above baseline */
1926:            + max2(height1-base1-1,height2-base2-1); /*+max descending below*/
1927:     base   = max2(base1,base2);         /* max space above baseline */
1928:     break;
1929:   case 2:                               /* centered +/- \raisebox lifts */
1930:     base1 -= lift1;  base2 -= lift2;    /* reset to unlifted images */
1931:     /* --- start with default for centered, unlifted images --- */
1932:     height2 += 2*absval(lift2);         /* "virtual" height of overlay */
1933:     height = max2(height1,height2);     /* max height */
1934:     base   = base1 + (height-height1)/2; /* baseline for sp1 */
1935:     tlc2   = (height-height2)/2         /* top-left corner for overlay */
1936:            + (lift2>=0?0:2*absval(lift2)); /* "reflect" overlay below base */
1937:     break;
1938:   } /* --- end-of-switch(isalign) --- */
1939: width = max2(width1,width2+abs(offset2)); /* max width */
1940: pixsz = max2(pixsz1,pixsz2);            /* bitmap,bytemap becomes bytemap */
1941: /* -------------------------------------------------------------------------
1942: allocate concatted composite subraster
1943: -------------------------------------------------------------------------- */
1944: /* --- allocate returned subraster (and then initialize it) --- */
1945: if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
1946: ==   (subraster *)NULL ) goto end_of_job; /* failed, so quit */
1947: /* --- initialize subraster parameters --- */
1948: sp->type = IMAGERASTER;                 /* image */
1949: sp->baseline = base;                    /* composite baseline */
1950: sp->size = sp1->size;                   /* underlying char is sp1 */
1951: if ( isalign == 2 ) sp->baseline += lift1; /* adjust baseline */
1952: /* --- extract raster from subraster --- */
1953: rp = sp->image;                         /* raster allocated in subraster */
1954: /* -------------------------------------------------------------------------
1955: overlay sp1 and sp2 in new composite raster
1956: -------------------------------------------------------------------------- */
1957: switch ( isalign ) {
1958:   default:
1959:   case 0:                               /* centered, baselines not aligned */
1960:     rastput (rp, sp1->image, base-base1, (width-width1)/2, 1);  /*underlying*/
1961:     rastput (rp, sp2->image, (height-height2)/2,                /*overlaid*/
1962:                 (width-width2)/2+offset2, 0);
1963:     break;
1964:   case 1:                               /* baselines of sp1,sp2 aligned */
1965:     rastput (rp, sp1->image, base-base1, (width-width1)/2, 1);  /*underlying*/
1966:     rastput (rp, sp2->image, base-base2,                        /*overlaid*/
1967:                 (width-width2)/2+offset2, 0);
1968:     break;
1969:   case 2: if(1){                        /* centered +/- \raisebox lifts */
1970:     rastput (rp, sp1->image, base-base1, (width-width1)/2, 1);
1971:     rastput (rp, sp2->image, tlc2, (width-width2)/2+offset2, 0); }
1972:     break;
1973:   } /* --- end-of-switch(isalign) --- */
1974: /* -------------------------------------------------------------------------
1975: free input if requested
1976: -------------------------------------------------------------------------- */
1977: if ( isfree > 0 )                       /* caller wants input freed */
1978:   { if ( isfree==1 || isfree>2 ) delete_subraster(sp1); /* free sp1 */
1979:     if ( isfree >= 2 ) delete_subraster(sp2); }         /* and/or sp2 */
1980: /* -------------------------------------------------------------------------
1981: Back to caller with pointer to concatted subraster or with null for error
1982: -------------------------------------------------------------------------- */
1983: end_of_job:
1984:   return ( sp );                        /* back with subraster or null ptr */
1985: } /* --- end-of-function rastcompose() --- */
1986: 
1987: 
1988: /* ==========================================================================
1989:  * Function:    rastcat ( sp1, sp2, isfree )
1990:  * Purpose:     "Concatanates" subrasters sp1||sp2, leaving both unchanged
1991:  *              and returning a newly-allocated subraster.
1992:  *              Frees/deletes input sp1 and/or sp2 depending on value
1993:  *              of isfree (0=none, 1=sp1, 2=sp2, 3=both).
1994:  * --------------------------------------------------------------------------
1995:  * Arguments:   sp1 (I)         subraster *  to left-hand subraster
1996:  *              sp2 (I)         subraster *  to right-hand subraster
1997:  *              isfree (I)      int containing 1=free sp1 before return,
1998:  *                              2=free sp2, 3=free both, 0=free none.
1999:  * --------------------------------------------------------------------------
2000:  * Returns:     ( subraster * ) pointer to constructed subraster sp1||sp2
2001:  *                              or  NULL for any error
2002:  * --------------------------------------------------------------------------
2003:  * Notes:
2004:  * ======================================================================= */
2005: /* --- entry point --- */
2006: subraster *rastcat ( subraster *sp1, subraster *sp2, int isfree )
2007: {
2008: /* -------------------------------------------------------------------------
2009: Allocations and Declarations
2010: -------------------------------------------------------------------------- */
2011: subraster *new_subraster(), *sp=(subraster *)NULL; /* returned subraster */
2012: raster  *rp=(raster *)NULL;             /* new concatted raster */
2013: int     delete_subraster();             /* in case isfree non-zero */
2014: int     rastput();                      /*place sp1,sp2 in concatted raster*/
2015: int     type_raster();                  /* debugging display */
2016: int     base1   = sp1->baseline,        /*baseline for left-hand subraster*/
2017:         height1 = (sp1->image)->height, /* height for left-hand subraster */
2018:         width1  = (sp1->image)->width,  /* width for left-hand subraster */
2019:         pixsz1  = (sp1->image)->pixsz,  /* pixsz for left-hand subraster */
2020:         type1   = sp1->type,            /* image type for left-hand */
2021:         base2   = sp2->baseline,        /*baseline for right-hand subraster*/
2022:         height2 = (sp2->image)->height, /* height for right-hand subraster */
2023:         width2  = (sp2->image)->width,  /* width for right-hand subraster */
2024:         pixsz2  = (sp2->image)->pixsz,  /* pixsz for right-hand subraster */
2025:         type2   = sp2->type;            /* image type for right-hand */
2026: int     height=0, width=0, pixsz=0, base=0; /*concatted sp1||sp2 composite*/
2027: int     issmash = (smashmargin!=0?1:0), /* true to "squash" sp1||sp2 */
2028:         isopaque = (issmash?0:1),       /* not oppaque if smashing */
2029:         rastsmash(), isblank=0, nsmash=0, /* #cols to smash */
2030:         oldsmashmargin = smashmargin,   /* save original smashmargin */
2031:         oldblanksymspace = blanksymspace, /* save original blanksymspace */
2032:         oldnocatspace = isnocatspace;   /* save original isnocatspace */
2033: mathchardef *symdef1 = sp1->symdef,     /*mathchardef of last left-hand char*/
2034:         *symdef2 = sp2->symdef;         /* mathchardef of right-hand char */
2035: int     class1 = (symdef1==NULL?ORDINARY:symdef1->class), /* symdef->class */
2036:         class2 = (symdef2==NULL?ORDINARY:symdef2->class), /* or default */
2037:         smash1 = (symdef1!=NULL)&&(class1==ORDINARY||class1==VARIABLE||
2038:                   class1==OPENING||class1==CLOSING||class1==PUNCTION),
2039:         smash2 = (symdef2!=NULL)&&(class2==ORDINARY||class2==VARIABLE||
2040:                   class2==OPENING||class2==CLOSING||class2==PUNCTION),
2041:         space = fontsize/2+1;           /* #cols between sp1 and sp2 */
2042: int     isfrac = (type1 == FRACRASTER   /* sp1 is a \frac */
2043:                   && class2 == PUNCTION); /* and sp2 is punctuation */
2044: /* -------------------------------------------------------------------------
2045: Initialization
2046: -------------------------------------------------------------------------- */
2047: /* --- determine inter-character space from character class --- */
2048: if ( !isstring )
2049:   space = max2(2,(symspace[class1][class2] + fontsize-3)); /* space */
2050: else space = 1;                         /* space for ascii string */
2051: if ( isnocatspace > 0 ) {               /* spacing explicitly turned off */
2052:   space = 0;                            /* reset space */
2053:   isnocatspace--; }                     /* and decrement isnocatspace flag */
2054: if ( 0 && sp1->type == BLANKSIGNAL ) space=0; /*implicitly turn off spacing*/
2055: if ( sp1->type==BLANKSIGNAL && sp2->type==BLANKSIGNAL ) /* both blank */
2056:   space = 0;                            /* no extra space between spaces */
2057: if ( sp2->type != BLANKSIGNAL )         /* not a blank space signal */
2058:   if ( blanksymspace != 0 ) {           /* and we have a space adjustment */
2059:     space = max2(0,space+blanksymspace); /* adjust as much as possible */
2060:     blanksymspace = 0; }                /* and reset adjustment */
2061: if ( msgfp!=NULL && msglevel>=999 )     /* display space results */
2062:   { fprintf(msgfp,"rastcat> space=%d, blanksymspace=%d, isnocatspace=%d\n",
2063:     space,oldblanksymspace,oldnocatspace);  fflush(msgfp); }
2064: /* --- determine smash --- */
2065: if ( !isstring && !isfrac )             /* don't smash strings or \frac's */
2066:  if ( issmash ) {                       /* raster smash wanted */
2067:    int  maxsmash = rastsmash(sp1,sp2),  /* calculate max smash space */
2068:         margin = smashmargin;           /* init margin without delta */
2069:    if ( (1 && smash1 && smash2)         /* concatanating two chars */
2070:    ||   (1 && type1!=IMAGERASTER && type2!=IMAGERASTER
2071:            && type1!=FRACRASTER  && type2!=FRACRASTER ) )
2072:      /*maxsmash = 0;*/                  /* turn off smash */
2073:      margin = max2(space-1,0);          /* force small smashmargin */
2074:    else                                 /* adjust for delta if images */
2075:      if ( issmashdelta )                /* smashmargin is a delta value */
2076:        margin += fontsize;              /* add displaystyle base to margin */
2077:    if ( maxsmash == blanksignal )       /* sp2 is intentional blank */
2078:      isblank = 1;                       /* set blank flag signal */
2079:    else                                 /* see how much extra space we have*/
2080:      if ( maxsmash > margin )           /* enough space for adjustment */
2081:        nsmash = maxsmash-margin;        /* make adjustment */
2082:    if ( msgfp!=NULL && msglevel>=99 )   /* display smash results */
2083:      { fprintf(msgfp,"rastcat> maxsmash=%d, margin=%d, nsmash=%d\n",
2084:        maxsmash,margin,nsmash);
2085:        fprintf(msgfp,"rastcat> type1=%d,2=%d, class1=%d,2=%d\n", type1,type2,
2086:        (symdef1==NULL?-999:class1),(symdef2==NULL?-999:class2));
2087:        fflush(msgfp); }
2088:    } /* --- end-of-if(issmash) --- */
2089: /* --- determine height, width and baseline of composite raster --- */
2090: if ( !isstring )
2091:  { height = max2(base1+1,base2+1)       /* max height above baseline */
2092:           + max2(height1-base1-1,height2-base2-1); /*+ max descending below*/
2093:    width  = width1+width2 + space-nsmash; /*add widths and space-smash*/
2094:    width  = max3(width,width1,width2); } /* don't "over-smash" composite */
2095: else                                    /* ascii string */
2096:  { height = 1;                          /* default */
2097:    width  = width1 + width2 + space - 1; } /* no need for two nulls */
2098: pixsz  = max2(pixsz1,pixsz2);           /* bitmap||bytemap becomes bytemap */
2099: base   = max2(base1,base2);             /* max space above baseline */
2100: if ( msgfp!=NULL && msglevel>=9999 )    /* display components */
2101:   { fprintf(msgfp,"rastcat> Left-hand ht,width,pixsz,base = %d,%d,%d,%d\n",
2102:     height1,width1,pixsz1,base1);
2103:     type_raster(sp1->image,msgfp);      /* display left-hand raster */
2104:     fprintf(msgfp,"rastcat> Right-hand ht,width,pixsz,base = %d,%d,%d,%d\n",
2105:     height2,width2,pixsz2,base2);
2106:     type_raster(sp2->image,msgfp);      /* display right-hand raster */
2107:     fprintf(msgfp,
2108:     "rastcat> Composite ht,width,smash,pixsz,base = %d,%d,%d,%d,%d\n",
2109:     height,width,nsmash,pixsz,base);
2110:     fflush(msgfp); }                    /* flush msgfp buffer */
2111: /* -------------------------------------------------------------------------
2112: allocate concatted composite subraster
2113: -------------------------------------------------------------------------- */
2114: /* --- allocate returned subraster (and then initialize it) --- */
2115: if ( msgfp!=NULL && msglevel>=9999 )
2116:   { fprintf(msgfp,"rastcat> calling new_subraster(%d,%d,%d)\n",
2117:     width,height,pixsz); fflush(msgfp); }
2118: if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
2119: ==   (subraster *)NULL )                /* failed */
2120:   { if ( msgfp!=NULL && msglevel>=1 )   /* report failure */
2121:       { fprintf(msgfp,"rastcat> new_subraster(%d,%d,%d) failed\n",
2122:         width,height,pixsz); fflush(msgfp); }
2123:     goto end_of_job; }                  /* failed, so quit */
2124: /* --- initialize subraster parameters --- */
2125: /* sp->type = (!isstring?STRINGRASTER:ASCIISTRING); */  /*concatted string*/
2126: if ( !isstring )
2127:   sp->type = /*type2;*//*(type1==type2?type2:IMAGERASTER);*/
2128:         (type2!=CHARASTER? type2 :
2129:         (type1!=CHARASTER&&type1!=BLANKSIGNAL
2130:          &&type1!=FRACRASTER?type1:IMAGERASTER));
2131: else
2132:   sp->type = ASCIISTRING;               /* concatted ascii string */
2133: sp->symdef = symdef2;                   /* rightmost char is sp2 */
2134: sp->baseline = base;                    /* composite baseline */
2135: sp->size = sp2->size;                   /* rightmost char is sp2 */
2136: if ( isblank )                          /* need to propagate blanksignal */
2137:   sp->type = blanksignal;               /* may not be completely safe??? */
2138: /* --- extract raster from subraster --- */
2139: rp = sp->image;                         /* raster allocated in subraster */
2140: /* -------------------------------------------------------------------------
2141: overlay sp1 and sp2 in new composite raster
2142: -------------------------------------------------------------------------- */
2143: if ( msgfp!=NULL && msglevel>=9999 )
2144:   { fprintf(msgfp,"rastcat> calling rastput() to concatanate left||right\n");
2145:     fflush(msgfp); }                    /* flush msgfp buffer */
2146: if ( !isstring )
2147:  rastput (rp, sp1->image, base-base1,   /* overlay left-hand */
2148:  max2(0,nsmash-width1), 1);             /* plus any residual smash space */
2149: else
2150:  memcpy(rp->pixmap,(sp1->image)->pixmap,width1-1);  /*init left string*/
2151: if ( msgfp!=NULL && msglevel>=9999 )
2152:   { type_raster(sp->image,msgfp);       /* display composite raster */
2153:     fflush(msgfp); }                    /* flush msgfp buffer */
2154: if ( !isstring )
2155:  { int  fracbase = ( isfrac?            /* baseline for punc after \frac */
2156:         max2(fraccenterline,base2):base ); /*adjust baseline or use original*/
2157:    rastput (rp, sp2->image, fracbase-base2, /* overlay right-hand */
2158:    max2(0,width1+space-nsmash), isopaque); /* minus any smashed space */
2159:    if ( 1 && type1 == FRACRASTER        /* we're done with \frac image */
2160:    &&   type2 != FRACRASTER )           /* unless we have \frac\frac */
2161:      fraccenterline = NOVALUE;          /* so reset centerline signal */
2162:    if ( fraccenterline != NOVALUE )     /* sp2 is a fraction */
2163:      fraccenterline += (base-base2); }  /* so adjust its centerline */
2164: else
2165:  { strcpy((char *)(rp->pixmap)+width1-1+space,(char *)((sp2->image)->pixmap));
2166:    ((char *)(rp->pixmap))[width1+width2+space-2] = '\000'; } /*null-term*/
2167: if ( msgfp!=NULL && msglevel>=9999 )
2168:   { type_raster(sp->image,msgfp);       /* display composite raster */
2169:     fflush(msgfp); }                    /* flush msgfp buffer */
2170: /* -------------------------------------------------------------------------
2171: free input if requested
2172: -------------------------------------------------------------------------- */
2173: if ( isfree > 0 )                       /* caller wants input freed */
2174:   { if ( isfree==1 || isfree>2 ) delete_subraster(sp1); /* free sp1 */
2175:     if ( isfree >= 2 ) delete_subraster(sp2); }         /* and/or sp2 */
2176: /* -------------------------------------------------------------------------
2177: Back to caller with pointer to concatted subraster or with null for error
2178: -------------------------------------------------------------------------- */
2179: end_of_job:
2180:   smashmargin = oldsmashmargin;         /* reset original smashmargin */
2181:   return ( sp );                        /* back with subraster or null ptr */
2182: } /* --- end-of-function rastcat() --- */
2183: 
2184: 
2185: /* ==========================================================================
2186:  * Function:    rastack ( sp1, sp2, base, space, iscenter, isfree )
2187:  * Purpose:     Stack subrasters sp2 atop sp1, leaving both unchanged
2188:  *              and returning a newly-allocated subraster,
2189:  *              whose baseline is sp1's if base=1, or sp2's if base=2.
2190:  *              Frees/deletes input sp1 and/or sp2 depending on value
2191:  *              of isfree (0=none, 1=sp1, 2=sp2, 3=both).
2192:  * --------------------------------------------------------------------------
2193:  * Arguments:   sp1 (I)         subraster *  to lower subraster
2194:  *              sp2 (I)         subraster *  to upper subraster
2195:  *              base (I)        int containing 1 if sp1 is baseline,
2196:  *                              or 2 if sp2 is baseline.
2197:  *              space (I)       int containing #rows blank space inserted
2198:  *                              between sp1's image and sp2's image.
2199:  *              iscenter (I)    int containing 1 to center both sp1 and sp2
2200:  *                              in stacked array, 0 to left-justify both
2201:  *              isfree (I)      int containing 1=free sp1 before return,
2202:  *                              2=free sp2, 3=free both, 0=free none.
2203:  * --------------------------------------------------------------------------
2204:  * Returns:     ( subraster * ) pointer to constructed subraster sp2 atop sp1
2205:  *                              or  NULL for any error
2206:  * --------------------------------------------------------------------------
2207:  * Notes:
2208:  * ======================================================================= */
2209: /* --- entry point --- */
2210: subraster *rastack ( subraster *sp1, subraster *sp2,
2211:                         int base, int space, int iscenter, int isfree )
2212: {
2213: /* -------------------------------------------------------------------------
2214: Allocations and Declarations
2215: -------------------------------------------------------------------------- */
2216: subraster *new_subraster(), *sp=(subraster *)NULL; /* returned subraster */
2217: raster  *rp=(raster *)NULL;             /* new stacked raster in sp */
2218: int     delete_subraster();             /* in case isfree non-zero */
2219: int     rastput();                      /* place sp1,sp2 in stacked raster */
2220: int     base1   = sp1->baseline,        /* baseline for lower subraster */
2221:         height1 = (sp1->image)->height, /* height for lower subraster */
2222:         width1  = (sp1->image)->width,  /* width for lower subraster */
2223:         pixsz1  = (sp1->image)->pixsz,  /* pixsz for lower subraster */
2224:         base2   = sp2->baseline,        /* baseline for upper subraster */
2225:         height2 = (sp2->image)->height, /* height for upper subraster */
2226:         width2  = (sp2->image)->width,  /* width for upper subraster */
2227:         pixsz2  = (sp2->image)->pixsz;  /* pixsz for upper subraster */
2228: int     height=0, width=0, pixsz=0, baseline=0; /*for stacked sp2 atop sp1*/
2229: mathchardef *symdef1 = sp1->symdef,     /* mathchardef of right lower char */
2230:         *symdef2 = sp2->symdef;         /* mathchardef of right upper char */
2231: /* -------------------------------------------------------------------------
2232: Initialization
2233: -------------------------------------------------------------------------- */
2234: /* --- determine height, width and baseline of composite raster --- */
2235: height   = height1 + space + height2;   /* sum of heights plus space */
2236: width    = max2(width1,width2);         /* max width is overall width */
2237: pixsz    = max2(pixsz1,pixsz2);         /* bitmap||bytemap becomes bytemap */
2238: baseline = (base==1? height2+space+base1 : (base==2? base2 : 0));
2239: /* -------------------------------------------------------------------------
2240: allocate stacked composite subraster (with embedded raster)
2241: -------------------------------------------------------------------------- */
2242: /* --- allocate returned subraster (and then initialize it) --- */
2243: if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
2244: ==   (subraster *)NULL ) goto end_of_job; /* failed, so quit */
2245: /* --- initialize subraster parameters --- */
2246: sp->type = IMAGERASTER;                 /* stacked rasters */
2247: sp->symdef = (base==1? symdef1 : (base==2? symdef2 : NULL)); /* symdef */
2248: sp->baseline = baseline;                /* composite baseline */
2249: sp->size = (base==1? sp1->size : (base==2? sp2->size : NORMALSIZE)); /*size*/
2250: /* --- extract raster from subraster --- */
2251: rp = sp->image;                         /* raster embedded in subraster */
2252: /* -------------------------------------------------------------------------
2253: overlay sp1 and sp2 in new composite raster
2254: -------------------------------------------------------------------------- */
2255: if ( iscenter == 1 )                    /* center both sp1 and sp2 */
2256:   { rastput (rp, sp2->image, 0, (width-width2)/2, 1);  /* overlay upper */
2257:     rastput (rp, sp1->image, height2+space, (width-width1)/2, 1); } /*lower*/
2258: else                                    /* left-justify both sp1 and sp2 */
2259:   { rastput (rp, sp2->image, 0, 0, 1);  /* overlay upper */
2260:     rastput (rp, sp1->image, height2+space, 0, 1); } /*lower*/
2261: /* -------------------------------------------------------------------------
2262: free input if requested
2263: -------------------------------------------------------------------------- */
2264: if ( isfree > 0 )                       /* caller wants input freed */
2265:   { if ( isfree==1 || isfree>2 ) delete_subraster(sp1); /* free sp1 */
2266:     if ( isfree>=2 ) delete_subraster(sp2); } /* and/or sp2 */
2267: /* -------------------------------------------------------------------------
2268: Back to caller with pointer to stacked subraster or with null for error
2269: -------------------------------------------------------------------------- */
2270: end_of_job:
2271:   return ( sp );                        /* back with subraster or null ptr */
2272: } /* --- end-of-function rastack() --- */
2273: 
2274: 
2275: /* ==========================================================================
2276:  * Function:    rastile ( tiles, ntiles )
2277:  * Purpose:     Allocate and build up a composite raster
2278:  *              from the ntiles components/characters supplied in tiles.
2279:  * --------------------------------------------------------------------------
2280:  * Arguments:   tiles (I)       subraster *  to array of subraster structs
2281:  *                              describing the components and their locations
2282:  *              ntiles (I)      int containing number of subrasters in tiles[]
2283:  * --------------------------------------------------------------------------
2284:  * Returns:     ( raster * )    ptr to composite raster,
2285:  *                              or NULL for any error.
2286:  * --------------------------------------------------------------------------
2287:  * Notes:     o The top,left corner of a raster is row=0,col=0
2288:  *              with row# increasing as you move down,
2289:  *              and col# increasing as you move right.
2290:  *              Metafont numbers rows with the baseline=0,
2291:  *              so the top row is a positive number that
2292:  *              decreases as you move down.
2293:  *            o rastile() is no longer used.
2294:  *              It was used by an earlier rasterize() algorithm,
2295:  *              and I've left it in place should it be needed again.
2296:  *              But recent changes haven't been tested/exercised.
2297:  * ======================================================================= */
2298: /* --- entry point --- */
2299: raster  *rastile ( subraster *tiles, int ntiles )
2300: {
2301: /* -------------------------------------------------------------------------
2302: Allocations and Declarations
2303: -------------------------------------------------------------------------- */
2304: raster  *new_raster(), *composite=(raster *)NULL;  /*raster back to caller*/
2305: int     width=0, height=0, pixsz=0, /*width,height,pixsz of composite raster*/
2306:         toprow=9999, rightcol=-999, /* extreme upper-right corner of tiles */
2307:         botrow=-999, leftcol=9999;  /* extreme lower-left corner of tiles */
2308: int     itile;                  /* tiles[] index */
2309: int     rastput();              /* overlay each tile in composite raster */
2310: /* -------------------------------------------------------------------------
2311: run through tiles[] to determine dimensions for composite raster
2312: -------------------------------------------------------------------------- */
2313: /* --- determine row and column bounds of composite raster --- */
2314: for ( itile=0; itile<ntiles; itile++ )
2315:   {
2316:   subraster *tile = &(tiles[itile]);            /* ptr to current tile */
2317:   /* --- upper-left corner of composite --- */
2318:   toprow = min2(toprow, tile->toprow);
2319:   leftcol = min2(leftcol, tile->leftcol);
2320:   /* --- lower-right corner of composite --- */
2321:   botrow = max2(botrow, tile->toprow + (tile->image)->height - 1);
2322:   rightcol = max2(rightcol, tile->leftcol + (tile->image)->width  - 1);
2323:   /* --- pixsz of composite --- */
2324:   pixsz = max2(pixsz,(tile->image)->pixsz);
2325:   } /* --- end-of-for(itile) --- */
2326: /* --- calculate width and height from bounds --- */
2327: width  = rightcol - leftcol + 1;
2328: height = botrow - toprow + 1;
2329: /* --- sanity check (quit if bad dimensions) --- */
2330: if ( width<1 || height<1 ) goto end_of_job;
2331: /* -------------------------------------------------------------------------
2332: allocate composite raster, and embed tiles[] within it
2333: -------------------------------------------------------------------------- */
2334: /* --- allocate composite raster --- */
2335: if ( (composite=new_raster(width,height,pixsz)) /*allocate composite raster*/
2336: ==   (raster *)NULL ) goto end_of_job;          /* and quit if failed */
2337: /* --- embed tiles[] in composite --- */
2338: for ( itile=0; itile<ntiles; itile++ )
2339:   { subraster *tile = &(tiles[itile]);          /* ptr to current tile */
2340:     rastput (composite, tile->image,            /* overlay tile image at...*/
2341:       tile->toprow-toprow, tile->leftcol-leftcol, 1); } /*upper-left corner*/
2342: /* -------------------------------------------------------------------------
2343: Back to caller with composite raster (or null for any error)
2344: -------------------------------------------------------------------------- */
2345: end_of_job:
2346:   return ( composite );                 /* back with composite or null ptr */
2347: } /* --- end-of-function rastile() --- */
2348: 
2349: 
2350: /* ==========================================================================
2351:  * Function:    rastsmash ( sp1, sp2 )
2352:  * Purpose:     When concatanating sp1||sp2, calculate #pixels
2353:  *              we can "smash sp2 left"
2354:  * --------------------------------------------------------------------------
2355:  * Arguments:   sp1 (I)         subraster *  to left-hand raster
2356:  *              sp2 (I)         subraster *  to right-hand raster
2357:  * --------------------------------------------------------------------------
2358:  * Returns:     ( int )         max #pixels we can smash sp1||sp2,
2359:  *                              or "blanksignal" if sp2 intentionally blank,
2360:  *                              or 0 for any error.
2361:  * --------------------------------------------------------------------------
2362:  * Notes:     o
2363:  * ======================================================================= */
2364: /* --- entry point --- */
2365: int     rastsmash ( subraster *sp1, subraster *sp2 )
2366: {
2367: /* -------------------------------------------------------------------------
2368: Allocations and Declarations
2369: -------------------------------------------------------------------------- */
2370: int     nsmash = 0;                     /* #pixels to smash sp1||sp2 */
2371: int     base1   = sp1->baseline,        /*baseline for left-hand subraster*/
2372:         height1 = (sp1->image)->height, /* height for left-hand subraster */
2373:         width1  = (sp1->image)->width,  /* width for left-hand subraster */
2374:         base2   = sp2->baseline,        /*baseline for right-hand subraster*/
2375:         height2 = (sp2->image)->height, /* height for right-hand subraster */
2376:         width2  = (sp2->image)->width;  /* width for right-hand subraster */
2377: int     base = max2(base1,base2),       /* max ascenders - 1 above baseline*/
2378:         top1=base-base1, top2=base-base2, /* top irow indexes for sp1, sp2 */
2379:         bot1=top1+height1-1, bot2=top2+height2-1, /* bot irow indexes */
2380:         height = max2(bot1,bot2)+1;     /* total height */
2381: int     irow1=0,irow2=0, icol=0;        /* row,col indexes */
2382: int     firstcol1[1025], nfirst1=0,     /* 1st sp1 col containing set pixel*/
2383:         firstcol2[1025], nfirst2=0;     /* 1st sp2 col containing set pixel*/
2384: int     smin=9999, xmin=9999,ymin=9999; /* min separation (s=x+y) */
2385: int     type_raster();                  /* display debugging output */
2386: /* -------------------------------------------------------------------------
2387: find right edge of sp1 and left edge of sp2 (these will be abutting edges)
2388: -------------------------------------------------------------------------- */
2389: /* --- check args --- */
2390: if ( isstring ) goto end_of_job;        /* ignore string rasters */
2391: if ( 0 && istextmode ) goto end_of_job; /* don't smash in text mode */
2392: if ( height > 1023 ) goto end_of_job;   /* don't try to smash huge image */
2393: if ( sp2->type == blanksignal )         /*blanksignal was propagated to us*/
2394:   goto end_of_job;                      /* don't smash intentional blank */
2395: /* --- init firstcol1[], firstcol2[] --- */
2396: for ( irow1=0; irow1<height; irow1++ )  /* for each row */
2397:   firstcol1[irow1] = firstcol2[irow1] = blanksignal; /* signal empty rows */
2398: /* --- set firstcol2[] indicating left edge of sp2 --- */
2399: for ( irow2=top2; irow2<=bot2; irow2++ ) /* for each row inside sp2 */
2400:   for ( icol=0; icol<width2; icol++ )   /* find first non-empty col in row */
2401:     if ( getpixel(sp2->image,irow2-top2,icol) != 0 ) /* found a set pixel */
2402:       { firstcol2[irow2] = icol;        /* icol is #cols from left edge */
2403:         nfirst2++;                      /* bump #rows containing set pixels*/
2404:         break; }                        /* and go on to next row */
2405: if ( nfirst2 < 1 )                      /*right-hand sp2 is completely blank*/
2406:   { nsmash = blanksignal;               /* signal intentional blanks */
2407:     goto end_of_job; }                  /* don't smash intentional blanks */
2408: /* --- now check if preceding image in sp1 was an intentional blank --- */
2409: if ( sp1->type == blanksignal )         /*blanksignal was propagated to us*/
2410:   goto end_of_job;                      /* don't smash intentional blank */
2411: /* --- set firstcol1[] indicating right edge of sp1 --- */
2412: for ( irow1=top1; irow1<=bot1; irow1++ ) /* for each row inside sp1 */
2413:   for ( icol=width1-1; icol>=0; icol-- ) /* find last non-empty col in row */
2414:     if ( getpixel(sp1->image,irow1-top1,icol) != 0 ) /* found a set pixel */
2415:       { firstcol1[irow1] = (width1-1)-icol; /* save #cols from right edge */
2416:         nfirst1++;                      /* bump #rows containing set pixels*/
2417:         break; }                        /* and go on to next row */
2418: if ( nfirst1 < 1 )                      /*left-hand sp1 is completely blank*/
2419:   goto end_of_job;                      /* don't smash intentional blanks */
2420: /* -------------------------------------------------------------------------
2421: find minimum separation
2422: -------------------------------------------------------------------------- */
2423: for ( irow2=top2; irow2<=bot2; irow2++ ) { /* check each row inside sp2 */
2424:  int margin1, margin2=firstcol2[irow2]; /* #cols to first set pixel */
2425:  if ( margin2 != blanksignal ) {        /* irow2 not an empty/blank row */
2426:   for ( irow1=max2(irow2-smin,top1); ; irow1++ )
2427:    if ( irow1 > min2(irow2+smin,bot1) ) break; /* upper bound check */
2428:    else
2429:     if ( (margin1=firstcol1[irow1]) != blanksignal ) { /*have non-blank row*/
2430:      int dx=(margin1+margin2), dy=absval(irow2-irow1), ds=dx+dy; /* deltas */
2431:      if ( ds >= smin ) continue;        /* min unchanged */
2432:      if ( dy>smashmargin && dx<xmin && smin<9999 ) continue; /* dy alone */
2433:      smin=ds; xmin=dx; ymin=dy;         /* set new min */
2434:      } /* --- end-of-if(margin1!=blanksignal) --- */
2435:   } /* --- end-of-if(margin2!=blanksignal) --- */
2436:  if ( smin<2 ) goto end_of_job;         /* can't smash */
2437:  } /* --- end-of-for(irow2) --- */
2438: /*nsmash = min2(xmin,width2);*/         /* permissible smash */
2439: nsmash = xmin;                          /* permissible smash */
2440: /* -------------------------------------------------------------------------
2441: Back to caller with #pixels to smash sp1||sp2
2442: -------------------------------------------------------------------------- */
2443: end_of_job:
2444:   /* --- debugging output --- */
2445:   if ( msgfp!=NULL && msglevel >= 99 )  /* display for debugging */
2446:     { fprintf(msgfp,"rastsmash> nsmash=%d, smashmargin=%d\n",
2447:       nsmash,smashmargin);
2448:       if ( msglevel >= 999 )            /* also display rasters */
2449:         { fprintf(msgfp,"rastsmash>left-hand image...\n");
2450:           if(sp1!=NULL) type_raster(sp1->image,msgfp); /* left image */
2451:           fprintf(msgfp,"rastsmash>right-hand image...\n");
2452:           if(sp2!=NULL) type_raster(sp2->image,msgfp); } /* right image */
2453:       fflush(msgfp); }
2454:   return ( nsmash );                    /* back with #smash pixels */
2455: } /* --- end-of-function rastsmash() --- */
2456: 
2457: 
2458: /* ==========================================================================
2459:  * Function:    rastsmashcheck ( term )
2460:  * Purpose:     Check an exponent term to see if its leading symbol
2461:  *              would make smashing dangerous
2462:  * --------------------------------------------------------------------------
2463:  * Arguments:   term (I)        char *  to null-terminated string
2464:  *                              containing right-hand exponent term about to
2465:  *                              be smashed against existing left-hand.
2466:  * --------------------------------------------------------------------------
2467:  * Returns:     ( int )         1 if it's okay to smash term, or
2468:  *                              0 if smash is dangerous.
2469:  * --------------------------------------------------------------------------
2470:  * Notes:     o
2471:  * ======================================================================= */
2472: /* --- entry point --- */
2473: int     rastsmashcheck ( char *term )
2474: {
2475: /* -------------------------------------------------------------------------
2476: Allocations and Declarations
2477: -------------------------------------------------------------------------- */
2478: int     isokay = 0;             /* 1 to signal okay to caller */
2479: static  char nosmashchars[64] = "-.,="; /* don't smash these leading chars */
2480: static  char *nosmashstrs[64] = { "\\frac", NULL }; /* or leading strings */
2481: static  char *grayspace[64] = { "\\tiny", "\\small", "\\normalsize",
2482:         "\\large", "\\Large", "\\LARGE", "\\huge", "\\Huge", NULL };
2483: char    *expression = term;     /* local ptr to beginning of expression */
2484: char    *token = NULL;  int i;  /* token = nosmashstrs[i] or grayspace[i] */
2485: /* -------------------------------------------------------------------------
2486: see if smash check enabled
2487: -------------------------------------------------------------------------- */
2488: if ( smashcheck < 1 ) {         /* no smash checking wanted */
2489:   if ( smashcheck >= 0 )        /* -1 means check should always fail */
2490:     isokay = 1;                 /* otherwise (if 0), signal okay to smash */
2491:   goto end_of_job; }            /* return to caller */
2492: /* -------------------------------------------------------------------------
2493: skip leading white and gray space
2494: -------------------------------------------------------------------------- */
2495: /* --- first check input --- */
2496: if ( isempty(term) ) goto end_of_job; /* no input so return 0 to caller */
2497: /* --- skip leading white space --- */
2498: skipwhite(term);                /* skip leading white space */
2499: if ( *term == '\000' ) goto end_of_job; /* nothing but white space */
2500: /* --- skip leading gray space --- */
2501: skipgray:
2502:  for ( i=0; (token=grayspace[i]) != NULL; i++ ) /* check each grayspace */
2503:   if ( strncmp(term,token,strlen(token)) == 0 ) { /* found grayspace */
2504:    term += strlen(token);       /* skip past this grayspace token */
2505:    skipwhite(term);             /* and skip any subsequent white space */
2506:    if ( *term == '\000' ) {     /* nothing left so quit */
2507:      if ( msgfp!=NULL && msglevel >= 99 ) /* display for debugging */
2508:        fprintf(msgfp,"rastsmashcheck> only grayspace in %.32s\n",expression);
2509:      goto end_of_job; }
2510:    goto skipgray; }             /* restart grayspace check from beginning */
2511: /* -------------------------------------------------------------------------
2512: check for leading no-smash single char
2513: -------------------------------------------------------------------------- */
2514: /* --- don't smash if term begins with a "nosmash" char --- */
2515: if ( (token=strchr(nosmashchars,*term)) != NULL ) {
2516:   if ( msgfp!=NULL && msglevel >= 99 )  /* display for debugging */
2517:     fprintf(msgfp,"rastsmashcheck> char %.1s found in %.32s\n",token,term);
2518:   goto end_of_job; }
2519: /* -------------------------------------------------------------------------
2520: check for leading no-smash token
2521: -------------------------------------------------------------------------- */
2522: for ( i=0; (token=nosmashstrs[i]) != NULL; i++ ) /* check each nosmashstr */
2523:  if ( strncmp(term,token,strlen(token)) == 0 ) { /* found a nosmashstr */
2524:   if ( msgfp!=NULL && msglevel >= 99 )  /* display for debugging */
2525:     fprintf(msgfp,"rastsmashcheck> token %s found in %.32s\n",token,term);
2526:   goto end_of_job; }            /* so don't smash term */
2527: /* -------------------------------------------------------------------------
2528: back to caller
2529: -------------------------------------------------------------------------- */
2530: isokay = 1;                     /* no problem, so signal okay to smash */
2531: end_of_job:
2532:   if ( msgfp!=NULL && msglevel >= 999 ) /* display for debugging */
2533:     fprintf(msgfp,"rastsmashcheck> returning isokay=%d for \"%.32s\"\n",
2534:     isokay,(expression==NULL?"<no input>":expression));
2535:   return ( isokay );            /* back to caller with 1 if okay to smash */
2536: } /* --- end-of-function rastsmashcheck() --- */
2537: 
2538: 
2539: /* ==========================================================================
2540:  * Function:    accent_subraster ( accent, width, height, direction, pixsz )
2541:  * Purpose:     Allocate a new subraster of width x height
2542:  *              (or maybe different dimensions, depending on accent),
2543:  *              and draw an accent (\hat or \vec or \etc) that fills it
2544:  * --------------------------------------------------------------------------
2545:  * Arguments:   accent (I)      int containing either HATACCENT or VECACCENT,
2546:  *                              etc, indicating the type of accent desired
2547:  *              width (I)       int containing desired width of accent (#cols)
2548:  *              height (I)      int containing desired height of accent(#rows)
2549:  *              direction (I)   int containing desired direction of accent,
2550:  *                              +1=right, -1=left, 0=left/right
2551:  *              pixsz (I)       int containing 1 for bitmap, 8 for bytemap
2552:  * --------------------------------------------------------------------------
2553:  * Returns:     ( subraster * ) ptr to newly-allocated subraster with accent,
2554:  *                              or NULL for any error.
2555:  * --------------------------------------------------------------------------
2556:  * Notes:     o Some accents have internally-determined dimensions,
2557:  *              and caller should check dimensions in returned subraster
2558:  * ======================================================================= */
2559: /* --- entry point --- */
2560: subraster *accent_subraster (  int accent, int width, int height,
2561: int direction, int pixsz )
2562: {
2563: /* -------------------------------------------------------------------------
2564: Allocations and Declarations
2565: -------------------------------------------------------------------------- */
2566: /* --- general info --- */
2567: raster  *new_raster(), *rp=NULL;        /*raster containing desired accent*/
2568: subraster *new_subraster(), *sp=NULL;   /* subraster returning accent */
2569: int     delete_raster(), delete_subraster(); /*free allocated raster on err*/
2570: int     line_raster(),                  /* draws lines */
2571:         rule_raster(),                  /* draw solid boxes */
2572:         thickness = 1;                  /* line thickness */
2573: /*int   pixval = (pixsz==1? 1 : (pixsz==8?255:(-1)));*/ /*black pixel value*/
2574: /* --- other working info --- */
2575: int     col0, col1,                     /* cols for line */
2576:         row0, row1;                     /* rows for line */
2577: subraster *get_delim(), *accsp=NULL;    /*find suitable cmex10 symbol/accent*/
2578: /* --- info for under/overbraces, tildes, etc --- */
2579: char    brace[16];                      /*"{" for over, "}" for under, etc*/
2580: raster  *rastrot(),                     /* rotate { for overbrace, etc */
2581:         *rastcpy();                     /* may need copy of original */
2582: subraster *arrow_subraster();           /* rightarrow for vec */
2583: subraster *rastack();                   /* stack accent atop extra space */
2584: int     iswidthneg = 0;                 /* set true if width<0 arg passed */
2585: int     serifwidth=0;                   /* serif for surd */
2586: int     isBig=0;                        /* true for ==>arrow, false for -->*/
2587: /* -------------------------------------------------------------------------
2588: initialization
2589: -------------------------------------------------------------------------- */
2590: if ( width < 0 ) { width=(-width); iswidthneg=1; } /* set neg width flag */
2591: /* -------------------------------------------------------------------------
2592: outer switch() traps accents that may change caller's height,width
2593: -------------------------------------------------------------------------- */
2594: switch ( accent )
2595:  {
2596:  default:
2597:   /* -----------------------------------------------------------------------
2598:   inner switch() first allocates fixed-size raster for accents that don't
2599:   ------------------------------------------------------------------------ */
2600:   if ( (rp = new_raster(width,height,pixsz)) /* allocate fixed-size raster */
2601:   !=   NULL )                           /* and if we succeeded... */
2602:    switch ( accent )                    /* ...draw requested accent in it */
2603:     {
2604:     /* --- unrecognized request --- */
2605:     default: delete_raster(rp);         /* unrecognized accent requested */
2606:         rp = NULL;  break;              /* so free raster and signal error */
2607:     /* --- bar request --- */
2608:     case UNDERBARACCENT:
2609:     case BARACCENT:
2610:         thickness = 1; /*height-1;*/    /* adjust thickness */
2611:         if ( accent == BARACCENT )      /* bar is above expression */
2612:          { row0 = row1 = max2(height-3,0); /* row numbers for overbar */
2613:            line_raster(rp,row0,0,row1,width-1,thickness); } /*blanks at bot*/
2614:         else                            /* underbar is below expression */
2615:          { row0 = row1 = min2(2,height-1); /* row numbers for underbar */
2616:            line_raster(rp,row0,0,row1,width-1,thickness); } /*blanks at top*/
2617:         break;
2618:     /* --- dot request --- */
2619:     case DOTACCENT:
2620:         thickness = height-1;           /* adjust thickness */
2621:         /*line_raster(rp,0,width/2,1,(width/2)+1,thickness);*//*centered dot*/
2622:         rule_raster(rp,0,(width+1-thickness)/2,thickness,thickness,3); /*box*/
2623:         break;
2624:     /* --- ddot request --- */
2625:     case DDOTACCENT:
2626:         thickness = height-1;           /* adjust thickness */
2627:         col0 = max2((width+1)/3-(thickness/2)-1,0); /* one-third of width */
2628:         col1 = min2((2*width+1)/3-(thickness/2)+1,width-thickness); /*2/3rds*/
2629:         if ( col0+thickness >= col1 )   /* dots overlap */
2630:           { col0 = max2(col0-1,0);      /* try moving left dot more left */
2631:             col1 = min2(col1+1,width-thickness); } /* and right dot right */
2632:         if ( col0+thickness >= col1 )   /* dots _still_ overlap */
2633:           thickness = max2(thickness-1,1); /* so try reducing thickness */
2634:         /*line_raster(rp,0,col0,1,col0+1,thickness);*//*set dot at 1st third*/
2635:         /*line_raster(rp,0,col1,1,col1+1,thickness);*//*and another at 2nd*/
2636:         rule_raster(rp,0,col0,thickness,thickness,3); /*box at 1st third*/
2637:         rule_raster(rp,0,col1,thickness,thickness,3); /*box at 2nd third*/
2638:         break;
2639:     /* --- hat request --- */
2640:     case HATACCENT:
2641:         thickness = 1; /*(width<=12? 2 : 3);*/  /* adjust thickness */
2642:         line_raster(rp,height-1,0,0,width/2,thickness);    /* / part of hat*/
2643:         line_raster(rp,0,(width-1)/2,height-1,width-1,thickness); /* \ part*/
2644:         break;
2645:     /* --- sqrt request --- */
2646:     case SQRTACCENT:
2647:         serifwidth = SURDSERIFWIDTH(height); /* leading serif on surd */
2648:         col1 = SQRTWIDTH(height,(iswidthneg?1:2)) - 1; /*right col of sqrt*/
2649:         /*col0 = (col1-serifwidth+2)/3;*/ /* midpoint col of sqrt */
2650:         col0 = (col1-serifwidth+1)/2;   /* midpoint col of sqrt */
2651:         row0 = max2(1,((height+1)/2)-2); /* midpoint row of sqrt */
2652:         row1 = height-1;                /* bottom row of sqrt */
2653:         /*line_raster(rp,row0,0,row1,col0,thickness);*/ /*descending portion*/
2654:         line_raster(rp,row0+serifwidth,0,row0,serifwidth,thickness);
2655:         line_raster(rp,row0,serifwidth,row1,col0,thickness); /* descending */
2656:         line_raster(rp,row1,col0,0,col1,thickness); /* ascending portion */
2657:         line_raster(rp,0,col1,0,width-1,thickness); /*overbar of thickness 1*/
2658:         break;
2659:     } /* --- end-of-inner-switch(accent) --- */
2660:     break;                              /* break from outer accent switch */
2661:  /* --- underbrace, overbrace request --- */
2662:  case UNDERBRACE:
2663:  case OVERBRACE:
2664:     if ( accent == UNDERBRACE ) strcpy(brace,"}"); /* start with } brace */
2665:     if ( accent ==  OVERBRACE ) strcpy(brace,"{"); /* start with { brace */
2666:     if ( (accsp=get_delim(brace,width,CMEX10)) /* use width for height */
2667:     !=  NULL )                          /* found desired brace */
2668:       { rp = rastrot(accsp->image);     /* rotate 90 degrees clockwise */
2669:         delete_subraster(accsp); }      /* and free subraster "envelope" */
2670:     break;
2671:  /* --- hat request --- */
2672:  case HATACCENT:
2673:     if ( accent == HATACCENT ) strcpy(brace,"<"); /* start with < */
2674:     if ( (accsp=get_delim(brace,width,CMEX10)) /* use width for height */
2675:     !=  NULL )                          /* found desired brace */
2676:       { rp = rastrot(accsp->image);     /* rotate 90 degrees clockwise */
2677:         delete_subraster(accsp); }      /* and free subraster "envelope" */
2678:     break;
2679:  /* --- vec request --- */
2680:  case VECACCENT:
2681:     height = 2*(height/2) + 1;          /* force height odd */
2682:     if ( absval(direction) >= 9 ) {     /* want ==> arrow rather than --> */
2683:       isBig = 1;                        /* signal "Big" arrow */
2684:       direction -= 10; }                /* reset direction = +1, -1, or 0 */
2685:     if ((accsp=arrow_subraster(width,height,pixsz,direction,isBig)) /*arrow*/
2686:     !=  NULL )                          /* succeeded */
2687:         { rp = accsp->image;            /* "extract" raster with bitmap */
2688:           free((void *)accsp); }        /* and free subraster "envelope" */
2689:     break;
2690:  /* --- tilde request --- */
2691:  case TILDEACCENT:
2692:     accsp=(width<25? get_delim("\\sim",-width,CMSY10) :
2693:                      get_delim("~",-width,CMEX10)); /*width search for tilde*/
2694:     if ( accsp !=  NULL )               /* found desired tilde */
2695:       if ( (sp=rastack(new_subraster(1,1,pixsz),accsp,1,0,1,3))/*space below*/
2696:       !=  NULL )                        /* have tilde with space below it */
2697:         { rp = sp->image;               /* "extract" raster with bitmap */
2698:           free((void *)sp);             /* and free subraster "envelope" */
2699:           leftsymdef = NULL; }          /* so \tilde{x}^2 works properly */
2700:     break;
2701:  } /* --- end-of-outer-switch(accent) --- */
2702: /* -------------------------------------------------------------------------
2703: if we constructed accent raster okay, embed it in a subraster and return it
2704: -------------------------------------------------------------------------- */
2705: /* --- if all okay, allocate subraster to contain constructed raster --- */
2706: if ( rp != NULL ) {                     /* accent raster constructed okay */
2707:   if ( (sp=new_subraster(0,0,0))        /* allocate subraster "envelope" */
2708:   ==   NULL )                           /* and if we fail to allocate */
2709:     delete_raster(rp);                  /* free now-unneeded raster */
2710:   else                                  /* subraster allocated okay */
2711:     { /* --- init subraster parameters, embedding raster in it --- */
2712:       sp->type = IMAGERASTER;           /* constructed image */
2713:       sp->image = rp;                   /* raster we just constructed */
2714:       sp->size = (-1);                  /* can't set font size here */
2715:       sp->baseline = 0; }               /* can't set baseline here */
2716:   } /* --- end-of-if(rp!=NULL) --- */
2717: /* --- return subraster containing desired accent to caller --- */
2718: return ( sp );                          /* return accent or NULL to caller */
2719: } /* --- end-of-function accent_subraster() --- */
2720: 
2721: 
2722: /* ==========================================================================
2723:  * Function:    arrow_subraster ( width, height, pixsz, drctn, isBig )
2724:  * Purpose:     Allocate a raster/subraster and draw left/right arrow in it
2725:  * --------------------------------------------------------------------------
2726:  * Arguments:   width (I)       int containing number of cols for arrow
2727:  *              height (I)      int containing number of rows for arrow
2728:  *              pixsz (I)       int containing 1 for bitmap, 8 for bytemap
2729:  *              drctn (I)       int containing +1 for right arrow,
2730:  *                              or -1 for left, 0 for leftright
2731:  *              isBig (I)       int containing 1/true for \Long arrows,
2732:  *                              or false for \long arrows, i.e.,
2733:  *                              true for ===> or false for --->.
2734:  * --------------------------------------------------------------------------
2735:  * Returns:     ( subraster * ) ptr to constructed left/right arrow
2736:  *                              or NULL for any error.
2737:  * --------------------------------------------------------------------------
2738:  * Notes:     o
2739:  * ======================================================================= */
2740: /* --- entry point --- */
2741: subraster *arrow_subraster ( int width, int height, int pixsz,
2742:                                 int drctn, int isBig )
2743: {
2744: /* -------------------------------------------------------------------------
2745: Allocations and Declarations
2746: -------------------------------------------------------------------------- */
2747: subraster *new_subraster(), *arrowsp=NULL; /* allocate arrow subraster */
2748: int     rule_raster();                  /* draw arrow line */
2749: int     irow, midrow=height/2;          /* index, midrow is arrowhead apex */
2750: int     icol, thickness=(height>15?2:2); /* arrowhead thickness and index */
2751: int     pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */
2752: int     ipix,                           /* raster pixmap[] index */
2753:         npix = width*height;            /* #pixels malloced in pixmap[] */
2754: /* -------------------------------------------------------------------------
2755: allocate raster/subraster and draw arrow line
2756: -------------------------------------------------------------------------- */
2757: if ( height < 3 ) { height=3; midrow=1; }       /* set minimum height */
2758: if ( (arrowsp=new_subraster(width,height,pixsz)) /* allocate empty raster */
2759: ==   NULL ) goto end_of_job;                    /* and quit if failed */
2760: if ( !isBig )                                   /* single line */
2761:   rule_raster(arrowsp->image,midrow,0,width,1,0); /*draw line across midrow*/
2762: else
2763:   { int delta = (width>6? (height>15? 3: (height>7? 2 : 1)) : 1);
2764:     rule_raster(arrowsp->image,midrow-delta,delta,width-2*delta,1,0);
2765:     rule_raster(arrowsp->image,midrow+delta,delta,width-2*delta,1,0); }
2766: /* -------------------------------------------------------------------------
2767: construct arrowhead(s)
2768: -------------------------------------------------------------------------- */
2769: for ( irow=0; irow<height; irow++ )             /* for each row of arrow */
2770:   {
2771:   int   delta = abs(irow-midrow);               /*arrowhead offset for irow*/
2772:   /* --- right arrowhead --- */
2773:   if ( drctn >= 0 )                             /* right arrowhead wanted */
2774:     for ( icol=0; icol<thickness; icol++ )      /* for arrowhead thickness */
2775:      { ipix = ((irow+1)*width - 1) - delta - icol; /* rightmost-delta-icol */
2776:        if ( ipix >= 0 ) {                               /* bounds check */
2777:         if ( pixsz == 1 )                       /* have a bitmap */
2778:           setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
2779:         else                                    /* should have a bytemap */
2780:          if ( pixsz == 8 )                      /* check pixsz for bytemap */
2781:           ((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
2782:   /* --- left arrowhead (same as right except for ipix calculation) --- */
2783:   if ( drctn <= 0 )                             /* left arrowhead wanted */
2784:     for ( icol=0; icol<thickness; icol++ )      /* for arrowhead thickness */
2785:      { ipix = irow*width + delta + icol;        /* leftmost bit+delta+icol */
2786:        if ( ipix < npix ) {                     /* bounds check */
2787:         if ( pixsz == 1 )                       /* have a bitmap */
2788:           setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
2789:         else                                    /* should have a bytemap */
2790:          if ( pixsz == 8 )                      /* check pixsz for bytemap */
2791:           ((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
2792:   } /* --- end-of-for(irow) --- */
2793: end_of_job:
2794:   return ( arrowsp );                   /*back to caller with arrow or NULL*/
2795: } /* --- end-of-function arrow_subraster() --- */
2796: 
2797: 
2798: /* ==========================================================================
2799:  * Function:    uparrow_subraster ( width, height, pixsz, drctn, isBig )
2800:  * Purpose:     Allocate a raster/subraster and draw up/down arrow in it
2801:  * --------------------------------------------------------------------------
2802:  * Arguments:   width (I)       int containing number of cols for arrow
2803:  *              height (I)      int containing number of rows for arrow
2804:  *              pixsz (I)       int containing 1 for bitmap, 8 for bytemap
2805:  *              drctn (I)       int containing +1 for up arrow,
2806:  *                              or -1 for down, or 0 for updown
2807:  *              isBig (I)       int containing 1/true for \Long arrows,
2808:  *                              or false for \long arrows, i.e.,
2809:  *                              true for ===> or false for --->.
2810:  * --------------------------------------------------------------------------
2811:  * Returns:     ( subraster * ) ptr to constructed up/down arrow
2812:  *                              or NULL for any error.
2813:  * --------------------------------------------------------------------------
2814:  * Notes:     o
2815:  * ======================================================================= */
2816: /* --- entry point --- */
2817: subraster *uparrow_subraster ( int width, int height, int pixsz,
2818:                                         int drctn, int isBig )
2819: {
2820: /* -------------------------------------------------------------------------
2821: Allocations and Declarations
2822: -------------------------------------------------------------------------- */
2823: subraster *new_subraster(), *arrowsp=NULL; /* allocate arrow subraster */
2824: int     rule_raster();                  /* draw arrow line */
2825: int     icol, midcol=width/2;           /* index, midcol is arrowhead apex */
2826: int     irow, thickness=(width>15?2:2); /* arrowhead thickness and index */
2827: int     pixval = (pixsz==1? 1 : (pixsz==8?255:(-1))); /* black pixel value */
2828: int     ipix,                           /* raster pixmap[] index */
2829:         npix = width*height;            /* #pixels malloced in pixmap[] */
2830: /* -------------------------------------------------------------------------
2831: allocate raster/subraster and draw arrow line
2832: -------------------------------------------------------------------------- */
2833: if ( width < 3 ) { width=3; midcol=1; }         /* set minimum width */
2834: if ( (arrowsp=new_subraster(width,height,pixsz)) /* allocate empty raster */
2835: ==   NULL ) goto end_of_job;                    /* and quit if failed */
2836: if ( !isBig )                                   /* single line */
2837:   rule_raster(arrowsp->image,0,midcol,1,height,0); /*draw line down midcol*/
2838: else
2839:   { int delta = (height>6? (width>15? 3: (width>7? 2 : 1)) : 1);
2840:     rule_raster(arrowsp->image,delta,midcol-delta,1,height-2*delta,0);
2841:     rule_raster(arrowsp->image,delta,midcol+delta,1,height-2*delta,0); }
2842: /* -------------------------------------------------------------------------
2843: construct arrowhead(s)
2844: -------------------------------------------------------------------------- */
2845: for ( icol=0; icol<width; icol++ )              /* for each col of arrow */
2846:   {
2847:   int   delta = abs(icol-midcol);               /*arrowhead offset for icol*/
2848:   /* --- up arrowhead --- */
2849:   if ( drctn >= 0 )                             /* up arrowhead wanted */
2850:     for ( irow=0; irow<thickness; irow++ )      /* for arrowhead thickness */
2851:      { ipix = (irow+delta)*width + icol;        /* leftmost+icol */
2852:        if ( ipix < npix ) {                     /* bounds check */
2853:         if ( pixsz == 1 )                       /* have a bitmap */
2854:           setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
2855:         else                                    /* should have a bytemap */
2856:          if ( pixsz == 8 )                      /* check pixsz for bytemap */
2857:           ((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
2858:   /* --- down arrowhead (same as up except for ipix calculation) --- */
2859:   if ( drctn <= 0 )                             /* down arrowhead wanted */
2860:     for ( irow=0; irow<thickness; irow++ )      /* for arrowhead thickness */
2861:      { ipix = (height-1-delta-irow)*width + icol; /* leftmost + icol */
2862:        if ( ipix > 0 ) {                        /* bounds check */
2863:         if ( pixsz == 1 )                       /* have a bitmap */
2864:           setlongbit((arrowsp->image)->pixmap,ipix);/*turn on arrowhead bit*/
2865:         else                                    /* should have a bytemap */
2866:          if ( pixsz == 8 )                      /* check pixsz for bytemap */
2867:           ((arrowsp->image)->pixmap)[ipix] = pixval; } }/*set arrowhead byte*/
2868:   } /* --- end-of-for(icol) --- */
2869: end_of_job:
2870:   return ( arrowsp );                   /*back to caller with arrow or NULL*/
2871: } /* --- end-of-function uparrow_subraster() --- */
2872: 
2873: 
2874: /* ==========================================================================
2875:  * Function:    rule_raster ( rp, top, left, width, height, type )
2876:  * Purpose:     Draw a solid or dashed line (or box) in existing raster rp,
2877:  *              starting at top,left with dimensions width,height.
2878:  * --------------------------------------------------------------------------
2879:  * Arguments:   rp (I)          raster *  to raster in which rule
2880:  *                              will be drawn
2881:  *              top (I)         int containing row at which top-left corner
2882:  *                              of rule starts (0 is topmost)
2883:  *              left (I)        int containing col at which top-left corner
2884:  *                              of rule starts (0 is leftmost)
2885:  *              width (I)       int containing number of cols for rule
2886:  *              height (I)      int containing number of rows for rule
2887:  *              type (I)        int containing 0 for solid rule,
2888:  *                              1 for horizontal dashes, 2 for vertical
2889:  *                              3 for solid rule with corners removed (bevel)
2890:  *                              4 for strut (nothing drawn)
2891:  * --------------------------------------------------------------------------
2892:  * Returns:     ( int )         1 if rule drawn okay,
2893:  *                              or 0 for any error.
2894:  * --------------------------------------------------------------------------
2895:  * Notes:     o Rule line is implicitly "horizontal" or "vertical" depending
2896:  *              on relative width,height dimensions.  It's a box if they're
2897:  *              more or less comparable.
2898:  * ======================================================================= */
2899: /* --- entry point --- */
2900: int     rule_raster ( raster *rp, int top, int left,
2901:                 int width, int height, int type )
2902: {
2903: /* -------------------------------------------------------------------------
2904: Allocations and Declarations
2905: -------------------------------------------------------------------------- */
2906: int     irow=0, icol=0;         /* indexes over rp raster */
2907: int     ipix = 0,               /* raster pixmap[] index */
2908:         npix = rp->width * rp->height; /* #pixels malloced in rp->pixmap[] */
2909: int     isfatal = 0;            /* true to abend on out-of-bounds error */
2910: int     hdash=1, vdash=2,       /* type for horizontal, vertical dashes */
2911:         bevel=99/*3*/, strut=4; /* type for bevel (turned off), strut */
2912: int     dashlen=3, spacelen=2,  /* #pixels for dash followed by space */
2913:         isdraw=1;               /* true when drawing dash (init for solid) */
2914: /* -------------------------------------------------------------------------
2915: Check args
2916: -------------------------------------------------------------------------- */
2917: if ( rp == (raster *)NULL ) {   /* no raster arg supplied */
2918:   if ( workingbox != (subraster *)NULL )  /* see if we have a workingbox */
2919:     rp = workingbox->image;     /* use workingbox if possible */
2920:   else return ( 0 ); }          /* otherwise signal error to caller */
2921: if ( type == bevel )            /* remove corners of solid box */
2922:   if ( width<3 || height<3 ) type=0; /* too small to remove corners */
2923: /* -------------------------------------------------------------------------
2924: Fill line/box
2925: -------------------------------------------------------------------------- */
2926: if ( width > 0 )                                /* zero width implies strut*/
2927:  for ( irow=top; irow<top+height; irow++ )      /* for each scan line */
2928:   {
2929:   if ( type == strut ) isdraw = 0;              /* draw nothing for strut */
2930:   if ( type == vdash )                          /*set isdraw for vert dash*/
2931:     isdraw = (((irow-top)%(dashlen+spacelen)) < dashlen);
2932:   ipix = irow*rp->width + left - 1;             /*first pixel preceding icol*/
2933:   for ( icol=left; icol<left+width; icol++ )    /* each pixel in scan line */
2934:     {
2935:     if ( type == bevel ) {                      /* remove corners of box */
2936:       if ( (irow==top && icol==left)            /* top-left corner */
2937:       ||   (irow==top && icol>=left+width-1)    /* top-right corner */
2938:       ||   (irow>=top+height-1 && icol==left)   /* bottom-left corner */
2939:       ||   (irow>=top+height-1 && icol>=left+width-1) ) /* bottom-right */
2940:         isdraw = 0;  else isdraw = 1; }         /*set isdraw to skip corner*/
2941:     if ( type == hdash )                        /*set isdraw for horiz dash*/
2942:       isdraw = (((icol-left)%(dashlen+spacelen)) < dashlen);
2943:     if ( ++ipix >= npix )                       /* bounds check failed */
2944:          if ( isfatal ) goto end_of_job;        /* abort if error is fatal */
2945:          else break;                            /*or just go on to next row*/
2946:     else                                        /*ibit is within rp bounds*/
2947:       if ( isdraw ) {                           /*and we're drawing this bit*/
2948:         if ( rp->pixsz == 1 )                   /* have a bitmap */
2949:           setlongbit(rp->pixmap,ipix);          /* so turn on bit in line */
2950:         else                                    /* should have a bytemap */
2951:          if ( rp->pixsz == 8 )                  /* check pixsz for bytemap */
2952:           ((unsigned char *)(rp->pixmap))[ipix] = 255; } /* set black byte */
2953:     } /* --- end-of-for(icol) --- */
2954:   } /* --- end-of-for(irow) --- */
2955: end_of_job:
2956:   return ( isfatal? (ipix<npix? 1:0) : 1 );
2957: } /* --- end-of-function rule_raster() --- */
2958: 
2959: 
2960: /* ==========================================================================
2961:  * Function:    line_raster ( rp,  row0, col0,  row1, col1,  thickness )
2962:  * Purpose:     Draw a line from row0,col0 to row1,col1 of thickness
2963:  *              in existing raster rp.
2964:  * --------------------------------------------------------------------------
2965:  * Arguments:   rp (I)          raster *  to raster in which a line
2966:  *                              will be drawn
2967:  *              row0 (I)        int containing row at which
2968:  *                              line will start (0 is topmost)
2969:  *              col0 (I)        int containing col at which
2970:  *                              line will start (0 is leftmost)
2971:  *              row1 (I)        int containing row at which
2972:  *                              line will end (rp->height-1 is bottom-most)
2973:  *              col1 (I)        int containing col at which
2974:  *                              line will end (rp->width-1 is rightmost)
2975:  *              thickness (I)   int containing number of pixels/bits
2976:  *                              thick the line will be
2977:  * --------------------------------------------------------------------------
2978:  * Returns:     ( int )         1 if line drawn okay,
2979:  *                              or 0 for any error.
2980:  * --------------------------------------------------------------------------
2981:  * Notes:     o if row0==row1, a horizontal line is drawn
2982:  *              between col0 and col1, with row0(==row1) the top row
2983:  *              and row0+(thickness-1) the bottom row
2984:  *            o if col0==col1, a vertical bar is drawn
2985:  *              between row0 and row1, with col0(==col1) the left col
2986:  *              and col0+(thickness-1) the right col
2987:  *            o if both the above, you get a square thickness x thickness
2988:  *              whose top-left corner is row0,col0.
2989:  * ======================================================================= */
2990: /* --- entry point --- */
2991: int     line_raster ( raster *rp, int row0, int col0,
2992:         int row1, int col1, int thickness )
2993: {
2994: /* -------------------------------------------------------------------------
2995: Allocations and Declarations
2996: -------------------------------------------------------------------------- */
2997: int     irow=0, icol=0,         /* indexes over rp raster */
2998:         locol=col0, hicol=col1, /* col limits at irow */
2999:         lorow=row0, hirow=row1; /* row limits at icol */
3000: int     width=rp->width, height=rp->height; /* dimensions of input raster */
3001: int     ipix = 0,               /* raster pixmap[] index */
3002:         npix = width*height;    /* #pixels malloced in rp->pixmap[] */
3003: int     isfatal = 0;            /* true to abend on out-of-bounds error */
3004: int     isline=(row1==row0), isbar=(col1==col0); /*true if slope a=0,\infty*/
3005: double  dy = row1-row0 /* + (row1>=row0? +1.0 : -1.0) */, /* delta-x */
3006:         dx = col1-col0 /* + (col1>=col0? +1.0 : -1.0) */, /* delta-y */
3007:         a= (isbar||isline? 0.0 : dy/dx), /* slope = tan(theta) = dy/dx */
3008:         xcol=0, xrow=0;         /* calculated col at irow, or row at icol */
3009: double  ar = ASPECTRATIO,       /* aspect ratio width/height of one pixel */
3010:         xwidth= (isline? 0.0 :  /*#pixels per row to get sloped line thcknss*/
3011:                 ((double)thickness)*sqrt((dx*dx)+(dy*dy*ar*ar))/fabs(dy*ar)),
3012:         xheight = 1.0;
3013: int     line_recurse(), isrecurse=1; /* true to draw line recursively */
3014: /* -------------------------------------------------------------------------
3015: Check args
3016: -------------------------------------------------------------------------- */
3017: if ( rp == (raster *)NULL ) {   /* no raster arg supplied */
3018:   if ( workingbox != (subraster *)NULL )  /* see if we have a workingbox */
3019:     rp = workingbox->image;     /* use workingbox if possible */
3020:   else return ( 0 ); }          /* otherwise signal error to caller */
3021: /* -------------------------------------------------------------------------
3022: Initialization
3023: -------------------------------------------------------------------------- */
3024: if ( msgfp!=NULL && msglevel>=29 ) {            /* debugging */
3025:    fprintf(msgfp,"line_raster> row,col0=%d,%d row,col1=%d,%d, thickness=%d\n"
3026:    "\t dy,dx=%3.1f,%3.1f, a=%4.3f, xwidth=%4.3f\n",
3027:    row0,col0, row1,col1, thickness,  dy,dx, a, xwidth); fflush(msgfp); }
3028: /* --- check for recursive line drawing --- */
3029: if ( isrecurse ) {              /* drawing lines recursively */
3030:  for ( irow=0; irow<thickness; irow++ )         /* each line 1 pixel thick */
3031:   { double xrow0=(double)row0, xcol0=(double)col0,
3032:         xrow1=(double)row1, xcol1=(double)col1;
3033:     if ( isline ) xrow0 = xrow1 = (double)(row0+irow);
3034:     else if ( isbar ) xcol0 = xcol1 = (double)(col0+irow);
3035:     if( xrow0>(-0.001) && xcol0>(-0.001)        /*check line inside raster*/
3036:     &&  xrow1<((double)(height-1)+0.001) && xcol1<((double)(width-1)+0.001) )
3037:       line_recurse(rp,xrow0,xcol0,xrow1,xcol1,thickness); }
3038:  return ( 1 ); }
3039: /* --- set params for horizontal line or vertical bar --- */
3040: if ( isline )                                   /*interpret row as top row*/
3041:   row1 = row0 + (thickness-1);                  /* set bottom row for line */
3042: if ( 0&&isbar )                                 /*interpret col as left col*/
3043:   hicol = col0 + (thickness-1);                 /* set right col for bar */
3044: /* -------------------------------------------------------------------------
3045: draw line one row at a time
3046: -------------------------------------------------------------------------- */
3047: for ( irow=min2(row0,row1); irow<=max2(row0,row1); irow++ ) /*each scan line*/
3048:   {
3049:   if ( !isbar && !isline )                      /* neither vert nor horiz */
3050:     { xcol  = col0 + ((double)(irow-row0))/a;   /* "middle" col in irow */
3051:       locol = max2((int)(xcol-0.5*(xwidth-1.0)),0); /* leftmost col */
3052:       hicol = min2((int)(xcol+0.5*(xwidth-0.0)),max2(col0,col1)); } /*right*/
3053:   if ( msgfp!=NULL && msglevel>=29 )            /* debugging */
3054:     fprintf(msgfp,"\t irow=%d, xcol=%4.2f, lo,hicol=%d,%d\n",
3055:     irow,xcol,locol,hicol);
3056:   ipix = irow*rp->width + min2(locol,hicol) - 1; /*first pix preceding icol*/
3057:   for ( icol=min2(locol,hicol); icol<=max2(locol,hicol); icol++ ) /*each pix*/
3058:     if ( ++ipix >= npix )                       /* bounds check failed */
3059:         if ( isfatal ) goto end_of_job; /* abort if error is fatal */
3060:         else break;                             /*or just go on to next row*/
3061:     else                                        /* turn on pixel in line */
3062:         if ( rp->pixsz == 1 )                   /* have a pixel bitmap */
3063:           setlongbit(rp->pixmap,ipix);          /* so turn on bit in line */
3064:         else                                    /* should have a bytemap */
3065:          if ( rp->pixsz == 8 )                  /* check pixsz for bytemap */
3066:           ((unsigned char *)(rp->pixmap))[ipix] = 255; /* set black byte */
3067:   } /* --- end-of-for(irow) --- */
3068: /* -------------------------------------------------------------------------
3069: now _redraw_ line one col at a time to avoid "gaps"
3070: -------------------------------------------------------------------------- */
3071: if ( 1 )
3072:  for ( icol=min2(col0,col1); icol<=max2(col0,col1); icol++ )/*each scan line*/
3073:   {
3074:   if ( !isbar && !isline )                      /* neither vert nor horiz */
3075:     { xrow  = row0 + ((double)(icol-col0))*a;   /* "middle" row in icol */
3076:       lorow = max2((int)(xrow-0.5*(xheight-1.0)),0); /* topmost row */
3077:       hirow = min2((int)(xrow+0.5*(xheight-0.0)),max2(row0,row1)); } /*bot*/
3078:   if ( msgfp!=NULL && msglevel>=29 )            /* debugging */
3079:     fprintf(msgfp,"\t icol=%d, xrow=%4.2f, lo,hirow=%d,%d\n",
3080:     icol,xrow,lorow,hirow);
3081:   ipix = irow*rp->width + min2(locol,hicol) - 1; /*first pix preceding icol*/
3082:   for ( irow=min2(lorow,hirow); irow<=max2(lorow,hirow); irow++ ) /*each pix*/
3083:     if ( irow<0 || irow>=rp->height
3084:     ||   icol<0 || icol>=rp->width )            /* bounds check */
3085:       if ( isfatal ) goto end_of_job;           /* abort if error is fatal */
3086:       else continue;                            /*or just go on to next row*/
3087:     else
3088:       setpixel(rp,irow,icol,255);               /* set pixel at irow,icol */
3089:   } /* --- end-of-for(irow) --- */
3090: /* -------------------------------------------------------------------------
3091: Back to caller with 1=okay, 0=failed.
3092: -------------------------------------------------------------------------- */
3093: end_of_job:
3094:   return ( isfatal? (ipix<npix? 1:0) : 1 );
3095: } /* --- end-of-function line_raster() --- */
3096: 
3097: 
3098: /* ==========================================================================
3099:  * Function:    line_recurse ( rp,  row0, col0,  row1, col1,  thickness )
3100:  * Purpose:     Draw a line from row0,col0 to row1,col1 of thickness
3101:  *              in existing raster rp.
3102:  * --------------------------------------------------------------------------
3103:  * Arguments:   rp (I)          raster *  to raster in which a line
3104:  *                              will be drawn
3105:  *              row0 (I)        double containing row at which
3106:  *                              line will start (0 is topmost)
3107:  *              col0 (I)        double containing col at which
3108:  *                              line will start (0 is leftmost)
3109:  *              row1 (I)        double containing row at which
3110:  *                              line will end (rp->height-1 is bottom-most)
3111:  *              col1 (I)        double containing col at which
3112:  *                              line will end (rp->width-1 is rightmost)
3113:  *              thickness (I)   int containing number of pixels/bits
3114:  *                              thick the line will be
3115:  * --------------------------------------------------------------------------
3116:  * Returns:     ( int )         1 if line drawn okay,
3117:  *                              or 0 for any error.
3118:  * --------------------------------------------------------------------------
3119:  * Notes:     o Recurses, drawing left- and right-halves of line
3120:  *              until a horizontal or vertical segment is found
3121:  * ======================================================================= */
3122: /* --- entry point --- */
3123: int     line_recurse ( raster *rp, double row0, double col0,
3124:         double row1, double col1, int thickness )
3125: {
3126: /* -------------------------------------------------------------------------
3127: Allocations and Declarations
3128: -------------------------------------------------------------------------- */
3129: double  delrow = fabs(row1-row0),       /* 0 if line horizontal */
3130:         delcol = fabs(col1-col0),       /* 0 if line vertical */
3131:         tolerance = 0.5;                /* draw line when it goes to point */
3132: double  midrow = 0.5*(row0+row1),       /* midpoint row */
3133:         midcol = 0.5*(col0+col1);       /* midpoint col */
3134: /* -------------------------------------------------------------------------
3135: recurse if either delta > tolerance
3136: -------------------------------------------------------------------------- */
3137: if ( delrow > tolerance                 /* row hasn't converged */
3138: ||   delcol > tolerance )               /* col hasn't converged */
3139:   { line_recurse(rp,row0,col0,midrow,midcol,thickness); /* left half */
3140:     line_recurse(rp,midrow,midcol,row1,col1,thickness); /* right half */
3141:     return ( 1 ); }
3142: /* -------------------------------------------------------------------------
3143: draw converged point
3144: -------------------------------------------------------------------------- */
3145: setpixel(rp,iround(midrow),iround(midcol),255); /*set pixel at midrow,midcol*/
3146: return ( 1 );
3147: } /* --- end-of-function line_recurse() --- */
3148: 
3149: 
3150: /* ==========================================================================
3151:  * Function:    circle_raster ( rp,  row0, col0,  row1, col1,
3152:  *              thickness, quads )
3153:  * Purpose:     Draw quad(rant)s of an ellipse in box determined by
3154:  *              diagonally opposite corner points (row0,col0) and
3155:  *              (row1,col1), of thickness pixels in existing raster rp.
3156:  * --------------------------------------------------------------------------
3157:  * Arguments:   rp (I)          raster *  to raster in which an ellipse
3158:  *                              will be drawn
3159:  *              row0 (I)        int containing 1st corner row bounding ellipse
3160:  *                              (0 is topmost)
3161:  *              col0 (I)        int containing 1st corner col bounding ellipse
3162:  *                              (0 is leftmost)
3163:  *              row1 (I)        int containing 2nd corner row bounding ellipse
3164:  *                              (rp->height-1 is bottom-most)
3165:  *              col1 (I)        int containing 2nd corner col bounding ellipse
3166:  *                              (rp->width-1 is rightmost)
3167:  *              thickness (I)   int containing number of pixels/bits
3168:  *                              thick the ellipse arc line will be
3169:  *              quads (I)       char * to null-terminated string containing
3170:  *                              any subset/combination of "1234" specifying
3171:  *                              which quadrant(s) of ellipse to draw.
3172:  *                              NULL ptr draws all four quadrants;
3173:  *                              otherwise 1=upper-right quadrant,
3174:  *                              2=uper-left, 3=lower-left, 4=lower-right,
3175:  *                              i.e., counterclockwise from 1=positive quad.
3176:  * --------------------------------------------------------------------------
3177:  * Returns:     ( int )         1 if ellipse drawn okay,
3178:  *                              or 0 for any error.
3179:  * --------------------------------------------------------------------------
3180:  * Notes:     o row0==row1 or col0==col1 are errors
3181:  *            o using ellipse equation x^2/a^2 + y^2/b^2 = 1
3182:  * ======================================================================= */
3183: /* --- entry point --- */
3184: int     circle_raster ( raster *rp, int row0, int col0,
3185:         int row1, int col1, int thickness, char *quads )
3186: {
3187: /* -------------------------------------------------------------------------
3188: Allocations and Declarations
3189: -------------------------------------------------------------------------- */
3190: /* --- lower-left and upper-right bounding points (in our coords) --- */
3191: int     lorow = min2(row0,row1),        /* lower bounding row (top of box) */
3192:         locol = min2(col0,col1),        /* lower bounding col (left of box)*/
3193:         hirow = max2(row0,row1),        /* upper bounding row (bot of box) */
3194:         hicol = max2(col0,col1);        /* upper bounding col (right of box)*/
3195: /* --- a and b ellipse params --- */
3196: int     width = hicol-locol+1,          /* width of bounding box */
3197:         height= hirow-lorow+1,          /* height of bounding box */
3198:         islandscape = (width>=height? 1:0); /*true if ellipse lying on side*/
3199: double  a = ((double)width)/2.0,        /* x=a when y=0 */
3200:         b = ((double)height)/2.0,       /* y=b when x=0 */
3201:         abmajor = (islandscape? a : b), /* max2(a,b) */
3202:         abminor = (islandscape? b : a), /* min2(a,b) */
3203:         abmajor2 = abmajor*abmajor,     /* abmajor^2 */
3204:         abminor2 = abminor*abminor;     /* abminor^2 */
3205: /* --- other stuff --- */
3206: int     imajor=0, nmajor=max2(width,height), /*index, #pixels on major axis*/
3207:         iminor=0, nminor=min2(width,height); /* solved index on minor axis */
3208: int     irow, icol,                     /* raster indexes at circumference */
3209:         rsign=1, csign=1;               /* row,col signs, both +1 in quad 1*/
3210: double  midrow= ((double)(row0+row1))/2.0, /* center row */
3211:         midcol= ((double)(col0+col1))/2.0; /* center col */
3212: double  xy, xy2,                        /* major axis ellipse coord */
3213:         yx2, yx;                        /* solved minor ellipse coord */
3214: int     isokay = 1;                     /* true if no pixels out-of-bounds */
3215: char    *qptr=NULL, *allquads="1234";   /* quadrants if input quads==NULL */
3216: int     circle_recurse(), isrecurse=1;  /* true to draw ellipse recursively*/
3217: /* -------------------------------------------------------------------------
3218: pixel-by-pixel along positive major axis, quit when it goes negative
3219: -------------------------------------------------------------------------- */
3220: if ( quads == NULL ) quads = allquads;  /* draw all quads, or only user's */
3221: if ( msgfp!=NULL && msglevel>=39 )      /* debugging */
3222:   fprintf(msgfp,"circle_raster> width,height;quads=%d,%d,%s\n",
3223:   width,height,quads);
3224: if ( nmajor < 1 ) isokay = 0;           /* problem with input args */
3225: else
3226:  {
3227:  if ( isrecurse )                       /* use recursive algorithm */
3228:   {
3229:   for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */
3230:    {
3231:    double theta0=0.0, theta1=0.0;       /* set thetas based on quadrant */
3232:    switch ( *qptr )                     /* check for quadrant 1,2,3,4 */
3233:     { default:                          /* unrecognized, assume quadrant 1 */
3234:       case '1': theta0=  0.0; theta1= 90.0; break;   /* first quadrant */
3235:       case '2': theta0= 90.0; theta1=180.0; break;   /* second quadrant */
3236:       case '3': theta0=180.0; theta1=270.0; break;   /* third quadrant */
3237:       case '4': theta0=270.0; theta1=360.0; break; } /* fourth quadrant */
3238:    circle_recurse(rp,row0,col0,row1,col1,thickness,theta0,theta1);
3239:    } /* --- end-of-for(qptr) --- */
3240:   return ( 1 );
3241:   } /* --- end-of-if(isrecurse) --- */
3242:  for ( imajor=(nmajor+1)/2; ; imajor-- )
3243:   {
3244:   /* --- xy is coord along major axis, yx is "solved" along minor axis --- */
3245:   xy  = ((double)imajor);               /* xy = abmajor ... 0 */
3246:   if ( xy < 0.0 ) break;                /* negative side symmetrical */
3247:   yx2 = abminor2*(1.0 - xy*xy/abmajor2); /* "solve" ellipse equation */
3248:   yx  = (yx2>0.0? sqrt(yx2) : 0.0);     /* take sqrt if possible */
3249:   iminor = iround(yx);                  /* nearest integer */
3250:   /* --- set pixels for each requested quadrant --- */
3251:   for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */
3252:    {
3253:    rsign = (-1);  csign = 1;            /* init row,col in user quadrant 1 */
3254:    switch ( *qptr )                     /* check for quadrant 1,2,3,4 */
3255:     { default: break;                   /* unrecognized, assume quadrant 1 */
3256:       case '4': rsign = 1; break;       /* row,col both pos in quadrant 4 */
3257:       case '3': rsign = 1;              /* row pos, col neg in quadrant 3 */
3258:       case '2': csign = (-1); break; }  /* row,col both neg in quadrant 2 */
3259:    irow = iround(midrow + (double)rsign*(islandscape?yx:xy));
3260:    irow = min2(hirow,max2(lorow,irow)); /* keep irow in bounds */
3261:    icol = iround(midcol + (double)csign*(islandscape?xy:yx));
3262:    icol = min2(hicol,max2(locol,icol)); /* keep icol in bounds */
3263:    if ( msgfp!=NULL && msglevel>=49 )   /* debugging */
3264:      fprintf(msgfp,"\t...imajor=%d; iminor,quad,irow,icol=%d,%c,%d,%d\n",
3265:      imajor,iminor,*qptr,irow,icol);
3266:    if ( irow<0 || irow>=rp->height      /* row outside raster */
3267:    ||   icol<0 || icol>=rp->width )     /* col outside raster */
3268:       { isokay = 0;                     /* signal out-of-bounds pixel */
3269:         continue; }                     /* but still try remaining points */
3270:    setpixel(rp,irow,icol,255);          /* set pixel at irow,icol */
3271:    } /* --- end-of-for(qptr) --- */
3272:   } /* --- end-of-for(imajor) --- */
3273:  /* ------------------------------------------------------------------------
3274:  now do it _again_ along minor axis to avoid "gaps"
3275:  ------------------------------------------------------------------------- */
3276:  if ( 1 && iminor>0 )
3277:   for ( iminor=(nminor+1)/2; ; iminor-- )
3278:    {
3279:    /* --- yx is coord along minor axis, xy is "solved" along major axis --- */
3280:    yx  = ((double)iminor);              /* yx = abminor ... 0 */
3281:    if ( yx < 0.0 ) break;               /* negative side symmetrical */
3282:    xy2 = abmajor2*(1.0 - yx*yx/abminor2); /* "solve" ellipse equation */
3283:    xy  = (xy2>0.0? sqrt(xy2) : 0.0);    /* take sqrt if possible */
3284:    imajor = iround(xy);                 /* nearest integer */
3285:    /* --- set pixels for each requested quadrant --- */
3286:    for ( qptr=quads; *qptr!='\000'; qptr++ ) /* for each character in quads */
3287:     {
3288:     rsign = (-1);  csign = 1;           /* init row,col in user quadrant 1 */
3289:     switch ( *qptr )                    /* check for quadrant 1,2,3,4 */
3290:      { default: break;                  /* unrecognized, assume quadrant 1 */
3291:        case '4': rsign = 1; break;      /* row,col both pos in quadrant 4 */
3292:        case '3': rsign = 1;             /* row pos, col neg in quadrant 3 */
3293:        case '2': csign = (-1); break; } /* row,col both neg in quadrant 2 */
3294:     irow = iround(midrow + (double)rsign*(islandscape?yx:xy));
3295:     irow = min2(hirow,max2(lorow,irow)); /* keep irow in bounds */
3296:     icol = iround(midcol + (double)csign*(islandscape?xy:yx));
3297:     icol = min2(hicol,max2(locol,icol)); /* keep icol in bounds */
3298:     if ( msgfp!=NULL && msglevel>=49 )  /* debugging */
3299:      fprintf(msgfp,"\t...iminor=%d; imajor,quad,irow,icol=%d,%c,%d,%d\n",
3300:      iminor,imajor,*qptr,irow,icol);
3301:     if ( irow<0 || irow>=rp->height     /* row outside raster */
3302:     ||   icol<0 || icol>=rp->width )    /* col outside raster */
3303:       { isokay = 0;                     /* signal out-of-bounds pixel */
3304:         continue; }                     /* but still try remaining points */
3305:     setpixel(rp,irow,icol,255);         /* set pixel at irow,icol */
3306:     } /* --- end-of-for(qptr) --- */
3307:    } /* --- end-of-for(iminor) --- */
3308:  } /* --- end-of-if/else(nmajor<1) --- */
3309: return ( isokay );
3310: } /* --- end-of-function circle_raster() --- */
3311: 
3312: 
3313: /* ==========================================================================
3314:  * Function:    circle_recurse ( rp,  row0, col0,  row1, col1,
3315:  *              thickness, theta0, theta1 )
3316:  * Purpose:     Recursively draws arc theta0<=theta<=theta1 of the ellipse
3317:  *              in box determined by diagonally opposite corner points
3318:  *              (row0,col0) and (row1,col1), of thickness pixels in raster rp.
3319:  * --------------------------------------------------------------------------
3320:  * Arguments:   rp (I)          raster *  to raster in which an ellipse
3321:  *                              will be drawn
3322:  *              row0 (I)        int containing 1st corner row bounding ellipse
3323:  *                              (0 is topmost)
3324:  *              col0 (I)        int containing 1st corner col bounding ellipse
3325:  *                              (0 is leftmost)
3326:  *              row1 (I)        int containing 2nd corner row bounding ellipse
3327:  *                              (rp->height-1 is bottom-most)
3328:  *              col1 (I)        int containing 2nd corner col bounding ellipse
3329:  *                              (rp->width-1 is rightmost)
3330:  *              thickness (I)   int containing number of pixels/bits
3331:  *                              thick the ellipse arc line will be
3332:  *              theta0 (I)      double containing first angle -360 -> +360
3333:  *              theta1 (I)      double containing second angle -360 -> +360
3334:  *                              0=x-axis, positive moving counterclockwise
3335:  * --------------------------------------------------------------------------
3336:  * Returns:     ( int )         1 if ellipse drawn okay,
3337:  *                              or 0 for any error.
3338:  * --------------------------------------------------------------------------
3339:  * Notes:     o row0==row1 or col0==col1 are errors
3340:  *            o using ellipse equation x^2/a^2 + y^2/b^2 = 1
3341:  *              Then, with x=r*cos(theta), y=r*sin(theta), ellipse
3342:  *              equation is r = ab/sqrt(a^2*sin^2(theta)+b^2*cos^2(theta))
3343:  * ======================================================================= */
3344: /* --- entry point --- */
3345: int     circle_recurse ( raster *rp, int row0, int col0,
3346:         int row1, int col1, int thickness, double theta0, double theta1 )
3347: {
3348: /* -------------------------------------------------------------------------
3349: Allocations and Declarations
3350: -------------------------------------------------------------------------- */
3351: /* --- lower-left and upper-right bounding points (in our coords) --- */
3352: int     lorow = min2(row0,row1),        /* lower bounding row (top of box) */
3353:         locol = min2(col0,col1),        /* lower bounding col (left of box)*/
3354:         hirow = max2(row0,row1),        /* upper bounding row (bot of box) */
3355:         hicol = max2(col0,col1);        /* upper bounding col (right of box)*/
3356: /* --- a and b ellipse params --- */
3357: int     width = hicol-locol+1,          /* width of bounding box */
3358:         height= hirow-lorow+1;          /* height of bounding box */
3359: double  a = ((double)width)/2.0,        /* col x=a when row y=0 */
3360:         b = ((double)height)/2.0,       /* row y=b when col x=0 */
3361:         ab=a*b, a2=a*a, b2=b*b;         /* product and squares */
3362: /* --- arc parameters --- */
3363: double  rads = 0.017453292,             /* radians per degree = 1/57.29578 */
3364:         lotheta = rads*dmod(min2(theta0,theta1),360), /* smaller angle */
3365:         hitheta = rads*dmod(max2(theta0,theta1),360), /* larger angle */
3366:         locos=cos(lotheta), losin=sin(lotheta), /* trigs for lotheta */
3367:         hicos=cos(hitheta), hisin=sin(hitheta), /* trigs for hitheta */
3368:         rlo = ab/sqrt(b2*locos*locos+a2*losin*losin), /* r for lotheta */
3369:         rhi = ab/sqrt(b2*hicos*hicos+a2*hisin*hisin), /* r for hitheta */
3370:         xlo=rlo*locos, ylo=rlo*losin,   /*col,row pixel coords for lotheta*/
3371:         xhi=rhi*hicos, yhi=rhi*hisin,   /*col,row pixel coords for hitheta*/
3372:         xdelta=fabs(xhi-xlo), ydelta=fabs(yhi-ylo), /* col,row deltas */
3373:         tolerance = 0.5;                /* convergence tolerance */
3374: /* -------------------------------------------------------------------------
3375: recurse if either delta > tolerance
3376: -------------------------------------------------------------------------- */
3377: if ( ydelta > tolerance                 /* row hasn't converged */
3378: ||   xdelta > tolerance )               /* col hasn't converged */
3379:   { double midtheta = 0.5*(theta0+theta1); /* mid angle for arc */
3380:     circle_recurse(rp,row0,col0,row1,col1,thickness,theta0,midtheta);  /*lo*/
3381:     circle_recurse(rp,row0,col0,row1,col1,thickness,midtheta,theta1); }/*hi*/
3382: /* -------------------------------------------------------------------------
3383: draw converged point
3384: -------------------------------------------------------------------------- */
3385: else
3386:   { double xcol=0.5*(xlo+xhi), yrow=0.5*(ylo+yhi),    /* relative to center*/
3387:         centerrow = 0.5*((double)(lorow+hirow)),      /* ellipse y-center */
3388:         centercol = 0.5*((double)(locol+hicol)),      /* ellipse x-center */
3389:         midrow=centerrow-yrow, midcol=centercol+xcol; /* pixel coords */
3390:     setpixel(rp,iround(midrow),iround(midcol),255); } /* set midrow,midcol */
3391: return ( 1 );
3392: } /* --- end-of-function circle_recurse() --- */
3393: 
3394: 
3395: /* ==========================================================================
3396:  * Function:    bezier_raster ( rp, r0,c0, r1,c1, rt,ct )
3397:  * Purpose:     Recursively draw bezier from r0,c0 to r1,c1
3398:  *              (with tangent point rt,ct) in existing raster rp.
3399:  * --------------------------------------------------------------------------
3400:  * Arguments:   rp (I)          raster *  to raster in which a line
3401:  *                              will be drawn
3402:  *              r0 (I)          double containing row at which
3403:  *                              bezier will start (0 is topmost)
3404:  *              c0 (I)          double containing col at which
3405:  *                              bezier will start (0 is leftmost)
3406:  *              r1 (I)          double containing row at which
3407:  *                              bezier will end (rp->height-1 is bottom-most)
3408:  *              c1 (I)          double containing col at which
3409:  *                              bezier will end (rp->width-1 is rightmost)
3410:  *              rt (I)          double containing row for tangent point
3411:  *              ct (I)          double containing col for tangent point
3412:  * --------------------------------------------------------------------------
3413:  * Returns:     ( int )         1 if line drawn okay,
3414:  *                              or 0 for any error.
3415:  * --------------------------------------------------------------------------
3416:  * Notes:     o Recurses, drawing left- and right-halves of bezier curve
3417:  *              until a point is found
3418:  * ======================================================================= */
3419: /* --- entry point --- */
3420: int     bezier_raster ( raster *rp, double r0, double c0,
3421:         double r1, double c1, double rt, double ct )
3422: {
3423: /* -------------------------------------------------------------------------
3424: Allocations and Declarations
3425: -------------------------------------------------------------------------- */
3426: double  delrow = fabs(r1-r0),           /* 0 if same row */
3427:         delcol = fabs(c1-c0),           /* 0 if same col */
3428:         tolerance = 0.5;                /* draw curve when it goes to point*/
3429: double  midrow = 0.5*(r0+r1),           /* midpoint row */
3430:         midcol = 0.5*(c0+c1);           /* midpoint col */
3431: int     irow=0, icol=0;                 /* point to be drawn */
3432: int     status = 1;                     /* return status */
3433: /* -------------------------------------------------------------------------
3434: recurse if either delta > tolerance
3435: -------------------------------------------------------------------------- */
3436: if ( delrow > tolerance                 /* row hasn't converged */
3437: ||   delcol > tolerance )               /* col hasn't converged */
3438:   { bezier_raster(rp, r0,c0,            /* left half */
3439:         0.5*(rt+midrow), 0.5*(ct+midcol),
3440:         0.5*(r0+rt), 0.5*(c0+ct) );
3441:     bezier_raster(rp, 0.5*(rt+midrow), 0.5*(ct+midcol), /* right half */
3442:         r1,c1,
3443:         0.5*(r1+rt), 0.5*(c1+ct) );
3444:     return ( 1 ); }
3445: /* -------------------------------------------------------------------------
3446: draw converged point
3447: -------------------------------------------------------------------------- */
3448: /* --- get integer point --- */
3449: irow = iround(midrow);                  /* row pixel coord */
3450: icol = iround(midcol);                  /* col pixel coord */
3451: /* --- bounds check --- */
3452: if ( irow>=0 && irow<rp->height         /* row in bounds */
3453: &&   icol>=0 && icol<rp->width )        /* col in bounds */
3454:         setpixel(rp,irow,icol,255);     /* so set pixel at irow,icol*/
3455: else    status = 0;                     /* bad status if out-of-bounds */
3456: return ( status );
3457: } /* --- end-of-function bezier_raster() --- */
3458: 
3459: 
3460: /* ==========================================================================
3461:  * Function:    border_raster ( rp, ntop, nbot, isline, isfree )
3462:  * Purpose:     Allocate a new raster containing a copy of input rp,
3463:  *              along with ntop extra rows at top and nbot at bottom,
3464:  *              and whose width is either adjusted correspondingly,
3465:  *              or is automatically enlarged to a multiple of 8
3466:  *              with original bitmap centered
3467:  * --------------------------------------------------------------------------
3468:  * Arguments:   rp (I)          raster *  to raster on which a border
3469:  *                              is to be placed
3470:  *              ntop (I)        int containing number extra rows at top.
3471:  *                              if negative, abs(ntop) used, and same
3472:  *                              number of extra cols added at left.
3473:  *              nbot (I)        int containing number extra rows at bottom.
3474:  *                              if negative, abs(nbot) used, and same
3475:  *                              number of extra cols added at right.
3476:  *              isline (I)      int containing 0 to leave border pixels clear
3477:  *                              or >0 to draw a line around border of
3478:  *                              thickness isline pixels.  See Notes below.
3479:  *              isfree (I)      int containing true to free rp before return
3480:  * --------------------------------------------------------------------------
3481:  * Returns:     ( raster * )    ptr to bordered raster,
3482:  *                              or NULL for any error.
3483:  * --------------------------------------------------------------------------
3484:  * Notes:     o The isline arg also controls which sides border lines
3485:  *              are drawn for.  To do this, isline is interpreted as
3486:  *              thickness + 100*sides  so that, e.g., passing isline=601
3487:  *              is interpreted as sides=6 and thickness=1.  And
3488:  *              sides is further interpreted as 1=left side, 2=top,
3489:  *              4=right and 8=bottom.  For example, sides=6 where 6=2+4
3490:  *              draws the top and right borders only.  15 draws all four
3491:  *              sides.  And 0 (no sides value embedded in isline)
3492:  *              draws all four sides, too.
3493:  * ======================================================================= */
3494: /* --- entry point --- */
3495: raster  *border_raster ( raster *rp, int ntop, int nbot,
3496:                         int isline, int isfree )
3497: {
3498: /* -------------------------------------------------------------------------
3499: Allocations and Declarations
3500: -------------------------------------------------------------------------- */
3501: raster  *new_raster(), *bp=(raster *)NULL;  /*raster back to caller*/
3502: int     rastput();              /* overlay rp in new bordered raster */
3503: int     width  = (rp==NULL?0:rp->width),  /* width of raster */
3504:         height = (rp==NULL?0:rp->height), /* height of raster */
3505:         istopneg=0, isbotneg=0,         /* true if ntop or nbot negative */
3506:         leftmargin = 0;         /* adjust width to whole number of bytes */
3507: int     left=1, top=1, right=1, bot=1;  /* frame sides to draw */
3508: int     delete_raster();                /* free input rp if isfree is true */
3509: /* -------------------------------------------------------------------------
3510: Initialization
3511: -------------------------------------------------------------------------- */
3512: if ( rp == NULL ) goto end_of_job;      /* no input raster provided */
3513: if ( isstring || (1 && rp->height==1) ) /* explicit string signal or infer */
3514:   { bp=rp; goto end_of_job; }           /* return ascii string unchanged */
3515: /* --- check for negative args --- */
3516: if ( ntop < 0 ) { ntop = -ntop; istopneg=1; } /*flip positive and set flag*/
3517: if ( nbot < 0 ) { nbot = -nbot; isbotneg=1; } /*flip positive and set flag*/
3518: /* --- adjust height for ntop and nbot margins --- */
3519: height += (ntop+nbot);                  /* adjust height for margins */
3520: /* --- adjust width for left and right margins --- */
3521: if ( istopneg || isbotneg )     /*caller wants nleft=ntop and/or nright=nbot*/
3522:   { /* --- adjust width (and leftmargin) as requested by caller -- */
3523:     if ( istopneg ) { width += ntop; leftmargin = ntop; }
3524:     if ( isbotneg )   width += nbot;  }
3525: else
3526:   { /* --- or adjust width (and leftmargin) to whole number of bytes --- */
3527:     leftmargin = (width%8==0? 0 : 8-(width%8)); /*makes width multiple of 8*/
3528:     width += leftmargin;                /* width now multiple of 8 */
3529:     leftmargin /= 2; }                  /* center original raster */
3530: /* --- check which sides to draw --- */
3531: if ( isline > 100 ) {                   /* sides arg embedded in isline */
3532:   int iside=0, sides=isline/100;        /* index, sides=1-15 from 101-1599 */
3533:   isline -= 100*sides;                  /* and remove sides from isline */
3534:   for ( iside=1; iside<=4; iside++ ) {  /* check left, top, right, bot */
3535:     int shift = sides/2;                /* shift sides left one bit */
3536:     if ( sides == 2*shift )             /* low-order bit is >>not<< set */
3537:       switch ( iside ) {                /* don't draw corresponding side */
3538:         default: break;                 /* internal error */
3539:         case 1: left = 0; break;        /* 1 = left side */
3540:         case 2: top  = 0; break;        /* 2 = top side */
3541:         case 3: right= 0; break;        /* 4 = tight side */
3542:         case 4: bot  = 0; break; }      /* 8 = bottom side */
3543:     sides = shift;                      /* ready for next side */
3544:     } /* --- end-of-for(iside) --- */
3545:   } /* --- end-of-if(isline>100) --- */
3546: /* -------------------------------------------------------------------------
3547: allocate bordered raster, and embed rp within it
3548: -------------------------------------------------------------------------- */
3549: /* --- allocate bordered raster --- */
3550: if ( (bp=new_raster(width,height,rp->pixsz))  /*allocate bordered raster*/
3551: ==   (raster *)NULL ) goto end_of_job;  /* and quit if failed */
3552: /* --- embed rp in it --- */
3553: rastput(bp,rp,ntop,leftmargin,1);       /* rp embedded in bp */
3554: /* -------------------------------------------------------------------------
3555: draw border if requested
3556: -------------------------------------------------------------------------- */
3557: if ( isline )
3558:  { int  irow, icol, nthick=isline;      /*height,width index, line thickness*/
3559:   /* --- draw left- and right-borders --- */
3560:   for ( irow=0; irow<height; irow++ )   /* for each row of bp */
3561:     for ( icol=0; icol<nthick; icol++ ) /* and each pixel of thickness */
3562:       { if(left){setpixel(bp,irow,icol,255);}           /* left border */
3563:         if(right){setpixel(bp,irow,width-1-icol,255);} } /* right border */
3564:   /* --- draw top- and bottom-borders --- */
3565:   for ( icol=0; icol<width; icol++ )    /* for each col of bp */
3566:     for ( irow=0; irow<nthick; irow++ ) /* and each pixel of thickness */
3567:       { if(top){setpixel(bp,irow,icol,255);}            /* top border */
3568:         if(bot){setpixel(bp,height-1-irow,icol,255);} } /* bottom border */
3569:  } /* --- end-of-if(isline) --- */
3570: /* -------------------------------------------------------------------------
3571: free rp if no longer needed
3572: -------------------------------------------------------------------------- */
3573: if ( isfree )                                   /*caller no longer needs rp*/
3574:   delete_raster(rp);                            /* so free it for him */
3575: /* -------------------------------------------------------------------------
3576: Back to caller with bordered raster (or null for any error)
3577: -------------------------------------------------------------------------- */
3578: end_of_job:
3579:   return ( bp );                        /* back with bordered or null ptr */
3580: } /* --- end-of-function border_raster() --- */
3581: 
3582: 
3583: /* ==========================================================================
3584:  * Function:    backspace_raster ( rp, nback, pback, minspace, isfree )
3585:  * Purpose:     Allocate a new raster containing a copy of input rp,
3586:  *              but with trailing nback columns removed.
3587:  *              If minspace>=0 then (at least) that many columns
3588:  *              of whitespace will be left in place, regardless of nback.
3589:  *              If minspace<0 then existing black pixels will be deleted
3590:  *              as required.
3591:  * --------------------------------------------------------------------------
3592:  * Arguments:   rp (I)          raster *  to raster on which a border
3593:  *                              is to be placed
3594:  *              nback (I)       int containing number of columns to
3595:  *                              backspace (>=0)
3596:  *              pback (O)       ptr to int returning #pixels actually
3597:  *                              backspaced (or NULL to not use)
3598:  *              minspace (I)    int containing number of columns
3599:  *                              of whitespace to be left in place
3600:  *              isfree (I)      int containing true to free rp before return
3601:  * --------------------------------------------------------------------------
3602:  * Returns:     ( raster * )    ptr to backspaced raster,
3603:  *                              or NULL for any error.
3604:  * --------------------------------------------------------------------------
3605:  * Notes:     o For \! negative space, for \hspace{-10}, etc.
3606:  * ======================================================================= */
3607: /* --- entry point --- */
3608: raster  *backspace_raster ( raster *rp, int nback, int *pback, int minspace,
3609:         int isfree )
3610: {
3611: /* -------------------------------------------------------------------------
3612: Allocations and Declarations
3613: -------------------------------------------------------------------------- */
3614: raster  *new_raster(), *bp=(raster *)NULL;  /* raster returned to caller */
3615: int     delete_raster();                /* free input rp if isfree is true */
3616: int     width  = (rp==NULL?0:rp->width),  /* original width of raster */
3617:         height = (rp==NULL?0:rp->height), /* height of raster */
3618:         mback = nback,                  /* nback adjusted for minspace */
3619:         newwidth = width,               /* adjusted width after backspace */
3620:         icol=0, irow=0;                 /* col,row index */
3621: if ( rp == NULL ) goto end_of_job;      /* no input given */
3622: /* -------------------------------------------------------------------------
3623: locate rightmost column of rp containing ink, and determine backspaced width
3624: -------------------------------------------------------------------------- */
3625: /* --- locate rightmost column of rp containing ink --- */
3626: if ( minspace >= 0 )                    /* only needed if given minspace */
3627:  for ( icol=width-1; icol>=0; icol-- )  /* find first non-empty col in row */
3628:   for ( irow=0; irow<height; irow++ )   /* for each row inside rp */
3629:    if ( getpixel(rp,irow,icol) != 0 ) { /* found a set pixel */
3630:      int whitecols = (width-1) - icol;  /* #cols containing white space */
3631:      mback = min2(nback,max2(0,whitecols-minspace)); /*leave minspace cols*/
3632:      goto gotright; }                   /* no need to look further */
3633: /* --- determine width of new backspaced raster --- */
3634: gotright:                               /* found col with ink (or rp empty)*/
3635:   if ( mback > width ) mback = width;   /* can't backspace before beginning*/
3636:   newwidth = max2(1,width-mback);       /* #cols in backspaced raster */
3637:   if ( pback != NULL ) *pback = width-newwidth; /* caller wants #pixels */
3638: /* -------------------------------------------------------------------------
3639: allocate new raster and fill it with leftmost cols of rp
3640: -------------------------------------------------------------------------- */
3641: /* --- allocate backspaced raster --- */
3642: if ( (bp=new_raster(newwidth,height,rp->pixsz)) /*allocate backspaced raster*/
3643: ==   (raster *)NULL ) goto end_of_job;  /* and quit if failed */
3644: /* --- fill new raster --- */
3645: if ( 1 || width-nback > 0 )             /* don't fill 1-pixel wide empty bp*/
3646:  for ( icol=0; icol<newwidth; icol++ )  /* find first non-empty col in row */
3647:   for ( irow=0; irow<height; irow++ )   /* for each row inside rp */
3648:     { int value = getpixel(rp,irow,icol); /* original pixel at irow,icol */
3649:       setpixel(bp,irow,icol,value); }   /* saved in backspaced raster */
3650: /* -------------------------------------------------------------------------
3651: Back to caller with backspaced raster (or null for any error)
3652: -------------------------------------------------------------------------- */
3653: end_of_job:
3654:   if ( msgfp!=NULL && msglevel>=999 ) { fprintf(msgfp, /* diagnostics */
3655:    "backspace_raster> nback=%d,minspace=%d,mback=%d, width:old=%d,new=%d\n",
3656:    nback,minspace,mback,width,newwidth); fflush(msgfp); }
3657:   if ( isfree && bp!=NULL ) delete_raster(rp); /* free original raster */
3658:   return ( bp );                        /* back with backspaced or null ptr*/
3659: } /* --- end-of-function backspace_raster() --- */
3660: 
3661: 
3662: /* ==========================================================================
3663:  * Function:    type_raster ( rp, fp )
3664:  * Purpose:     Emit an ascii dump representing rp, on fp.
3665:  * --------------------------------------------------------------------------
3666:  * Arguments:   rp (I)          ptr to raster struct for which an
3667:  *                              ascii dump is to be constructed.
3668:  *              fp (I)          File ptr to output device (defaults to
3669:  *                              stdout if passed as NULL).
3670:  * --------------------------------------------------------------------------
3671:  * Returns:     ( int )         1 if completed successfully,
3672:  *                              or 0 otherwise (for any error).
3673:  * --------------------------------------------------------------------------
3674:  * Notes:
3675:  * ======================================================================= */
3676: /* --- entry point --- */
3677: int     type_raster ( raster *rp, FILE *fp )
3678: {
3679: /* -------------------------------------------------------------------------
3680: Allocations and Declarations
3681: -------------------------------------------------------------------------- */
3682: static  int display_width = 72;         /* max columns for display */
3683: static  char display_chars[16] =        /* display chars for bytemap */
3684:         { ' ','1','2','3','4','5','6','7','8','9','A','B','C','D','E','*' };
3685: char    scanline[133];                  /* ascii image for one scan line */
3686: int     scan_width;                     /* #chars in scan (<=display_width)*/
3687: int     irow, locol,hicol=(-1);         /* height index, width indexes */
3688: raster  *gftobitmap(), *bitmaprp=rp;    /* convert .gf to bitmap if needed */
3689: int     delete_raster();                /*free bitmap converted for display*/
3690: /* --------------------------------------------------------------------------
3691: initialization
3692: -------------------------------------------------------------------------- */
3693: /* --- redirect null fp --- */
3694: if ( fp == (FILE *)NULL ) fp = stdout;  /* default fp to stdout if null */
3695: if ( msglevel >= 999 ) { fprintf(fp,    /* debugging diagnostics */
3696:   "type_raster> width=%d height=%d ...\n",
3697:   rp->width,rp->height); fflush(fp); }
3698: /* --- check for ascii string --- */
3699: if ( isstring                           /* pixmap has string, not raster */
3700: ||   (0 && rp->height==1) )             /* infer input rp is a string */
3701:  {
3702:  char *string = (char *)(rp->pixmap);   /*interpret pixmap as ascii string*/
3703:  int width = strlen(string);            /* #chars in ascii string */
3704:  while ( width > display_width-2 )      /* too big for one line */
3705:   { fprintf(fp,"\"%.*s\"\n",display_width-2,string); /*display leading chars*/
3706:     string += (display_width-2);        /* bump string past displayed chars*/
3707:     width -= (display_width-2); }       /* decrement remaining width */
3708:  fprintf(fp,"\"%.*s\"\n",width,string); /* display trailing chars */
3709:  return ( 1 );
3710:  } /* --- end-of-if(isstring) --- */
3711: /* --------------------------------------------------------------------------
3712: display ascii dump of bitmap image (in segments if display_width < rp->width)
3713: -------------------------------------------------------------------------- */
3714: if ( rp->format == 2                    /* input is .gf-formatted */
3715: ||   rp->format == 3 )
3716:   bitmaprp = gftobitmap(rp);            /* so convert it for display */
3717: if ( bitmaprp != NULL )                 /* if we have image for display */
3718:  while ( (locol=hicol+1) < rp->width )  /*start where prev segment left off*/
3719:   {
3720:   /* --- set hicol for this pass (locol set above) --- */
3721:   hicol += display_width;               /* show as much as display allows */
3722:   if (hicol >= rp->width) hicol = rp->width - 1; /*but not more than raster*/
3723:   scan_width = hicol-locol+1;           /* #chars in this scan */
3724:   if ( locol > 0 ) fprintf(fp,"----------\n"); /*separator between segments*/
3725:   /* ------------------------------------------------------------------------
3726:   display all scan lines for this local...hicol segment range
3727:   ------------------------------------------------------------------------ */
3728:   for ( irow=0; irow<rp->height; irow++ )  /* all scan lines for col range */
3729:     {
3730:     /* --- allocations and declarations --- */
3731:     int ipix,                           /* pixmap[] index for this scan */
3732:         lopix = irow*rp->width + locol; /*first pixmap[] pixel in this scan*/
3733:     /* --- set chars in scanline[] based on pixels in rp->pixmap[] --- */
3734:     for ( ipix=0; ipix<scan_width; ipix++ ) /* set each char */
3735:       if ( bitmaprp->pixsz == 1 )       /*' '=0 or '*'=1 to display bitmap*/
3736:         scanline[ipix]=(getlongbit(bitmaprp->pixmap,lopix+ipix)==1? '*':'.');
3737:       else                              /* should have a bytemap */
3738:        if ( bitmaprp->pixsz == 8 )      /* double-check pixsz for bytemap */
3739:         { int pixval = (int)((bitmaprp->pixmap)[lopix+ipix]), /*byte value*/
3740:           ichar = min2(15,pixval/16);   /* index for ' ', '1'...'e', '*' */
3741:           scanline[ipix] = display_chars[ichar]; } /*set ' ' for 0-15, etc*/
3742:     /* --- display completed scan line --- */
3743:     fprintf(fp,"%.*s\n",scan_width,scanline);
3744:     } /* --- end-of-for(irow) --- */
3745:   } /* --- end-of-while(hicol<rp->width) --- */
3746: /* -------------------------------------------------------------------------
3747: Back to caller with 1=okay, 0=failed.
3748: -------------------------------------------------------------------------- */
3749: if ( rp->format == 2                    /* input was .gf-format */
3750: ||   rp->format == 3 )
3751:   if ( bitmaprp != NULL )               /* and we converted it for display */
3752:     delete_raster(bitmaprp);            /* no longer needed, so free it */
3753: return ( 1 );
3754: } /* --- end-of-function type_raster() --- */
3755: 
3756: 
3757: /* ==========================================================================
3758:  * Function:    type_bytemap ( bp, grayscale, width, height, fp )
3759:  * Purpose:     Emit an ascii dump representing bp, on fp.
3760:  * --------------------------------------------------------------------------
3761:  * Arguments:   bp (I)          intbyte * to bytemap for which an
3762:  *                              ascii dump is to be constructed.
3763:  *              grayscale (I)   int containing #gray shades, 256 for 8-bit
3764:  *              width (I)       int containing #cols in bytemap
3765:  *              height (I)      int containing #rows in bytemap
3766:  *              fp (I)          File ptr to output device (defaults to
3767:  *                              stdout if passed as NULL).
3768:  * --------------------------------------------------------------------------
3769:  * Returns:     ( int )         1 if completed successfully,
3770:  *                              or 0 otherwise (for any error).
3771:  * --------------------------------------------------------------------------
3772:  * Notes:
3773:  * ======================================================================= */
3774: /* --- entry point --- */
3775: int     type_bytemap ( intbyte *bp, int grayscale,
3776:                         int width, int height, FILE *fp )
3777: {
3778: /* -------------------------------------------------------------------------
3779: Allocations and Declarations
3780: -------------------------------------------------------------------------- */
3781: static  int display_width = 72;         /* max columns for display */
3782: int     byte_width = 3,                 /* cols to display byte (ff+space) */
3783:         maxbyte = 0;                    /* if maxbyte<16, set byte_width=2 */
3784: int     white_byte = 0,                 /* show dots for white_byte's */
3785:         black_byte = grayscale-1;       /* show stars for black_byte's */
3786: char    scanline[133];                  /* ascii image for one scan line */
3787: int     scan_width,                     /* #chars in scan (<=display_width)*/
3788:         scan_cols;                      /* #cols in scan (hicol-locol+1) */
3789: int     ibyte,                          /* bp[] index */
3790:         irow, locol,hicol=(-1);         /* height index, width indexes */
3791: /* --------------------------------------------------------------------------
3792: initialization
3793: -------------------------------------------------------------------------- */
3794: /* --- redirect null fp --- */
3795: if ( fp == (FILE *)NULL ) fp = stdout;  /* default fp to stdout if null */
3796: /* --- check for ascii string --- */
3797: if ( isstring )                         /* bp has ascii string, not raster */
3798:  { width = strlen((char *)bp);          /* #chars in ascii string */
3799:    height = 1; }                        /* default */
3800: /* --- see if we can get away with byte_width=1 --- */
3801: for ( ibyte=0; ibyte<width*height; ibyte++ )  /* check all bytes */
3802:   { int byteval = (int)bp[ibyte];       /* current byte value */
3803:     if ( byteval < black_byte )         /* if it's less than black_byte */
3804:       maxbyte = max2(maxbyte,byteval); } /* then find max non-black value */
3805: if ( maxbyte < 16 )                     /* bytevals will fit in one column */
3806:   byte_width = 1;                       /* so reset display byte_width */
3807: /* --------------------------------------------------------------------------
3808: display ascii dump of bitmap image (in segments if display_width < rp->width)
3809: -------------------------------------------------------------------------- */
3810: while ( (locol=hicol+1) < width )       /*start where prev segment left off*/
3811:   {
3812:   /* --- set hicol for this pass (locol set above) --- */
3813:   hicol += display_width/byte_width;    /* show as much as display allows */
3814:   if (hicol >= width) hicol = width - 1; /* but not more than bytemap */
3815:   scan_cols = hicol-locol+1;            /* #cols in this scan */
3816:   scan_width = byte_width*scan_cols;    /* #chars in this scan */
3817:   if ( locol>0 && !isstring ) fprintf(fp,"----------\n"); /* separator */
3818:   /* ------------------------------------------------------------------------
3819:   display all scan lines for this local...hicol segment range
3820:   ------------------------------------------------------------------------ */
3821:   for ( irow=0; irow<height; irow++ )   /* all scan lines for col range */
3822:     {
3823:     /* --- allocations and declarations --- */
3824:     int  lobyte = irow*width + locol;   /* first bp[] byte in this scan */
3825:     char scanbyte[32];                  /* sprintf() buffer for byte */
3826:     /* --- set chars in scanline[] based on bytes in bytemap bp[] --- */
3827:     memset(scanline,' ',scan_width);    /* blank out scanline */
3828:     for ( ibyte=0; ibyte<scan_cols; ibyte++ ) /* set chars for each col */
3829:       { int byteval = (int)bp[lobyte+ibyte];  /* value of current byte */
3830:         memset(scanbyte,'.',byte_width); /* dot-fill scanbyte */
3831:         if ( byteval == black_byte )    /* but if we have a black byte */
3832:           memset(scanbyte,'*',byte_width); /* star-fill scanbyte instead */
3833:         if ( byte_width > 1 )           /* don't blank out single char */
3834:           scanbyte[byte_width-1] = ' '; /* blank-fill rightmost character */
3835:         if ( byteval != white_byte      /* format bytes that are non-white */
3836:         &&   byteval != black_byte )    /* and that are non-black */
3837:           sprintf(scanbyte,"%*x ",max2(1,byte_width-1),byteval); /*hex-format*/
3838:         memcpy(scanline+ibyte*byte_width,scanbyte,byte_width); } /*in line*/
3839:     /* --- display completed scan line --- */
3840:     fprintf(fp,"%.*s\n",scan_width,scanline);
3841:     } /* --- end-of-for(irow) --- */
3842:   } /* --- end-of-while(hicol<width) --- */
3843: /* -------------------------------------------------------------------------
3844: Back to caller with 1=okay, 0=failed.
3845: -------------------------------------------------------------------------- */
3846: return ( 1 );
3847: } /* --- end-of-function type_bytemap() --- */
3848: 
3849: 
3850: /* ==========================================================================
3851:  * Function:    xbitmap_raster ( rp, fp )
3852:  * Purpose:     Emit a mime xbitmap representing rp, on fp.
3853:  * --------------------------------------------------------------------------
3854:  * Arguments:   rp (I)          ptr to raster struct for which a mime
3855:  *                              xbitmap is to be constructed.
3856:  *              fp (I)          File ptr to output device (defaults to
3857:  *                              stdout if passed as NULL).
3858:  * --------------------------------------------------------------------------
3859:  * Returns:     ( int )         1 if completed successfully,
3860:  *                              or 0 otherwise (for any error).
3861:  * --------------------------------------------------------------------------
3862:  * Notes:
3863:  * ======================================================================= */
3864: /* --- entry point --- */
3865: int     xbitmap_raster ( raster *rp, FILE *fp )
3866: {
3867: /* -------------------------------------------------------------------------
3868: Allocations and Declarations
3869: -------------------------------------------------------------------------- */
3870: char    *title = "image";               /* dummy title */
3871: int     hex_bitmap();                   /* dump bitmap as hex bytes */
3872: /* --------------------------------------------------------------------------
3873: emit text to display mime xbitmap representation of rp->bitmap image
3874: -------------------------------------------------------------------------- */
3875: /* --- first redirect null fp --- */
3876: if ( fp == (FILE *)NULL ) fp = stdout;  /* default fp to stdout if null */
3877: /* --- check for ascii string --- */
3878: if ( isstring )                         /* pixmap has string, not raster */
3879:  return ( 0 );                          /* can't handle ascii string */
3880: /* --- emit prologue strings and hex dump of bitmap for mime xbitmap --- */
3881: fprintf( fp, "Content-type: image/x-xbitmap\n\n" );
3882: fprintf( fp, "#define %s_width %d\n#define %s_height %d\n",
3883:         title,rp->width, title,rp->height );
3884: fprintf( fp, "static char %s_bits[] = {\n", title );
3885: hex_bitmap(rp,fp,0,0);                  /* emit hex dump of bitmap bytes */
3886: fprintf (fp,"};\n");                    /* ending with "};" for C array */
3887: /* -------------------------------------------------------------------------
3888: Back to caller with 1=okay, 0=failed.
3889: -------------------------------------------------------------------------- */
3890: return ( 1 );
3891: } /* --- end-of-function xbitmap_raster() --- */
3892: 
3893: 
3894: /* ==========================================================================
3895:  * Function:    type_pbmpgm ( rp, ptype, file )
3896:  * Purpose:     Write pbm or pgm image of rp to file
3897:  * --------------------------------------------------------------------------
3898:  * Arguments:   rp (I)          ptr to raster struct for which
3899:  *                              a pbm/pgm file is to be written.
3900:  *              ptype (I)       int containing 1 for pbm, 2 for pgm, or
3901:  *                              0 to determine ptype from values in rp
3902:  *              file (I)        ptr to null-terminated char string
3903:  *                              containing name of fuke to be written
3904:  *                              (see notes below).
3905:  * --------------------------------------------------------------------------
3906:  * Returns:     ( int )         total #bytes written,
3907:  *                              or 0 for any error.
3908:  * --------------------------------------------------------------------------
3909:  * Notes:     o (a) If file==NULL, output is written to stdout;
3910:  *              (b) if *file=='\000' then file is taken as the
3911:  *                  address of an output buffer to which output
3912:  *                  is written (and is followed by a terminating
3913:  *                  '\0' which is not counted in #bytes returned);
3914:  *              (c) otherwise file is the filename (opened and
3915:  *                  closed internally) to which output is written,
3916:  *                  except that any final .ext extension is replaced
3917:  *                  by .pbm or .pgm depending on ptype.
3918:  * ======================================================================= */
3919: /* --- entry point --- */
3920: int     type_pbmpgm ( raster *rp, int ptype, char *file )
3921: {
3922: /* -------------------------------------------------------------------------
3923: Allocations and Declarations
3924: -------------------------------------------------------------------------- */
3925: int     isokay=0, nbytes=0;     /* completion flag, total #bytes written */
3926: int     irow=0, jcol=0;         /*height(row), width(col) indexes in raster*/
3927: int     pixmin=9999, pixmax=(-9999), /* min, max pixel value in raster */
3928:         ngray = 0;              /* #gray scale values */
3929: FILE    /* *fopen(), */ *fp=NULL; /* pointer to output file (or NULL) */
3930: char    outline[1024], outfield[256], /* output line, field */
3931:         cr[16] = "\n\000";      /* cr at end-of-line */
3932: int     maxlinelen = 70;        /* maximum allowed line length */
3933: int     pixfrac=6;              /* use (pixmax-pixmin)/pixfrac as step */
3934: static  char
3935:         *suffix[] = { NULL, ".pbm", ".pgm" },   /* file.suffix[ptype] */
3936:         *magic[] = { NULL, "P1", "P2" },        /*identifying "magic number"*/
3937:         *mode[] = { NULL, "w", "w" };           /* fopen() mode[ptype] */
3938: /* -------------------------------------------------------------------------
3939: check input, determine grayscale,  and set up output file if necessary
3940: -------------------------------------------------------------------------- */
3941: /* --- check input args --- */
3942: if ( rp == NULL ) goto end_of_job;      /* no input raster provided */
3943: if ( ptype != 0 )                       /* we'll determine ptype below */
3944:  if ( ptype<1 || ptype>2 ) goto end_of_job; /*invalid output graphic format*/
3945: /* --- determine largest (and smallest) value in pixmap --- */
3946: for ( irow=0; irow<rp->height; irow++ ) /* for each row, top-to-bottom */
3947:  for ( jcol=0; jcol<rp->width; jcol++ ) /* for each col, left-to-right */
3948:   { int pixval = getpixel(rp,irow,jcol);  /* value of pixel at irow,jcol */
3949:     pixmin = min2(pixmin,pixval);       /* new minimum */
3950:     pixmax = max2(pixmax,pixval); }     /* new maximum */
3951: ngray = 1 + (pixmax-pixmin);            /* should be 2 for b/w bitmap */
3952: if ( ptype == 0 )                       /* caller wants us to set ptype */
3953:   ptype = (ngray>=3?2:1);               /* use grayscale if >2 shades */
3954: /* --- open output file if necessary --- */
3955: if ( file == NULL ) fp = stdout;        /*null ptr signals output to stdout*/
3956: else if ( *file != '\000' ) {           /* explicit filename provided, so...*/
3957:   char  fname[512], *pdot=NULL;         /* file.ext, ptr to last . in fname*/
3958:   strncpy(fname,file,255);              /* local copy of file name */
3959:   fname[255] = '\000';                  /* make sure it's null terminated */
3960:   if ( (pdot=strrchr(fname,'.')) == NULL ) /*no extension on original name*/
3961:     strcat(fname,suffix[ptype]);        /* so add extension */
3962:   else                                  /* we already have an extension */
3963:     strcpy(pdot,suffix[ptype]);         /* so replace original extension */
3964:   if ( (fp = fopen(fname,mode[ptype]))  /* open output file */
3965:   ==   (FILE *)NULL ) goto end_of_job;  /* quit if failed to open */
3966:   } /* --- ens-of-if(*file!='\0') --- */
3967: /* -------------------------------------------------------------------------
3968: emit http headers if running as cgi
3969: -------------------------------------------------------------------------- */
3970: /* --- emit mime content-type line --- */
3971: if ( isquery && fp==stdout )            /* writing to http server */
3972:  { fprintf( stdout, "Cache-Control: max-age=9999\n" );
3973:    fprintf( stdout, "Content-type: text/plain\n\n" ); }
3974: /* -------------------------------------------------------------------------
3975: format and write header
3976: -------------------------------------------------------------------------- */
3977: /* --- format header info --- */
3978: *outline = '\000';                      /* initialize line buffer */
3979: strcat(outline,magic[ptype]);           /* begin file with "magic number" */
3980: strcat(outline,cr);                     /* followed by cr to end line */
3981: sprintf(outfield,"%d %d",rp->width,rp->height); /* format width and height */
3982: strcat(outline,outfield);               /* add width and height to header */
3983: strcat(outline,cr);                     /* followed by cr to end line */
3984: if ( ptype == 2 )                       /* need max grayscale value */
3985:   { sprintf(outfield,"%d",pixmax);      /* format maximum pixel value */
3986:     strcat(outline,outfield);           /* add max value to header */
3987:     strcat(outline,cr); }               /* followed by cr to end line */
3988: /* --- write header to file or memory buffer --- */
3989: if ( fp == NULL )                       /* if we have no open file... */
3990:   strcat(file,outline);                 /* add header to caller's buffer */
3991: else                                    /* or if we have an open file... */
3992:   if ( fputs(outline,fp)                /* try writing header to open file */
3993:   ==   EOF ) goto end_of_job;           /* return with error if failed */
3994: nbytes += strlen(outline);              /* bump output byte count */
3995: /* -------------------------------------------------------------------------
3996: format and write pixels
3997: -------------------------------------------------------------------------- */
3998: *outline = '\000';                      /* initialize line buffer */
3999: for ( irow=0; irow<=rp->height; irow++ ) /* for each row, top-to-bottom */
4000:  for ( jcol=0; jcol<rp->width; jcol++ ) { /* for each col, left-to-right */
4001:   /* --- format value at irow,jcol--- */
4002:   *outfield = '\000';                   /* init empty field */
4003:   if ( irow < rp->height ) {            /* check row index */
4004:     int pixval = getpixel(rp,irow,jcol);  /* value of pixel at irow,jcol */
4005:     if ( ptype == 1 )                   /* pixval must be 1 or 0 */
4006:       pixval = (pixval>pixmin+((pixmax-pixmin)/pixfrac)?1:0);
4007:     sprintf(outfield,"%d ",pixval); }   /* format pixel value */
4008:   /* --- write line if this value won't fit on it (or last line) --- */
4009:   if ( strlen(outline)+strlen(outfield)+strlen(cr) >= maxlinelen /*won't fit*/
4010:   ||   irow >= rp->height ) {           /* force writing last line */
4011:     strcat(outline,cr);                 /* add cr to end current line */
4012:     if ( fp == NULL )                   /* if we have no open file... */
4013:       strcat(file,outline);             /* add header to caller's buffer */
4014:     else                                /* or if we have an open file... */
4015:       if ( fputs(outline,fp)            /* try writing header to open file */
4016:       ==   EOF ) goto end_of_job;       /* return with error if failed */
4017:     nbytes += strlen(outline);          /* bump output byte count */
4018:     *outline = '\000';                  /* re-initialize line buffer */
4019:     } /* --- end-of-if(strlen>=maxlinelen) --- */
4020:   if ( irow >= rp->height ) break;      /* done after writing last line */
4021:   /* --- concatanate value to line -- */
4022:   strcat(outline,outfield);             /* concatanate value to line */
4023:   } /* --- end-of-for(jcol,irow) --- */
4024: isokay = 1;                             /* signal successful completion */
4025: /* -------------------------------------------------------------------------
4026: Back to caller with total #bytes written, or 0=failed.
4027: -------------------------------------------------------------------------- */
4028: end_of_job:
4029:   if ( fp != NULL                       /* output written to an open file */
4030:   &&   fp != stdout )                   /* and it's not just stdout */
4031:     fclose(fp);                         /* so close file before returning */
4032:   return ( (isokay?nbytes:0) );         /*back to caller with #bytes written*/
4033: } /* --- end-of-function type_pbmpgm() --- */
4034: 
4035: 
4036: /* ==========================================================================
4037:  * Function:    read_pbm ( fp, sf )
4038:  * Purpose:     read already-opened file (or pipe) containing pbm,
4039:  *              and return subraster corresponding to its bitmap image
4040:  * --------------------------------------------------------------------------
4041:  * Arguments:   fp (I)          FILE *ptr to already-opened file or pipe
4042:  *                              (the latter for \mathtex{} and maybe others)
4043:  *              sf (I)          double containing "shrink factor" .01<sf<.99
4044:  *                              to be applied to image
4045:  * --------------------------------------------------------------------------
4046:  * Returns:     ( subraster * ) ptr to subraster representing image of pbm
4047:  *                              or NULL for any error.
4048:  * --------------------------------------------------------------------------
4049:  * Notes:     o
4050:  * ======================================================================= */
4051: /* --- entry point --- */
4052: subraster *read_pbm ( FILE *fp, double sf )
4053: {
4054: /* -------------------------------------------------------------------------
4055: Allocations and Declarations
4056: -------------------------------------------------------------------------- */
4057: subraster *new_subraster(), *sp = NULL; /* subraster corresponding to pbm */
4058: raster  *rp = NULL;                     /* sp->image raster in subraster */
4059: raster  *ssrp=NULL, *imgsupsamp();      /* supersampled/shrunk rp */
4060: int     delete_subraster(),             /* in case of problem/error */
4061:         delete_raster();                /* in case we shrink/supersample rp */
4062: unsigned char *pixels = NULL;           /* pixels returned in pbm image */
4063: char    fline[8192],                    /* fgets(fline,8190,fp) buffer */
4064:         *lineptr=NULL, *endptr=NULL;    /* strtol ptr to 1st char after num*/
4065: char    *magicnumber = "P1";            /* filetype identification */
4066: int     gotmagic = 0;                   /* set true when magicnumber found */
4067: int     ncols=0, nrows=0,               /* image width,height from wget */
4068:         jcol=0, irow=0,                 /* col~width, row~height indexes */
4069:         ipixel=0, npixels=0,            /* pixel index, npixels=ncols*nrows */
4070:         pixval=0;                       /* pixval=strtol() from pipeline */
4071: int     pixsz = 1,                      /* bitmap in returned raster's pixmap*/
4072:         ssgrayscale = 2; /*256;*/       /* grayscale for imgsupsamp() */
4073: /* ---
4074:  * check input
4075:  * -------------- */
4076: if ( fp == NULL ) goto end_of_job;
4077: /* ---
4078:  * read the first few lines to get width, height
4079:  * ------------------------------------------------ */
4080: while ( 1 ) {                           /* read lines (shouldn't find eof) */
4081:   if ( fgets(fline,8190,fp) == NULL ) goto end_of_job; /* premature eof */
4082:   if ( gotmagic ) {                     /* next line has "width height" */
4083:     char *p = strchr(fline,(int)('#')); /* first look for comment char */
4084:     if ( p != (char *)NULL ) *p = '\000'; /* and terminate line at comment */
4085:     p = fline;  skipwhite(p);           /* now find first non-whitespace char*/
4086:     if ( *p == '\000' ) continue;       /* and skip empty lines */
4087:     ncols = (int)strtol(fline,&endptr,10); /* convert width */
4088:     nrows = (int)strtol(endptr,NULL,10); /* and convert height */
4089:     break; }                            /* and we're ready to pixelize... */
4090:   if ( strstr(fline,magicnumber) != NULL ) /* found magicnumber */
4091:     gotmagic = 1;                       /* set flag */
4092:   } /* --- end-of-while(1) --- */
4093: /* ---
4094:  * allocate pixels and xlate remainder of pipe
4095:  * ---------------------------------------------- */
4096: /* --- allocate pixels --- */
4097: npixels = nrows*ncols;                  /* that's what we need */
4098: if ( npixels<1 || npixels>9999999 ) goto end_of_job; /* sanity check */
4099: if ( (pixels = (unsigned char *)malloc(npixels)) /*ask for it if not insane*/
4100: ==   NULL ) goto end_of_job;            /* and quit if request denied */
4101: /* --- xlate remainder of pipe --- */
4102: ipixel = 0;                             /* start with first pixel */
4103: while ( 1 ) {                           /* read lines (shouldn't find eof) */
4104:   /* --- read next line --- */
4105:   if ( fgets(fline,511,fp) == NULL ) {  /* premature eof */
4106:     free(pixels);  pixels=NULL;         /* failed to xlate completely */
4107:     goto end_of_job; }                  /* free, reset return ptr, quit */
4108:   /* --- xlate 0's and 1's on the line --- */
4109:   lineptr = fline;                      /* start at beginning of line */
4110:   while ( ipixel < npixels ) {
4111:     while ( *lineptr != '\000' )        /* skip leading non-digits */
4112:       if ( isdigit(*lineptr) ) break;   /* done at first digit */
4113:       else lineptr++;                   /* or skip leading non-digit */
4114:     if ( *lineptr == '\000' ) break;    /* no more numbers on this line */
4115:     pixval = (int)strtol(lineptr,&endptr,10); /*convert next num to 0 or 1*/
4116:     pixels[ipixel++] = (unsigned char)pixval; /*i.e., let's hope it's 0 or 1*/
4117:     lineptr = endptr;                   /* move on to next number */
4118:     } /* --- end-of-while(ipixels<npixels) --- */
4119:   if ( ipixel >= npixels ) break;       /* pixelized image complete */
4120:   } /* --- end-of-while(1) --- */
4121: /* ---
4122:  * create subraster for pixelized image
4123:  * --------------------------------------- */
4124: if ( (sp = new_subraster(ncols,nrows,pixsz)) /* allocate/init subraster */
4125: ==   NULL ) goto end_of_job;            /* or quit if failed */
4126: if ( (rp = sp->image)                   /* image raster in new subraster */
4127: ==   NULL ) {                           /* apparently some internal problem */
4128:   delete_subraster(sp);                 /* free "buggy" subraster */
4129:   sp=NULL; goto end_of_job; }           /* return error to caller */
4130: sp->type = IMAGERASTER;                 /* set raster type as image */
4131: sp->baseline = (nrows+1)/2;             /* default baseline at image center */
4132: sp->toprow = sp->leftcol = 0;           /* init/unused */
4133: ipixel = 0;                             /* start with first pixel */
4134: /* --- copy 0/1 pixels from pbm to sp->image raster --- */
4135: for ( irow=0; irow<nrows; irow++ )      /* for each row, top-to-bottom */
4136:   for ( jcol=0; jcol<ncols; jcol++ ) {  /* for each col, left-to-right */
4137:     pixval = (int)pixels[ipixel++];     /* pbm image stored row-wise */
4138:     setpixel(rp,irow,jcol,pixval);      /* set corresponding value in image */
4139:     } /* --- end-of-for(irow,jcol) --- */
4140: /* --- apply sf (shrink factor) if requested --- */
4141: if ( sf>0.01 && sf<0.99 ) {             /* valid sf means shrink requested */
4142:   if ( (ssrp = imgsupsamp(rp,sf,ssgrayscale)) /* supersample rp to shrink it */
4143:   != NULL ) {                           /* succeeded, so replace rp with ssrp*/
4144:     /* printf("read_pbm> imgsupsamp() succeeded\n"); */
4145:     sp->image = ssrp;                   /* replaced rp with ssrp */
4146:     sp->baseline = (int)( (sf*((double)nrows)+1.0)/2.0 + 0.5 );
4147:     delete_raster(rp); }                /* free no-longer-needed rp */
4148:   /* else printf("read_pbm> imgsupsamp() failed\n"); */
4149:   } /* --- end-of-if(sf>.01&&sf<.99) --- */
4150: end_of_job:
4151:   if ( pixels != NULL ) free((void *)pixels); /* raster's pixmap has image */
4152:   return ( sp );                        /*back to caller with pbm subraster*/
4153: } /* --- end-of-function read_pbm() --- */
4154: 
4155: 
4156: /* ==========================================================================
4157:  * Function:    cstruct_chardef ( cp, fp, col1 )
4158:  * Purpose:     Emit a C struct of cp on fp, starting in col1.
4159:  * --------------------------------------------------------------------------
4160:  * Arguments:   cp (I)          ptr to chardef struct for which
4161:  *                              a C struct is to be generated.
4162:  *              fp (I)          File ptr to output device (defaults to
4163:  *                              stdout if passed as NULL).
4164:  *              col1 (I)        int containing 0...65; output lines
4165:  *                              are preceded by col1 blanks.
4166:  * --------------------------------------------------------------------------
4167:  * Returns:     ( int )         1 if completed successfully,
4168:  *                              or 0 otherwise (for any error).
4169:  * --------------------------------------------------------------------------
4170:  * Notes:
4171:  * ======================================================================= */
4172: /* --- entry point --- */
4173: int     cstruct_chardef ( chardef *cp, FILE *fp, int col1 )
4174: {
4175: /* -------------------------------------------------------------------------
4176: Allocations and Declarations
4177: -------------------------------------------------------------------------- */
4178: char    field[64];              /* field within output line */
4179: int     cstruct_raster(),       /* emit a raster */
4180:         emit_string();          /* emit a string and comment */
4181: /* -------------------------------------------------------------------------
4182: emit   charnum, location, name  /  hirow, hicol,  lorow, locol
4183: -------------------------------------------------------------------------- */
4184: /* --- charnum, location, name --- */
4185: sprintf(field,"{ %3d,%5d,\n", cp->charnum,cp->location);  /*char#,location*/
4186: emit_string ( fp, col1, field, "character number, location");
4187: /* --- toprow, topleftcol,   botrow, botleftcol  --- */
4188: sprintf(field,"  %3d,%2d,  %3d,%2d,\n",         /* format... */
4189:   cp->toprow,cp->topleftcol,                    /* toprow, topleftcol, */
4190:   cp->botrow,cp->botleftcol);                   /* and botrow, botleftcol */
4191: emit_string ( fp, col1, field, "topleft row,col, and botleft row,col");
4192: /* -------------------------------------------------------------------------
4193: emit raster and chardef's closing brace, and then return to caller
4194: -------------------------------------------------------------------------- */
4195: cstruct_raster(&cp->image,fp,col1+4);           /* emit raster */
4196: emit_string ( fp, 0, "  }", NULL);              /* emit closing brace */
4197: return ( 1 );                   /* back to caller with 1=okay, 0=failed */
4198: } /* --- end-of-function cstruct_chardef() --- */
4199: 
4200: 
4201: /* ==========================================================================
4202:  * Function:    cstruct_raster ( rp, fp, col1 )
4203:  * Purpose:     Emit a C struct of rp on fp, starting in col1.
4204:  * --------------------------------------------------------------------------
4205:  * Arguments:   rp (I)          ptr to raster struct for which
4206:  *                              a C struct is to be generated.
4207:  *              fp (I)          File ptr to output device (defaults to
4208:  *                              stdout if passed as NULL).
4209:  *              col1 (I)        int containing 0...65; output lines
4210:  *                              are preceded by col1 blanks.
4211:  * --------------------------------------------------------------------------
4212:  * Returns:     ( int )         1 if completed successfully,
4213:  *                              or 0 otherwise (for any error).
4214:  * --------------------------------------------------------------------------
4215:  * Notes:
4216:  * ======================================================================= */
4217: /* --- entry point --- */
4218: int     cstruct_raster ( raster *rp, FILE *fp, int col1 )
4219: {
4220: /* -------------------------------------------------------------------------
4221: Allocations and Declarations
4222: -------------------------------------------------------------------------- */
4223: char    field[64];              /* field within output line */
4224: char    typecast[64] = "(pixbyte *)"; /* type cast for pixmap string */
4225: int     hex_bitmap();           /* to emit raster bitmap */
4226: int     emit_string();          /* emit a string and comment */
4227: /* -------------------------------------------------------------------------
4228: emit width and height
4229: -------------------------------------------------------------------------- */
4230: sprintf(field,"{ %2d,  %3d,%2d,%2d, %s\n", /* format width,height,pixsz */
4231:         rp->width,rp->height,rp->format,rp->pixsz,typecast);
4232: emit_string ( fp, col1, field, "width,ht, fmt,pixsz,map...");
4233: /* -------------------------------------------------------------------------
4234: emit bitmap and closing brace, and return to caller
4235: -------------------------------------------------------------------------- */
4236: hex_bitmap(rp,fp,col1+2,1);     /* emit bitmap */
4237: emit_string ( fp, 0, " }", NULL); /* emit closing brace */
4238: return ( 1 );                   /* back to caller with 1=okay, 0=failed */
4239: } /* --- end-of-function cstruct_raster() --- */
4240: 
4241: 
4242: /* ==========================================================================
4243:  * Function:    hex_bitmap ( rp, fp, col1, isstr )
4244:  * Purpose:     Emit a hex dump of the bitmap of rp on fp, starting in col1.
4245:  *              If isstr (is string) is true, the dump is of the form
4246:  *                      "\x01\x02\x03\x04\x05..."
4247:  *              Otherwise, if isstr is false, the dump is of the form
4248:  *                      0x01,0x02,0x03,0x04,0x05...
4249:  * --------------------------------------------------------------------------
4250:  * Arguments:   rp (I)          ptr to raster struct for which
4251:  *                              a hex dump is to be constructed.
4252:  *              fp (I)          File ptr to output device (defaults to
4253:  *                              stdout if passed as NULL).
4254:  *              col1 (I)        int containing 0...65; output lines
4255:  *                              are preceded by col1 blanks.
4256:  *              isstr (I)       int specifying dump format as described above
4257:  * --------------------------------------------------------------------------
4258:  * Returns:     ( int )         1 if completed successfully,
4259:  *                              or 0 otherwise (for any error).
4260:  * --------------------------------------------------------------------------
4261:  * Notes:
4262:  * ======================================================================= */
4263: /* --- entry point --- */
4264: int     hex_bitmap ( raster *rp, FILE *fp, int col1, int isstr )
4265: {
4266: /* -------------------------------------------------------------------------
4267: Allocations and Declarations
4268: -------------------------------------------------------------------------- */
4269: int     ibyte,                          /* pixmap[ibyte] index */
4270:         nbytes = pixbytes(rp);          /*#bytes in bitmap or .gf-formatted*/
4271: char    stub[64]="                                ";/* col1 leading blanks */
4272: int     linewidth = 64,                 /* (roughly) rightmost column */
4273:         colwidth = (isstr? 4:5);        /* #cols required for each byte */
4274: int     ncols = (linewidth-col1)/colwidth; /* new line after ncols bytes */
4275: /* --------------------------------------------------------------------------
4276: initialization
4277: -------------------------------------------------------------------------- */
4278: /* --- redirect null fp --- */
4279: if ( fp == (FILE *)NULL ) fp = stdout;  /* default fp to stdout if null */
4280: /* --- emit initial stub if wanted --- */
4281: if ( col1 > 0 ) fprintf(fp,"%.*s",col1,stub); /* stub preceding 1st line */
4282: /* --------------------------------------------------------------------------
4283: emit hex dump of rp->bitmap image
4284: -------------------------------------------------------------------------- */
4285: if ( isstr ) fprintf(fp,"\"");          /* opening " before first line */
4286: for ( ibyte=0; ibyte<nbytes; ibyte++ )  /* one byte at a time */
4287:   {
4288:   /* --- display a byte as hex char or number, depending on isstr --- */
4289:   if ( isstr )                          /* string format wanted */
4290:     fprintf(fp,"\\x%02x",(rp->pixmap)[ibyte]);  /*print byte as hex char*/
4291:   else                                  /* comma-separated format wanted */
4292:     fprintf(fp,"0x%02x",(rp->pixmap)[ibyte]);   /*print byte as hex number*/
4293:   /* --- add a separator and newline, etc, as necessary --- */
4294:   if ( ibyte < nbytes-1)                /* not the last byte yet */
4295:     {
4296:     if ( !isstr ) fprintf(fp,",");      /* follow hex number with comma */
4297:     if ( (ibyte+1)%ncols==0 ) {         /* need new line after every ncols */
4298:       if ( !isstr )                     /* for hex numbers format ... */
4299:         fprintf(fp,"\n%.*s",col1,stub); /* ...just need newline and stub */
4300:       else                              /* for string format... */
4301:         fprintf(fp,"\"\n%.*s\"",col1,stub); } /*...need closing, opening "s*/
4302:     } /* --- end-of-if(ibyte<nbytes-1) --- */
4303:   } /* --- end-of-for(ibyte) --- */
4304: if ( isstr ) fprintf(fp,"\"");          /* closing " after last line */
4305: return ( 1 );                           /* back with 1=okay, 0=failed */
4306: } /* --- end-of-function hex_bitmap() --- */
4307: 
4308: 
4309: /* ==========================================================================
4310:  * Function:    emit_string ( fp, col1, string, comment )
4311:  * Purpose:     Emit string on fp, starting in col1,
4312:  *              and followed by right-justified comment.
4313:  * --------------------------------------------------------------------------
4314:  * Arguments:   fp (I)          File ptr to output device (defaults to
4315:  *                              stdout if passed as NULL).
4316:  *              col1 (I)        int containing 0 or #blanks preceding string
4317:  *              string (I)      char *  containing string to be emitted.
4318:  *                              If last char of string is '\n',
4319:  *                              the emitted line ends with a newline,
4320:  *                              otherwise not.
4321:  *              comment (I)     NULL or char * containing right-justified
4322:  *                              comment (we enclose between /star and star/)
4323:  * --------------------------------------------------------------------------
4324:  * Returns:     ( int )         1 if completed successfully,
4325:  *                              or 0 otherwise (for any error).
4326:  * --------------------------------------------------------------------------
4327:  * Notes:     o
4328:  * ======================================================================= */
4329: /* --- entry point --- */
4330: int     emit_string ( FILE *fp, int col1, char *string, char *comment )
4331: {
4332: /* -------------------------------------------------------------------------
4333: Allocations and Declarations
4334: -------------------------------------------------------------------------- */
4335: char    line[256];              /* construct line with caller's fields */
4336: int     fieldlen;               /* #chars in one of caller's fields */
4337: int     linelen = 72;           /*line length (for right-justified comment)*/
4338: int     isnewline = 0;          /* true to emit \n at end of line */
4339: /* --------------------------------------------------------------------------
4340: construct line containing prolog, string, epilog, and finally comment
4341: -------------------------------------------------------------------------- */
4342: /* --- init line --- */
4343: memset(line,' ',255);                   /* start line with blanks */
4344: /* --- embed string into line --- */
4345: if ( string != NULL )                   /* if caller gave us a string... */
4346:   { fieldlen = strlen(string);          /* #cols required for string */
4347:     if ( string[fieldlen-1] == '\n' )   /* check last char for newline */
4348:       { isnewline = 1;                  /* got it, so set flag */
4349:         fieldlen--; }                   /* but don't print it yet */
4350:     memcpy(line+col1,string,fieldlen);  /* embid string starting at col1 */
4351:     col1 += fieldlen; }                 /* bump col past epilog */
4352: /* --- embed comment into line --- */
4353: if ( comment != NULL )                  /* if caller gave us a comment... */
4354:   { fieldlen = 6 + strlen(comment);     /* plus  /star, star/, 2 spaces */
4355:     if ( linelen-fieldlen < col1 )      /* comment won't fit */
4356:       fieldlen -= (col1 - (linelen-fieldlen)); /* truncate comment to fit */
4357:     if ( fieldlen > 6 )                 /* can fit all or part of comment */
4358:       sprintf(line+linelen-fieldlen,"/%c %.*s %c/", /* so embed it in line */
4359:         '*', fieldlen-6,comment, '*');
4360:     col1 = linelen; }                   /* indicate line filled */
4361: /* --- line completed --- */
4362: line[col1] = '\000';                    /* null-terminate completed line */
4363: /* -------------------------------------------------------------------------
4364: emit line, then back to caller with 1=okay, 0=failed.
4365: -------------------------------------------------------------------------- */
4366: /* --- first redirect null fp --- */
4367: if ( fp == (FILE *)NULL ) fp = stdout;  /* default fp to stdout if null */
4368: /* --- emit line (and optional newline) --- */
4369: fprintf(fp,"%.*s",linelen,line);        /* no more than linelen chars */
4370: if ( isnewline ) fprintf(fp,"\n");      /*caller wants terminating newline*/
4371: return ( 1 );
4372: } /* --- end-of-function emit_string() --- */
4373: 
4374: 
4375: /* ==========================================================================
4376:  * Function:    gftobitmap ( gf )
4377:  * Purpose:     convert .gf-like pixmap to bitmap image
4378:  * --------------------------------------------------------------------------
4379:  * Arguments:   gf (I)          raster * to struct in .gf-format
4380:  * --------------------------------------------------------------------------
4381:  * Returns:     ( raster * )    image-format raster * if successful,
4382:  *                              or NULL for any error.
4383:  * --------------------------------------------------------------------------
4384:  * Notes:     o
4385:  * ======================================================================= */
4386: /* --- entry point --- */
4387: raster  *gftobitmap ( raster *gf )
4388: {
4389: /* -------------------------------------------------------------------------
4390: Allocations and Declarations
4391: -------------------------------------------------------------------------- */
4392: raster  *new_raster(), *rp=NULL;        /* image raster retuned to caller */
4393: int     width=0, height=0, totbits=0;   /* gf->width, gf->height, #bits */
4394: int     format=0, icount=0, ncounts=0,  /*.gf format, count index, #counts*/
4395:         ibit=0, bitval=0;               /* bitmap index, bit value */
4396: int     isrepeat = 1,                   /* true to process repeat counts */
4397:         repeatcmds[2] = {255,15},       /*opcode for repeat/duplicate count*/
4398:         nrepeats=0, irepeat=0,          /* scan line repeat count,index */
4399:         wbits = 0;                      /* count bits to width of scan line*/
4400: /* -------------------------------------------------------------------------
4401: initialization
4402: -------------------------------------------------------------------------- */
4403: /* --- check args --- */
4404: if ( gf == NULL ) goto end_of_job;      /* input raster not provided */
4405: format = gf->format;                    /* 2 or 3 */
4406: if ( format!=2 && format!=3 ) goto end_of_job; /* invalid raster format */
4407: ncounts = gf->pixsz;                    /*pixsz is really #counts in pixmap*/
4408: /* --- allocate output raster with proper dimensions for bitmap --- */
4409: width=gf->width;  height=gf->height;    /* dimensions of raster */
4410: if ( (rp = new_raster(width,height,1))  /* allocate new raster and bitmap */
4411: ==   NULL ) goto end_of_job;            /* quit if failed to allocate */
4412: totbits = width*height;                 /* total #bits in image */
4413: /* -------------------------------------------------------------------------
4414: fill bitmap
4415: -------------------------------------------------------------------------- */
4416: for ( icount=0,bitval=0; icount<ncounts; icount++ )
4417:   {
4418:   int   nbits = (int)(getbyfmt(format,gf->pixmap,icount)); /*#bits to set*/
4419:   if ( isrepeat                         /* we're proxessing repeat counts */
4420:   &&   nbits == repeatcmds[format-2] ) { /* and repeat opcode found */
4421:    if ( nrepeats == 0 )                 /* recursive repeat is error */
4422:     { nrepeats = (int)(getbyfmt(format,gf->pixmap,icount+1));/*repeat count*/
4423:       nbits = (int)(getbyfmt(format,gf->pixmap,icount+2)); /*#bits to set*/
4424:       icount += 2; }                    /* bump byte/nibble count */
4425:    else                                 /* some internal error occurred */
4426:     if ( msgfp!=NULL && msglevel>=1 )   /* report error */
4427:      fprintf(msgfp,"gftobitmap> found embedded repeat command\n"); }
4428:   if ( 0 )
4429:     fprintf(stdout,
4430:     "gftobitmap> icount=%d bitval=%d nbits=%d ibit=%d totbits=%d\n",
4431:     icount,bitval,nbits,ibit,totbits);
4432:   for ( ; nbits>0; nbits-- )            /* count down */
4433:     { if ( ibit >= totbits ) goto end_of_job; /* overflow check */
4434:       for ( irepeat=0; irepeat<=nrepeats; irepeat++ )
4435:        if ( bitval == 1 )               /* set pixel */
4436:         { setlongbit(rp->pixmap,(ibit+irepeat*width)); }
4437:        else                             /* clear pixel */
4438:         { unsetlongbit(rp->pixmap,(ibit+irepeat*width)); }
4439:       if ( nrepeats > 0 ) wbits++;      /* count another repeated bit */
4440:       ibit++; }                         /* bump bit index */
4441:   bitval = 1-bitval;                    /* flip bit value */
4442:   if ( wbits >= width ) {               /* completed repeats */
4443:    ibit += nrepeats*width;              /*bump bit count past repeated scans*/
4444:    if ( wbits > width )                 /* out-of alignment error */
4445:     if ( msgfp!=NULL && msglevel>=1 )   /* report error */
4446:      fprintf(msgfp,"gftobitmap> width=%d wbits=%d\n",width,wbits);
4447:    wbits = nrepeats = 0; }              /* reset repeat counts */
4448:   } /* --- end-of-for(icount) --- */
4449: end_of_job:
4450:   return ( rp );                        /* back to caller with image */
4451: } /* --- end-of-function gftobitmap() --- */
4452: 
4453: 
4454: /* ==========================================================================
4455:  * Function:    get_symdef ( symbol )
4456:  * Purpose:     returns mathchardef struct for symbol
4457:  * --------------------------------------------------------------------------
4458:  * Arguments:   symbol (I)      char *  containing symbol
4459:  *                              whose corresponding mathchardef is wanted
4460:  * --------------------------------------------------------------------------
4461:  * Returns:     ( mathchardef * )  pointer to struct defining symbol,
4462:  *                              or NULL for any error
4463:  * --------------------------------------------------------------------------
4464:  * Notes:     o Input symbol need only contain a leading substring to match,
4465:  *              e.g., \gam passed in symbol will match \gamma in the table.
4466:  *              If the table contains two or more possible matches,
4467:  *              the shortest is returned, e.g., input \e will return with
4468:  *              data for \eta rather than \epsilon.  To get \epsilon,
4469:  *              you must pass a leading substring long enough to eliminate
4470:  *              shorter table matches, i.e., in this case \ep
4471:  * ======================================================================= */
4472: /* --- entry point --- */
4473: mathchardef *get_symdef ( char *symbol )
4474: {
4475: /* -------------------------------------------------------------------------
4476: Allocations and Declarations
4477: -------------------------------------------------------------------------- */
4478: mathchardef *symdefs = symtable;        /* table of mathchardefs */
4479: int     ligdef=0, get_ligature();       /* or we may have a ligature */
4480: int     idef = 0,                       /* symdefs[] index */
4481:         bestdef = (-9999);              /*index of shortest matching symdef*/
4482: int     symlen = strlen(symbol),        /* length of input symbol */
4483:         deflen, minlen=9999;            /*length of shortest matching symdef*/
4484: int     /*alnumsym = (symlen==1 && isalnum(*symbol)),*/ /*alphanumeric sym*/
4485:         alphasym = (symlen==1 && isalpha(*symbol)), /* or alpha symbol */
4486:         slashsym = (*symbol=='\\');     /* or \backslashed symbol */
4487: int     family = fontinfo[fontnum].family; /* current font family */
4488: static  char *displaysyms[][2] = {      /*xlate to Big sym for \displaystyle*/
4489:         /* --- see table on page 536 in TLC2 --- */
4490:         {"\\int",       "\\Bigint"},
4491:         {"\\oint",      "\\Bigoint"},
4492:         {"\\sum",       "\\Bigsum"},
4493:         {"\\prod",      "\\Bigprod"},
4494:         {"\\coprod",    "\\Bigcoprod"},
4495:         /* --- must be 'big' when related to similar binary operators --- */
4496:         {"\\bigcup",    "\\Bigcup"},
4497:         {"\\bigsqcup",  "\\Bigsqcup"},
4498:         {"\\bigcap",    "\\Bigcap"},
4499:         /*{"\\bigsqcap", "\\sqcap"},*/  /* don't have \Bigsqcap */
4500:         {"\\bigodot",   "\\Bigodot"},
4501:         {"\\bigoplus",  "\\Bigoplus"},
4502:         {"\\bigominus", "\\ominus"},
4503:         {"\\bigotimes", "\\Bigotimes"},
4504:         {"\\bigoslash", "\\oslash"},
4505:         {"\\biguplus",  "\\Biguplus"},
4506:         {"\\bigwedge",  "\\Bigwedge"},
4507:         {"\\bigvee",    "\\Bigvee"},
4508:         {NULL, NULL} };
4509: /* -------------------------------------------------------------------------
4510: First check for ligature
4511: -------------------------------------------------------------------------- */
4512: isligature = 0;                         /* init signal for no ligature */
4513: if ( family == CYR10 )                  /*only check for cyrillic ligatures*/
4514:  if ( (ligdef=get_ligature(subexprptr,family)) /* check for ligature */
4515:  >=    0  )                             /* found a ligature */
4516:   { bestdef = ligdef;                   /* set bestdef for ligature */
4517:     isligature = 1;                     /* signal we found a ligature */
4518:     goto end_of_job; }                  /* so just give it to caller */
4519: /* -------------------------------------------------------------------------
4520: If in \displaystyle mode, first xlate int to Bigint, etc.
4521: -------------------------------------------------------------------------- */
4522: if ( isdisplaystyle > 1 )               /* we're in \displaystyle mode */
4523:   for ( idef=0; ; idef++ ) {            /* lookup symbol in displaysyms */
4524:     char *fromsym = displaysyms[idef][0], /* look for this symbol */
4525:          *tosym = displaysyms[idef][1];   /* and xlate it to this symbol */
4526:     if ( fromsym == NULL ) break;       /* end-of-table */
4527:     if ( !strcmp(symbol,fromsym) )      /* found a match */
4528:       { if ( msglevel>=99 && msgfp!=NULL ) /* debugging output */
4529:          { fprintf(msgfp,"get_symdef> isdisplaystyle=%d, xlated %s to %s\n",
4530:            isdisplaystyle,symbol,tosym); fflush(msgfp); }
4531:         symbol = tosym;                 /* so look up tosym instead */
4532:         symlen = strlen(symbol);        /* reset symbol length */
4533:         break; }                        /* no need to search further */
4534:     } /* --- end-of-for(idef) --- */
4535: /* -------------------------------------------------------------------------
4536: search symdefs[] in order for first occurrence of symbol
4537: -------------------------------------------------------------------------- */
4538: for ( idef=0; ;idef++ )                 /* until trailer record found */
4539:   if ( symdefs[idef].symbol == NULL ) break; /* reached end-of-table */
4540:   else                                  /* check against caller's symbol */
4541:     if ( strncmp(symbol,symdefs[idef].symbol,symlen) == 0 ) /* found match */
4542:      if ( (fontnum==0||family==CYR10)   /* mathmode, so check every match */
4543:      || (1 && symdefs[idef].handler!=NULL) /* or check every directive */
4544:      || (1 && istextmode && slashsym)   /*text mode and \backslashed symbol*/
4545:      || (0 && istextmode && (!alphasym  /* text mode and not alpha symbol */
4546:         || symdefs[idef].handler!=NULL))   /* or text mode and directive */
4547:      || (symdefs[idef].family==family   /* have correct family */
4548:         && symdefs[idef].handler==NULL) )  /* and not a handler collision */
4549: #if 0
4550:      || (fontnum==1 && symdefs[idef].family==CMR10)   /*textmode && rm text*/
4551:      || (fontnum==2 && symdefs[idef].family==CMMI10)  /*textmode && it text*/
4552:      || (fontnum==3 && symdefs[idef].family==BBOLD10  /*textmode && bb text*/
4553:         && symdefs[idef].handler==NULL)
4554:      || (fontnum==4 && symdefs[idef].family==CMMIB10  /*textmode && bf text*/
4555:         && symdefs[idef].handler==NULL) )
4556: #endif
4557:       if ( (deflen=strlen(symdefs[idef].symbol)) < minlen ) /*new best match*/
4558:         { bestdef = idef;               /* save index of new best match */
4559:           if ( (minlen = deflen)        /* and save its len for next test */
4560:           ==  symlen ) break; }         /*perfect match, so return with it*/
4561: if ( bestdef < 0 )                      /* failed to look up symbol */
4562:   if ( fontnum != 0 )                   /* we're in a restricted font mode */
4563:     { int oldfontnum = fontnum;         /* save current font family */
4564:       mathchardef *symdef = NULL;       /* lookup result with fontnum=0 */
4565:       fontnum = 0;                      /*try to look up symbol in any font*/
4566:       symdef = get_symdef(symbol);      /* repeat lookup with fontnum=0 */
4567:       fontnum = oldfontnum;             /* reset font family */
4568:       return symdef; }                  /* caller gets fontnum=0 lookup */
4569: end_of_job:
4570:  if ( msgfp!=NULL && msglevel>=999 )    /* debugging output */
4571:   { fprintf(msgfp,
4572:     "get_symdef> symbol=%s matches symtable[%d]=%s (isligature=%d)\n",
4573:     symbol,bestdef,(bestdef<0?"NotFound":symdefs[bestdef].symbol),isligature);
4574:     fflush(msgfp); }
4575:  return ( (bestdef<0? NULL : &(symdefs[bestdef])) );/*NULL or best symdef[]*/
4576: } /* --- end-of-function get_symdef() --- */
4577: 
4578: 
4579: /* ==========================================================================
4580:  * Function:    get_ligature ( expression, family )
4581:  * Purpose:     returns symtable[] index for ligature
4582:  * --------------------------------------------------------------------------
4583:  * Arguments:   expression (I)  char *  containing ligature
4584:  *                              whose corresponding mathchardef is wanted
4585:  *              family (I)      int containing NOVALUE for any family,
4586:  *                              or, e.g., CYR10 for cyrillic, etc.
4587:  * --------------------------------------------------------------------------
4588:  * Returns:     ( int )         symtable[] index defining ligature,
4589:  *                              or -9999 if no ligature found or for any error
4590:  * --------------------------------------------------------------------------
4591:  * Notes:     o
4592:  * ======================================================================= */
4593: /* --- entry point --- */
4594: int     get_ligature ( char *expression, int family )
4595: {
4596: /* -------------------------------------------------------------------------
4597: Allocations and Declarations
4598: -------------------------------------------------------------------------- */
4599: mathchardef *symdefs = symtable;        /* table of mathchardefs */
4600: char    *ligature = expression /*- 1*/, /* expression ptr */
4601:         *symbol = NULL;                 /* symdefs[idef].symbol */
4602: int     liglen = strlen(ligature);      /* #chars remaining in expression */
4603: int     iscyrfam = (family==CYR10);     /* true for cyrillic families */
4604: int     idef = 0,                       /* symdefs[] index */
4605:         bestdef = (-9999),              /*index of longest matching symdef*/
4606:         maxlen=(-9999);                 /*length of longest matching symdef*/
4607: /* -------------------------------------------------------------------------
4608: search symdefs[] in order for first occurrence of symbol
4609: -------------------------------------------------------------------------- */
4610: if ( !isstring ) {                      /* no ligatures in "string" mode */
4611:  for ( idef=0; ;idef++ )                /* until trailer record found */
4612:   if ( (symbol=symdefs[idef].symbol) == NULL ) break; /* end-of-table */
4613:   else {                                /* check against caller's ligature */
4614:     int symlen = strlen(symbol);        /* #chars in symbol */
4615:     if ( ( symlen>1 || iscyrfam )       /*ligature >1 char long or cyrillic*/
4616:     &&   symlen <= liglen               /* and enough remaining chars */
4617:     &&   ( *symbol!='\\' || iscyrfam )  /* not escaped or cyrillic */
4618:     &&   symdefs[idef].handler == NULL ) /* and not a handler */
4619:      if ( strncmp(ligature,symbol,symlen) == 0 ) /* found match */
4620:       if ( family < 0                   /* no family specifies */
4621:       ||   symdefs[idef].family == family ) /* or have correct family */
4622:        if ( symlen > maxlen )           /* new longest ligature */
4623:         { bestdef = idef;               /* save index of new best match */
4624:           maxlen = symlen; }            /* and save its len for next test */
4625:     } /* --- end-of-if/else(symbol==NULL) --- */
4626:  if ( msgfp!=NULL && msglevel>=999 )    /* debugging output */
4627:   { fprintf(msgfp,"get_ligature> ligature=%.4s matches symtable[%d]=%s\n",
4628:     ligature,bestdef,(bestdef<0?"NotFound":symdefs[bestdef].symbol));
4629:     fflush(msgfp); }
4630:  } /* --- end-of-if(!isstring) --- */
4631: return ( bestdef );                     /* -9999 or index of best symdef[] */
4632: } /* --- end-of-function get_ligature --- */
4633: 
4634: 
4635: /* ==========================================================================
4636:  * Function:    get_chardef ( symdef, size )
4637:  * Purpose:     returns chardef ptr containing data for symdef at given size
4638:  * --------------------------------------------------------------------------
4639:  * Arguments:   symdef (I)      mathchardef *  corresponding to symbol
4640:  *                              whose corresponding chardef is wanted
4641:  *              size (I)        int containing 0-5 for desired size
4642:  * --------------------------------------------------------------------------
4643:  * Returns:     ( chardef * )   pointer to struct defining symbol at size,
4644:  *                              or NULL for any error
4645:  * --------------------------------------------------------------------------
4646:  * Notes:     o if size unavailable, the next-closer-to-normalsize
4647:  *              is returned instead.
4648:  * ======================================================================= */
4649: /* --- entry point --- */
4650: chardef *get_chardef ( mathchardef *symdef, int size )
4651: {
4652: /* -------------------------------------------------------------------------
4653: Allocations and Declarations
4654: -------------------------------------------------------------------------- */
4655: fontfamily  *fonts = fonttable;         /* table of font families */
4656: chardef **fontdef,                      /*tables for desired font, by size*/
4657:         *gfdata = (chardef *)NULL;      /* chardef for symdef,size */
4658: int     ifont;                          /* fonts[] index */
4659: int     family, charnum;                /* indexes retrieved from symdef */
4660: int     sizeinc = 0,                    /*+1 or -1 to get closer to normal*/
4661:         normalsize = 2;                 /* this size always present */
4662: int     isBig = 0;                      /*true if symbol's 1st char is upper*/
4663: char    *symptr = NULL;                 /* look for 1st alpha of symbol */
4664: /* -------------------------------------------------------------------------
4665: initialization
4666: -------------------------------------------------------------------------- */
4667: /* --- check symdef --- */
4668: if ( symdef == NULL ) goto end_of_job;  /* get_symdef() probably failed */
4669: /* --- get local copy of indexes from symdef --- */
4670: family = symdef->family;                /* font family containing symbol */
4671: charnum = symdef->charnum;              /* char# of symbol within font */
4672: /* --- check for supersampling --- */
4673: if ( issupersampling )                  /* check for supersampling fonts */
4674:  if ( fonts != ssfonttable )            /* uh oh--probably internal error */
4675:   { fonts = ssfonttable; }              /* force it */
4676: /* --- check requested size, and set size increment --- */
4677: if ( 0 && issupersampling )             /* set size index for supersampling */
4678:   size = LARGESTSIZE+1;                 /* index 1 past largest size */
4679: else                                    /* low pass indexes 0...LARGESTSIZE */
4680:   {
4681:   if( size<0 ) size = 0;                /* size was definitely too small */
4682:   if( size>LARGESTSIZE ) size = LARGESTSIZE;  /* or definitely too large */
4683:   if( size<normalsize ) sizeinc = (+1); /*use next larger if size too small*/
4684:   if( size>normalsize ) sizeinc = (-1); /*or next smaller if size too large*/
4685:   }
4686: /* --- check for really big symbol (1st char of symbol name uppercase) --- */
4687: for ( symptr=symdef->symbol; *symptr!='\000'; symptr++ ) /*skip leading \'s*/
4688:   if ( isalpha(*symptr) )               /* found leading alpha char */
4689:     { isBig = isupper(*symptr);         /* is 1st char of name uppercase? */
4690:       if ( !isBig                       /* 1st char lowercase */
4691:       &&   strlen(symptr) >= 4 )        /* but followed by at least 3 chars */
4692:        isBig = !memcmp(symptr,"big\\",4) /* isBig if name starts with big\ */
4693:         || !memcmp(symptr,"bigg",4);    /* or with bigg */
4694:       break; }                          /* don't check beyond 1st char */
4695: /* -------------------------------------------------------------------------
4696: find font family in table of fonts[]
4697: -------------------------------------------------------------------------- */
4698: /* --- look up font family --- */
4699: for ( ifont=0; ;ifont++ )               /* until trailer record found */
4700:   if ( fonts[ifont].family < 0 ) {      /* error, no such family */
4701:     if ( msgfp!=NULL && msglevel>=99 ) { /* emit error */
4702:      fprintf(msgfp,"get_chardef> failed to find font family %d\n",
4703:      family); fflush(msgfp); }
4704:     goto end_of_job; }                  /* quit if can't find font family*/
4705:   else if ( fonts[ifont].family == family ) break; /* found font family */
4706: /* --- get local copy of table for this family by size --- */
4707: fontdef = fonts[ifont].fontdef;         /* font by size */
4708: /* -------------------------------------------------------------------------
4709: get font in desired size, or closest available size, and return symbol
4710: -------------------------------------------------------------------------- */
4711: /* --- get font in desired size --- */
4712: while ( 1 )                             /* find size or closest available */
4713:   if ( fontdef[size] != NULL ) break;   /* found available size */
4714:   else                                  /* adjust size closer to normal */
4715:     if ( size == NORMALSIZE             /* already normal so no more sizes,*/
4716:     || sizeinc == 0 ) {                 /* or must be supersampling */
4717:       if ( msgfp!=NULL && msglevel>=99 ) { /* emit error */
4718:         fprintf(msgfp,"get_chardef> failed to find font size %d\n",
4719:         size); fflush(msgfp); }
4720:       goto end_of_job; }                /* quit if can't find desired size */
4721:     else                                /*bump size 1 closer to NORMALSIZE*/
4722:       size += sizeinc;                  /* see if adjusted size available */
4723: /* --- ptr to chardef struct --- */
4724: gfdata = &((fontdef[size])[charnum]);   /*ptr to chardef for symbol in size*/
4725: /* -------------------------------------------------------------------------
4726: kludge to tweak CMEX10 (which appears to have incorrect descenders)
4727: -------------------------------------------------------------------------- */
4728: if ( family == CMEX10 )                 /* cmex10 needs tweak */
4729:   { int height = gfdata->toprow - gfdata->botrow + 1; /*total height of char*/
4730:     gfdata->botrow = (isBig? (-height/3) : (-height/4));
4731:     gfdata->toprow = gfdata->botrow + gfdata->image.height; }
4732: /* -------------------------------------------------------------------------
4733: return subraster containing chardef data for symbol in requested size
4734: -------------------------------------------------------------------------- */
4735: end_of_job:
4736:  if ( msgfp!=NULL && msglevel>=999 )
4737:   { if (symdef == NULL) fprintf(msgfp,"get_chardef> input symdef==NULL\n");
4738:     else
4739:      fprintf(msgfp,"get_chardef> requested symbol=\"%s\" size=%d  %s\n",
4740:      symdef->symbol,size,(gfdata==NULL?"FAILED":"Succeeded"));
4741:     fflush(msgfp); }
4742:  return ( gfdata );                     /*ptr to chardef for symbol in size*/
4743: } /* --- end-of-function get_chardef() --- */
4744: 
4745: 
4746: /* ==========================================================================
4747:  * Function:    get_charsubraster ( symdef, size )
4748:  * Purpose:     returns new subraster ptr containing
4749:  *              data for symdef at given size
4750:  * --------------------------------------------------------------------------
4751:  * Arguments:   symdef (I)      mathchardef *  corresponding to symbol whose
4752:  *                              corresponding chardef subraster is wanted
4753:  *              size (I)        int containing 0-5 for desired size
4754:  * --------------------------------------------------------------------------
4755:  * Returns:     ( subraster * ) pointer to struct defining symbol at size,
4756:  *                              or NULL for any error
4757:  * --------------------------------------------------------------------------
4758:  * Notes:     o just wraps a subraster envelope around get_chardef()
4759:  * ======================================================================= */
4760: /* --- entry point --- */
4761: subraster *get_charsubraster ( mathchardef *symdef, int size )
4762: {
4763: /* -------------------------------------------------------------------------
4764: Allocations and Declarations
4765: -------------------------------------------------------------------------- */
4766: chardef *get_chardef(), *gfdata=NULL;   /* chardef struct for symdef,size */
4767: int     get_baseline();                 /* baseline of gfdata */
4768: subraster *new_subraster(), *sp=NULL;   /* subraster containing gfdata */
4769: raster  *bitmaprp=NULL, *gftobitmap();  /* convert .gf-format to bitmap */
4770: raster  *rotp=NULL, *rastrot3d();       /* rotate character if utheta>0 */
4771: int     delete_subraster();             /* in case gftobitmap() fails */
4772: int     delete_raster();                /* in case IMAGERASTER replaced */
4773: int     aasupsamp(),                    /*antialias char with supersampling*/
4774:         grayscale=256;                  /* aasupersamp() parameters */
4775: /* -------------------------------------------------------------------------
4776: look up chardef for symdef at size, and embed data (gfdata) in subraster
4777: -------------------------------------------------------------------------- */
4778: if ( (gfdata=get_chardef(symdef,size))  /* look up chardef for symdef,size */
4779: !=   NULL )                             /* and check that we found it */
4780:  if ( (sp=new_subraster(0,0,0))         /* allocate subraster "envelope" */
4781:  !=   NULL )                            /* and check that we succeeded */
4782:   {
4783:   raster *image = &(gfdata->image);     /* ptr to chardef's bitmap or .gf */
4784:   int format = image->format;           /* 1=bitmap, else .gf */
4785:   sp->symdef = symdef;                  /* replace NULL with caller's arg */
4786:   sp->size = size;                      /*replace default with caller's size*/
4787:   sp->baseline = get_baseline(gfdata);  /* get baseline of character */
4788:   if ( format == 1 )                    /* already a bitmap */
4789:    { sp->type = CHARASTER;              /* static char raster */
4790:      sp->image = image; }               /* store ptr to its bitmap */
4791:   else                                  /* need to convert .gf-to-bitmap */
4792:    if ( (bitmaprp = gftobitmap(image))  /* convert */
4793:    !=   (raster *)NULL )                /* successful */
4794:     { sp->type = IMAGERASTER;           /* allocated raster will be freed */
4795:       sp->image = bitmaprp; }           /* store ptr to converted bitmap */
4796:    else                                 /* conversion failed */
4797:     { delete_subraster(sp);             /* free unneeded subraster */
4798:       sp = (subraster *)NULL;           /* signal error to caller */
4799:       goto end_of_job; }                /* quit */
4800:   if ( issupersampling )                /* antialias character right here */
4801:     {
4802:     raster *aa = NULL;                  /* antialiased char raster */
4803:     int status = aasupsamp(sp->image,&aa,shrinkfactor,grayscale);
4804:     if ( status )                       /* supersampled successfully */
4805:       { int baseline = sp->baseline;    /* baseline before supersampling */
4806:         int height = gfdata->image.height; /* #rows before supersampling */
4807:         sp->image = aa;                 /* replace chardef with ss image */
4808:         if ( baseline >= height-1 )     /* baseline at bottom of char */
4809:           sp->baseline = aa->height -1; /* so keep it at bottom */
4810:         else                            /* char has descenders */
4811:           sp->baseline /= shrinkfactor; /* rescale baseline */
4812:         sp->type = IMAGERASTER; }       /* character is an image raster */
4813:     } /* --- end-of-if(issupersampling) --- */
4814:   if ( absval(utheta) > 1.0e-6 ) {      /* character 3d-rotation wanted */
4815:     rotp = rastrot3d(sp->image,&uaxis,utheta); /* rotate image around uaxis */
4816:     if ( sp->type != CHARASTER )        /* not a static character raster */
4817:       delete_raster(sp->image);         /* so free currently allocated image */
4818:     sp->image = rotp;                   /* and replace it with rotated image */
4819:     sp->type = IMAGERASTER;             /* allocated raster will be freed */
4820:     } /* --- end-of-if(absval(utheta)>1.0e-6) --- */
4821:   } /* --- end-of-if(sp!=NULL) --- */
4822: end_of_job:
4823:  if ( msgfp!=NULL && msglevel>=999 )
4824:   { fprintf(msgfp,"get_charsubraster> requested symbol=\"%s\" baseline=%d"
4825:     " %s %s\n", symdef->symbol, (sp==NULL?0:sp->baseline),
4826:     (sp==NULL?"FAILED":"Succeeded"), (gfdata==NULL?"(gfdata=NULL)":" "));
4827:     fflush(msgfp); }
4828: return ( sp );                          /* back to caller */
4829: } /* --- end-of-function get_charsubraster() --- */
4830: 
4831: 
4832: /* ==========================================================================
4833:  * Function:    get_symsubraster ( symbol, size )
4834:  * Purpose:     returns new subraster ptr containing
4835:  *              data for symbol at given size
4836:  * --------------------------------------------------------------------------
4837:  * Arguments:   symbol (I)      char *  corresponding to symbol
4838:  *                              whose corresponding subraster is wanted
4839:  *              size (I)        int containing 0-5 for desired size
4840:  * --------------------------------------------------------------------------
4841:  * Returns:     ( subraster * ) pointer to struct defining symbol at size,
4842:  *                              or NULL for any error
4843:  * --------------------------------------------------------------------------
4844:  * Notes:     o just combines get_symdef() and get_charsubraster()
4845:  * ======================================================================= */
4846: /* --- entry point --- */
4847: subraster *get_symsubraster ( char *symbol, int size )
4848: {
4849: /* -------------------------------------------------------------------------
4850: Allocations and Declarations
4851: -------------------------------------------------------------------------- */
4852: subraster *sp=NULL, *get_charsubraster(); /* subraster containing gfdata */
4853: mathchardef *symdef=NULL, *get_symdef(); /* mathchardef lookup for symbol */
4854: /* -------------------------------------------------------------------------
4855: look up mathchardef for symbol
4856: -------------------------------------------------------------------------- */
4857: if ( symbol != NULL )                   /* user supplied input symbol */
4858:   symdef = get_symdef(symbol);          /*look up corresponding mathchardef*/
4859: /* -------------------------------------------------------------------------
4860: look up chardef for mathchardef and wrap a subraster structure around data
4861: -------------------------------------------------------------------------- */
4862: if ( symdef != NULL )                   /* lookup succeeded */
4863:   sp = get_charsubraster(symdef,size);  /* so get symbol data in subraster */
4864: return ( sp );                          /* back to caller with sp or NULL */
4865: } /* --- end-of-function get_symsubraster() --- */
4866: 
4867: 
4868: /* ==========================================================================
4869:  * Function:    get_baseline ( gfdata )
4870:  * Purpose:     returns baseline for a chardef struct
4871:  * --------------------------------------------------------------------------
4872:  * Arguments:   gfdata (I)      chardef *  containing chardef for symbol
4873:  *                              whose baseline is wanted
4874:  * --------------------------------------------------------------------------
4875:  * Returns:     ( int )         baseline for symdef,
4876:  *                              or -1 for any error
4877:  * --------------------------------------------------------------------------
4878:  * Notes:     o Unlike TeX, the top-left corners of our rasters are (0,0),
4879:  *              with (row,col) increasing as you move down and right.
4880:  *              Baselines are calculated with respect to this scheme,
4881:  *              so 0 would mean the very top row is on the baseline
4882:  *              and everything else descends below the baseline.
4883:  * ======================================================================= */
4884: /* --- entry point --- */
4885: int     get_baseline ( chardef *gfdata )
4886: {
4887: /* -------------------------------------------------------------------------
4888: Allocations and Declarations
4889: -------------------------------------------------------------------------- */
4890: int     /*toprow = gfdata->toprow,*/    /*TeX top row from .gf file info*/
4891:         botrow = gfdata->botrow,        /*TeX bottom row from .gf file info*/
4892:         height = gfdata->image.height;  /* #rows comprising symbol */
4893: /* -------------------------------------------------------------------------
4894: give caller baseline
4895: -------------------------------------------------------------------------- */
4896: return ( (height-1) + botrow );         /* note: descenders have botrow<0 */
4897: } /* --- end-of-function get_baseline() --- */
4898: 
4899: 
4900: /* ==========================================================================
4901:  * Function:    get_delim ( char *symbol, int height, int family )
4902:  * Purpose:     returns subraster corresponding to the samllest
4903:  *              character containing symbol, but at least as large as height,
4904:  *              and in caller's family (if specified).
4905:  *              If no symbol character as large as height is available,
4906:  *              then the largest availabale character is returned instead.
4907:  * --------------------------------------------------------------------------
4908:  * Arguments:   symbol (I)      char *  containing (substring of) desired
4909:  *                              symbol, e.g., if symbol="(", then any
4910:  *                              mathchardef like "(" or "\\(", etc, match.
4911:  *              height (I)      int containing minimum acceptable height
4912:  *                              for returned character
4913:  *              family (I)      int containing -1 to consider all families,
4914:  *                              or, e.g., CMEX10 for only that family
4915:  * --------------------------------------------------------------------------
4916:  * Returns:     ( subraster * ) best matching character available,
4917:  *                              or NULL for any error
4918:  * --------------------------------------------------------------------------
4919:  * Notes:     o If height is passed as negative, its absolute value is used
4920:  *              but the best-fit width is searched for (rather than height)
4921:  * ======================================================================= */
4922: /* --- entry point --- */
4923: subraster *get_delim ( char *symbol, int height, int family )
4924: {
4925: /* -------------------------------------------------------------------------
4926: Allocations and Declarations
4927: -------------------------------------------------------------------------- */
4928: mathchardef *symdefs = symtable;        /* table of mathchardefs */
4929: subraster *get_charsubraster(), *sp=(subraster *)NULL; /* best match char */
4930: subraster *make_delim();                /* construct delim if can't find it*/
4931: chardef *get_chardef(), *gfdata=NULL;   /* get chardef struct for a symdef */
4932: char    lcsymbol[256], *symptr,         /* lowercase symbol for comparison */
4933:         *unescsymbol = symbol;          /* unescaped symbol */
4934: int     symlen = (symbol==NULL?0:strlen(symbol)), /* #chars in caller's sym*/
4935:         deflen = 0;                     /* length of symdef (aka lcsymbol) */
4936: int     idef = 0,                       /* symdefs[] index */
4937:         bestdef = (-9999),              /* index of best fit symdef */
4938:         bigdef = (-9999);               /*index of biggest (in case no best)*/
4939: int     size = 0,                       /* size index 0...LARGESTSIZE */
4940:         bestsize = (-9999),             /* index of best fit size */
4941:         bigsize = (-9999);              /*index of biggest (in case no best)*/
4942: int     defheight, bestheight=9999,     /* height of best fit symdef */
4943:         bigheight = (-9999);            /*height of biggest(in case no best)*/
4944: int     iswidth = 0;                    /* true if best-fit width desired */
4945: int     isunesc = 0,                    /* true if leading escape removed */
4946:         issq=0, isoint=0;               /* true for \sqcup,etc, \oint,etc */
4947: int     iscurly = 0;                    /* true for StMary's curly symbols */
4948: char    *bigint="bigint", *bigoint="bigoint"; /* substitutes for int, oint */
4949: /* -------------------------------------------------------------------------
4950: determine if searching height or width, and search symdefs[] for best-fit
4951: -------------------------------------------------------------------------- */
4952: /* --- arg checks --- */
4953: if ( symlen < 1 ) return (sp);          /* no input symbol suplied */
4954: if ( strcmp(symbol,"e") == 0 ) return(sp); /* e causes segfault??? */
4955: if ( strstr(symbol,"curly") != NULL ) iscurly=1; /* user wants curly delim */
4956: /* --- ignore leading escapes for CMEX10 --- */
4957: if ( 1 )                                /* ignore leading escape */
4958:  if ( (family==CMEX10 || family==CMSYEX) ) { /* for CMEX10 or CMSYEX */
4959:   if ( strstr(symbol,"sq") != NULL )    /* \sq symbol requested */
4960:      issq = 1;                          /* seq \sq signal */
4961:   if ( strstr(symbol,"oint") != NULL )  /* \oint symbol requested */
4962:      isoint = 1;                        /* seq \oint signal */
4963:   if ( *symbol=='\\' )                  /* have leading \ */
4964:    { unescsymbol = symbol+1;            /* push past leading \ */
4965:      if ( --symlen < 1 ) return(sp);    /* one less char */
4966:      if ( strcmp(unescsymbol,"int") == 0 ) /* \int requested by caller */
4967:        unescsymbol = bigint;            /* but big version looks better */
4968:      if ( strcmp(unescsymbol,"oint") == 0 ) /* \oint requested by caller */
4969:        unescsymbol = bigoint;           /* but big version looks better */
4970:      symlen = strlen(unescsymbol);      /* explicitly recalculate length */
4971:      isunesc = 1; }                     /* signal leading escape removed */
4972:   } /* --- end-of-if(family) --- */
4973: /* --- determine whether searching for best-fit height or width --- */
4974: if ( height < 0 )                       /* negative signals width search */
4975:   { height = (-height);                 /* flip "height" positive */
4976:     iswidth = 1; }                      /* set flag for width search */
4977: /* --- search symdefs[] for best-fit height (or width) --- */
4978: for ( idef=0; ;idef++ )                 /* until trailer record found */
4979:  {
4980:  char *defsym = symdefs[idef].symbol;   /* local copies */
4981:  int  deffam  = symdefs[idef].family;
4982:  if ( defsym == NULL ) break;           /* reached end-of-table */
4983:  else                                   /* check against caller's symbol */
4984:   if ( family<0 || deffam == family     /* if explicitly in caller's family*/
4985:   ||  (family==CMSYEX && (deffam==CMSY10||deffam==CMEX10||deffam==STMARY10)) )
4986:     {
4987:     strcpy(lcsymbol,defsym);            /* local copy of symdefs[] symbol */
4988:     if ( isunesc && *lcsymbol=='\\' )   /* ignored leading \ in symbol */
4989:      {strsqueeze(lcsymbol,1);}          /*so squeeze it out of lcsymbol too*/
4990:     if ( 0 )                            /* don't ignore case */
4991:      for ( symptr=lcsymbol; *symptr!='\000'; symptr++ )/*for each symbol ch*/
4992:       if ( isalpha(*symptr) ) *symptr=tolower(*symptr);/*lowercase the char*/
4993:     deflen = strlen(lcsymbol);          /* #chars in symbol we're checking */
4994:     if ((symptr=strstr(lcsymbol,unescsymbol)) != NULL) /*found caller's sym*/
4995:      if ( (isoint || strstr(lcsymbol,"oint")==NULL) /* skip unwanted "oint"*/
4996:      &&   (issq || strstr(lcsymbol,"sq")==NULL) ) /* skip unwanted "sq" */
4997:       if ( ( deffam == CMSY10 ?         /* CMSY10 or not CMSY10 */
4998:           symptr == lcsymbol            /* caller's sym is a prefix */
4999:           && deflen == symlen:          /* and same length */
5000:           (iscurly || strstr(lcsymbol,"curly")==NULL) &&/*not unwanted curly*/
5001:           (symptr == lcsymbol           /* caller's sym is a prefix */
5002:           || symptr == lcsymbol+deflen-symlen) ) ) /* or a suffix */
5003:        for ( size=0; size<=LARGESTSIZE; size++ ) /* check all font sizes */
5004:         if ( (gfdata=get_chardef(&(symdefs[idef]),size)) != NULL ) /*got one*/
5005:           { defheight = gfdata->image.height;   /* height of this character */
5006:             if ( iswidth )              /* width search wanted instead... */
5007:               defheight = gfdata->image.width;  /* ...so substitute width */
5008:             leftsymdef = &(symdefs[idef]);      /* set symbol class, etc */
5009:             if ( defheight>=height && defheight<bestheight ) /*new best fit*/
5010:               { bestdef=idef; bestsize=size;    /* save indexes of best fit */
5011:                 bestheight = defheight; }       /* and save new best height */
5012:             if ( defheight >= bigheight )       /* new biggest character */
5013:               { bigdef=idef; bigsize=size;      /* save indexes of biggest */
5014:                 bigheight = defheight; }        /* and save new big height */
5015:           } /* --- end-of-if(gfdata!=NULL) --- */
5016:     } /* --- end-of-if(family) --- */
5017:  } /* --- end-of-for(idef) --- */
5018: /* -------------------------------------------------------------------------
5019: construct subraster for best fit character, and return it to caller
5020: -------------------------------------------------------------------------- */
5021: if ( bestdef >= 0 )                     /* found a best fit for caller */
5022:   sp = get_charsubraster(&(symdefs[bestdef]),bestsize); /* best subraster */
5023: if ( (sp==NULL && height-bigheight>5)   /* try to construct delim */
5024: ||   bigdef < 0 )                       /* delim not in font tables */
5025:   sp = make_delim(symbol,(iswidth?-height:height)); /* try to build delim */
5026: if ( sp==NULL && bigdef>=0 )            /* just give biggest to caller */
5027:   sp = get_charsubraster(&(symdefs[bigdef]),bigsize); /* biggest subraster */
5028: if ( msgfp!=NULL && msglevel>=99 )
5029:     fprintf(msgfp,"get_delim> symbol=%.50s, height=%d family=%d isokay=%s\n",
5030:     (symbol==NULL?"null":symbol),height,family,(sp==NULL?"fail":"success"));
5031: return ( sp );
5032: } /* --- end-of-function get_delim() --- */
5033: 
5034: 
5035: /* ==========================================================================
5036:  * Function:    make_delim ( char *symbol, int height )
5037:  * Purpose:     constructs subraster corresponding to symbol
5038:  *              exactly as large as height,
5039:  * --------------------------------------------------------------------------
5040:  * Arguments:   symbol (I)      char *  containing, e.g., if symbol="("
5041:  *                              for desired delimiter
5042:  *              height (I)      int containing height
5043:  *                              for returned character
5044:  * --------------------------------------------------------------------------
5045:  * Returns:     ( subraster * ) constructed delimiter
5046:  *                              or NULL for any error
5047:  * --------------------------------------------------------------------------
5048:  * Notes:     o If height is passed as negative, its absolute value is used
5049:  *              and interpreted as width (rather than height)
5050:  * ======================================================================= */
5051: /* --- entry point --- */
5052: subraster *make_delim ( char *symbol, int height )
5053: {
5054: /* -------------------------------------------------------------------------
5055: Allocations and Declarations
5056: -------------------------------------------------------------------------- */
5057: subraster *sp = (subraster *)NULL,      /* subraster returned to caller */
5058:         *new_subraster();               /* allocate subraster */
5059: subraster *get_symsubraster(),          /* look up delim pieces in cmex10 */
5060:         *symtop=NULL, *symbot=NULL, *symmid=NULL, *symbar=NULL, /* pieces */
5061:         *topsym=NULL, *botsym=NULL, *midsym=NULL, *barsym=NULL, /* +filler */
5062:         *rastack(), *rastcat();         /* stack pieces, concat filler */
5063: int     isdrawparen = 0;                /*1=draw paren, 0=build from pieces*/
5064: raster  *rasp = (raster *)NULL;         /* sp->image */
5065: int     isokay=0, delete_subraster();   /* set true if delimiter drawn ok */
5066: int     pixsz = 1,                      /* pixels are one bit each */
5067:         symsize = 0;                    /* size arg for get_symsubraster() */
5068: int     thickness = 1;                  /* drawn lines are one pixel thick */
5069: int     aspectratio = 8;                /* default height/width for parens */
5070: int     iswidth = 0,                    /*true if width specified by height*/
5071:         width = height;                 /* #pixels width (e.g., of ellipse)*/
5072: char    *lp=NULL,  *rp=NULL,            /* check symbol for left or right */
5073:         *lp2=NULL, *rp2=NULL,           /* synonym for lp,rp */
5074:         *lp3=NULL, *rp3=NULL,           /* synonym for lp,rp */
5075:         *lp4=NULL, *rp4=NULL;           /* synonym for lp,rp */
5076: int     circle_raster(),                /* ellipse for ()'s in sp->image */
5077:         rule_rsater(),                  /* horizontal or vertical lines */
5078:         line_raster();                  /* line between two points */
5079: subraster *uparrow_subraster();         /* up/down arrows */
5080: int     isprealloc = 1;                 /*pre-alloc subraster, except arrow*/
5081: int     oldsmashmargin = smashmargin,   /* save original smashmargin */
5082:         wasnocatspace = isnocatspace;   /* save original isnocatspace */
5083: /* -------------------------------------------------------------------------
5084: initialization
5085: -------------------------------------------------------------------------- */
5086: /* --- determine whether constructing height or width --- */
5087: if ( height < 0 )                       /* negative "height" signals width */
5088:   { width = height = (-height);         /* flip height positive */
5089:     iswidth = 1; }                      /* set flag for width */
5090: if ( height < 3 ) goto end_of_job;      /* too small, must be error */
5091: /* --- set default width (or height) accordingly --- */
5092: if ( iswidth ) height =  (width+(aspectratio+1)/2)/aspectratio;
5093: else            width = (height+(aspectratio+1)/2)/aspectratio;
5094: if ( strchr(symbol,'=') != NULL         /* left or right || bracket wanted */
5095: ||   strstr(symbol,"\\|") != NULL       /* same || in standard tex notation*/
5096: ||   strstr(symbol,"dbl") != NULL )     /* semantic bracket with ||'s */
5097:   width = max2(width,6);                /* need space between two |'s */
5098: if ( width < 2 ) width=2;               /* set min width */
5099: if ( strchr(symbol,'(') != NULL         /* if left ( */
5100: ||   strchr(symbol,')') != NULL )       /* or right ) paren wanted */
5101:   { width = (3*width)/2;                /* adjust width */
5102:     if ( !isdrawparen ) isprealloc=0; } /* don't prealloc if building */
5103: if ( strchr(symbol,'/') != NULL         /* left / */
5104: ||   strstr(symbol,"\\\\") != NULL      /* or \\ for right \ */
5105: ||   strstr(symbol,"backsl") != NULL )  /* or \backslash for \ */
5106:   width = max2(height/3,5);
5107: if ( strstr(symbol,"arrow") != NULL )   /* arrow wanted */
5108:   { width = min2(height/3,20);          /* adjust width */
5109:     isprealloc = 0; }                   /* don't preallocate subraster */
5110: if ( strchr(symbol,'{') != NULL         /* if left { */
5111: ||   strchr(symbol,'}') != NULL )       /* or right } brace wanted */
5112:   { isprealloc = 0; }                   /* don't preallocate */
5113: /* --- allocate and initialize subraster for constructed delimiter --- */
5114: if ( isprealloc )                       /* pre-allocation wanted */
5115:  { if ( (sp=new_subraster(width,height,pixsz)) /* allocate new subraster */
5116:    ==   NULL )  goto end_of_job;        /* quit if failed */
5117:    /* --- initialize delimiter subraster parameters --- */
5118:    sp->type = IMAGERASTER;              /* image */
5119:    sp->symdef = NULL;                   /* not applicable for image */
5120:    sp->baseline = height/2 + 2;         /* is a little above center good? */
5121:    sp->size = NORMALSIZE;               /* size (probably unneeded) */
5122:    rasp = sp->image; }                  /* pointer to image in subraster */
5123: /* -------------------------------------------------------------------------
5124: ( ) parens
5125: -------------------------------------------------------------------------- */
5126: if ( (lp=strchr(symbol,'(')) != NULL    /* left ( paren wanted */
5127: ||   (rp=strchr(symbol,')')) != NULL )  /* right ) paren wanted */
5128:   {
5129:   if ( isdrawparen ) {                  /* draw the paren */
5130:    int  mywidth = min2(width,20);       /* max width for ()'s */
5131:    circle_raster ( rasp,                /* embedded raster image */
5132:         0, 0,                           /* row0,col0 are upper-left corner */
5133:         height-1, mywidth-1,            /* row1,col1 are lower-right */
5134:         thickness,                      /* line thickness is 1 pixel */
5135:         (rp==NULL?"23":"41") );         /* "1234" quadrants to be drawn */
5136:    isokay = 1; }                        /* set flag */
5137:   else {
5138:    int  isleft = (lp!=NULL?1:0);        /* true for left, false for right */
5139:    char *parentop = (isleft?"\\leftparentop":"\\rightparentop"),
5140:         *parenbot = (isleft?"\\leftparenbot":"\\rightparenbot"),
5141:         *parenbar = (isleft?"\\leftparenbar":"\\rightparenbar");
5142:    int  baseht=0, barht=0,              /* height of base=top+bot, bar */
5143:         ibar=0, nbars=0;                /* bar index, #bars between top&bot*/
5144:    int  largestsize = min2(2,LARGESTSIZE), /* largest size for parens */
5145:         topfill=(isleft?0:0), botfill=(isleft?0:0),
5146:         barfill=(isleft?0:7);           /* alignment fillers */
5147:    /* --- get pieces at largest size smaller than total height --- */
5148:    for ( symsize=largestsize; symsize>=0; symsize-- ) /*largest to smallest*/
5149:     {
5150:     /* --- get pieces at current test size --- */
5151:     isokay = 1;                         /* check for all pieces */
5152:     if ( (symtop=get_symsubraster(parentop,symsize)) == NULL ) isokay=0;
5153:     if ( (symbot=get_symsubraster(parenbot,symsize)) == NULL ) isokay=0;
5154:     if ( (symbar=get_symsubraster(parenbar,symsize)) == NULL ) isokay=0;
5155:     /* --- check sum of pieces against total desired height --- */
5156:     if ( isokay ) {                     /* all pieces retrieved */
5157:       baseht = (symtop->image)->height + (symbot->image)->height; /*top+bot*/
5158:       barht  = (symbar->image)->height; /* bar height */
5159:       if ( baseht < height+5 ) break;   /* largest base that's not too big */
5160:       if ( symsize < 1 ) break;         /* or smallest available base */
5161:       } /* --- end-of-if(isokay) --- */
5162:     /* --- free test pieces that were too big --- */
5163:     if ( symtop != NULL ) delete_subraster(symtop); /* free top */
5164:     if ( symbot != NULL ) delete_subraster(symbot); /* free bot */
5165:     if ( symbar != NULL ) delete_subraster(symbar); /* free bar */
5166:     isokay = 0;                         /* nothing available */
5167:     if ( symsize < 1 ) break;           /* leave isokay=0 after smallest */
5168:     } /* --- end-of-for(symsize) --- */
5169:    /* --- construct brace from pieces --- */
5170:    if ( isokay ) {                      /* we have the pieces */
5171:     /* --- add alignment fillers --- */
5172:     smashmargin=0;  isnocatspace=99;    /*turn off rastcat smashing,space*/
5173:     topsym = (topfill>0?rastcat(new_subraster(topfill,1,1),symtop,3):symtop);
5174:     botsym = (botfill>0?rastcat(new_subraster(botfill,1,1),symbot,3):symbot);
5175:     barsym = (barfill>0?rastcat(new_subraster(barfill,1,1),symbar,3):symbar);
5176:     smashmargin = oldsmashmargin;       /* reset smashmargin */
5177:     isnocatspace = wasnocatspace;       /* reset isnocatspace */
5178:     /* --- #bars needed between top and bot --- */
5179:     nbars = (barht<1?0:max2(0,1+(height-baseht)/barht)); /* #bars needed */
5180:     /* --- stack pieces --- */
5181:     sp = topsym;                        /* start with top piece */
5182:     if ( nbars > 0 )                    /* need nbars between top and bot */
5183:       for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2);
5184:     sp = rastack(botsym,sp,1,0,0,3);    /* bottom below bars or middle */
5185:     delete_subraster(barsym);           /* barsym no longer needed */
5186:     } /* --- end-of-if(isokay) --- */
5187:    } /* --- end-of-if/else(isdrawparen) --- */
5188:   } /* --- end-of-if(left- or right-() paren wanted) --- */
5189: /* -------------------------------------------------------------------------
5190: { } braces
5191: -------------------------------------------------------------------------- */
5192: else
5193:  if ( (lp=strchr(symbol,'{')) != NULL   /* left { brace wanted */
5194:  ||   (rp=strchr(symbol,'}')) != NULL ) /* right } brace wanted */
5195:   {
5196:   int   isleft = (lp!=NULL?1:0);        /* true for left, false for right */
5197:   char  *bracetop = (isleft?"\\leftbracetop":"\\rightbracetop"),
5198:         *bracebot = (isleft?"\\leftbracebot":"\\rightbracebot"),
5199:         *bracemid = (isleft?"\\leftbracemid":"\\rightbracemid"),
5200:         *bracebar = (isleft?"\\leftbracebar":"\\rightbracebar");
5201:   int   baseht=0, barht=0,              /* height of base=top+bot+mid, bar */
5202:         ibar=0, nbars=0;                /* bar index, #bars above,below mid*/
5203:   int   largestsize = min2(2,LARGESTSIZE), /* largest size for braces */
5204:         topfill=(isleft?4:0), botfill=(isleft?4:0),
5205:         midfill=(isleft?0:4), barfill=(isleft?4:4); /* alignment fillers */
5206:   /* --- get pieces at largest size smaller than total height --- */
5207:   for ( symsize=largestsize; symsize>=0; symsize-- ) /*largest to smallest*/
5208:     {
5209:     /* --- get pieces at current test size --- */
5210:     isokay = 1;                         /* check for all pieces */
5211:     if ( (symtop=get_symsubraster(bracetop,symsize)) == NULL ) isokay=0;
5212:     if ( (symbot=get_symsubraster(bracebot,symsize)) == NULL ) isokay=0;
5213:     if ( (symmid=get_symsubraster(bracemid,symsize)) == NULL ) isokay=0;
5214:     if ( (symbar=get_symsubraster(bracebar,symsize)) == NULL ) isokay=0;
5215:     /* --- check sum of pieces against total desired height --- */
5216:     if ( isokay ) {                     /* all pieces retrieved */
5217:       baseht = (symtop->image)->height + (symbot->image)->height
5218:         + (symmid->image)->height;      /* top+bot+mid height */
5219:       barht = (symbar->image)->height;  /* bar height */
5220:       if ( baseht < height+5 ) break;   /* largest base that's not too big */
5221:       if ( symsize < 1 ) break;         /* or smallest available base */
5222:       } /* --- end-of-if(isokay) --- */
5223:     /* --- free test pieces that were too big --- */
5224:     if ( symtop != NULL ) delete_subraster(symtop); /* free top */
5225:     if ( symbot != NULL ) delete_subraster(symbot); /* free bot */
5226:     if ( symmid != NULL ) delete_subraster(symmid); /* free mid */
5227:     if ( symbar != NULL ) delete_subraster(symbar); /* free bar */
5228:     isokay = 0;                         /* nothing available */
5229:     if ( symsize < 1 ) break;           /* leave isokay=0 after smallest */
5230:     } /* --- end-of-for(symsize) --- */
5231:   /* --- construct brace from pieces --- */
5232:   if ( isokay ) {                       /* we have the pieces */
5233:     /* --- add alignment fillers --- */
5234:     smashmargin=0;  isnocatspace=99;    /*turn off rastcat smashing,space*/
5235:     topsym = (topfill>0?rastcat(new_subraster(topfill,1,1),symtop,3):symtop);
5236:     botsym = (botfill>0?rastcat(new_subraster(botfill,1,1),symbot,3):symbot);
5237:     midsym = (midfill>0?rastcat(new_subraster(midfill,1,1),symmid,3):symmid);
5238:     barsym = (barfill>0?rastcat(new_subraster(barfill,1,1),symbar,3):symbar);
5239:     smashmargin = oldsmashmargin;       /* reset smashmargin */
5240:     isnocatspace = wasnocatspace;       /* reset isnocatspace */
5241:     /* --- #bars needed on each side of mid piece --- */
5242:     nbars = (barht<1?0:max2(0,1+(height-baseht)/barht/2)); /*#bars per side*/
5243:     /* --- stack pieces --- */
5244:     sp = topsym;                        /* start with top piece */
5245:     if ( nbars > 0 )                    /* need nbars above middle */
5246:       for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2);
5247:     sp = rastack(midsym,sp,1,0,0,3);    /*mid after top or bars*/
5248:     if ( nbars > 0 )                    /* need nbars below middle */
5249:       for ( ibar=1; ibar<=nbars; ibar++ ) sp = rastack(barsym,sp,1,0,0,2);
5250:     sp = rastack(botsym,sp,1,0,0,3);    /* bottom below bars or middle */
5251:     delete_subraster(barsym);           /* barsym no longer needed */
5252:     } /* --- end-of-if(isokay) --- */
5253:   } /* --- end-of-if(left- or right-{} brace wanted) --- */
5254: /* -------------------------------------------------------------------------
5255: [ ] brackets
5256: -------------------------------------------------------------------------- */
5257: else
5258:  if ( (lp=strchr(symbol,'[')) != NULL   /* left [ bracket wanted */
5259:  ||   (rp=strchr(symbol,']')) != NULL   /* right ] bracket wanted */
5260:  ||   (lp2=strstr(symbol,"lceil")) != NULL /* left ceiling wanted */
5261:  ||   (rp2=strstr(symbol,"rceil")) != NULL /* right ceiling wanted */
5262:  ||   (lp3=strstr(symbol,"lfloor")) != NULL /* left floor wanted */
5263:  ||   (rp3=strstr(symbol,"rfloor")) != NULL /* right floor wanted */
5264:  ||   (lp4=strstr(symbol,"llbrack")) != NULL /* left semantic bracket */
5265:  ||   (rp4=strstr(symbol,"rrbrack")) != NULL ) /* right semantic bracket */
5266:   {
5267:   /* --- use rule_raster ( rasp, top, left, width, height, type=0 ) --- */
5268:   int   mywidth = min2(width,12),       /* max width for horizontal bars */
5269:         wthick = 1;                     /* thickness of top.bottom bars */
5270:   thickness = (height<25?1:2);          /* set lines 1 or 2 pixels thick */
5271:   if ( lp2!=NULL || rp2!=NULL || lp3!=NULL || rp3 !=NULL ) /*ceil or floor*/
5272:     wthick = thickness;                 /* same thickness for top/bot bar */
5273:   if ( lp3==NULL && rp3==NULL )         /* set top bar if floor not wanted */
5274:     rule_raster(rasp, 0,0, mywidth,wthick, 0); /* top horizontal bar */
5275:   if ( lp2==NULL && rp2==NULL )         /* set bot bar if ceil not wanted */
5276:     rule_raster(rasp, height-wthick,0, mywidth,thickness, 0); /* bottom */
5277:   if ( lp!=NULL || lp2!=NULL || lp3!=NULL || lp4!=NULL ) /* left bracket */
5278:    rule_raster(rasp, 0,0, thickness,height, 0); /* left vertical bar */
5279:   if ( lp4 != NULL )                    /* 2nd left vertical bar needed */
5280:    rule_raster(rasp, 0,thickness+1, 1,height, 0); /* 2nd left vertical bar */
5281:   if ( rp!=NULL || rp2!=NULL || rp3!=NULL || rp4!=NULL ) /* right bracket */
5282:    rule_raster(rasp, 0,mywidth-thickness, thickness,height, 0); /* right */
5283:   if ( rp4 != NULL )                    /* 2nd right vertical bar needed */
5284:    rule_raster(rasp, 0,mywidth-thickness-2, 1,height, 0); /*2nd right vert*/
5285:   isokay = 1;                           /* set flag */
5286:   } /* --- end-of-if(left- or right-[] bracket wanted) --- */
5287: /* -------------------------------------------------------------------------
5288: < > brackets
5289: -------------------------------------------------------------------------- */
5290: else
5291:  if ( (lp=strchr(symbol,'<')) != NULL   /* left < bracket wanted */
5292:  ||   (rp=strchr(symbol,'>')) != NULL ) /* right > bracket wanted */
5293:   {
5294:   /* --- use line_raster( rasp,  row0, col0,  row1, col1,  thickness ) --- */
5295:   int   mywidth = min2(width,12),       /* max width for brackets */
5296:         mythick = 1;                    /* all lines one pixel thick */
5297:   thickness = (height<25?1:2);          /* set line pixel thickness */
5298:   if ( lp != NULL )                     /* left < bracket wanted */
5299:     { line_raster(rasp,height/2,0,0,mywidth-1,mythick);
5300:       if ( thickness>1 )
5301:         line_raster(rasp,height/2,1,0,mywidth-1,mythick);
5302:       line_raster(rasp,height/2,0,height-1,mywidth-1,mythick);
5303:       if ( thickness>1 )
5304:         line_raster(rasp,height/2,1,height-1,mywidth-1,mythick); }
5305:   if ( rp != NULL )                     /* right > bracket wanted */
5306:     { line_raster(rasp,height/2,mywidth-1,0,0,mythick);
5307:       if ( thickness>1 )
5308:         line_raster(rasp,height/2,mywidth-2,0,0,mythick);
5309:       line_raster(rasp,height/2,mywidth-1,height-1,0,mythick);
5310:       if ( thickness>1 )
5311:         line_raster(rasp,height/2,mywidth-2,height-1,0,mythick); }
5312:   isokay = 1;                           /* set flag */
5313:   } /* --- end-of-if(left- or right-<> bracket wanted) --- */
5314: /* -------------------------------------------------------------------------
5315: / \ delimiters
5316: -------------------------------------------------------------------------- */
5317: else
5318:  if ( (lp=strchr(symbol,'/')) != NULL   /* left /  wanted */
5319:  ||   (rp=strstr(symbol,"\\\\")) != NULL /* right \ wanted */
5320:  ||   (rp2=strstr(symbol,"backsl")) != NULL ) /* right \ wanted */
5321:   {
5322:   /* --- use line_raster( rasp,  row0, col0,  row1, col1,  thickness ) --- */
5323:   int   mywidth = width;                /* max width for / \ */
5324:   thickness = 1;                        /* set line pixel thickness */
5325:   if ( lp != NULL )                     /* left / wanted */
5326:     line_raster(rasp,0,mywidth-1,height-1,0,thickness);
5327:   if ( rp!=NULL || rp2!=NULL )          /* right \ wanted */
5328:     line_raster(rasp,0,0,height-1,mywidth-1,thickness);
5329:   isokay = 1;                           /* set flag */
5330:   } /* --- end-of-if(left- or right-/\ delimiter wanted) --- */
5331: /* -------------------------------------------------------------------------
5332: arrow delimiters
5333: -------------------------------------------------------------------------- */
5334: else
5335:  if ( strstr(symbol,"arrow") != NULL )  /* arrow delimiter wanted */
5336:   {
5337:   /* --- use uparrow_subraster(width,height,pixsz,drctn,isBig) --- */
5338:   int   mywidth = width;                /* max width for / \ */
5339:   int   isBig = (strstr(symbol,"Up")!=NULL /* isBig if we have an Up */
5340:                 || strstr(symbol,"Down")!=NULL); /* or a Down */
5341:   int   drctn = +1;                     /* init for uparrow */
5342:   if ( strstr(symbol,"down")!=NULL      /* down if we have down */
5343:   ||   strstr(symbol,"Down")!=NULL )    /* or Down */
5344:    { drctn = (-1);                      /* reset direction to down */
5345:      if ( strstr(symbol,"up")!=NULL     /* updown if we have up or Up */
5346:      ||   strstr(symbol,"Up")!=NULL )   /* and down or Down */
5347:       drctn = 0; }                      /* reset direction to updown */
5348:   sp = uparrow_subraster(mywidth,height,pixsz,drctn,isBig);
5349:   if ( sp != NULL )
5350:    { sp->type = IMAGERASTER;            /* image */
5351:      sp->symdef = NULL;                 /* not applicable for image */
5352:      sp->baseline = height/2 + 2;       /* is a little above center good? */
5353:      sp->size = NORMALSIZE;             /* size (probably unneeded) */
5354:      isokay = 1; }                      /* set flag */
5355:   } /* --- end-of-if(arrow delimiter wanted) --- */
5356: /* -------------------------------------------------------------------------
5357: \- for | | brackets or \= for || || brackets
5358: -------------------------------------------------------------------------- */
5359: else
5360:  if ( (lp=strchr(symbol,'-')) != NULL   /* left or right | bracket wanted */
5361:  ||  (lp2=strchr(symbol,'|')) != NULL   /* synonym for | bracket */
5362:  ||   (rp=strchr(symbol,'=')) != NULL   /* left or right || bracket wanted */
5363:  || (rp2=strstr(symbol,"\\|"))!= NULL ) /* || in standard tex notation */
5364:   {
5365:   /* --- rule_raster ( rasp, top, left, width, height, type=0 ) --- */
5366:   int   midcol = width/2;               /* middle col, left of mid if even */
5367:   if ( rp  != NULL                      /* left or right || bracket wanted */
5368:   ||   rp2 != NULL )                    /* or || in standard tex notation */
5369:    { thickness = (height<75?1:2);       /* each | of || 1 or 2 pixels thick*/
5370:      rule_raster(rasp, 0,max2(0,midcol-2), thickness,height, 0); /* left */
5371:      rule_raster(rasp, 0,min2(width,midcol+2), thickness,height, 0); }
5372:   else                                  /*nb, lp2 spuriously set if rp2 set*/
5373:    if ( lp  != NULL                     /* left or right | bracket wanted */
5374:    ||   lp2 != NULL )                   /* ditto for synomym */
5375:     { thickness = (height<75?1:2);      /* set | 1 or 2 pixels thick */
5376:       rule_raster(rasp, 0,midcol, thickness,height, 0); } /*mid vertical bar*/
5377:   isokay = 1;                           /* set flag */
5378:   } /* --- end-of-if(left- or right-[] bracket wanted) --- */
5379: /* -------------------------------------------------------------------------
5380: back to caller
5381: -------------------------------------------------------------------------- */
5382: end_of_job:
5383:   if ( msgfp!=NULL && msglevel>=99 )
5384:     fprintf(msgfp,"make_delim> symbol=%.50s, isokay=%d\n",
5385:     (symbol==NULL?"null":symbol),isokay);
5386:   if ( !isokay )                        /* don't have requested delimiter */
5387:     { if (sp!=NULL) delete_subraster(sp); /* so free unneeded structure */
5388:       sp = NULL; }                      /* and signal error to caller */
5389:   return ( sp );                        /*back to caller with delim or NULL*/
5390: } /* --- end-of-function make_delim() --- */
5391: 
5392: 
5393: /* ==========================================================================
5394:  * Function:    texchar ( expression, chartoken )
5395:  * Purpose:     scans expression, returning either its first character,
5396:  *              or the next \sequence if that first char is \,
5397:  *              and a pointer to the first expression char past that.
5398:  * --------------------------------------------------------------------------
5399:  * Arguments:   expression (I)  char * to first char of null-terminated
5400:  *                              string containing valid LaTeX expression
5401:  *                              to be scanned
5402:  *              chartoken (O)   char * to null-terminated string returning
5403:  *                              either the first (non-whitespace) character
5404:  *                              of expression if that char isn't \, or else
5405:  *                              the \ and everything following it up to
5406:  *                              the next non-alphabetic character (but at
5407:  *                              least one char following the \ even if
5408:  *                              it's non-alpha)
5409:  * --------------------------------------------------------------------------
5410:  * Returns:     ( char * )      ptr to the first char of expression
5411:  *                              past returned chartoken,
5412:  *                              or NULL for any parsing error.
5413:  * --------------------------------------------------------------------------
5414:  * Notes:     o Does *not* skip leading whitespace, but simply
5415:  *              returns any whitespace character as the next character.
5416:  * ======================================================================= */
5417: /* --- entry point --- */
5418: char    *texchar ( char *expression, char *chartoken )
5419: {
5420: /* -------------------------------------------------------------------------
5421: Allocations and Declarations
5422: -------------------------------------------------------------------------- */
5423: int     esclen = 0,                             /*length of escape sequence*/
5424:         maxesclen = 128;                        /* max len of esc sequence */
5425: char    *ptoken = chartoken;                    /* ptr into chartoken */
5426: int     iprefix = 0;                            /* prefix index */
5427: static  char *prefixes[] =                      /*e.g., \big followed by ( */
5428:         { /* "\\left", "\\right", */
5429:           "\\big",  "\\Big",  "\\bigg",  "\\Bigg",
5430:           "\\bigl", "\\Bigl", "\\biggl", "\\Biggl",
5431:           "\\bigr", "\\Bigr", "\\biggr", "\\Biggr", NULL };
5432: static  char *starred[] =                       /* may be followed by * */
5433:         { "\\hspace",  "\\!",  NULL };
5434: /* -------------------------------------------------------------------------
5435: just return the next char if it's not \
5436: -------------------------------------------------------------------------- */
5437: /* --- error check for end-of-string --- */
5438: *ptoken = '\000';                               /* init in case of error */
5439: if ( expression == NULL ) return(NULL);         /* nothing to scan */
5440: if ( *expression == '\000' ) return(NULL);      /* nothing to scan */
5441: /* --- always returning first character (either \ or some other char) --- */
5442: *ptoken++ = *expression++;                      /* here's first character */
5443: /* --- if first char isn't \, then just return it to caller --- */
5444: if ( !isthischar(*(expression-1),ESCAPE) )      /* not a \, so return char */
5445:   { *ptoken = '\000';                           /* add a null terminator */
5446:     goto end_of_job; }                          /* ptr past returned char */
5447: if ( *expression == '\000' )                    /* \ is very last char */
5448:   { *chartoken = '\000';                        /* flush bad trailing \ */
5449:     return(NULL); }                             /* and signal end-of-job */
5450: /* -------------------------------------------------------------------------
5451: we have an escape sequence, so return all alpha chars following \
5452: -------------------------------------------------------------------------- */
5453: /* --- accumulate chars until first non-alpha char found --- */
5454: for ( ; isalpha(*expression); esclen++ )        /* till first non-alpha... */
5455:   { if ( esclen < maxesclen-3 )                 /* more room in chartoken */
5456:       *ptoken++ = *expression;                  /*copy alpha char, bump ptr*/
5457:     expression++; }                             /* bump expression ptr */
5458: /* --- if we have a prefix, append next texchar, e.g., \big( --- */
5459: *ptoken = '\000';                               /* set null for compare */
5460: for ( iprefix=0; prefixes[iprefix] != NULL; iprefix++ ) /* run thru list */
5461:  if ( strcmp(chartoken,prefixes[iprefix]) == 0 ) /* have an exact match */
5462:   { char nextchar[256];  int nextlen=0;         /* texchar after prefix */
5463:     skipwhite(expression);                      /* skip space after prefix*/
5464:     expression = texchar(expression,nextchar);  /* get nextchar */
5465:     if ( (nextlen = strlen(nextchar)) > 0 )     /* #chars in nextchar */
5466:       { strcpy(ptoken,nextchar);                /* append nextchar */
5467:         ptoken += strlen(nextchar);             /* point to null terminator*/
5468:         esclen += strlen(nextchar); }           /* and bump escape length */
5469:     break; }                                    /* stop checking prefixes */
5470: /* --- every \ must be followed by at least one char, e.g., \[ --- */
5471: if ( esclen < 1 )                               /* \ followed by non-alpha */
5472:   *ptoken++ = *expression++;                    /*copy non-alpha, bump ptrs*/
5473: *ptoken = '\000';                               /* null-terminate token */
5474: /* --- check for \hspace* or other starred commands --- */
5475: for ( iprefix=0; starred[iprefix] != NULL; iprefix++ ) /* run thru list */
5476:  if ( strcmp(chartoken,starred[iprefix]) == 0 ) /* have an exact match */
5477:   if ( *expression == '*' )                     /* follows by a * */
5478:    { *ptoken++ = *expression++;                 /* copy * and bump ptr */
5479:      *ptoken = '\000';                          /* null-terminate token */
5480:      break; }                                   /* stop checking */
5481: /* --- respect spaces in text mode, except first space after \escape --- */
5482: if ( esclen >= 1 ) {                            /*only for alpha \sequences*/
5483:   if ( istextmode )                             /* in \rm or \it text mode */
5484:    if ( isthischar(*expression,WHITEDELIM) )    /* delim follows \sequence */
5485:     expression++; }                             /* so flush delim */
5486: /* --- back to caller --- */
5487: end_of_job:
5488:   if ( msgfp!=NULL && msglevel>=999 )
5489:     { fprintf(msgfp,"texchar> returning token = \"%s\"\n",chartoken);
5490:       fflush(msgfp); }
5491:   return ( expression );                        /*ptr to 1st non-alpha char*/
5492: } /* --- end-of-function texchar() --- */
5493: 
5494: 
5495: /* ==========================================================================
5496:  * Function:    texsubexpr (expression,subexpr,maxsubsz,
5497:  *              left,right,isescape,isdelim)
5498:  * Purpose:     scans expression, returning everything between a balanced
5499:  *              left{...right} subexpression if the first non-whitespace
5500:  *              char of expression is an (escaped or unescaped) left{,
5501:  *              or just the next texchar() otherwise,
5502:  *              and a pointer to the first expression char past that.
5503:  * --------------------------------------------------------------------------
5504:  * Arguments:   expression (I)  char * to first char of null-terminated
5505:  *                              string containing valid LaTeX expression
5506:  *                              to be scanned
5507:  *              subexpr (O)     char * to null-terminated string returning
5508:  *                              either everything between a balanced {...}
5509:  *                              subexpression if the first char is {,
5510:  *                              or the next texchar() otherwise.
5511:  *              maxsubsz (I)    int containing max #bytes returned
5512:  *                              in subexpr buffer (0 means unlimited)
5513:  *              left (I)        char * specifying allowable left delimiters
5514:  *                              that begin subexpression, e.g., "{[(<"
5515:  *              right (I)       char * specifying matching right delimiters
5516:  *                              in the same order as left, e.g., "}])>"
5517:  *              isescape (I)    int controlling whether escaped and/or
5518:  *                              unescaped left,right are matched;
5519:  *                              see isbrace() comments below for details.
5520:  *              isdelim (I)     int containing true (non-zero) to return
5521:  *                              the leading left and trailing right delims
5522:  *                              (if any were found) along with subexpr,
5523:  *                              or containing false=0 to return subexpr
5524:  *                              without its delimiters
5525:  * --------------------------------------------------------------------------
5526:  * Returns:     ( char * )      ptr to the first char of expression
5527:  *                              past returned subexpr (see Notes),
5528:  *                              or NULL for any parsing error.
5529:  * --------------------------------------------------------------------------
5530:  * Notes:     o If subexpr is of the form left{...right},
5531:  *              the outer {}'s are returned as part of subexpr
5532:  *              if isdelim is true; if isdelim is false the {}'s aren't
5533:  *              returned.  In either case the returned pointer is
5534:  *              *always* bumped past the closing right}, even if
5535:  *              that closing right} isn't returned in subexpr.
5536:  *            o If subexpr is not of the form left{...right},
5537:  *              the returned pointer is on the character immediately
5538:  *              following the last character returned in subexpr
5539:  *            o \. acts as LaTeX \right. and matches any \left(
5540:  *              And it also acts as a LaTeX \left. and matches any \right)
5541:  * ======================================================================= */
5542: /* --- entry point --- */
5543: char    *texsubexpr ( char *expression, char *subexpr, int maxsubsz,
5544:         char *left, char *right, int isescape, int isdelim )
5545: {
5546: /* -------------------------------------------------------------------------
5547: Allocations and Declarations
5548: -------------------------------------------------------------------------- */
5549: char    *texchar();             /*next char (or \sequence) from expression*/
5550: char    *leftptr, leftdelim[256] = "(\000", /* left( found in expression */
5551:         rightdelim[256] = ")\000"; /* and matching right) */
5552: char    *origexpression=expression, *origsubexpr=subexpr; /*original inputs*/
5553: char    *strtexchr(), *texleft(); /* check for \left, and get it */
5554: int     gotescape = 0,          /* true if leading char of expression is \ */
5555:         prevescape = 0;         /* while parsing, true if preceding char \ */
5556: int     isbrace();              /* check for left,right braces */
5557: int     isanyright = 1;         /* true matches any right with left, (...] */
5558: int     isleftdot = 0;          /* true if left brace is a \. */
5559: int     nestlevel = 1;          /* current # of nested braces */
5560: int     subsz=0 /*,maxsubsz=MAXSUBXSZ*/; /*#chars in returned subexpr buffer*/
5561: /* -------------------------------------------------------------------------
5562: skip leading whitespace and just return the next char if it's not {
5563: -------------------------------------------------------------------------- */
5564: /* --- skip leading whitespace and error check for end-of-string --- */
5565: *subexpr = '\000';                              /* init in case of error */
5566: if ( expression == NULL ) return(NULL);         /*can't dereference null ptr*/
5567: skipwhite(expression);                          /* leading whitespace gone */
5568: if ( *expression == '\000' ) return(NULL);      /* nothing left to scan */
5569: /* --- set maxsubsz --- */
5570: if ( maxsubsz < 1 ) maxsubsz = MAXSUBXSZ-2;     /* input 0 means unlimited */
5571: /* --- check for escape --- */
5572: if ( isthischar(*expression,ESCAPE) )           /* expression is escaped */
5573:   gotescape = 1;                                /* so set flag accordingly */
5574: /* --- check for \left...\right --- */
5575: if ( gotescape )                                /* begins with \ */
5576:  if ( memcmp(expression+1,"left",4) )           /* and followed by left */
5577:   if ( strchr(left,'l') != NULL )               /* caller wants \left's */
5578:    if ( strtexchr(expression,"\\left") == expression ) /*expression=\left...*/
5579:     { char *pright = texleft(expression,subexpr,maxsubsz, /* find ...\right*/
5580:         (isdelim?NULL:leftdelim),rightdelim);
5581:       if ( isdelim ) strcat(subexpr,rightdelim); /* caller wants delims */
5582:       return ( pright );                        /*back to caller past \right*/
5583:     } /* --- end-of-if(expression=="\\left") --- */
5584: /* --- if first char isn't left{ or script, just return it to caller --- */
5585: if ( !isbrace(expression,left,isescape) ) {     /* not a left{ */
5586:   if ( !isthischar(*expression,SCRIPTS) )       /* and not a script */
5587:     return ( texchar(expression,subexpr) );     /* next char to caller */
5588:   else /* --- kludge for super/subscripts to accommodate texscripts() --- */
5589:     { *subexpr++ = *expression;                 /* signal script */
5590:       *subexpr = '\000';                        /* null-terminate subexpr */
5591:       return ( expression ); } }                /* leave script in stream */
5592: /* --- extract left and find matching right delimiter --- */
5593: *leftdelim  = *(expression+gotescape);          /* the left( in expression */
5594: if ( (gotescape && *leftdelim == '.')           /* we have a left \. */
5595: ||   (gotescape && isanyright) )                /*or are matching any right*/
5596:   { isleftdot = 1;                              /* so just set flag */
5597:     *leftdelim = '\000'; }                      /* and reset leftdelim */
5598: else                                            /* find matching \right */
5599:   if ( (leftptr=strchr(left,*leftdelim)) != NULL ) /* ptr to that left( */
5600:     *rightdelim = right[(int)(leftptr-left)];   /* get the matching right) */
5601:   else                                          /* can't happen -- pgm bug */
5602:     return ( NULL );                            /*just signal eoj to caller*/
5603: /* -------------------------------------------------------------------------
5604: accumulate chars between balanced {}'s, i.e., till nestlevel returns to 0
5605: -------------------------------------------------------------------------- */
5606: /* --- first initialize by bumping past left{ or \{ --- */
5607: if ( isdelim )   *subexpr++ = *expression++;    /*caller wants { in subexpr*/
5608:   else expression++;                            /* always bump past left{ */
5609: if ( gotescape ) {                              /*need to bump another char*/
5610:   if ( isdelim ) *subexpr++ = *expression++;    /* caller wants char, too */
5611:   else expression++; }                          /* else just bump past it */
5612: /* --- set maximum size for numerical arguments --- */
5613: if ( 0 )                                        /* check turned on or off? */
5614:  if ( !isescape && !isdelim )                   /*looking for numerical arg*/
5615:   maxsubsz = 96;                                /* set max arg size */
5616: /* --- search for matching right} --- */
5617: while ( 1 )                                     /*until balanced right} */
5618:   {
5619:   /* --- error check for end-of-string --- */
5620:   if ( *expression == '\000' )                  /* premature end-of-string */
5621:     { if ( 0 && (!isescape && !isdelim) )       /*looking for numerical arg,*/
5622:         { expression = origexpression;          /* so end-of-string is error*/
5623:           subexpr = origsubexpr; }              /* so reset all ptrs */
5624:       if ( isdelim ) {                          /* generate fake right */
5625:         if ( gotescape )                        /* need escaped right */
5626:           { *subexpr++ = '\\';                  /* set escape char */
5627:             *subexpr++ = '.'; }                 /* and fake \right. */
5628:         else                                    /* escape not wanted */
5629:             *subexpr++ = *rightdelim; }         /* so fake actual right */
5630:       *subexpr = '\000';                        /* null-terminate subexpr */
5631:       return ( expression ); }                  /* back with final token */
5632:   /* --- check preceding char for escape --- */
5633:   if ( isthischar(*(expression-1),ESCAPE) )     /* previous char was \ */
5634:         prevescape = 1-prevescape;              /* so flip escape flag */
5635:   else  prevescape = 0;                         /* or turn flag off */
5636:   /* --- check for { and } (un/escaped as per leading left) --- */
5637:   if ( gotescape == prevescape )                /* escaped iff leading is */
5638:     { /* --- check for (closing) right delim and see if we're done --- */
5639:       if ( isthischar(*expression,rightdelim)   /* found a right} */
5640:       ||   (isleftdot && isthischar(*expression,right)) /*\left. matches all*/
5641:       ||   (prevescape && isthischar(*expression,".")) ) /*or found \right. */
5642:         if ( --nestlevel < 1 )                  /*\right balances 1st \left*/
5643:           { if ( isdelim )                      /*caller wants } in subexpr*/
5644:               *subexpr++ = *expression;         /* so end subexpr with } */
5645:             else                                /*check for \ before right}*/
5646:               if ( prevescape )                 /* have unwanted \ */
5647:                 *(subexpr-1) = '\000';          /* so replace it with null */
5648:             *subexpr = '\000';                  /* null-terminate subexpr */
5649:             return ( expression+1 ); }          /* back with char after } */
5650:       /* --- check for (another) left{ --- */
5651:       if ( isthischar(*expression,leftdelim)    /* found another left{ */
5652:       ||   (isleftdot && isthischar(*expression,left)) ) /* any left{ */
5653:         nestlevel++;
5654:     } /* --- end-of-if(gotescape==prevescape) --- */
5655:   /* --- not done, so copy char to subexpr and continue with next char --- */
5656:   if ( ++subsz < maxsubsz-5 )                   /* more room in subexpr */
5657:     *subexpr++ = *expression;                   /* so copy char and bump ptr*/
5658:   expression++;                                 /* bump expression ptr */
5659:   } /* --- end-of-while(1) --- */
5660: } /* --- end-of-function texsubexpr() --- */
5661: 
5662: 
5663: /* ==========================================================================
5664:  * Function:    texleft (expression,subexpr,maxsubsz,ldelim,rdelim)
5665:  * Purpose:     scans expression, starting after opening \left,
5666:  *              and returning ptr after matching closing \right.
5667:  *              Everything between is returned in subexpr, if given.
5668:  *              Likewise, if given, ldelim returns delimiter after \left
5669:  *              and rdelim returns delimiter after \right.
5670:  *              If ldelim is given, the returned subexpr doesn't include it.
5671:  *              If rdelim is given, the returned pointer is after that delim.
5672:  * --------------------------------------------------------------------------
5673:  * Arguments:   expression (I)  char * to first char of null-terminated
5674:  *                              string immediately following opening \left
5675:  *              subexpr (O)     char * to null-terminated string returning
5676:  *                              either everything between balanced
5677:  *                              \left ... \right.  If leftdelim given,
5678:  *                              subexpr does _not_ contain that delimiter.
5679:  *              maxsubsz (I)    int containing max #bytes returned
5680:  *                              in subexpr buffer (0 means unlimited)
5681:  *              ldelim (O)      char * returning delimiter following
5682:  *                              opening \left
5683:  *              rdelim (O)      char * returning delimiter following
5684:  *                              closing \right
5685:  * --------------------------------------------------------------------------
5686:  * Returns:     ( char * )      ptr to the first char of expression
5687:  *                              past closing \right, or past closing
5688:  *                              right delimiter if rdelim!=NULL,
5689:  *                              or NULL for any error.
5690:  * --------------------------------------------------------------------------
5691:  * Notes:     o
5692:  * ======================================================================= */
5693: /* --- entry point --- */
5694: char    *texleft ( char *expression, char *subexpr, int maxsubsz,
5695:         char *ldelim, char *rdelim )
5696: {
5697: /* -------------------------------------------------------------------------
5698: Allocations and Declarations
5699: -------------------------------------------------------------------------- */
5700: char    *texchar(),                     /* get delims after \left,\right */
5701:         *strtexchr(), *pright=expression; /* locate matching \right */
5702: static  char left[16]="\\left", right[16]="\\right"; /* tex delimiters */
5703: int     sublen = 0;                     /* #chars between \left...\right */
5704: /* -------------------------------------------------------------------------
5705: initialization
5706: -------------------------------------------------------------------------- */
5707: /* --- init output --- */
5708: if ( subexpr != NULL ) *subexpr = '\000'; /* init subexpr, if given */
5709: if ( ldelim  != NULL ) *ldelim  = '\000'; /* init ldelim,  if given */
5710: if ( rdelim  != NULL ) *rdelim  = '\000'; /* init rdelim,  if given */
5711: /* --- check args --- */
5712: if ( expression == NULL ) goto end_of_job; /* no input supplied */
5713: if ( *expression == '\000' ) goto end_of_job; /* nothing after \left */
5714: /* --- determine left delimiter  --- */
5715: if ( ldelim != NULL )                   /* caller wants left delim */
5716:  { skipwhite(expression);               /* interpret \left ( as \left( */
5717:    expression = texchar(expression,ldelim); } /*delim from expression*/
5718: /* -------------------------------------------------------------------------
5719: locate \right balancing opening \left
5720: -------------------------------------------------------------------------- */
5721: /* --- first \right following \left --- */
5722: if ( (pright=strtexchr(expression,right)) /* look for \right after \left */
5723: !=   NULL ) {                           /* found it */
5724:  /* --- find matching \right by pushing past any nested \left's --- */
5725:  char *pleft = expression;              /* start after first \left( */
5726:  while ( 1 ) {                          /*break when matching \right found*/
5727:   /* -- locate next nested \left if there is one --- */
5728:   if ( (pleft=strtexchr(pleft,left))    /* find next \left */
5729:   ==   NULL ) break;                    /*no more, so matching \right found*/
5730:   pleft += strlen(left);                /* push ptr past \left token */
5731:   if ( pleft >= pright ) break;         /* not nested if \left after \right*/
5732:   /* --- have nested \left, so push forward to next \right --- */
5733:   if ( (pright=strtexchr(pright+strlen(right),right)) /* find next \right */
5734:   ==   NULL ) break;                    /* ran out of \right's */
5735:   } /* --- end-of-while(1) --- */
5736:  } /* --- end-of-if(pright!=NULL) --- */
5737: /* --- set subexpression length, push pright past \right --- */
5738: if ( pright != (char *)NULL )           /* found matching \right */
5739:  { sublen = (int)(pright-expression);   /* #chars between \left...\right */
5740:    pright += strlen(right); }           /* so push pright past \right */
5741: /* -------------------------------------------------------------------------
5742: get rightdelim and subexpr between \left...\right
5743: -------------------------------------------------------------------------- */
5744: /* --- get delimiter following \right --- */
5745: if ( rdelim != NULL ) {                 /* caller wants right delim */
5746:  if ( pright == (char *)NULL )          /* assume \right. at end of exprssn*/
5747:   { strcpy(rdelim,".");                 /* set default \right. */
5748:     sublen = strlen(expression);        /* use entire remaining expression */
5749:     pright = expression + sublen; }     /* and push pright to end-of-string*/
5750:  else                                   /* have explicit matching \right */
5751:   { skipwhite(pright);                  /* interpret \right ) as \right) */
5752:     pright = texchar(pright,rdelim);    /* pull delim from expression */
5753:     if ( *rdelim == '\000' ) strcpy(rdelim,"."); } } /* or set \right. */
5754: /* --- get subexpression between \left...\right --- */
5755: if ( sublen > 0 )                       /* have subexpr */
5756:  if ( subexpr != NULL ) {               /* and caller wants it */
5757:   if ( maxsubsz > 0 ) sublen = min2(sublen,maxsubsz-1); /* max buffer size */
5758:   memcpy(subexpr,expression,sublen);    /* stuff between \left...\right */
5759:   subexpr[sublen] = '\000'; }           /* null-terminate subexpr */
5760: end_of_job:
5761:   if ( msglevel>=99 && msgfp!=NULL )
5762:     { fprintf(msgfp,"texleft> ldelim=%s, rdelim=%s, subexpr=%.128s\n",
5763:       (ldelim==NULL?"none":ldelim),(rdelim==NULL?"none":rdelim),
5764:       (subexpr==NULL?"none":subexpr)); fflush(msgfp); }
5765:   return ( pright );
5766: } /* --- end-of-function texleft --- */
5767: 
5768: 
5769: /* ==========================================================================
5770:  * Function:    texscripts ( expression, subscript, superscript, which )
5771:  * Purpose:     scans expression, returning subscript and/or superscript
5772:  *              if expression is of the form _x^y or ^{x}_{y},
5773:  *              or any (valid LaTeX) permutation of the above,
5774:  *              and a pointer to the first expression char past "scripts"
5775:  * --------------------------------------------------------------------------
5776:  * Arguments:   expression (I)  char * to first char of null-terminated
5777:  *                              string containing valid LaTeX expression
5778:  *                              to be scanned
5779:  *              subscript (O)   char * to null-terminated string returning
5780:  *                              subscript (without _), if found, or "\000"
5781:  *              superscript (O) char * to null-terminated string returning
5782:  *                              superscript (without ^), if found, or "\000"
5783:  *              which (I)       int containing 1 for subscript only,
5784:  *                              2 for superscript only, >=3 for either/both
5785:  * --------------------------------------------------------------------------
5786:  * Returns:     ( char * )      ptr to the first char of expression
5787:  *                              past returned "scripts" (unchanged
5788:  *                              except for skipped whitespace if
5789:  *                              neither subscript nor superscript found),
5790:  *                              or NULL for any parsing error.
5791:  * --------------------------------------------------------------------------
5792:  * Notes:     o an input expression like ^a^b_c will return superscript="b",
5793:  *              i.e., totally ignoring all but the last "script" encountered
5794:  * ======================================================================= */
5795: /* --- entry point --- */
5796: char    *texscripts ( char *expression, char *subscript,
5797:                         char *superscript, int which )
5798: {
5799: /* -------------------------------------------------------------------------
5800: Allocations and Declarations
5801: -------------------------------------------------------------------------- */
5802: char    *texsubexpr();          /* next subexpression from expression */
5803: int     gotsub=0, gotsup=0;     /* check that we don't eat, e.g., x_1_2 */
5804: /* -------------------------------------------------------------------------
5805: init "scripts"
5806: -------------------------------------------------------------------------- */
5807: if ( subscript != NULL ) *subscript = '\000';   /*init in case no subscript*/
5808: if ( superscript!=NULL ) *superscript = '\000'; /*init in case no super*/
5809: /* -------------------------------------------------------------------------
5810: get subscript and/or superscript from expression
5811: -------------------------------------------------------------------------- */
5812: while ( expression != NULL ) {
5813:   skipwhite(expression);                        /* leading whitespace gone */
5814:   if ( *expression == '\000' ) return(expression); /* nothing left to scan */
5815:   if ( isthischar(*expression,SUBSCRIPT)        /* found _ */
5816:   &&   (which==1 || which>2 ) )                 /* and caller wants it */
5817:     { if ( gotsub                               /* found 2nd subscript */
5818:       ||   subscript == NULL ) break;           /* or no subscript buffer */
5819:       gotsub = 1;                               /* set subscript flag */
5820:       expression = texsubexpr(expression+1,subscript,0,"{","}",0,0); }
5821:   else                                          /* no _, check for ^ */
5822:     if ( isthischar(*expression,SUPERSCRIPT)    /* found ^ */
5823:     &&   which>=2  )                            /* and caller wants it */
5824:       { if ( gotsup                             /* found 2nd superscript */
5825:         ||   superscript == NULL ) break;       /* or no superscript buffer*/
5826:         gotsup = 1;                             /* set superscript flag */
5827:         expression = texsubexpr(expression+1,superscript,0,"{","}",0,0); }
5828:     else                                        /* neither _ nor ^ */
5829:       return ( expression );                    /*return ptr past "scripts"*/
5830:   } /* --- end-of-while(expression!=NULL) --- */
5831: return ( expression );
5832: } /* --- end-of-function texscripts() --- */
5833: 
5834: 
5835: /* ==========================================================================
5836:  * Function:    isbrace ( expression, braces, isescape )
5837:  * Purpose:     checks leading char(s) of expression for a brace,
5838:  *              either escaped or unescaped depending on isescape,
5839:  *              except that { and } are always matched, if they're
5840:  *              in braces, regardless of isescape.
5841:  * --------------------------------------------------------------------------
5842:  * Arguments:   expression (I)  char * to first char of null-terminated
5843:  *                              string containing a valid LaTeX expression
5844:  *                              whose leading char(s) are checked for braces
5845:  *                              that begin subexpression, e.g., "{[(<"
5846:  *              braces (I)      char * specifying matching brace delimiters
5847:  *                              to be checked for, e.g., "{[(<" or "}])>"
5848:  *              isescape (I)    int containing 0 to match only unescaped
5849:  *                              braces, e.g., (...) or {...}, etc,
5850:  *                              or containing 1 to match only escaped
5851:  *                              braces, e.g., \(...\) or \[...\], etc,
5852:  *                              or containing 2 to match either.
5853:  *                              But note: if {,} are in braces
5854:  *                              then they're *always* matched whether
5855:  *                              escaped or not, regardless of isescape.
5856:  * --------------------------------------------------------------------------
5857:  * Returns:     ( int )         1 if the leading char(s) of expression
5858:  *                              is a brace, or 0 if not.
5859:  * --------------------------------------------------------------------------
5860:  * Notes:     o
5861:  * ======================================================================= */
5862: /* --- entry point --- */
5863: int     isbrace ( char *expression, char *braces, int isescape )
5864: {
5865: /* -------------------------------------------------------------------------
5866: Allocations and Declarations
5867: -------------------------------------------------------------------------- */
5868: int     gotescape = 0,          /* true if leading char is an escape */
5869:         gotbrace = 0;           /*true if first non-escape char is a brace*/
5870: /* -------------------------------------------------------------------------
5871: check for brace
5872: -------------------------------------------------------------------------- */
5873: /* --- first check for end-of-string or \= ligature --- */
5874: if ( *expression == '\000'                      /* nothing to check */
5875: ||   isligature ) goto end_of_job;              /* have a \= ligature */
5876: /* --- check leading char for escape --- */
5877: if ( isthischar(*expression,ESCAPE) )           /* expression is escaped */
5878:   { gotescape = 1;                              /* so set flag accordingly */
5879:     expression++; }                             /* and bump past escape */
5880: /* --- check (maybe next char) for brace --- */
5881: if ( isthischar(*expression,braces) )           /* expression is braced */
5882:   gotbrace = 1;                                 /* so set flag accordingly */
5883: if ( gotescape && *expression == '.' )          /* \. matches any brace */
5884:   gotbrace = 1;                                 /* set flag */
5885: /* --- check for TeX brace { or } --- */
5886: if ( gotbrace && isthischar(*expression,"{}") ) /*expression has TeX brace*/
5887:   if ( isescape ) isescape = 2;                 /* reset escape flag */
5888: /* -------------------------------------------------------------------------
5889: back to caller
5890: -------------------------------------------------------------------------- */
5891: end_of_job:
5892:  if ( msglevel>=999 && msgfp!=NULL )
5893:   { fprintf(msgfp,"isbrace> expression=%.8s, gotbrace=%d (isligature=%d)\n",
5894:     expression,gotbrace,isligature); fflush(msgfp); }
5895:  if ( gotbrace &&                               /* found a brace */
5896:      ( isescape==2 ||                           /* escape irrelevant */
5897:        gotescape==isescape )                    /* un/escaped as requested */
5898:    ) return ( 1 );  return ( 0 );               /* return 1,0 accordingly */
5899: } /* --- end-of-function isbrace() --- */
5900: 
5901: 
5902: /* ==========================================================================
5903:  * Function:    preamble ( expression, size, subexpr )
5904:  * Purpose:     parses $-terminated preamble, if present, at beginning
5905:  *              of expression, re-setting size if necessary, and
5906:  *              returning any other parameters besides size in subexpr.
5907:  * --------------------------------------------------------------------------
5908:  * Arguments:   expression (I)  char * to first char of null-terminated
5909:  *                              string containing LaTeX expression possibly
5910:  *                              preceded by $-terminated preamble
5911:  *              size (I/O)      int *  containing 0-4 default font size,
5912:  *                              and returning size modified by first
5913:  *                              preamble parameter (or unchanged)
5914:  *              subexpr(O)      char *  returning any remaining preamble
5915:  *                              parameters past size
5916:  * --------------------------------------------------------------------------
5917:  * Returns:     ( char * )      ptr to first char past preamble in expression
5918:  *                              or NULL for any parsing error.
5919:  * --------------------------------------------------------------------------
5920:  * Notes:     o size can be any number >=0. If preceded by + or -, it's
5921:  *              interpreted as an increment to input size; otherwise
5922:  *              it's interpreted as the size.
5923:  *            o if subexpr is passed as NULL ptr, then returned expression
5924:  *              ptr will have "flushed" and preamble parameters after size
5925:  * ======================================================================= */
5926: /* --- entry point --- */
5927: char    *preamble ( char *expression, int *size, char *subexpr )
5928: {
5929: /* -------------------------------------------------------------------------
5930: Allocations and Declarations
5931: -------------------------------------------------------------------------- */
5932: char    pretext[512], *prep=expression, /*pream from expression, ptr into it*/
5933:         *dollar, *comma;                /* preamble delimiters */
5934: int     prelen = 0,                     /* preamble length */
5935:         sizevalue = 0,                  /* value of size parameter */
5936:         isfontsize = 0,                 /*true if leading fontsize present*/
5937:         isdelta = 0;                    /*true to increment passed size arg*/
5938: /* -------------------------------------------------------------------------
5939: initialization
5940: -------------------------------------------------------------------------- */
5941: if ( subexpr != NULL )                  /* caller passed us an address */
5942:   *subexpr = '\000';                    /* so init assuming no preamble */
5943: if ( expression == NULL ) goto end_of_job; /* no input */
5944: if ( *expression == '\000' ) goto end_of_job; /* input is an empty string */
5945: /* -------------------------------------------------------------------------
5946: process preamble if present
5947: -------------------------------------------------------------------------- */
5948: /*process_preamble:*/
5949: if ( (dollar=strchr(expression,'$'))    /* $ signals preceding preamble */
5950: !=   NULL ) {                           /* found embedded $ */
5951:  if ( (prelen = (int)(dollar-expression)) /*#chars in expression preceding $*/
5952:  > 0 ) {                                /* must have preamble preceding $ */
5953:   if ( prelen < 65 ) {                  /* too long for a prefix */
5954:    memcpy(pretext,expression,prelen);   /* local copy of preamble */
5955:    pretext[prelen] = '\000';            /* null-terminated */
5956:    if ( strchr(pretext,*(ESCAPE))==NULL /*shouldn't be an escape in preamble*/
5957:    &&   strchr(pretext,'{') == NULL ) { /*shouldn't be a left{ in preamble*/
5958:     /* --- skip any leading whitespace  --- */
5959:     prep = pretext;                     /* start at beginning of preamble */
5960:     skipwhite(prep);                    /* skip any leading white space */
5961:     /* --- check for embedded , or leading +/- (either signalling size) --- */
5962:     if ( isthischar(*prep,"+-") )       /* have leading + or - */
5963:      isdelta = 1;                       /* so use size value as increment */
5964:     comma = strchr(pretext,',');        /* , signals leading size param */
5965:     /* --- process leading size parameter if present --- */
5966:     if ( comma != NULL                  /* size param explicitly signalled */
5967:     ||   isdelta || isdigit(*prep) ) {  /* or inferred implicitly */
5968:       /* --- parse size parameter and reset size accordingly --- */
5969:       if( comma != NULL ) *comma = '\000';/*, becomes null, terminating size*/
5970:       sizevalue = atoi(prep);           /* convert size string to integer */
5971:       if ( size != NULL )               /* caller passed address for size */
5972:         *size = (isdelta? *size+sizevalue : sizevalue); /* so reset size */
5973:       /* --- finally, set flag and shift size parameter out of preamble --- */
5974:       isfontsize = 1;                   /*set flag showing font size present*/
5975:       if ( comma != NULL )              /*2/15/12-isn't this superfluous???*/
5976:         {strsqueezep(pretext,comma+1);} /* squeeze out leading size param */
5977:      } /* --- end-of-if(comma!=NULL||etc) --- */
5978:     /* --- copy any preamble params following size to caller's subexpr --- */
5979:     if ( comma != NULL || !isfontsize ) /*preamb contains params past size*/
5980:      if ( subexpr != NULL )             /* caller passed us an address */
5981:       strcpy(subexpr,pretext);          /*so return extra params to caller*/
5982:     /* --- finally, set prep to shift preamble out of expression --- */
5983:     prep = expression + prelen+1;       /* set prep past $ in expression */
5984:     } /* --- end-of-if(strchr(pretext,*ESCAPE)==NULL) --- */
5985:    } /* --- end-of-if(prelen<65) --- */
5986:   } /* --- end-of-if(prelen>0) --- */
5987:  else {                                 /* $ is first char of expression */
5988:   int ndollars = 0;                     /* number of $...$ pairs removed */
5989:   prep = expression;                    /* start at beginning of expression*/
5990:   while ( *prep == '$' ) {              /* remove all matching $...$'s */
5991:    int  explen = strlen(prep)-1;        /* index of last char in expression*/
5992:    if ( explen < 2 ) break;             /* no $...$'s left to remove */
5993:    if ( prep[explen] != '$' ) break;    /* unmatched $ */
5994:    prep[explen] = '\000';               /* remove trailing $ */
5995:    prep++;                              /* and remove matching leading $ */
5996:    ndollars++;                          /* count another pair removed */
5997:    } /* --- end-of-while(*prep=='$') --- */
5998:   ispreambledollars = ndollars;         /* set flag to fix \displaystyle */
5999:   if ( ndollars == 1 )                  /* user submitted $...$ expression */
6000:     isdisplaystyle = 0;                 /* so set \textstyle */
6001:   if ( ndollars > 1 )                   /* user submitted $$...$$ */
6002:     isdisplaystyle = 2;                 /* so set \displaystyle */
6003:   /*goto process_preamble;*/            /*check for preamble after leading $*/
6004:   } /* --- end-of-if/else(prelen>0) --- */
6005:  } /* --- end-of-if(dollar!=NULL) --- */
6006: /* -------------------------------------------------------------------------
6007: back to caller
6008: -------------------------------------------------------------------------- */
6009: end_of_job:
6010:   return ( prep );                      /*expression, or ptr past preamble*/
6011: } /* --- end-of-function preamble() --- */
6012: 
6013: 
6014: /* ==========================================================================
6015:  * Function:    mimeprep ( expression )
6016:  * Purpose:     preprocessor for mimeTeX input, e.g.,
6017:  *              (a) removes comments,
6018:  *              (b) converts \left( to \( and \right) to \),
6019:  *              (c) xlates &html; special chars to equivalent latex
6020:  *              Should only be called once (after unescape_url())
6021:  * --------------------------------------------------------------------------
6022:  * Arguments:   expression (I/O) char * to first char of null-terminated
6023:  *                              string containing mimeTeX/LaTeX expression,
6024:  *                              and returning preprocessed string
6025:  * --------------------------------------------------------------------------
6026:  * Returns:     ( char * )      ptr to input expression,
6027:  *                              or NULL for any parsing error.
6028:  * --------------------------------------------------------------------------
6029:  * Notes:     o
6030:  * ======================================================================= */
6031: /* --- entry point --- */
6032: char    *mimeprep ( char *expression )
6033: {
6034: /* -------------------------------------------------------------------------
6035: Allocations and Declarations
6036: -------------------------------------------------------------------------- */
6037: char    *expptr=expression,             /* ptr within expression */
6038:         *tokptr=NULL,                   /*ptr to token found in expression*/
6039:         *texsubexpr(), argval[8192];    /*parse for macro args after token*/
6040: char    *strchange();                   /* change leading chars of string */
6041: int     strreplace();                   /* replace nnn with actual num, etc*/
6042: char    *strwstr();                     /*use strwstr() instead of strstr()*/
6043: char    *findbraces();                  /*find left { and right } for \atop*/
6044: int     idelim=0,                       /* left- or right-index */
6045:         isymbol=0;                      /*symbols[],rightcomment[],etc index*/
6046: int     xlateleft = 0;                  /* true to xlate \left and \right */
6047: /* ---
6048:  * comments
6049:  * -------- */
6050: char    *leftptr=NULL;                  /* find leftcomment in expression */
6051: static  char *leftcomment = "%%",       /* open comment */
6052:         *rightcomment[] = {"\n", "%%", NULL}; /* close comments */
6053: /* ---
6054:  * special long (more than 1-char) \left and \right delimiters
6055:  * ----------------------------------------------------------- */
6056: static  char *leftfrom[] =              /* xlate any \left suffix... */
6057:    { "\\|",                             /* \left\| */
6058:      "\\{",                             /* \left\{ */
6059:      "\\langle",                        /* \left\langle */
6060:      NULL } ; /* --- end-of-leftfrom[] --- */
6061: static  char *leftto[] =                /* ...to this instead */
6062:    { "=",                               /* = */
6063:      "{",                               /* { */
6064:      "<",                               /* < */
6065:      NULL } ; /* --- end-of-leftto[] --- */
6066: static  char *rightfrom[] =             /* xlate any \right suffix... */
6067:    { "\\|",                             /* \right\| */
6068:      "\\}",                             /* \right\} */
6069:      "\\rangle",                        /* \right\rangle */
6070:      NULL } ; /* --- end-of-rightfrom[] --- */
6071: static  char *rightto[] =               /* ...to this instead */
6072:    { "=",                               /* = */
6073:      "}",                               /* } */
6074:      ">",                               /* > */
6075:      NULL } ; /* --- end-of-rightto[] --- */
6076: /* ---
6077:  * { \atop }-like commands
6078:  * ----------------------- */
6079: char    *atopsym=NULL;                  /* atopcommands[isymbol] */
6080: static  char *atopcommands[] =          /* list of {a+b\command c+d}'s */
6081:    { "\\over",                          /* plain tex for \frac */
6082:      "\\choose",                        /* binomial coefficient */
6083:    #ifndef NOATOP                       /*noatop preserves old mimeTeX rule*/
6084:      "\\atop",
6085:    #endif
6086:      NULL } ; /* --- end-of-atopcommands[] --- */
6087: static  char *atopdelims[] =            /* delims for atopcommands[] */
6088:    { NULL, NULL,                        /* \\over has no delims */
6089:      "\\left(", "\\right)",             /* \\choose has ( ) delims*/
6090:    #ifndef NOATOP                       /*noatop preserves old mimeTeX rule*/
6091:      NULL, NULL,                        /* \\atop has no delims */
6092:    #endif
6093:      NULL, NULL } ; /* --- end-of-atopdelims[] --- */
6094: /* ---
6095:  * html special/escape chars converted to latex equivalents
6096:  * -------------------------------------------------------- */
6097: char    *htmlsym=NULL;                  /* symbols[isymbol].html */
6098: static  struct { char *html; char *args; char *latex; } symbols[] =
6099:  { /* --------------------------------------------
6100:      user-supplied newcommands
6101:    -------------------------------------------- */
6102:    #ifdef NEWCOMMANDS                   /* -DNEWCOMMANDS=\"filename.h\" */
6103:      #include NEWCOMMANDS
6104:    #endif
6105:    /* --------------------------------------------
6106:      Specials        termchar  value...
6107:    -------------------------------------------- */
6108:    { "\\version",       NULL, "{\\small\\red\\text \\fbox{\\begin{gather}"
6109:         "mime\\TeX version \\versionnumber \\\\"
6110:         "last revised \\revisiondate \\\\ \\copyrighttext \\\\"
6111:         "see \\homepagetext for details \\end{gather}}}" },
6112:    { "\\copyright",     NULL,
6113:         "{\\small\\red\\text \\fbox{\\begin{gather}"
6114:         "mimeTeX \\copyrighttext \\\\"
6115:         "see \\homepagetext for details \\end{gather}}}" },
6116:    { "\\versionnumber", NULL, "{\\text " VERSION "}" },
6117:    { "\\revisiondate",  NULL, "{\\text " REVISIONDATE "}" },
6118:    { "\\copyrighttext", NULL, "{\\text " COPYRIGHTTEXT ".}" },
6119:    { "\\homepagetext",  NULL,
6120:         "{\\text http://www.forkosh.com/mimetex.html}" },
6121:    /* --------------------------------------------
6122:      Cyrillic  termchar  mimeTeX equivalent...
6123:    -------------------------------------------- */
6124:    { "\\\'G",   "embed\\","{\\acute{G}}" },
6125:    { "\\\'g",   "embed\\","{\\acute{g}}" },
6126:    { "\\\'K",   "embed\\","{\\acute{K}}" },
6127:    { "\\\'k",   "embed\\","{\\acute{k}}" },
6128:    { "\\u U",   "embed\\","{\\breve{U}}" },
6129:    { "\\u u",   "embed\\","{\\breve{u}}" },
6130:    /*{ "\\\"E", "embed\\","{\\ddot{E}}" },*/
6131:    /*{ "\\\"e", "embed\\","{\\ddot{e}}" },*/
6132:    { "\\\"I",   "embed\\","{\\ddot{\\=I}}" },
6133:    { "\\\"\\i", "embed\\","{\\ddot{\\=\\i}}" },
6134:    /* --------------------------------------------
6135:      LaTeX Macro #args,default  template...
6136:    -------------------------------------------- */
6137:    { "\\lvec",  "2n",   "{#2_1,\\cdots,#2_{#1}}" },
6138:    { "\\grave", "1",    "{\\stackrel{\\Huge\\gravesym}{#1}}" }, /* \grave */
6139:    { "\\acute", "1",    "{\\stackrel{\\Huge\\acutesym}{#1}}" }, /* \acute */
6140:    { "\\check", "1",    "{\\stackrel{\\Huge\\checksym}{#1}}" }, /* \check */
6141:    { "\\breve", "1",    "{\\stackrel{\\Huge\\brevesym}{#1}}" }, /* \breve */
6142:    { "\\buildrel","3",  "{\\stackrel{#1}{#3}}" }, /* ignore #2 = \over */
6143:    { "\\overset", NULL, "\\stackrel" },         /* just an alias */
6144:    { "\\underset", "2", "\\relstack{#2}{#1}" }, /* reverse args */
6145:    { "\\dfrac", "2",    "{\\frac{#1}{#2}}" },
6146:    { "\\binom", "2",    "{\\begin{pmatrix}{#1}\\\\{#2}\\end{pmatrix}}" },
6147:    { "\\aangle","26",   "{\\boxaccent{#1}{#2}}" },
6148:    { "\\actuarial","2 ","{#1\\boxaccent{6}{#2}}" }, /*comprehensive sym list*/
6149:    { "\\boxaccent","2", "{\\fbox[,#1]{#2}}" },
6150:    { "\\password", "1", "\000" },
6151:    { "\\comment", "1",  "\000" },
6152:    /* --------------------------------------------
6153:      html char termchar  LaTeX equivalent...
6154:    -------------------------------------------- */
6155:    { "&quot",   ";",    "\"" },         /* &quot; is first, &#034; */
6156:    { "&amp",    ";",    "&" },
6157:    { "&lt",     ";",    "<" },
6158:    { "&gt",     ";",    ">" },
6159:    /*{ "&#092", ";",    "\\" },*/       /* backslash */
6160:    { "&backslash",";",  "\\" },
6161:    { "&nbsp",   ";",    "~" },
6162:    { "&iexcl",  ";",    "{\\raisebox{-2}{\\rotatebox{180}{!}}}" },
6163:    { "&brvbar", ";",    "|" },
6164:    { "&plusmn", ";",    "\\pm" },
6165:    { "&sup2",   ";",    "{{}^2}" },
6166:    { "&sup3",   ";",    "{{}^3}" },
6167:    { "&micro",  ";",    "\\mu" },
6168:    { "&sup1",   ";",    "{{}^1}" },
6169:    { "&frac14", ";",    "{\\frac14}" },
6170:    { "&frac12", ";",    "{\\frac12}" },
6171:    { "&frac34", ";",    "{\\frac34}" },
6172:    { "&iquest", ";",    "{\\raisebox{-2}{\\rotatebox{180}{?}}}" },
6173:    { "&Acirc",  ";",    "{\\rm~\\hat~A}" },
6174:    { "&Atilde", ";",    "{\\rm~\\tilde~A}" },
6175:    { "&Auml",   ";",    "{\\rm~\\ddot~A}" },
6176:    { "&Aring",  ";",    "{\\rm~A\\limits^{-1$o}}" },
6177:    { "&atilde", ";",    "{\\rm~\\tilde~a}" },
6178:    { "&yuml",   ";",    "{\\rm~\\ddot~y}" }, /* &yuml; is last, &#255; */
6179:    { "&#",      ";",    "{[\\&\\#nnn?]}" },  /* all other explicit &#nnn's */
6180:    /* --------------------------------------------
6181:      html tag     termchar    LaTeX equivalent...
6182:    -------------------------------------------- */
6183:    { "< br >",    "embed\\i", "\\\\" },
6184:    { "< br / >",  "embed\\i", "\\\\" },
6185:    { "< dd >",    "embed\\i", " \000" },
6186:    { "< / dd >",  "embed\\i", " \000" },
6187:    { "< dl >",    "embed\\i", " \000" },
6188:    { "< / dl >",  "embed\\i", " \000" },
6189:    { "< p >",     "embed\\i", " \000" },
6190:    { "< / p >",   "embed\\i", " \000" },
6191:    /* --------------------------------------------
6192:      garbage      termchar  LaTeX equivalent...
6193:    -------------------------------------------- */
6194:    { "< tex >",   "embed\\i", " \000" },
6195:    { "< / tex >", "embed\\i", " \000" },
6196:    /* --------------------------------------------
6197:      LaTeX   termchar   mimeTeX equivalent...
6198:    -------------------------------------------- */
6199:    { "\\AA",    NULL,   "{\\rm~A\\limits^{-1$o}}" },
6200:    { "\\aa",    NULL,   "{\\rm~a\\limits^{-1$o}}" },
6201:    { "\\bmod",  NULL,   "{\\hspace2{\\rm~mod}\\hspace2}" },
6202:    { "\\mod",   NULL,   "{\\hspace2{\\rm~mod}\\hspace2}" },
6203:    { "\\vdots", NULL,   "{\\raisebox3{\\rotatebox{90}{\\ldots}}}" },
6204:    { "\\dots",  NULL,   "{\\cdots}" },
6205:    { "\\cdots", NULL,   "{\\raisebox3{\\ldots}}" },
6206:    { "\\ldots", NULL,   "{\\fs4.\\hspace1.\\hspace1.}" },
6207:    { "\\ddots", NULL,   "{\\fs4\\raisebox8.\\hspace1\\raisebox4."
6208:                         "\\hspace1\\raisebox0.}"},
6209:    { "\\cong",  NULL,   "{\\raisebox{-4}{\\approx\\atop-}}" },
6210:    { "\\notin", NULL,   "{\\not\\in}" },
6211:    { "\\neq",   NULL,   "{\\not=}" },
6212:    { "\\ne",    NULL,   "{\\not=}" },
6213:    { "\\pm",    NULL,   "{\\math\\plusminus}" }, /* don't have \bf\pm */
6214:    { "\\mapsto", NULL,  "{\\rule[fs/2]{1}{5+fs}\\hspace{-99}\\to}" },
6215:    { "\\hbar",  NULL,   "{\\compose~h{{\\fs{-1}-\\atop\\vspace3}}}" },
6216:    { "\\angle", NULL, "{\\compose{\\hspace{3}\\lt}{\\circle(10,15;-80,80)}}"},
6217:    { "\\textcelsius", NULL, "{\\textdegree C}"},
6218:    { "\\textdegree", NULL, "{\\Large^{^{\\tiny\\mathbf o}}}"},
6219:    { "\\cr",    NULL,   "\\\\" },
6220:    /*{ "\\colon",       NULL,   "{:}" },*/
6221:    { "\\iiint", NULL,   "{\\int\\int\\int}\\limits" },
6222:    { "\\iint",  NULL,   "{\\int\\int}\\limits" },
6223:    { "\\Bigiint", NULL, "{\\Bigint\\Bigint}\\limits" },
6224:    { "\\bigsqcap",NULL, "{\\fs{+4}\\sqcap}" },
6225:    { "\\_",     "embed","{\\underline{\\ }}" }, /* displayed underscore */
6226:    { "!`",      NULL,   "{\\raisebox{-2}{\\rotatebox{180}{!}}}" },
6227:    { "?`",      NULL,   "{\\raisebox{-2}{\\rotatebox{180}{?}}}" },
6228:    { "^\'",     "embed","\'" }, /* avoid ^^ when re-xlating \' below */
6229:    { "\'\'\'\'","embed","^{\\fs{-1}\\prime\\prime\\prime\\prime}" },
6230:    { "\'\'\'",  "embed","^{\\fs{-1}\\prime\\prime\\prime}" },
6231:    { "\'\'",    "embed","^{\\fs{-1}\\prime\\prime}" },
6232:    { "\'",      "embed","^{\\fs{-1}\\prime}" },
6233:    { "\\rightleftharpoons",NULL,"{\\rightharpoonup\\atop\\leftharpoondown}" },
6234:    { "\\therefore",NULL,"{\\Huge\\raisebox{-4}{.\\atop.\\,.}}" },
6235:    { "\\LaTeX", NULL,   "{\\rm~L\\raisebox{3}{\\fs{-1}A}\\TeX}" },
6236:    { "\\TeX",   NULL,   "{\\rm~T\\raisebox{-3}{E}X}" },
6237:    { "\\cyan",  NULL,   "{\\reverse\\red\\reversebg}" },
6238:    { "\\magenta",NULL,  "{\\reverse\\green\\reversebg}" },
6239:    { "\\yellow",NULL,   "{\\reverse\\blue\\reversebg}" },
6240:    { "\\cancel",NULL,   "\\Not" },
6241:    { "\\hhline",NULL,   "\\Hline" },
6242:    { "\\Hline", NULL,   "\\hline\\,\\\\\\hline" },
6243:    /* -----------------------------------------------------------------------
6244:      As per emails with Zbigniew Fiedorowicz <fiedorow@math.ohio-state.edu>
6245:      "Algebra Syntax"  termchar   mimeTeX/LaTeX equivalent...
6246:    ----------------------------------------------------------------------- */
6247:    { "sqrt",    "1",    "{\\sqrt{#1}}" },
6248:    { "sin",     "1",    "{\\sin{#1}}" },
6249:    { "cos",     "1",    "{\\cos{#1}}" },
6250:    { "asin",    "1",    "{\\sin^{-1}{#1}}" },
6251:    { "acos",    "1",    "{\\cos^{-1}{#1}}" },
6252:    { "exp",     "1",    "{{\\rm~e}^{#1}}" },
6253:    { "det",     "1",    "{\\left|{#1}\\right|}" },
6254:    /* --------------------------------------------
6255:      LaTeX Constant    termchar   value...
6256:    -------------------------------------------- */
6257:    { "\\thinspace",     NULL,   "\\," },
6258:    { "\\thinmathspace", NULL,   "\\," },
6259:    { "\\textwidth",     NULL,   "400" },
6260:    /* --- end-of-table indicator --- */
6261:    { NULL,      NULL,   NULL }
6262:  } ; /* --- end-of-symbols[] --- */
6263: /* ---
6264:  * html &#nn chars converted to latex equivalents
6265:  * ---------------------------------------------- */
6266: int     htmlnum=0;                      /* numbers[inum].html */
6267: static  struct { int html; char *latex; } numbers[] =
6268:  { /* ---------------------------------------
6269:     html num  LaTeX equivalent...
6270:    --------------------------------------- */
6271:    { 9,         " " },                  /* horizontal tab */
6272:    { 10,        " " },                  /* line feed */
6273:    { 13,        " " },                  /* carriage return */
6274:    { 32,        " " },                  /* space */
6275:    { 33,        "!" },                  /* exclamation point */
6276:    { 34,        "\"" },                 /* &quot; */
6277:    { 35,        "#" },                  /* hash mark */
6278:    { 36,        "$" },                  /* dollar */
6279:    { 37,        "%" },                  /* percent */
6280:    { 38,        "&" },                  /* &amp; */
6281:    { 39,        "\'" },                 /* apostrophe (single quote) */
6282:    { 40,        ")" },                  /* left parenthesis */
6283:    { 41,        ")" },                  /* right parenthesis */
6284:    { 42,        "*" },                  /* asterisk */
6285:    { 43,        "+" },                  /* plus */
6286:    { 44,        "," },                  /* comma */
6287:    { 45,        "-" },                  /* hyphen (minus) */
6288:    { 46,        "." },                  /* period */
6289:    { 47,        "/" },                  /* slash */
6290:    { 58,        ":" },                  /* colon */
6291:    { 59,        ";" },                  /* semicolon */
6292:    { 60,        "<" },                  /* &lt; */
6293:    { 61,        "=" },                  /* = */
6294:    { 62,        ">" },                  /* &gt; */
6295:    { 63,        "\?" },                 /* question mark */
6296:    { 64,        "@" },                  /* commercial at sign */
6297:    { 91,        "[" },                  /* left square bracket */
6298:    { 92,        "\\" },                 /* backslash */
6299:    { 93,        "]" },                  /* right square bracket */
6300:    { 94,        "^" },                  /* caret */
6301:    { 95,        "_" },                  /* underscore */
6302:    { 96,        "`" },                  /* grave accent */
6303:    { 123,       "{" },                  /* left curly brace */
6304:    { 124,       "|" },                  /* vertical bar */
6305:    { 125,       "}" },                  /* right curly brace */
6306:    { 126,       "~" },                  /* tilde */
6307:    { 160,       "~" },                  /* &nbsp; (use tilde for latex) */
6308:    { 166,       "|" },                  /* &brvbar; (broken vertical bar) */
6309:    { 173,       "-" },                  /* &shy; (soft hyphen) */
6310:    { 177,       "{\\pm}" },             /* &plusmn; (plus or minus) */
6311:    { 215,       "{\\times}" },          /* &times; (plus or minus) */
6312:    { -999,      NULL }
6313:  } ; /* --- end-of-numbers[] --- */
6314: /* -------------------------------------------------------------------------
6315: first remove comments
6316: -------------------------------------------------------------------------- */
6317: expptr = expression;                    /* start search at beginning */
6318: while ( (leftptr=strstr(expptr,leftcomment)) != NULL ) /*found leftcomment*/
6319:   {
6320:   char  *rightsym=NULL;                 /* rightcomment[isymbol] */
6321:   expptr = leftptr+strlen(leftcomment); /* start rightcomment search here */
6322:   /* --- check for any closing rightcomment, in given precedent order --- */
6323:   if ( *expptr != '\000' )              /*have chars after this leftcomment*/
6324:    for(isymbol=0; (rightsym=rightcomment[isymbol]) != NULL; isymbol++)
6325:     if ( (tokptr=strstr(expptr,rightsym)) != NULL ) /*found rightcomment*/
6326:      { tokptr += strlen(rightsym);      /* first char after rightcomment */
6327:        if ( *tokptr == '\000' )         /*nothing after this rightcomment*/
6328:         { *leftptr = '\000';            /*so terminate expr at leftcomment*/
6329:           break; }                      /* and stop looking for comments */
6330:        *leftptr = '~';                  /* replace entire comment by ~ */
6331:        strsqueezep(leftptr+1,tokptr);   /* squeeze out comment */
6332:        goto next_comment; }             /* stop looking for rightcomment */
6333:   /* --- no rightcomment after opening leftcomment --- */
6334:   *leftptr = '\000';                    /* so terminate expression */
6335:   /* --- resume search past squeezed-out comment --- */
6336:   next_comment:
6337:     if ( *leftptr == '\000' ) break;    /* reached end of expression */
6338:     expptr = leftptr+1;                 /*resume search after this comment*/
6339:   } /* --- end-of-while(leftptr!=NULL) --- */
6340: /* -------------------------------------------------------------------------
6341: run thru table, converting all occurrences of each macro to its expansion
6342: -------------------------------------------------------------------------- */
6343: for(isymbol=0; (htmlsym=symbols[isymbol].html) != NULL; isymbol++)
6344:   {
6345:   int   htmllen = strlen(htmlsym);      /* length of escape, _without_ ; */
6346:   int   isalgebra = isalpha((int)(*htmlsym)); /* leading char alphabetic */
6347:   int   isembedded = 0,                 /* true to xlate even if embedded */
6348:         istag=0, isamp=0,               /* true for <tag>, &char; symbols */
6349:         isstrwstr = 0,                  /* true to use strwstr() */
6350:         wstrlen = 0;                    /* length of strwstr() match */
6351:   char  *aleft="{([<|", *aright="})]>|"; /*left,right delims for alg syntax*/
6352:   char  embedkeywd[99] = "embed",       /* keyword to signal embedded token*/
6353:         embedterm = '\000';             /* char immediately after embed */
6354:   int   embedlen = strlen(embedkeywd);  /* #chars in embedkeywd */
6355:   char  *args = symbols[isymbol].args,  /* number {}-args, optional []-arg */
6356:         *htmlterm = args,               /*if *args nonumeric, then html term*/
6357:         *latexsym = symbols[isymbol].latex, /*latex replacement for htmlsym*/
6358:         errorsym[256];                  /*or latexsym may point to error msg*/
6359:   char  abuff[8192];  int iarg,nargs=0; /* macro expansion params */
6360:   char  wstrwhite[99];                  /* whitespace chars for strwstr() */
6361:   skipwhite(htmlsym);                   /*skip any bogus leading whitespace*/
6362:   htmllen = strlen(htmlsym);            /* reset length of html token */
6363:   istag = (isthischar(*htmlsym,"<")?1:0); /* html <tag> starts with < */
6364:   isamp = (isthischar(*htmlsym,"&")?1:0); /* html &char; starts with & */
6365:   if ( args != NULL )                   /*we have args (or htmlterm) param*/
6366:    if ( *args != '\000' ) {             /* and it's not an empty string */
6367:     if ( strchr("0123456789",*args) != NULL ) /* is 1st char #args=0-9 ? */
6368:      { htmlterm = NULL;                 /* if so, then we have no htmlterm */
6369:        *abuff = *args;  abuff[1] = '\000'; /* #args char in ascii buffer */
6370:        nargs = atoi(abuff); }           /* interpret #args to numeric */
6371:     else if ( strncmp(args,embedkeywd,embedlen) == 0 )/*xlate embedded token*/
6372:      { int arglen = strlen(args);       /* length of "embed..." string */
6373:        htmlterm = NULL;                 /* if so, then we have no htmlterm */
6374:        isembedded = 1 ;                 /* turn on embedded flag */
6375:        if ( arglen > embedlen )         /* have embed "allow escape" flag */
6376:          embedterm = args[embedlen];    /* char immediately after "embed" */
6377:        if (arglen > embedlen+1) {       /* have embed,flag,white for strwstr*/
6378:          isstrwstr = 1;                 /* turn on strwtsr flag */
6379:          strcpy(wstrwhite,args+embedlen+1); } } /*and set its whitespace arg*/
6380:     } /* --- end-of-if(*args!='\000') --- */
6381:   expptr = expression;                  /* re-start search at beginning */
6382:   while ( ( tokptr=(!isstrwstr?strstr(expptr,htmlsym): /* just use strtsr */
6383:   strwstr(expptr,htmlsym,wstrwhite,&wstrlen)) ) /* or use our strwstr */
6384:   != NULL ) {                           /* found another sym */
6385:       int  toklen = (!isstrwstr?htmllen:wstrlen); /* length of matched sym */
6386:       char termchar = *(tokptr+toklen), /* char terminating html sequence */
6387:            prevchar = (tokptr==expptr?' ':*(tokptr-1));/*char preceding html*/
6388:       int  isescaped = (isthischar(prevchar,ESCAPE)?1:0); /* token escaped?*/
6389:       int  escapelen = toklen;          /* total length of escape sequence */
6390:       int  isflush = 0;                 /* true to flush (don't xlate) */
6391:       /* --- check odd/even backslashes preceding tokens --- */
6392:       if ( isescaped ) {                /* have one preceding backslash */
6393:         char *p = tokptr-1;             /* ptr to that preceding backslash */
6394:         while ( p != expptr ) {         /* and we may have more preceding */
6395:           p--; if(!isthischar(*p,ESCAPE))break; /* but we don't, so quit */
6396:           isescaped = 1-isescaped; } }  /* or flip isescaped flag if we do */
6397:       /* --- init with "trivial" abuff,escapelen from symbols[] table --- */
6398:       *abuff = '\000';                  /* default to empty string */
6399:       if ( latexsym != NULL )           /* table has .latex xlation */
6400:        if ( *latexsym != '\000' )       /* and it's not an empty string */
6401:         strcpy(abuff,latexsym);         /* so get local copy */
6402:       if ( !isembedded )                /*embedded sequences not terminated*/
6403:        if ( htmlterm != NULL )          /* sequence may have terminator */
6404:         escapelen += (isthischar(termchar,htmlterm)?1:0); /*add terminator*/
6405:       /* --- don't xlate if we just found prefix of longer symbol, etc --- */
6406:       if ( !isembedded ) {              /* not embedded */
6407:         if ( isescaped )                /* escaped */
6408:           isflush = 1;                  /* set flag to flush escaped token */
6409:         if ( !istag && isalpha((int)termchar) ) /* followed by alpha */
6410:           isflush = 1;                  /* so just a prefix of longer symbol*/
6411:         if ( isalpha((int)(*htmlsym)) ) /* symbol starts with alpha */
6412:           if ( (!isspace(prevchar)&&isalpha(prevchar)) ) /* just a suffix*/
6413:             isflush = 1; }              /* set flag to flush token */
6414:       if ( isembedded )                 /* for embedded token */
6415:        if ( isescaped )                 /* and embedded \token escaped */
6416:         if ( !isthischar(embedterm,ESCAPE) ) /* don't xlate escaped \token */
6417:           isflush = 1;                  /* set flag to flush token */
6418:       if ( isflush )                    /* don't xlate this token */
6419:         { expptr = tokptr+1;/*toklen;*/ /* just resume search after token */
6420:           continue; }                   /* but don't replace it */
6421:       /* --- check for &# prefix signalling &#nnn; --- */
6422:       if ( strcmp(htmlsym,"&#") == 0 ) { /* replacing special &#nnn; chars */
6423:        /* --- accumulate chars comprising number following &# --- */
6424:        char anum[32];                   /* chars comprising number after &# */
6425:        int  inum = 0;                   /* no chars accumulated yet */
6426:        while ( termchar != '\000' ) {   /* don't go past end-of-string */
6427:          if ( !isdigit((int)termchar) ) break; /* and don't go past digits */
6428:          if ( inum > 10 ) break;        /* some syntax error in expression */
6429:          anum[inum] = termchar;         /* accumulate this digit */
6430:          inum++;  toklen++;             /* bump field length, token length */
6431:          termchar = *(tokptr+toklen); } /* char terminating html sequence */
6432:        anum[inum] = '\000';             /* null-terminate anum */
6433:        escapelen = toklen;              /* length of &#nnn; sequence */
6434:        if ( htmlterm != NULL )          /* sequence may have terminator */
6435:          escapelen += (isthischar(termchar,htmlterm)?1:0); /*add terminator*/
6436:        /* --- look up &#nnn in number[] table --- */
6437:        htmlnum = atoi(anum);            /* convert anum[] to an integer */
6438:        strninit(errorsym,latexsym,128); /* init error message */
6439:        latexsym = errorsym;             /* init latexsym as error message */
6440:        strreplace(latexsym,"nnn",anum,1); /*place actual &#num in message*/
6441:        for ( inum=0; numbers[inum].html>=0; inum++ ) /* run thru numbers[] */
6442:          if ( htmlnum ==  numbers[inum].html ) { /* till we find a match */
6443:            latexsym = numbers[inum].latex; /* latex replacement */
6444:            break; }                     /* no need to look any further */
6445:        if ( latexsym != NULL )          /* table has .latex xlation */
6446:         if ( *latexsym != '\000' )      /* and it's not an empty string */
6447:          strcpy(abuff,latexsym);        /* so get local copy */
6448:        } /* --- end-of-if(strcmp(htmlsym,"&#")==0) --- */
6449:       /* --- substitute macro arguments --- */
6450:       if ( nargs > 0 )                  /*substitute #1,#2,... in latexsym*/
6451:        {
6452:        char *arg1ptr = tokptr+escapelen;/* nargs begin after macro literal */
6453:        char *optarg = args+1;           /* ptr 1 char past #args digit 0-9 */
6454:        expptr = arg1ptr;                /* ptr to beginning of next arg */
6455:        for ( iarg=1; iarg<=nargs; iarg++ ) /* one #`iarg` arg at a time */
6456:         {
6457:         char argsignal[32] = "#1",      /* #1...#9 signals arg replacement */
6458:         *argsigptr = NULL;              /* ptr to argsignal in abuff[] */
6459:         /* --- get argument value --- */
6460:         *argval = '\000';               /* init arg as empty string */
6461:         skipwhite(expptr);              /* and skip leading white space */
6462:         if ( iarg==1 && *optarg!='\000' /* check for optional [arg] */
6463:         &&   !isalgebra )               /* but not in "algebra syntax" */
6464:          { strcpy(argval,optarg);       /* init with default value */
6465:            if ( *expptr == '[' )        /* but user gave us [argval] */
6466:              expptr = texsubexpr(expptr,argval,0,"[","]",0,0); } /*so get it*/
6467:         else                            /* not optional, so get {argval} */
6468:          if ( *expptr != '\000' ) {     /* check that some argval provided */
6469:            if ( !isalgebra )            /* only { } delims for latex macro */
6470:              expptr = texsubexpr(expptr,argval,0,"{","}",0,0);/*get {argval}*/
6471:            else {                       /*any delim for algebra syntax macro*/
6472:              expptr = texsubexpr(expptr,argval,0,aleft,aright,0,1);
6473:              if ( isthischar(*argval,aleft) ) /* have delim-enclosed arg */
6474:                if ( *argval != '{' ) {  /* and it's not { }-enclosed */
6475:                  strchange(0,argval,"\\left"); /* insert opening \left, */
6476:                  strchange(0,argval+strlen(argval)-1,"\\right"); } }/*\right*/
6477:           } /* --- end-of-if(*expptr!='\000') --- */
6478:         /* --- (recursively) call mimeprep() to prep the argument --- */
6479:         if ( !isempty(argval) )         /* have an argument */
6480:           mimeprep(argval);             /* so (recursively) prep it */
6481:         /* --- "specials" (e.g., \password) check --- */
6482:         if ( strstr(htmlsym,"password") != NULL ) { /* have \password{} */
6483:           argval[64] = '\000';          /* make sure it's not too long */
6484:           strcpy(password,argval); }    /* save it for http_referer checks */
6485:         /* --- replace #`iarg` in macro with argval --- */
6486:         sprintf(argsignal,"#%d",iarg);  /* #1...#9 signals argument */
6487:         while ( (argsigptr=strstr(argval,argsignal)) != NULL ) /* #1...#9 */
6488:          {strsqueeze(argsigptr,strlen(argsignal));} /* can't be in argval */
6489:         while ( (argsigptr=strstr(abuff,argsignal)) != NULL ) /* #1...#9 */
6490:          strchange(strlen(argsignal),argsigptr,argval); /*replaced by argval*/
6491:         } /* --- end-of-for(iarg) --- */
6492:        escapelen += ((int)(expptr-arg1ptr)); /* add in length of all args */
6493:        } /* --- end-of-if(nargs>0) --- */
6494:       strchange(escapelen,tokptr,abuff); /*replace macro or html symbol*/
6495:       expptr = tokptr + strlen(abuff); /*resume search after macro / html*/
6496:       } /* --- end-of-while(tokptr!=NULL) --- */
6497:   } /* --- end-of-for(isymbol) --- */
6498: /* -------------------------------------------------------------------------
6499: convert \left( to \(  and  \right) to \),  etc.
6500: -------------------------------------------------------------------------- */
6501: if ( xlateleft )                        /* \left...\right xlation wanted */
6502:  for ( idelim=0; idelim<2; idelim++ )   /* 0 for \left  and  1 for \right */
6503:   {
6504:   char  *lrstr  = (idelim==0?"\\left":"\\right"); /* \left on 1st pass */
6505:   int   lrlen   = (idelim==0?5:6);      /* strlen() of \left or \right */
6506:   char  *braces = (idelim==0?LEFTBRACES ".":RIGHTBRACES "."), /*([{<or)]}>*/
6507:         **lrfrom= (idelim==0?leftfrom:rightfrom), /* long braces like \| */
6508:         **lrto  = (idelim==0?leftto:rightto), /* xlated to 1-char like = */
6509:         *lrsym  = NULL;                 /* lrfrom[isymbol] */
6510:   expptr = expression;                  /* start search at beginning */
6511:   while ( (tokptr=strstr(expptr,lrstr)) != NULL ) /* found \left or \right */
6512:     {
6513:     if ( isthischar(*(tokptr+lrlen),braces) ) /* followed by a 1-char brace*/
6514:       { strsqueeze((tokptr+1),(lrlen-1));/*so squeeze out "left" or "right"*/
6515:         expptr = tokptr+2; }            /* and resume search past brace */
6516:     else                                /* may be a "long" brace like \| */
6517:       {
6518:       expptr = tokptr+lrlen;            /*init to resume search past\left\rt*/
6519:       for(isymbol=0; (lrsym=lrfrom[isymbol]) != NULL; isymbol++)
6520:         { int symlen = strlen(lrsym);   /* #chars in delim, e.g., 2 for \| */
6521:           if ( memcmp(tokptr+lrlen,lrsym,symlen) == 0 ) /* found long delim*/
6522:             { strsqueeze((tokptr+1),(lrlen+symlen-2)); /*squeeze out delim*/
6523:               *(tokptr+1) = *(lrto[isymbol]); /* last char now 1-char delim*/
6524:               expptr = tokptr+2 - lrlen; /* resume search past 1-char delim*/
6525:               break; }                  /* no need to check more lrsym's */
6526:         } /* --- end-of-for(isymbol) --- */
6527:       } /* --- end-of-if/else(isthischar()) --- */
6528:     } /* --- end-of-while(tokptr!=NULL) --- */
6529:   } /* --- end-of-for(idelim) --- */
6530: /* -------------------------------------------------------------------------
6531: run thru table, converting all {a+b\atop c+d} to \atop{a+b}{c+d}
6532: -------------------------------------------------------------------------- */
6533: for(isymbol=0; (atopsym=atopcommands[isymbol]) != NULL; isymbol++)
6534:   {
6535:   int   atoplen = strlen(atopsym);      /* #chars in \atop */
6536:   expptr = expression;                  /* re-start search at beginning */
6537:   while ( (tokptr=strstr(expptr,atopsym)) != NULL ) /* found another atop */
6538:     { char *leftbrace=NULL, *rightbrace=NULL; /*ptr to opening {, closing }*/
6539:       char termchar = *(tokptr+atoplen); /* \atop followed by terminator */
6540:       if ( msgfp!=NULL && msglevel>=999 )
6541:         { fprintf(msgfp,"mimeprep> offset=%d rhs=\"%s\"\n",
6542:           (int)(tokptr-expression),tokptr);
6543:           fflush(msgfp); }
6544:       if ( isalpha((int)termchar) )     /*we just have prefix of longer sym*/
6545:         { expptr = tokptr+atoplen;      /* just resume search after prefix */
6546:           continue; }                   /* but don't process it */
6547:       leftbrace  = findbraces(expression,tokptr);     /* find left { */
6548:       rightbrace = findbraces(NULL,tokptr+atoplen-1); /* find right } */
6549:       if ( leftbrace==NULL || rightbrace==NULL )
6550:         { expptr += atoplen;  continue; } /* skip command if didn't find */
6551:       else                              /* we have bracketed { \atop } */
6552:         {
6553:         int  leftlen  = (int)(tokptr-leftbrace) - 1, /* #chars in left arg */
6554:              rightlen = (int)(rightbrace-tokptr) - atoplen, /* and in right*/
6555:              totlen   = (int)(rightbrace-leftbrace) + 1; /*tot in { \atop }*/
6556:         char *open=atopdelims[2*isymbol], *close=atopdelims[2*isymbol+1];
6557:         char arg[8192], command[8192];  /* left/right args, new \atop{}{} */
6558:         *command = '\000';              /* start with null string */
6559:         if (open!=NULL) strcat(command,open); /* add open delim if needed */
6560:         strcat(command,atopsym);        /* add command with \atop */
6561:         arg[0] = '{';                   /* arg starts with { */
6562:         memcpy(arg+1,leftbrace+1,leftlen); /* extract left-hand arg */
6563:         arg[leftlen+1] = '\000';        /* and null terminate it */
6564:         strcat(command,arg);            /* concatanate {left-arg to \atop */
6565:         strcat(command,"}{");           /* close left-arg, open right-arg */
6566:         memcpy(arg,tokptr+atoplen,rightlen); /* right-hand arg */
6567:         arg[rightlen] = '}';            /* add closing } */
6568:         arg[rightlen+1] = '\000';       /* and null terminate it */
6569:         if ( isthischar(*arg,WHITEMATH) ) /* 1st char was mandatory space */
6570:           {strsqueeze(arg,1);}          /* so squeeze it out */
6571:         strcat(command,arg);            /* concatanate right-arg} */
6572:         if (close!=NULL) strcat(command,close); /* add close delim if needed*/
6573:         strchange(totlen-2,leftbrace+1,command); /* {\atop} --> {\atop{}{}} */
6574:         expptr = leftbrace+strlen(command); /*resume search past \atop{}{}*/
6575:         }
6576:     } /* --- end-of-while(tokptr!=NULL) --- */
6577:   } /* --- end-of-for(isymbol) --- */
6578: /* -------------------------------------------------------------------------
6579: back to caller with preprocessed expression
6580: -------------------------------------------------------------------------- */
6581: if ( msgfp!=NULL && msglevel>=99 )      /* display preprocessed expression */
6582:   { fprintf(msgfp,"mimeprep> expression=\"\"%s\"\"\n",expression);
6583:     fflush(msgfp); }
6584: return ( expression );
6585: } /* --- end-of-function mimeprep() --- */
6586: 
6587: 
6588: /* ==========================================================================
6589:  * Function:    strchange ( int nfirst, char *from, char *to )
6590:  * Purpose:     Changes the nfirst leading chars of `from` to `to`.
6591:  *              For example, to change char x[99]="12345678" to "123ABC5678"
6592:  *              call strchange(1,x+3,"ABC")
6593:  * --------------------------------------------------------------------------
6594:  * Arguments:   nfirst (I)      int containing #leading chars of `from`
6595:  *                              that will be replace by `to`
6596:  *              from (I/O)      char * to null-terminated string whose nfirst
6597:  *                              leading chars will be replaced by `to`
6598:  *              to (I)          char * to null-terminated string that will
6599:  *                              replace the nfirst leading chars of `from`
6600:  * --------------------------------------------------------------------------
6601:  * Returns:     ( char * )      ptr to first char of input `from`
6602:  *                              or NULL for any error.
6603:  * --------------------------------------------------------------------------
6604:  * Notes:     o If strlen(to)>nfirst, from must have memory past its null
6605:  *              (i.e., we don't do a realloc)
6606:  * ======================================================================= */
6607: /* --- entry point --- */
6608: char    *strchange ( int nfirst, char *from, char *to )
6609: {
6610: /* -------------------------------------------------------------------------
6611: Allocations and Declarations
6612: -------------------------------------------------------------------------- */
6613: int     tolen = (to==NULL?0:strlen(to)), /* #chars in replacement string */
6614:         nshift = abs(tolen-nfirst);     /*need to shift from left or right*/
6615: /* -------------------------------------------------------------------------
6616: shift from left or right to accommodate replacement of its nfirst chars by to
6617: -------------------------------------------------------------------------- */
6618: if ( tolen < nfirst )                   /* shift left is easy */
6619:   {strsqueeze(from,nshift);}            /* memmove avoids overlap memory */
6620: if ( tolen > nfirst )                   /* need more room at start of from */
6621:   { char *pfrom = from+strlen(from);    /* ptr to null terminating from */
6622:     for ( ; pfrom>=from; pfrom-- )      /* shift all chars including null */
6623:       *(pfrom+nshift) = *pfrom; }       /* shift chars nshift places right */
6624: /* -------------------------------------------------------------------------
6625: from has exactly the right number of free leading chars, so just put to there
6626: -------------------------------------------------------------------------- */
6627: if ( tolen != 0 )                       /* make sure to not empty or null */
6628:   memcpy(from,to,tolen);                /* chars moved into place */
6629: return ( from );                        /* changed string back to caller */
6630: } /* --- end-of-function strchange() --- */
6631: 
6632: 
6633: /* ==========================================================================
6634:  * Function:    strreplace (char *string, char *from, char *to, int nreplace)
6635:  * Purpose:     Changes the first nreplace occurrences of 'from' to 'to'
6636:  *              in string, or all occurrences if nreplace=0.
6637:  * --------------------------------------------------------------------------
6638:  * Arguments:   string (I/0)    char * to null-terminated string in which
6639:  *                              occurrence of 'from' will be replaced by 'to'
6640:  *              from (I)        char * to null-terminated string
6641:  *                              to be replaced by 'to'
6642:  *              to (I)          char * to null-terminated string that will
6643:  *                              replace 'from'
6644:  *              nreplace (I)    int containing (maximum) number of
6645:  *                              replacements, or 0 to replace all.
6646:  * --------------------------------------------------------------------------
6647:  * Returns:     ( int )         number of replacements performed,
6648:  *                              or 0 for no replacements or -1 for any error.
6649:  * --------------------------------------------------------------------------
6650:  * Notes:     o
6651:  * ======================================================================= */
6652: /* --- entry point --- */
6653: int     strreplace ( char *string, char *from, char *to, int nreplace )
6654: {
6655: /* -------------------------------------------------------------------------
6656: Allocations and Declarations
6657: -------------------------------------------------------------------------- */
6658: int     fromlen = (from==NULL?0:strlen(from)), /* #chars to be replaced */
6659:         tolen = (to==NULL?0:strlen(to)); /* #chars in replacement string */
6660: char    *pfrom = (char *)NULL,          /*ptr to 1st char of from in string*/
6661:         *pstring = string,              /*ptr past previously replaced from*/
6662:         *strchange();                   /* change 'from' to 'to' */
6663: int     nreps = 0;                      /* #replacements returned to caller*/
6664: /* -------------------------------------------------------------------------
6665: repace occurrences of 'from' in string to 'to'
6666: -------------------------------------------------------------------------- */
6667: if ( string == (char *)NULL             /* no input string */
6668: ||   (fromlen<1 && nreplace<=0) )       /* replacing empty string forever */
6669:   nreps = (-1);                         /* so signal error */
6670: else                                    /* args okay */
6671:   while (nreplace<1 || nreps<nreplace)  /* up to #replacements requested */
6672:     {
6673:     if ( fromlen > 0 )                  /* have 'from' string */
6674:       pfrom = strstr(pstring,from);     /*ptr to 1st char of from in string*/
6675:     else  pfrom = pstring;              /*or empty from at start of string*/
6676:     if ( pfrom == (char *)NULL ) break; /*no more from's, so back to caller*/
6677:     if ( strchange(fromlen,pfrom,to)    /* leading 'from' changed to 'to' */
6678:     ==   (char *)NULL ) { nreps=(-1); break; } /* signal error to caller */
6679:     nreps++;                            /* count another replacement */
6680:     pstring = pfrom+tolen;              /* pick up search after 'to' */
6681:     if ( *pstring == '\000' ) break;    /* but quit at end of string */
6682:     } /* --- end-of-while() --- */
6683: return ( nreps );                       /* #replacements back to caller */
6684: } /* --- end-of-function strreplace() --- */
6685: 
6686: 
6687: /* ==========================================================================
6688:  * Function:    strwstr (char *string, char *substr, char *white, int *sublen)
6689:  * Purpose:     Find first substr in string, but wherever substr contains
6690:  *              a whitespace char (in white), string may contain any number
6691:  *              (including 0) of whitespace chars. If white contains I or i,
6692:  *              then match is case-insensitive (and I,i _not_ whitespace).
6693:  * --------------------------------------------------------------------------
6694:  * Arguments:   string (I)      char * to null-terminated string in which
6695:  *                              first occurrence of substr will be found
6696:  *              substr (I)      char * to null-terminated string containing
6697:  *                              "template" that will be searched for
6698:  *              white (I)       char * to null-terminated string containing
6699:  *                              whitespace chars.  If NULL or empty, then
6700:  *                              "~ \t\n\r\f\v" (WHITEMATH in mimetex.h) used.
6701:  *                              If white contains I or i, then match is
6702:  *                              case-insensitive (and I,i _not_ considered
6703:  *                              whitespace).
6704:  *              sublen (O)      address of int returning "length" of substr
6705:  *                              found in string (which may be longer or
6706:  *                              shorter than substr itself).
6707:  * --------------------------------------------------------------------------
6708:  * Returns:     ( char * )      ptr to first char of substr in string
6709:  *                              or NULL if not found or for any error.
6710:  * --------------------------------------------------------------------------
6711:  * Notes:     o Wherever a single whitespace char appears in substr,
6712:  *              the corresponding position in string may contain any
6713:  *              number (including 0) of whitespace chars, e.g.,
6714:  *              string="abc   def" and string="abcdef" both match
6715:  *              substr="c d" at offset 2 of string.
6716:  *            o If substr="c  d" (two spaces between c and d),
6717:  *              then string must have at least one space, so now "abcdef"
6718:  *              doesn't match.  In general, the minimum number of spaces
6719:  *              in string is the number of spaces in substr minus 1
6720:  *              (so 1 space in substr permits 0 spaces in string).
6721:  *            o Embedded spaces are counted in sublen, e.g.,
6722:  *              string="c   d" (three spaces) matches substr="c d"
6723:  *              with sublen=5 returned.  But string="ab   c   d" will
6724:  *              also match substr="  c d" returning sublen=5 and
6725:  *              a ptr to the "c".  That is, the mandatory preceding
6726:  *              space is _not_ counted as part of the match.
6727:  *              But all the embedded space is counted.
6728:  *              (An inconsistent bug/feature is that mandatory
6729:  *              terminating space is counted.)
6730:  *            o Moreover, string="c   d" matches substr="  c d", i.e.,
6731:  *              the very beginning of a string is assumed to be preceded
6732:  *              by "virtual blanks".
6733:  * ======================================================================= */
6734: /* --- entry point --- */
6735: char    *strwstr ( char *string, char *substr, char *white, int *sublen )
6736: {
6737: /* -------------------------------------------------------------------------
6738: Allocations and Declarations
6739: -------------------------------------------------------------------------- */
6740: char    *psubstr=substr, *pstring=string,/*ptr to current char in substr,str*/
6741:         *pfound = (char *)NULL;         /*ptr to found substr back to caller*/
6742: char    *pwhite=NULL, whitespace[256];  /* callers white whithout i,I */
6743: int     iscase = (white==NULL?1:        /* case-sensitive if i,I in white */
6744:         strchr(white,'i')==NULL && strchr(white,'I')==NULL);
6745: int     foundlen = 0;                   /* length of substr found in string*/
6746: int     nstrwhite=0, nsubwhite=0,       /* #leading white chars in str,sub */
6747:         nminwhite=0;                    /* #mandatory leading white in str */
6748: int     nstrchars=0, nsubchars=0,       /* #non-white chars to be matched */
6749:         isncmp=0;                       /*strncmp() or strncasecmp() result*/
6750: /* -------------------------------------------------------------------------
6751: Initialization
6752: -------------------------------------------------------------------------- */
6753: /* --- set up whitespace --- */
6754: strcpy(whitespace,WHITEMATH);           /*default if no user input for white*/
6755: if ( white != NULL )                    /*user provided ptr to white string*/
6756:  if ( *white != '\000' ) {              /*and it's not just an empty string*/
6757:    strcpy(whitespace,white);            /* so use caller's white spaces */
6758:    while ( (pwhite=strchr(whitespace,'i')) != NULL ) /* have an embedded i */
6759:      {strsqueeze(pwhite,1);}            /* so squeeze it out */
6760:    while ( (pwhite=strchr(whitespace,'I')) != NULL ) /* have an embedded I */
6761:      {strsqueeze(pwhite,1);}            /* so squeeze it out */
6762:    if ( *whitespace == '\000' )         /* caller's white just had i,I */
6763:      strcpy(whitespace,WHITEMATH); }    /* so revert back to default */
6764: /* -------------------------------------------------------------------------
6765: Find first occurrence of substr in string
6766: -------------------------------------------------------------------------- */
6767: if ( string != NULL )                   /* caller passed us a string ptr */
6768:  while ( *pstring != '\000' ) {         /* break when string exhausted */
6769:   char  *pstrptr = pstring;             /* (re)start at next char in string*/
6770:   int   leadingwhite = 0;               /* leading whitespace */
6771:   psubstr = substr;                     /* start at beginning of substr */
6772:   foundlen = 0;                         /* reset length of found substr */
6773:   if ( substr != NULL )                 /* caller passed us a substr ptr */
6774:    while ( *psubstr != '\000' ) {       /*see if pstring begins with substr*/
6775:     /* --- check for end-of-string before finding match --- */
6776:     if ( *pstrptr == '\000' )           /* end-of-string without a match */
6777:       goto nextstrchar;                 /* keep trying with next char */
6778:     /* --- actual amount of whitespace in string and substr --- */
6779:     nsubwhite = strspn(psubstr,whitespace); /* #leading white chars in sub */
6780:     nstrwhite = strspn(pstrptr,whitespace); /* #leading white chars in str */
6781:     nminwhite = max2(0,nsubwhite-1);    /* #mandatory leading white in str */
6782:     /* --- check for mandatory leading whitespace in string --- */
6783:     if ( pstrptr != string )            /*not mandatory at start of string*/
6784:       if ( nstrwhite < nminwhite )      /* too little leading white space */
6785:         goto nextstrchar;               /* keep trying with next char */
6786:     /* ---hold on to #whitespace chars in string preceding substr match--- */
6787:     if ( pstrptr == pstring )           /* whitespace at start of substr */
6788:       leadingwhite = nstrwhite;         /* save it as leadingwhite */
6789:     /* --- check for optional whitespace --- */
6790:     if ( psubstr != substr )            /* always okay at start of substr */
6791:       if ( nstrwhite>0 && nsubwhite<1 ) /* too much leading white space */
6792:         goto nextstrchar;               /* keep trying with next char */
6793:     /* --- skip any leading whitespace in substr and string --- */
6794:     psubstr += nsubwhite;               /* push past leading sub whitespace*/
6795:     pstrptr += nstrwhite;               /* push past leading str whitespace*/
6796:     /* --- now get non-whitespace chars that we have to match --- */
6797:     nsubchars = strcspn(psubstr,whitespace); /* #non-white chars in sub */
6798:     nstrchars = strcspn(pstrptr,whitespace); /* #non-white chars in str */
6799:     if ( nstrchars < nsubchars )        /* too few chars for match */
6800:       goto nextstrchar;                 /* keep trying with next char */
6801:     /* --- see if next nsubchars are a match --- */
6802:     isncmp = (iscase? strncmp(pstrptr,psubstr,nsubchars): /*case sensitive*/
6803:                 strncasecmp(pstrptr,psubstr,nsubchars)); /*case insensitive*/
6804:     if ( isncmp != 0 )                  /* no match */
6805:       goto nextstrchar;                 /* keep trying with next char */
6806:     /* --- push past matched chars --- */
6807:     psubstr += nsubchars;  pstrptr += nsubchars;  /*nsubchars were matched*/
6808:     } /* --- end-of-while(*psubstr!='\000') --- */
6809:   pfound = pstring + leadingwhite;      /* found match starting at pstring */
6810:   foundlen = (int)(pstrptr-pfound);     /* consisting of this many chars */
6811:   goto end_of_job;                      /* back to caller */
6812:   /* ---failed to find substr, continue trying with next char in string--- */
6813:   nextstrchar:                          /* continue outer loop */
6814:     pstring++;                          /* bump to next char in string */
6815:   } /* --- end-of-while(*pstring!='\000') --- */
6816: /* -------------------------------------------------------------------------
6817: Back to caller with ptr to first occurrence of substr in string
6818: -------------------------------------------------------------------------- */
6819: end_of_job:
6820:   if ( msglevel>=999 && msgfp!=NULL) {  /* debugging/diagnostic output */
6821:     fprintf(msgfp,"strwstr> str=\"%.72s\" sub=\"%s\" found at offset %d\n",
6822:     string,substr,(pfound==NULL?(-1):(int)(pfound-string))); fflush(msgfp); }
6823:   if ( sublen != NULL )                 /*caller wants length of found substr*/
6824:     *sublen = foundlen;                 /* give it to him along with ptr */
6825:   return ( pfound );                    /*ptr to first found substr, or NULL*/
6826: } /* --- end-of-function strwstr() --- */
6827: 
6828: 
6829: /* ==========================================================================
6830:  * Function:    strdetex ( s, mode )
6831:  * Purpose:     Removes/replaces any LaTeX math chars in s
6832:  *              so that s can be displayed "verbatim",
6833:  *              e.g., for error messages.
6834:  * --------------------------------------------------------------------------
6835:  * Arguments:   s (I)           char * to null-terminated string
6836:  *                              whose math chars are to be removed/replaced
6837:  *              mode (I)        int containing 0 to _not_ use macros (i.e.,
6838:  *                              mimeprep won't be called afterwards),
6839:  *                              or containing 1 to use macros that will
6840:  *                              be expanded by a subsequent call to mimeprep.
6841:  * --------------------------------------------------------------------------
6842:  * Returns:     ( char * )      ptr to "cleaned" copy of s
6843:  *                              or "" (empty string) for any error.
6844:  * --------------------------------------------------------------------------
6845:  * Notes:     o The returned pointer addresses a static buffer,
6846:  *              so don't call strdetex() again until you're finished
6847:  *              with output from the preceding call.
6848:  * ======================================================================= */
6849: /* --- entry point --- */
6850: char    *strdetex ( char *s, int mode )
6851: {
6852: /* -------------------------------------------------------------------------
6853: Allocations and Declarations
6854: -------------------------------------------------------------------------- */
6855: static  char sbuff[4096];               /* copy of s with no math chars */
6856: int     strreplace();                   /* replace _ with -, etc */
6857: /* -------------------------------------------------------------------------
6858: Make a clean copy of s
6859: -------------------------------------------------------------------------- */
6860: /* --- check input --- */
6861: *sbuff = '\000';                        /* initialize in case of error */
6862: if ( isempty(s) ) goto end_of_job;      /* no input */
6863: /* --- start with copy of s --- */
6864: strninit(sbuff,s,2048);                 /* leave room for replacements */
6865: /* --- make some replacements -- we *must* replace \ { } first --- */
6866: strreplace(sbuff,"\\","\\backslash~\\!\\!",0);  /*change all \'s to text*/
6867: strreplace(sbuff,"{", "\\lbrace~\\!\\!",0);     /*change all {'s to \lbrace*/
6868: strreplace(sbuff,"}", "\\rbrace~\\!\\!",0);     /*change all }'s to \rbrace*/
6869: /* --- now our further replacements may contain \directives{args} --- */
6870: if( mode >= 1 ) strreplace(sbuff,"_","\\_",0);  /* change all _'s to \_ */
6871: else strreplace(sbuff,"_","\\underline{\\qquad}",0); /*change them to text*/
6872: if(0)strreplace(sbuff,"<","\\textlangle ",0);   /* change all <'s to text */
6873: if(0)strreplace(sbuff,">","\\textrangle ",0);   /* change all >'s to text */
6874: if(0)strreplace(sbuff,"$","\\textdollar ",0);   /* change all $'s to text */
6875: strreplace(sbuff,"$","\\$",0);                  /* change all $'s to \$ */
6876: strreplace(sbuff,"&","\\&",0);                  /* change all &'s to \& */
6877: strreplace(sbuff,"%","\\%",0);                  /* change all %'s to \% */
6878: strreplace(sbuff,"#","\\#",0);                  /* change all #'s to \# */
6879: /*strreplace(sbuff,"~","\\~",0);*/              /* change all ~'s to \~ */
6880: strreplace(sbuff,"^","{\\fs{+2}\\^}",0);        /* change all ^'s to \^ */
6881: end_of_job:
6882:   return ( sbuff );                     /* back with clean copy of s */
6883: } /* --- end-of-function strdetex() --- */
6884: 
6885: 
6886: /* ==========================================================================
6887:  * Function:    strtexchr ( char *string, char *texchr )
6888:  * Purpose:     Find first texchr in string, but texchr must be followed
6889:  *              by non-alpha
6890:  * --------------------------------------------------------------------------
6891:  * Arguments:   string (I)      char * to null-terminated string in which
6892:  *                              first occurrence of delim will be found
6893:  *              texchr (I)      char * to null-terminated string that
6894:  *                              will be searched for
6895:  * --------------------------------------------------------------------------
6896:  * Returns:     ( char * )      ptr to first char of texchr in string
6897:  *                              or NULL if not found or for any error.
6898:  * --------------------------------------------------------------------------
6899:  * Notes:     o texchr should contain its leading \, e.g., "\\left"
6900:  * ======================================================================= */
6901: /* --- entry point --- */
6902: char    *strtexchr ( char *string, char *texchr )
6903: {
6904: /* -------------------------------------------------------------------------
6905: Allocations and Declarations
6906: -------------------------------------------------------------------------- */
6907: char    delim, *ptexchr=(char *)NULL;   /* ptr returned to caller*/
6908: char    *pstring = string;              /* start or continue up search here*/
6909: int     texchrlen = (texchr==NULL?0:strlen(texchr)); /* #chars in texchr */
6910: /* -------------------------------------------------------------------------
6911: locate texchr in string
6912: -------------------------------------------------------------------------- */
6913: if ( string != (char *)NULL             /* check that we got input string */
6914: &&   texchrlen > 0 ) {                  /* and a texchr to search for */
6915:  while ( (ptexchr=strstr(pstring,texchr)) /* look for texchr in string */
6916:  != (char *)NULL )                      /* found it */
6917:   if ( (delim = ptexchr[texchrlen])     /* char immediately after texchr */
6918:   ==   '\000' ) break;                  /* texchr at very end of string */
6919:   else                                  /* if there are chars after texchr */
6920:    if ( isalpha(delim)                  /*texchr is prefix of longer symbol*/
6921:    ||   0 )                             /* other tests to be determined */
6922:     pstring = ptexchr + texchrlen;      /* continue search after texchr */
6923:    else                                 /* passed all tests */
6924:     break; }                            /*so return ptr to texchr to caller*/
6925: return ( ptexchr );                     /* ptr to texchar back to caller */
6926: } /* --- end-of-function strtexchr() --- */
6927: 
6928: 
6929: /* ==========================================================================
6930:  * Function:    findbraces ( char *expression, char *command )
6931:  * Purpose:     If expression!=NULL, finds opening left { preceding command;
6932:  *              if expression==NULL, finds closing right } after command.
6933:  *              For example, to parse out {a+b\over c+d} call findbraces()
6934:  *              twice.
6935:  * --------------------------------------------------------------------------
6936:  * Arguments:   expression (I)  NULL to find closing right } after command,
6937:  *                              or char * to null-terminated string to find
6938:  *                              left opening { preceding command.
6939:  *              command (I)     char * to null-terminated string whose
6940:  *                              first character is usually the \ of \command
6941:  * --------------------------------------------------------------------------
6942:  * Returns:     ( char * )      ptr to either opening { or closing },
6943:  *                              or NULL for any error.
6944:  * --------------------------------------------------------------------------
6945:  * Notes:     o
6946:  * ======================================================================= */
6947: /* --- entry point --- */
6948: char    *findbraces ( char *expression, char *command )
6949: {
6950: /* -------------------------------------------------------------------------
6951: Allocations and Declarations
6952: -------------------------------------------------------------------------- */
6953: int     isopen = (expression==NULL?0:1); /* true to find left opening { */
6954: char    *left="{", *right="}",          /* delims bracketing {x\command y} */
6955:         *delim = (isopen?left:right),   /* delim we want,  { if isopen */
6956:         *match = (isopen?right:left),   /* matching delim, } if isopen */
6957:         *brace = NULL;                  /* ptr to delim returned to caller */
6958: int     inc = (isopen?-1:+1);           /* pointer increment */
6959: int     level = 1;                      /* nesting level, for {{}\command} */
6960: char    *ptr = command;                 /* start search here */
6961: int     setbrace = 1;                   /* true to set {}'s if none found */
6962: /* -------------------------------------------------------------------------
6963: search for left opening { before command, or right closing } after command
6964: -------------------------------------------------------------------------- */
6965: while ( 1 )                             /* search for brace, or until end */
6966:   {
6967:   /* --- next char to check for delim --- */
6968:   ptr += inc;                           /* bump ptr left or right */
6969:   /* --- check for beginning or end of expression --- */
6970:   if ( isopen )                         /* going left, check for beginning */
6971:        { if ( ptr < expression ) break; } /* went before start of string */
6972:   else { if ( *ptr == '\000' ) break; } /* went past end of string */
6973:   /* --- don't check this char if it's escaped --- */
6974:   if ( !isopen || ptr>expression )      /* very first char can't be escaped*/
6975:     if ( isthischar(*(ptr-1),ESCAPE) )  /* escape char precedes current */
6976:       continue;                         /* so don't check this char */
6977:   /* --- check for delim --- */
6978:   if ( isthischar(*ptr,delim) )         /* found delim */
6979:     if ( --level == 0 )                 /* and it's not "internally" nested*/
6980:       { brace = ptr;                    /* set ptr to brace */
6981:         goto end_of_job; }              /* and return it to caller */
6982:   /* --- check for matching delim --- */
6983:   if ( isthischar(*ptr,match) )         /* found matching delim */
6984:     level++;                            /* so bump nesting level */
6985:   } /* --- end-of-while(1) --- */
6986: end_of_job:
6987:   if ( brace == (char *)NULL )          /* open{ or close} not found */
6988:     if ( setbrace )                     /* want to force one at start/end? */
6989:       brace = ptr;                      /* { before expressn, } after cmmnd*/
6990:   return ( brace );                     /*back to caller with delim or NULL*/
6991: } /* --- end-of-function findbraces() --- */
6992: 
6993: 
6994: #if 0  /* --- enhanced version from mathtex below --- */
6995: /* ==========================================================================
6996:  * Function:    strpspn ( char *s, char *reject, char *segment )
6997:  * Purpose:     finds the initial segment of s containing no chars
6998:  *              in reject that are outside (), [] and {} parens, e.g.,
6999:  *                 strpspn("abc(---)def+++","+-",segment) returns
7000:  *                 segment="abc(---)def" and a pointer to the first '+' in s
7001:  *              because the -'s are enclosed in () parens.
7002:  * --------------------------------------------------------------------------
7003:  * Arguments:   s (I)           (char *)pointer to null-terminated string
7004:  *                              whose initial segment is desired
7005:  *              reject (I)      (char *)pointer to null-terminated string
7006:  *                              containing the "reject chars"
7007:  *              segment (O)     (char *)pointer returning null-terminated
7008:  *                              string comprising the initial segment of s
7009:  *                              that contains non-rejected chars outside
7010:  *                              (),[],{} parens, i.e., all the chars up to
7011:  *                              but not including the returned pointer.
7012:  *                              (That's the entire string if no non-rejected
7013:  *                              chars are found.)
7014:  * --------------------------------------------------------------------------
7015:  * Returns:     ( char * )      pointer to first reject-char found in s
7016:  *                              outside parens, or a pointer to the
7017:  *                              terminating '\000' of s if there are
7018:  *                              no reject chars in s outside all () parens.
7019:  * --------------------------------------------------------------------------
7020:  * Notes:     o the return value is _not_ like strcspn()'s
7021:  *            o improperly nested (...[...)...] are not detected,
7022:  *              but are considered "balanced" after the ]
7023:  *            o if reject not found, segment returns the entire string s
7024:  *            o leading/trailing whitespace is trimmed from returned segment
7025:  * ======================================================================= */
7026: /* --- entry point --- */
7027: char    *strpspn ( char *s, char *reject, char *segment )
7028: {
7029: /* -------------------------------------------------------------------------
7030: Allocations and Declarations
7031: -------------------------------------------------------------------------- */
7032: char    *ps = s;                        /* current pointer into s */
7033: int     depth = 0;                      /* () paren nesting level */
7034: int     seglen=0, maxseg=2047;          /* segment length, max allowed */
7035: /* -------------------------------------------------------------------------
7036: initialization
7037: -------------------------------------------------------------------------- */
7038: /* --- check arguments --- */
7039: if ( isempty(s) )                       /* no input string supplied */
7040:   goto end_of_job;                      /* no reject chars supplied */
7041: /* -------------------------------------------------------------------------
7042: find first char from s outside () parens (and outside ""'s) and in reject
7043: -------------------------------------------------------------------------- */
7044: while ( *ps != '\000' ) {               /* search till end of input string */
7045:   if ( isthischar(*ps,"([{") ) depth++; /* push another paren */
7046:   if ( isthischar(*ps,")]}") ) depth--; /* or pop another paren */
7047:   if ( depth < 1 ) {                    /* we're outside all parens */
7048:     if ( isempty(reject) ) break;       /* no reject so break immediately */
7049:     if ( isthischar(*ps,reject) ) break; } /* only break on a reject char */
7050:   if ( segment != NULL )                /* caller gave us segment */
7051:     if ( seglen < maxseg )              /* don't overflow segment buffer */
7052:       memcpy(segment+seglen,ps,1);      /* so copy non-reject char */
7053:   seglen += 1;  ps += 1;                /* bump to next char */
7054:   } /* --- end-of-while(*ps!=0) --- */
7055: end_of_job:
7056:   if ( segment != NULL ) {              /* caller gave us segment */
7057:     if ( isempty(reject) ) {            /* no reject char */
7058:       segment[min2(seglen,maxseg)] = *ps;  seglen++; } /*closing )]} to seg*/
7059:     segment[min2(seglen,maxseg)] = '\000'; /* null-terminate the segment */
7060:     trimwhite(segment); }               /* trim leading/trailing whitespace*/
7061:   return ( ps );                        /* back to caller */
7062: } /* --- end-of-function strpspn() --- */
7063: #endif
7064: 
7065: 
7066: /* ==========================================================================
7067:  * Function:    isstrstr ( char *string, char *snippets, int iscase )
7068:  * Purpose:     determine whether any substring of 'string'
7069:  *              matches any of the comma-separated list of 'snippets',
7070:  *              ignoring case if iscase=0.
7071:  * --------------------------------------------------------------------------
7072:  * Arguments:   string (I)      char * containing null-terminated
7073:  *                              string that will be searched for
7074:  *                              any one of the specified snippets
7075:  *              snippets (I)    char * containing null-terminated,
7076:  *                              comma-separated list of snippets
7077:  *                              to be searched for in string
7078:  *              iscase (I)      int containing 0 for case-insensitive
7079:  *                              comparisons, or 1 for case-sensitive
7080:  * --------------------------------------------------------------------------
7081:  * Returns:     ( int )         1 if any snippet is a substring of
7082:  *                              string, 0 if not
7083:  * --------------------------------------------------------------------------
7084:  * Notes:     o
7085:  * ======================================================================= */
7086: /* --- entry point --- */
7087: int     isstrstr ( char *string, char *snippets, int iscase )
7088: {
7089: /* -------------------------------------------------------------------------
7090: Allocations and Declarations
7091: -------------------------------------------------------------------------- */
7092: int     status = 0;                     /*1 if any snippet found in string*/
7093: char    snip[256], *snipptr = snippets, /* munge through each snippet */
7094:         delim = ',', *delimptr = NULL;  /* separated by delim's */
7095: char    stringcp[4096], *cp = stringcp; /*maybe lowercased copy of string*/
7096: /* -------------------------------------------------------------------------
7097: initialization
7098: -------------------------------------------------------------------------- */
7099: /* --- arg check --- */
7100: if ( string==NULL || snippets==NULL ) goto end_of_job; /* missing arg */
7101: if ( *string=='\000' || *snippets=='\000' ) goto end_of_job; /* empty arg */
7102: /* --- copy string and lowercase it if case-insensitive --- */
7103: strninit(stringcp,string,4064);         /* local copy of string */
7104: if ( !iscase )                          /* want case-insensitive compares */
7105:   for ( cp=stringcp; *cp != '\000'; cp++ ) /* so for each string char */
7106:     if ( isupper(*cp) ) *cp = tolower(*cp); /*lowercase any uppercase chars*/
7107: /* -------------------------------------------------------------------------
7108: extract each snippet and see if it's a substring of string
7109: -------------------------------------------------------------------------- */
7110: while ( snipptr != NULL )               /* while we still have snippets */
7111:   {
7112:   /* --- extract next snippet --- */
7113:   if ( (delimptr = strchr(snipptr,delim)) /* locate next comma delim */
7114:   ==   NULL )                           /*not found following last snippet*/
7115:     { strninit(snip,snipptr,255);       /* local copy of last snippet */
7116:       snipptr = NULL; }                 /* signal end-of-string */
7117:   else                                  /* snippet ends just before delim */
7118:     { int sniplen = (int)(delimptr-snipptr) - 1;  /* #chars in snippet */
7119:       memcpy(snip,snipptr,sniplen);     /* local copy of snippet chars */
7120:       snip[sniplen] = '\000';           /* null-terminated snippet */
7121:       snipptr = delimptr + 1; }         /* next snippet starts after delim */
7122:   /* --- lowercase snippet if case-insensitive --- */
7123:   if ( !iscase )                        /* want case-insensitive compares */
7124:     for ( cp=snip; *cp != '\000'; cp++ ) /* so for each snippet char */
7125:       if ( isupper(*cp) ) *cp=tolower(*cp); /*lowercase any uppercase chars*/
7126:   /* --- check if snippet in string --- */
7127:   if ( strstr(stringcp,snip) != NULL )  /* found snippet in string */
7128:     { status = 1;                       /* so reset return status */
7129:       break; }                          /* no need to check any further */
7130:   } /* --- end-of-while(*snipptr!=0) --- */
7131: end_of_job: return ( status );          /*1 if snippet found in list, else 0*/
7132: } /* --- end-of-function isstrstr() --- */
7133: 
7134: 
7135: /* ==========================================================================
7136:  * Function:    isnumeric ( s )
7137:  * Purpose:     determine if s is an integer
7138:  * --------------------------------------------------------------------------
7139:  * Arguments:   s (I)           (char *)pointer to null-terminated string
7140:  *                              that's checked for a leading + or -
7141:  *                              followed by digits
7142:  * --------------------------------------------------------------------------
7143:  * Returns:     ( int )         1 if s is numeric, 0 if it is not
7144:  * --------------------------------------------------------------------------
7145:  * Notes:     o
7146:  * ======================================================================= */
7147: /* --- entry point --- */
7148: int     isnumeric ( char *s )
7149: {
7150: /* -------------------------------------------------------------------------
7151: determine whether s is an integer
7152: -------------------------------------------------------------------------- */
7153: int     status = 0;                     /* return 0 if not numeric, 1 if is*/
7154: char    *p = s;                         /* pointer into s */
7155: if ( isempty(s) ) goto end_of_job;      /* missing arg or empty string */
7156: skipwhite(p);                           /*check for leading +or- after space*/
7157: if ( *p=='+' || *p=='-' ) p++;          /* skip leading + or - */
7158: for ( ; *p != '\000'; p++ ) {           /* check rest of s for digits */
7159:   if ( isdigit(*p) ) continue;          /* still got uninterrupted digits */
7160:   if ( !isthischar(*p,WHITESPACE) ) goto end_of_job; /* non-numeric char */
7161:   skipwhite(p);                         /* skip all subsequent whitespace */
7162:   if ( *p == '\000' ) break;            /* trailing whitespace okay */
7163:   goto end_of_job;                      /* embedded whitespace non-numeric */
7164:   } /* --- end-of-for(*p) --- */
7165: status = 1;                             /* numeric after checks succeeded */
7166: end_of_job:
7167:   return ( status );                    /*back to caller with 1=string, 0=no*/
7168: } /* --- end-of-function isnumeric() --- */
7169: 
7170: 
7171: /* ==========================================================================
7172:  * Function:    evalterm ( STORE *store, char *term )
7173:  * Purpose:     evaluates a term
7174:  * --------------------------------------------------------------------------
7175:  * Arguments:   store (I/O)     STORE * containing environment
7176:  *                              in which term is to be evaluated
7177:  *              term (I)        char * containing null-terminated string
7178:  *                              with a term like "3" or "a" or "a+3"
7179:  *                              whose value is to be determined
7180:  * --------------------------------------------------------------------------
7181:  * Returns:     ( int )         value of term,
7182:  *                              or NOVALUE for any error
7183:  * --------------------------------------------------------------------------
7184:  * Notes:     o Also evaluates index?a:b:c:etc, returning a if index<=0,
7185:  *              b if index=1, etc, and the last value if index is too large.
7186:  *              Each a:b:c:etc can be another expression, including another
7187:  *              (index?a:b:c:etc) which must be enclosed in parentheses.
7188:  * ======================================================================= */
7189: /* --- entry point --- */
7190: int     evalterm ( STORE *store, char *term )
7191: {
7192: /* -------------------------------------------------------------------------
7193: Allocations and Declarations
7194: -------------------------------------------------------------------------- */
7195: int     termval = 0;                    /* term value returned to caller */
7196: char    token[2048] = "\000",           /* copy term */
7197:         *delim = NULL;                  /* delim '(' or '?' in token */
7198: /*int   evalwff(),*/                    /* recurse to evaluate terms */
7199: /*      evalfunc();*/                   /* evaluate function(arg1,arg2,...)*/
7200: char    *strpspn();                     /* span delims */
7201: int     getstore();                     /* lookup variables */
7202: int     isnumeric();                    /* numeric=constant, else variable */
7203: static  int evaltermdepth = 0;          /* recursion depth */
7204: int     novalue = (-89123456);          /* dummy (for now) error signal */
7205: /* -------------------------------------------------------------------------
7206: Initialization
7207: -------------------------------------------------------------------------- */
7208: if ( ++evaltermdepth > 99 ) goto end_of_job; /*probably recursing forever*/
7209: if ( store==NULL || isempty(term) ) goto end_of_job; /*check for missing arg*/
7210: skipwhite(term);                        /* skip any leading whitespace */
7211: /* -------------------------------------------------------------------------
7212: First look for conditional of the form term?term:term:...
7213: -------------------------------------------------------------------------- */
7214: /* ---left-hand part of conditional is chars preceding "?" outside ()'s--- */
7215: delim = strpspn(term,"?",token);        /* chars preceding ? outside () */
7216: if ( *delim != '\000' ) {               /* found conditional expression */
7217:   int ncolons = 0;                      /* #colons we've found so far */
7218:   if ( *token != '\000' )               /* evaluate "index" value on left */
7219:     if ( (termval=evalterm(store,token)) /* evaluate left-hand term */
7220:     == novalue ) goto end_of_job;       /* return error if failed */
7221:   while ( *delim != '\000' ) {          /* still have chars in term */
7222:     delim++; *token='\000';             /* initialize for next "value:" */
7223:     if ( *delim == '\000' ) break;      /* no more values */
7224:     delim = strpspn(delim,":",token);   /* chars preceding : outside () */
7225:     if ( ncolons++ >= termval ) break;  /* have corresponding term */
7226:     } /* --- end-of-while(*delim!='\000')) --- */
7227:   if ( *token != '\000' )               /* have x:x:value:x:x on right */
7228:     termval=evalterm(store,token);      /* so evaluate it */
7229:   goto end_of_job;                      /* return result to caller */
7230:   } /* --- end-of-if(*delim!='\000')) --- */
7231: /* -------------------------------------------------------------------------
7232: evaluate a+b recursively
7233: -------------------------------------------------------------------------- */
7234: /* --- left-hand part of term is chars preceding "/+-*%" outside ()'s --- */
7235: term = strpspn(term,"/+-*%",token);     /* chars preceding /+-*% outside ()*/
7236: /* --- evaluate a+b, a-b, etc --- */
7237: if ( *term != '\000' ) {                /* found arithmetic operation */
7238:   int leftval=0, rightval=0;            /* init leftval for unary +a or -a */
7239:   if ( *token != '\000' )               /* or eval for binary a+b or a-b */
7240:     if ( (leftval=evalterm(store,token)) /* evaluate left-hand term */
7241:     == novalue ) goto end_of_job;       /* return error if failed */
7242:   if ( (rightval=evalterm(store,term+1)) /* evaluate right-hand term */
7243:   == novalue ) goto end_of_job;         /* return error if failed */
7244:   switch ( *term ) {                    /* perform requested arithmetic */
7245:     default: break;                     /* internal error */
7246:     case '+': termval = leftval+rightval;  break;  /* addition */
7247:     case '-': termval = leftval-rightval;  break;  /* subtraction */
7248:     case '*': termval = leftval*rightval;  break;  /* multiplication */
7249:     case '/': if ( rightval != 0 )      /* guard against divide by zero */
7250:                 termval = leftval/rightval;  break; /* integer division */
7251:     case '%': if ( rightval != 0 )      /* guard against divide by zero */
7252:                 termval = leftval%rightval;  break; /*left modulo right */
7253:     } /* --- end-of-switch(*relation) --- */
7254:   goto end_of_job;                      /* return result to caller */
7255:   } /* --- end-of-if(*term!='\000')) --- */
7256: /* -------------------------------------------------------------------------
7257: check for parenthesized expression or term of the form function(arg1,arg2,...)
7258: -------------------------------------------------------------------------- */
7259: if ( (delim = strchr(token,'(')) != NULL ) { /* token contains a ( */
7260:   /* --- strip trailing paren (if there hopefully is one) --- */
7261:   int  toklen = strlen(token);          /* total #chars in token */
7262:   if ( token[toklen-1] == ')' )         /* found matching ) at end of token*/
7263:     token[--toklen] = '\000';           /* remove trailing ) */
7264:   /* --- handle parenthesized subexpression --- */
7265:   if ( *token == '(' ) {                /* have parenthesized expression */
7266:     strsqueeze(token,1);                /* so squeeze out leading ( */
7267:     /* --- evaluate edited term --- */
7268:     trimwhite(token);                   /* trim leading/trailing whitespace*/
7269:     termval = evalterm(store,token); }  /* evaluate token recursively */
7270:   /* --- handle function(arg1,arg2,...) --- */
7271:   else {                                /* have function(arg1,arg2,...) */
7272:     *delim = '\000';                    /* separate function name and args */
7273:     /*termval = evalfunc(store,token,delim+1);*/ } /* evaluate function */
7274:   goto end_of_job; }                    /* return result to caller */
7275: /* -------------------------------------------------------------------------
7276: evaluate constants directly, or recursively evaluate variables, etc
7277: -------------------------------------------------------------------------- */
7278: if ( *token != '\000' ) {               /* empty string */
7279:   if ( isnumeric(token) )               /* have a constant */
7280:     termval = atoi(token);              /* convert ascii-to-int */
7281:   else {                                /* variable or "stored proposition"*/
7282:     termval = getstore(store,token); }  /* look up token */
7283:   } /* --- end-of-if(*token!=0) --- */
7284: /* -------------------------------------------------------------------------
7285: back to caller with truth value of proposition
7286: -------------------------------------------------------------------------- */
7287: end_of_job:
7288:   /* --- back to caller --- */
7289:   if ( evaltermdepth > 0 ) evaltermdepth--;  /* pop recursion depth */
7290:   return ( termval );                   /* back to caller with value */
7291: } /* --- end-of-function evalterm() --- */
7292: 
7293: 
7294: /* ==========================================================================
7295:  * Function:    getstore ( store, identifier )
7296:  * Purpose:     finds identifier in store and returns corresponding value
7297:  * --------------------------------------------------------------------------
7298:  * Arguments:   store (I)       (STORE *)pointer to store containing
7299:  *                              the desired identifier
7300:  *              identifier (I)  (char *)pointer to null-terminated string
7301:  *                              containing the identifier whose value
7302:  *                              is to be returned
7303:  * --------------------------------------------------------------------------
7304:  * Returns:     ( int )         identifier's corresponding value,
7305:  *                              or 0 if identifier not found (or any error)
7306:  * --------------------------------------------------------------------------
7307:  * Notes:     o
7308:  * ======================================================================= */
7309: /* --- entry point --- */
7310: int     getstore ( STORE *store, char *identifier )
7311: {
7312: /* -------------------------------------------------------------------------
7313: Allocations and Declarations
7314: -------------------------------------------------------------------------- */
7315: int     value = 0;              /* store[istore].value for identifier */
7316: int     istore=0;               /* store[] index containing identifier */
7317: char    seek[512], hide[512];   /* identifier arg, identifier in store */
7318: /* --- first check args --- */
7319: if ( store==NULL || isempty(identifier)) goto end_of_job; /* missing arg */
7320: strninit(seek,identifier,500);  /* local copy of caller's identifier */
7321: trimwhite(seek);                /* remove leading/trailing whitespace */
7322: /* --- loop over store --- */
7323: for ( istore=0; istore<MAXSTORE; istore++ ) { /* until end-of-table */
7324:   char *idstore = store[istore].identifier; /* ptr to identifier in store */
7325:   if ( isempty(idstore) )       /* empty id signals eot */
7326:     break;                      /* de-reference any default/error value */
7327:   strninit(hide,idstore,500);   /* local copy of store[] identifier */
7328:   trimwhite(hide);              /* remove leading/trailing whitespace */
7329:   if ( !strcmp(hide,seek) )     /* found match */
7330:     break;                      /* de-reference corresponding value */
7331:   } /* --- end-of-for(istore) --- */
7332: if ( store[istore].value != NULL ) /* address of int supplied */
7333:   value = *(store[istore].value);  /* return de-referenced int */
7334: end_of_job:
7335:   return ( value );                     /* store->values[istore] or NULL */
7336: } /* --- end-of-function getstore() --- */
7337: 
7338: 
7339: /* ==========================================================================
7340:  * Function:    getdirective(string, directive, iscase, isvalid, nargs, args)
7341:  * Purpose:     Locates the first \directive{arg1}...{nargs} in string,
7342:  *              returns arg1...nargs in args[],
7343:  *              and removes \directive and its args from string.
7344:  * --------------------------------------------------------------------------
7345:  * Arguments:   string (I/0)    char * to null-terminated string from which
7346:  *                              the first occurrence of \directive will be
7347:  *                              interpreted and removed
7348:  *              directive (I)   char * to null-terminated string containing
7349:  *                              the \directive to be interpreted in string
7350:  *              iscase (I)      int containing 1 if match of \directive
7351:  *                              in string should be case-sensitive,
7352:  *                              or 0 if match is case-insensitive.
7353:  *              isvalid (I)     int containing validity check option:
7354:  *                              0=no checks, 1=must be numeric
7355:  *              nargs (I)       int containing (maximum) number of
7356:  *                              {args} following \directive, or 0 if none.
7357:  *              args (O)        void * interpreted as (char *) if nargs=1
7358:  *                              to return the one and only arg,
7359:  *                              or interpreted as (char **) if nargs>1
7360:  *                              to array of returned arg strings
7361:  * --------------------------------------------------------------------------
7362:  * Returns:     ( char * )      ptr to first char after removed \directive, or
7363:  *                              NULL if \directive not found, or any error.
7364:  * --------------------------------------------------------------------------
7365:  * Notes:     o If optional [arg]'s are found, they're stored in the global
7366:  *              optionalargs[] buffer, and the noptional counter is bumped.
7367:  *            o set global argformat's decimal digits for each arg,
7368:  *              e.g., 1357... means 1 for 1st arg, 3 for 2nd, 5 for 3rd, etc.
7369:  *              0 for an arg is the default format (i.e., argformat=0),
7370:  *              and means it's formatted as a LaTeX {arg} or [arg].
7371:  *              1 for an arg means arg terminated by first non-alpha char
7372:  *              2 means arg terminated by {   (e.g., as for /def)
7373:  *              8 means arg terminated by first whitespace char
7374:  * ======================================================================= */
7375: /* --- entry point --- */
7376: char    *getdirective ( char *string, char *directive,
7377:         int iscase, int isvalid, int nargs, void *args )
7378: {
7379: /* -------------------------------------------------------------------------
7380: Allocations and Declarations
7381: -------------------------------------------------------------------------- */
7382: int     iarg = (-1);                    /* init to signal error */
7383: char    *pfirst = NULL,                 /* ptr to 1st char of directive */
7384:         *plast = NULL,                  /* ptr past last char of last arg */
7385:         *plbrace=NULL, *prbrace=NULL;   /* ptr to left,right brace of arg */
7386: int     fldlen = 0;                     /* #chars between { and } delims */
7387: char    argfld[512];                    /* {arg} characters */
7388: int     nfmt=0, /*isnegfmt=0,*/         /* {arg} format */
7389:         argfmt[9]={0,0,0,0,0,0,0,0,0};  /* argformat digits */
7390: int     gotargs = (args==NULL?0:1);     /* true if args array supplied */
7391: int     isdalpha = 1;                   /* true if directive ends with alpha*/
7392: char    *strpspn(char *s,char *reject,char *segment); /*non-() not in rej*/
7393: #if 0 /* --- declared globally above --- */
7394: /* ---
7395:  * mathtex global variables (not used by mimetex)
7396:  * ------------------------------------------------- */
7397: int     noptional = 0;
7398: char    optionalargs[10][32];
7399: int     argformat = 0;
7400: int     optionalpos = 0;
7401: #endif
7402: /* -------------------------------------------------------------------------
7403: Find first \directive in string
7404: -------------------------------------------------------------------------- */
7405: noptional = 0;                          /* no optional [args] yet */
7406: for ( iarg=0; iarg<8; iarg++ )          /* for each one... */
7407:   *optionalargs[iarg] = '\000';         /* re-init optional [arg] buffer */
7408: if ( argformat != 0 ) {                 /* have argformat */
7409:   int   myfmt = argformat;              /* local copy */
7410:   if ( myfmt < 0 ) { /*isnegfmt=1;*/ myfmt=(-myfmt); } /* check sign */
7411:   while ( myfmt>0 && nfmt<9 ) {         /* have more format digits */
7412:     argfmt[nfmt] = myfmt%10;            /* store low-order decimal digit */
7413:     myfmt /= 10;                        /* and shift it out */
7414:     nfmt++; }                           /* count another format digit */
7415:   } /* --- end-of-if(argformat!=0) --- */
7416: if ( isempty(directive) ) goto end_of_job; /* no input \directive given */
7417: if ( !isalpha((int)(directive[strlen(directive)-1])) )isdalpha=0;/*not alpha*/
7418: pfirst = string;                        /* start at beginning of string */
7419: while ( 1 ) {                           /* until we find \directive */
7420:   if ( !isempty(pfirst) )               /* still have string from caller */
7421:     pfirst =                            /* ptr to 1st char of directive */
7422:      (iscase>0? strstr(pfirst,directive): /* case-sensistive match */
7423:       strcasestr(pfirst,directive));    /* case-insensistive match */
7424:   if ( isempty(pfirst) ) {              /* \directive not found in string */
7425:     pfirst = NULL;                      /* signal \directive not found */
7426:     goto end_of_job; }                  /* quit, signalling error to caller*/
7427:   plast = pfirst + strlen(directive);   /*ptr to fist char past directive*/
7428:   if ( !isdalpha || !isalpha((int)(*plast)) ) break; /* found \directive */
7429:   pfirst = plast;                       /* keep looking */
7430:   plast = NULL;                         /* reset plast */
7431:   } /* --- end-of-while(1) --- */
7432: if ( nargs < 0 ) {                      /* optional [arg] may be present */
7433:   nargs = -nargs;                       /* flip sign back to positive */
7434:   /*noptional = 1;*/ }                  /* and set optional flag */
7435: /* -------------------------------------------------------------------------
7436: Get arguments
7437: -------------------------------------------------------------------------- */
7438: iarg = 0;                               /* no args yet */
7439: if ( nargs > 0 )                        /* \directive has {args} */
7440:   while ( iarg < nargs+noptional ) {    /* get each arg */
7441:     int karg = iarg-noptional;          /* non-optional arg index */
7442:     int kfmt = (nfmt<=karg?0:argfmt[nfmt-karg-1]); /* arg format digit */
7443:     /* --- find left { and right } arg delimiters --- */
7444:     plbrace = plast;                    /*ptr to fist char past previous arg*/
7445:     skipwhite(plbrace);                 /* push it to first non-white char */
7446:     if ( isempty(plbrace) ) break;      /* reached end-of-string */
7447:     /* --- check LaTeX for single-char arg or {arg} or optional [arg] --- */
7448:     if ( kfmt == 0 ) {                  /* interpret LaTeX {arg} format */
7449:      if ( !isthischar(*plbrace,(iarg==optionalpos+noptional?"{[":"{")) ) {
7450:       /* --- single char argument --- */
7451:       plast = plbrace + 1;              /* first char after single-char arg*/
7452:       argfld[0] = *plbrace;             /* arg field is this one char */
7453:       argfld[1] = '\000'; }             /* null-terminate field */
7454:      else {                             /* have {arg} or optional [arg] */
7455:       /* note: to use for validation, need robust {} match like strpspn() */
7456:       if ( (prbrace = strchr(plbrace,(*plbrace=='{'?'}':']'))) /*right }or]*/
7457:       ==   NULL ) break;                /*and no more args if no right brace*/
7458:       if ( 1 )                          /* true to use strpspn() */
7459:         prbrace = strpspn(plbrace,NULL,NULL); /* push to matching } or ] */
7460:       plast = prbrace + 1;              /* first char after right brace */
7461:       /* --- extract arg field between { and } delimiters --- */
7462:       fldlen = (int)(prbrace-plbrace) - 1; /* #chars between { and } delims*/
7463:       if ( fldlen >= 256 ) fldlen=255;  /* don't overflow argfld[] buffer */
7464:       if ( fldlen > 0 )                 /* have chars in field */
7465:         memcpy(argfld,plbrace+1,fldlen); /*copy field chars to local buffer*/
7466:       argfld[fldlen] = '\000';          /* and null-terminate field */
7467:       trimwhite(argfld);                /* trim whitespace from argfld */
7468:       } /* --- end-of-if/else(!isthischar(*plbrace,...)) --- */
7469:      } /* --- end-of-if(kfmt==0) --- */
7470:     /* --- check plain TeX for arg terminated by whitespace --- */
7471:     if ( kfmt != 0 ) {                  /* interpret plain TeX arg format */
7472:      char *parg = NULL;                 /* ptr into arg, used as per kfmt */
7473:      plast = plbrace;                   /* start at first char of arg */
7474:      if ( *plast == '\\' ) plast++;     /* skip leading \command backslash */
7475:      /* --- interpret arg according to its format --- */
7476:      switch ( kfmt ) {
7477:        case 1:
7478:        default: skipcommand(plast); break; /* push ptr to non-alpha char */
7479:        case 2: parg = strchr(plast,'{'); /* next arg always starts with { */
7480:         if ( parg != NULL ) plast=parg; else plast++; /* up to { or 1 char */
7481:         break;
7482:        case 8: findwhite(plast); break; /*ptr to whitespace after last char*/
7483:        } /* --- end-of-switch(kfmt) --- */
7484:      /* --- extract arg field --- */
7485:      fldlen = (int)(plast-plbrace);     /* #chars between in field */
7486:      if ( fldlen >= 256 ) fldlen=255;   /* don't overflow argfld[] buffer */
7487:      if ( fldlen > 0 )                  /* have chars in field */
7488:        memcpy(argfld,plbrace,fldlen);   /*copy field chars to local buffer*/
7489:      argfld[fldlen] = '\000';           /* and null-terminate field */
7490:      if ( 1 ) { trimwhite(argfld); }    /* trim whitespace from argfld */
7491:      } /* --- end-of-if(kfmt!=0) --- */
7492:     if ( isvalid != 0 ) {               /* argfld[] validity check desired */
7493:      if ( isvalid == 1 ) {              /* numeric check wanted */
7494:        int validlen = strspn(argfld," +-.0123456789"); /*very simple check*/
7495:        argfld[validlen] = '\000'; }     /* truncate invalid chars */
7496:      } /* --- end-of-if(isvalid!=0) --- */
7497:     /* --- store argument field in caller's array --- */
7498:     if ( kfmt==0 && *plbrace=='[' ) {   /*store [arg] as optionalarg instead*/
7499:      if ( noptional < 8 ) {             /* don't overflow our buffer */
7500:        strninit(optionalargs[noptional],argfld,254); } /*copy to optionalarg*/
7501:      noptional++; }                     /* count another optional [arg] */
7502:     else                                /*{args} returned in caller's array*/
7503:      if ( gotargs ) {                   /*caller supplied address or array*/
7504:       if ( nargs < 2 )                  /*just one arg, so it's an address*/
7505:         strcpy((char *)args,argfld);    /* so copy arg field there */
7506:       else {                            /* >1 arg, so it's a ptr array */
7507:         char *argptr = ((char **)args)[karg]; /* arg ptr in array of ptrs */
7508:         if ( argptr != NULL )           /* array has iarg-th address */
7509:           strcpy(argptr,argfld);        /* so copy arg field there */
7510:         else gotargs = 0; } }           /* no more addresses in array */
7511:     /* --- completed this arg --- */
7512:     iarg++;                             /* bump arg count */
7513:     } /* --- end-of-while(iarg<nargs) --- */
7514: /* -------------------------------------------------------------------------
7515: Back to caller
7516: -------------------------------------------------------------------------- */
7517: end_of_job:
7518:   if ( 1 ) argformat = 0;               /* always/never reset global arg */
7519:   if ( 1 ) optionalpos = 0;             /* always/never reset global arg */
7520:   if ( pfirst!=NULL && plast!=NULL )    /* have directive field delims */
7521:     {strsqueeze(pfirst,((int)(plast-pfirst)));} /* squeeze out directive */
7522:   return ( pfirst );                    /* ptr to 1st char after directive */
7523: } /* --- end-of-function getdirective() --- */
7524: 
7525: 
7526: /* ==========================================================================
7527:  * Function:    strpspn ( char *s, char *reject, char *segment )
7528:  * Purpose:     finds the initial segment of s containing no chars
7529:  *              in reject that are outside (), [] and {} parens, e.g.,
7530:  *                 strpspn("abc(---)def+++","+-",segment) returns
7531:  *                 segment="abc(---)def" and a pointer to the first '+' in s
7532:  *              because the -'s are enclosed in () parens.
7533:  * --------------------------------------------------------------------------
7534:  * Arguments:   s (I)           (char *)pointer to null-terminated string
7535:  *                              whose initial segment is desired
7536:  *              reject (I)      (char *)pointer to null-terminated string
7537:  *                              containing the "reject chars"
7538:  *                              If reject contains a " or a ', then the
7539:  *                              " or ' isn't itself a reject char,
7540:  *                              but other reject chars within quoted
7541:  *                              strings (or substrings of s) are spanned.
7542:  *              segment (O)     (char *)pointer returning null-terminated
7543:  *                              string comprising the initial segment of s
7544:  *                              that contains non-rejected chars outside
7545:  *                              (),[],{} parens, i.e., all the chars up to
7546:  *                              but not including the returned pointer.
7547:  *                              (That's the entire string if no non-rejected
7548:  *                              chars are found.)
7549:  * --------------------------------------------------------------------------
7550:  * Returns:     ( char * )      pointer to first reject-char found in s
7551:  *                              outside parens, or a pointer to the
7552:  *                              terminating '\000' of s if there are
7553:  *                              no reject chars in s outside all () parens.
7554:  *                              But if reject is empty, returns pointer
7555:  *                              to matching )]} outside all parens.
7556:  * --------------------------------------------------------------------------
7557:  * Notes:     o the return value is _not_ like strcspn()'s
7558:  *            o improperly nested (...[...)...] are not detected,
7559:  *              but are considered "balanced" after the ]
7560:  *            o if reject not found, segment returns the entire string s
7561:  *            o but if reject is empty, returns segment up to and including
7562:  *              matching )]}
7563:  *            o leading/trailing whitespace is trimmed from returned segment
7564:  * ======================================================================= */
7565: /* --- entry point --- */
7566: char    *strpspn ( char *s, char *reject, char *segment )
7567: {
7568: /* -------------------------------------------------------------------------
7569: Allocations and Declarations
7570: -------------------------------------------------------------------------- */
7571: char    *ps = s;                        /* current pointer into s */
7572: char    *strqspn(char *s,char *q,int isunescape); /*span quoted string*/
7573: char    qreject[256]="\000", *pq=qreject, *pr=reject; /*find "or' in reject*/
7574: int     isqspan = 0;                    /* true to span quoted strings */
7575: int     depth = 0;                      /* () paren nesting level */
7576: int     seglen=0, maxseg=2047;          /* segment length, max allowed */
7577: int     isescaped=0, checkescapes=1;    /* signals escaped chars */
7578: /* -------------------------------------------------------------------------
7579: initialization
7580: -------------------------------------------------------------------------- */
7581: /* --- check arguments --- */
7582: if ( isempty(s)                         /* no input string supplied */
7583: /*||   isempty(reject)*/ ) goto end_of_job; /* no reject chars supplied */
7584: /* --- set up qreject w/o quotes --- */
7585: if ( !isempty(reject) )                 /* have reject string from caller */
7586:   while ( *pr != '\000' ) {             /* until end-of-reject string */
7587:     if ( !isthischar(*pr,"\"\'") )      /* not a " or ' */
7588:       *pq++ = *pr;                      /* copy actual reject char */
7589:     else isqspan = 1;                   /* span rejects in quoted strings */
7590:     pr++; }                             /* next reject char from caller */
7591: *pq = '\000';                           /* null-terminate qreject */
7592: /* -------------------------------------------------------------------------
7593: find first char from s outside () parens (and outside ""'s) and in reject
7594: -------------------------------------------------------------------------- */
7595: while ( *ps != '\000' ) {               /* search till end of input string */
7596:   int spanlen = 1;                      /*span 1 non-reject, non-quoted char*/
7597:   if ( !isescaped ) {                   /* ignore escaped \(,\[,\{,\),\],\}*/
7598:     if ( isthischar(*ps,"([{") ) depth++;   /* push another paren */
7599:     if ( isthischar(*ps,")]}") ) depth--; } /* or pop another paren */
7600:   if ( depth < 1 ) {                    /* we're outside all parens */
7601:     if ( isqspan )                      /* span rejects in quoted strings */
7602:       if ( isthischar(*ps,"\"\'") ) {   /* and we're at opening quote */
7603:         pq = strqspn(ps,NULL,0);        /* locate matching closing quote */
7604:         if ( pq != ps )                 /* detected start of quoted string */
7605:          if ( *pq == *ps )              /* and found closing quote */
7606:           spanlen = ((int)(pq-ps)) + 1; } /* span the entire quoted string */
7607:     if ( isempty(qreject) ) break;      /* no reject so break immediately */
7608:     if ( isthischar(*ps,qreject) ) break; } /* only break on a reject char */
7609:   if ( checkescapes )                   /* if checking escape sequences */
7610:     isescaped = (*ps=='\\'?1:0);        /* reset isescaped signal */
7611:   if ( segment != NULL ) {              /* caller gave us segment */
7612:     int copylen = min2(spanlen,maxseg-seglen); /* don't overflow segment */
7613:     if ( copylen > 0 )                  /* have room in segment buffer */
7614:       memcpy(segment+seglen,ps,copylen); } /* so copy non-reject chars */
7615:   seglen += spanlen;  ps += spanlen;    /* bump to next char */
7616:   } /* --- end-of-while(*ps!=0) --- */
7617: end_of_job:
7618:   if ( segment != NULL ) {              /* caller gave us segment */
7619:     if ( isempty(qreject) && !isempty(s) ) { /* no reject char */
7620:       segment[min2(seglen,maxseg)] = *ps;  seglen++; } /*closing )]} to seg*/
7621:     segment[min2(seglen,maxseg)] = '\000'; /* null-terminate the segment */
7622:     trimwhite(segment); }               /* trim leading/trailing whitespace*/
7623:   return ( ps );                        /* back to caller */
7624: } /* --- end-of-function strpspn() --- */
7625: 
7626: 
7627: /* ==========================================================================
7628:  * Function:    strqspn ( char *s, char *q, int isunescape )
7629:  * Purpose:     finds matching/closing " or ' in quoted string
7630:  *              that begins with " or ', and optionally changes
7631:  *              escaped quotes to unescaped quotes.
7632:  * --------------------------------------------------------------------------
7633:  * Arguments:   s (I)           (char *)pointer to null-terminated string
7634:  *                              that begins with " or ',
7635:  *              q (O)           (char *)pointer returning null-terminated
7636:  *                              quoted token, with or without outer quotes,
7637:  *                              and with or without escaped inner quotes
7638:  *                              changed to unescaped quotes, depending
7639:  *                              on isunescape.
7640:  *              isunescape (I)  int containing 1 to change \" to " if s
7641:  *                              is "quoted" or change \' to ' if 'quoted',
7642:  *                              or containing 2 to change both \" and \'
7643:  *                              to unescaped quotes.  Other \sequences aren't
7644:  *                              changed.  Note that \\" emits \".
7645:  *                              isunescape=0 makes no changes at all.
7646:  *                              Note: the following not implemented yet --
7647:  *                              If unescape is negative, its abs() is used,
7648:  *                              but outer quotes aren't included in q.
7649:  * --------------------------------------------------------------------------
7650:  * Returns:     ( char * )      pointer to matching/closing " or '
7651:  *                              (or to char after quote if isunescape<0),
7652:  *                              or terminating '\000' if none found,
7653:  *                              or unchanged (same as s) if not quoted string
7654:  * --------------------------------------------------------------------------
7655:  * Notes:     o
7656:  * ======================================================================= */
7657: /* --- entry point --- */
7658: char    *strqspn ( char *s, char *q, int isunescape )
7659: {
7660: /* -------------------------------------------------------------------------
7661: Allocations and Declarations
7662: -------------------------------------------------------------------------- */
7663: char    *ps = s,  *pq = q;              /* current pointer into s, q */
7664: char    quote = '\000';                 /* " or ' quote character */
7665: int     qcopy = (isunescape<0?0:1);     /* true to copy outer quotes */
7666: int     isescaped = 0;                  /* true to signal \escape sequence */
7667: int     maxqlen = 2400;                 /* max length of returned q */
7668: /* -------------------------------------------------------------------------
7669: Initialization
7670: -------------------------------------------------------------------------- */
7671: /* --- check args --- */
7672: if ( s == NULL ) goto end_of_job;       /* no string supplied */
7673: skipwhite(ps);                          /* skip leading whitespace */
7674: if ( *ps == '\000'                      /* string exhausted */
7675: ||   (!isthischar(*ps,"\"\'")) ) {      /* or not a " or ' quoted string */
7676:   ps = s;  goto end_of_job; }           /*signal error/not string to caller*/
7677: if ( isunescape < 0 ) isunescape = (-isunescape); /* flip positive */
7678: /* --- set quote character --- */
7679: quote = *ps;                            /* set quote character */
7680: if ( qcopy && q!=NULL ) *pq++ = quote;  /* and copy it to output token */
7681: /* -------------------------------------------------------------------------
7682: span characters between quotes
7683: -------------------------------------------------------------------------- */
7684: while ( *(++ps) != '\000' ) {           /* end-of-string always terminates */
7685:   /* --- process escaped chars --- */
7686:   if ( isescaped ) {                    /* preceding char was \ */
7687:     if ( *ps != '\\' ) isescaped = 0;   /* reset isescaped flag unless \\ */
7688:     if ( q != NULL ) {                  /* caller wants quoted token */
7689:       if ( isunescape==0                /* don't unescape anything */
7690:       ||   (isunescape==1 && *ps!=quote) /* escaped char not our quote */
7691:       ||   (isunescape==2 && (!isthischar(*ps,"\"\'"))) ) /* not any quote */
7692:         if ( --maxqlen > 0 )            /* so if there's room in token */
7693:           *pq++ = '\\';                 /*keep original \ in returned token*/
7694:       if ( !isescaped )                 /* will have to check 2nd \ in \\ */
7695:         if ( --maxqlen > 0 )            /* if there's room in token */
7696:           *pq++ = *ps; }                /* put escaped char in token */
7697:     continue; }                         /* go on to next char in string */
7698:   /* --- check if next char escaped --- */
7699:   if ( *ps == '\\' ) {                  /* found escape char */
7700:     isescaped=1; continue; }            /*set flag and process escaped char*/
7701:   /* --- check for unescaped closing quote --- */
7702:   if ( *ps == quote ) {                 /* got an unescaped quote */
7703:     if ( qcopy && q!=NULL ) *pq++ = quote; /* copy it to output token */
7704:     if ( 0 && !qcopy ) ps++;            /* return ptr to char after quote */
7705:     goto end_of_job; }                  /* back to caller */
7706:   /* --- process other chars --- */
7707:   if ( q != NULL )                      /* caller want token returned */
7708:     if ( --maxqlen > 0 )                /* and there's still room in token */
7709:       *pq++ = *ps;                      /* put char in  token */
7710:   } /* --- end-of-while(*(++ps)!='\000') --- */
7711: /*ps = NULL;*/  /*pq = q;*/             /* error if no closing quote found */
7712: end_of_job:
7713:   if ( q != NULL ) *pq = '\000';        /* null-terminate returned token */
7714:   return ( ps );                        /* return ptr to " or ', or NULL */
7715: } /* --- end-of-function strqspn() --- */
7716: 
7717: 
7718: /* ==========================================================================
7719:  * Functions:   int  unescape_url ( char *url, int isescape )
7720:  *              char x2c ( char *what )
7721:  * Purpose:     unescape_url replaces 3-character sequences %xx in url
7722:  *                  with the single character represented by hex xx.
7723:  *              x2c returns the single character represented by hex xx
7724:  *                  passed as a 2-character sequence in what.
7725:  * --------------------------------------------------------------------------
7726:  * Arguments:   url (I)         char * containing null-terminated
7727:  *                              string with embedded %xx sequences
7728:  *                              to be converted.
7729:  *              isescape (I)    int containing 1 to _not_ unescape
7730:  *                              \% sequences (0 would be NCSA default)
7731:  *              what (I)        char * whose first 2 characters are
7732:  *                              interpreted as ascii representations
7733:  *                              of hex digits.
7734:  * --------------------------------------------------------------------------
7735:  * Returns:     ( int )         unescape_url always returns 0.
7736:  *              ( char )        x2c returns the single char
7737:  *                              corresponding to hex xx passed in what.
7738:  * --------------------------------------------------------------------------
7739:  * Notes:     o These two functions were taken verbatim from util.c in
7740:  *   ftp://ftp.ncsa.uiuc.edu/Web/httpd/Unix/ncsa_httpd/cgi/ncsa-default.tar.Z
7741:  *            o Not quite "verbatim" -- I added the "isescape logic" 4-Dec-03
7742:  *              so unescape_url() can be safely applied to input which may or
7743:  *              may not have been url-encoded.  (Note: currently, all calls
7744:  *              to unescape_url() pass iescape=0, so it's not used.)
7745:  *            o Added +++'s to blank xlation on 24-Sep-06
7746:  *            o Added ^M,^F,etc to blank xlation 0n 01-Oct-06
7747:  * ======================================================================= */
7748: /* --- entry point --- */
7749: int unescape_url(char *url, int isescape) {
7750:     int x=0,y=0,prevescape=0,gotescape=0;
7751:     int xlateplus = (isplusblank==1?1:0); /* true to xlate plus to blank */
7752:     int strreplace();                   /* replace + with blank, if needed */
7753:     char x2c();
7754:     static char *hex="0123456789ABCDEFabcdef";
7755:     /* ---
7756:      * xlate ctrl chars to blanks
7757:      * -------------------------- */
7758:     if ( 1 ) {                          /* xlate ctrl chars to blanks */
7759:       char *ctrlchars = "\n\t\v\b\r\f\a\015";
7760:       int  seglen = strspn(url,ctrlchars); /*initial segment with ctrlchars*/
7761:       int  urllen = strlen(url);        /* total length of url string */
7762:       /* --- first, entirely remove ctrlchars from beginning and end --- */
7763:       if ( seglen > 0 ) {               /*have ctrlchars at start of string*/
7764:         strsqueeze(url,seglen);         /* squeeze out initial ctrlchars */
7765:         urllen -= seglen; }             /* string is now shorter */
7766:       while ( --urllen >= 0 )           /* now remove ctrlchars from end */
7767:         if ( isthischar(url[urllen],ctrlchars) ) /* ctrlchar at end */
7768:           url[urllen] = '\000';         /* re-terminate string before it */
7769:         else break;                     /* or we're done */
7770:       urllen++;                         /* length of url string */
7771:       /* --- now, replace interior ctrlchars with ~ blanks --- */
7772:       while ( (seglen=strcspn(url,ctrlchars)) < urllen ) /*found a ctrlchar*/
7773:         url[seglen] = '~';              /* replace ctrlchar with ~ */
7774:       } /* --- end-of-if(1) --- */
7775:     /* ---
7776:      * xlate +'s to blanks if requested or if deemed necessary
7777:      * ------------------------------------------------------- */
7778:     if ( isplusblank == (-1) ) {        /*determine whether or not to xlate*/
7779:       char *searchfor[] = { " ","%20", "%2B","%2b", "+++","++",
7780:         "+=+","+-+", NULL };
7781:       int  isearch = 0,                 /* searchfor[] index */
7782:            nfound[11] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; /*#occurrences*/
7783:       /* --- locate occurrences of searchfor[] strings in url --- */
7784:       for ( isearch=0; searchfor[isearch] != NULL; isearch++ ) {
7785:         char *psearch = url;            /* start search at beginning */
7786:         nfound[isearch] = 0;            /* init #occurrences count */
7787:         while ( (psearch=strstr(psearch,searchfor[isearch])) != NULL ) {
7788:           nfound[isearch] += 1;         /* count another occurrence */
7789:           psearch += strlen(searchfor[isearch]); } /*resume search after it*/
7790:         } /* --- end-of-for(isearch) --- */
7791:       /* --- apply some common-sense logic --- */
7792:       if ( nfound[0] + nfound[1] > 0 )  /* we have actual " "s or "%20"s */
7793:         isplusblank = xlateplus = 0;    /* so +++'s aren't blanks */
7794:       if ( nfound[2] + nfound[3] > 0 ) { /* we have "%2B" for +++'s */
7795:         if ( isplusblank != 0 )         /* and haven't disabled xlation */
7796:           isplusblank = xlateplus = 1;  /* so +++'s are blanks */
7797:         else                            /* we have _both_ "%20" and "%2b" */
7798:           xlateplus = 0; }              /* tough call */
7799:       if ( nfound[4] + nfound[5] > 0    /* we have multiple ++'s */
7800:       ||   nfound[6] + nfound[7] > 0 )  /* or we have a +=+ or +-+ */
7801:         if ( isplusblank != 0 )         /* and haven't disabled xlation */
7802:           xlateplus = 1;                /* so xlate +++'s to blanks */
7803:       } /* --- end-of-if(isplusblank==-1) --- */
7804:     if ( xlateplus > 0 ) {              /* want +'s xlated to blanks */
7805:       char *xlateto[] = { ""," "," "," + "," "," "," "," "," " };
7806:       while ( xlateplus > 0 ) {         /* still have +++'s to xlate */
7807:         char plusses[99] = "++++++++++++++++++++"; /* longest +++ string */
7808:         plusses[xlateplus] = '\000';    /* null-terminate +++'s */
7809:         strreplace(url,plusses,xlateto[xlateplus],0); /* xlate +++'s */
7810:         xlateplus--;                    /* next shorter +++ string */
7811:         } /* --- end-of-while(xlateplus>0) --- */
7812:       } /* --- end-of-if(xlateplus) --- */
7813:     isplusblank = 0;                    /* don't iterate this xlation */
7814:     /* ---
7815:      * xlate %nn to corresponding char
7816:      * ------------------------------- */
7817:     for(;url[y];++x,++y) {
7818:         gotescape = prevescape;
7819:         prevescape = (url[x]=='\\');
7820:         if((url[x] = url[y]) == '%')
7821:          if(!isescape || !gotescape)
7822:           if(isthischar(url[y+1],hex)
7823:           && isthischar(url[y+2],hex))
7824:             { url[x] = x2c(&url[y+1]);
7825:               y+=2; }
7826:     }
7827:     url[x] = '\0';
7828:     return 0;
7829: } /* --- end-of-function unescape_url() --- */
7830: /* --- entry point --- */
7831: char x2c(char *what) {
7832:     char digit;
7833:     digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
7834:     digit *= 16;
7835:     digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
7836:     return(digit);
7837: } /* --- end-of-function x2c() --- */
7838: #endif /* PART2 */
7839: 
7840: /* ---
7841:  * PART3
7842:  * ------ */
7843: #if !defined(PARTS) || defined(PART3)
7844: /* ==========================================================================
7845:  * Function:    rasterize ( expression, size )
7846:  * Purpose:     returns subraster corresponding to (a valid LaTeX) expression
7847:  *              at font size
7848:  * --------------------------------------------------------------------------
7849:  * Arguments:   expression (I)  char * to first char of null-terminated
7850:  *                              string containing valid LaTeX expression
7851:  *                              to be rasterized
7852:  *              size (I)        int containing 0-4 default font size
7853:  * --------------------------------------------------------------------------
7854:  * Returns:     ( subraster * ) ptr to subraster corresponding to expression,
7855:  *                              or NULL for any parsing error.
7856:  * --------------------------------------------------------------------------
7857:  * Notes:     o This is mimeTeX's "main" reusable entry point.  Easy to use:
7858:  *              just call it with a LaTeX expression, and get back a bitmap
7859:  *              of that expression.  Then do what you want with the bitmap.
7860:  * ======================================================================= */
7861: /* --- entry point --- */
7862: subraster *rasterize ( char *expression, int size )
7863: {
7864: /* -------------------------------------------------------------------------
7865: Allocations and Declarations
7866: -------------------------------------------------------------------------- */
7867: char    *preamble(), pretext[512];      /* process preamble, if present */
7868: char    chartoken[MAXSUBXSZ+1], *texsubexpr(), /*get subexpression from expr*/
7869:         *subexpr = chartoken;           /* token may be parenthesized expr */
7870: int     isbrace();                      /* check subexpr for braces */
7871: mathchardef *symdef, *get_symdef();     /*get mathchardef struct for symbol*/
7872: int     ligdef, get_ligature();         /*get symtable[] index for ligature*/
7873: int     natoms=0;                       /* #atoms/tokens processed so far */
7874: int     type_raster();                  /* display debugging output */
7875: subraster *rasterize(),                 /* recurse */
7876:         *rastparen(),                   /* handle parenthesized subexpr's */
7877:         *rastlimits();                  /* handle sub/superscripted expr's */
7878: subraster *rastcat(),                   /* concatanate atom subrasters */
7879:         *subrastcpy(),                  /* copy final result if a charaster*/
7880:         *new_subraster();               /* new subraster for isstring mode */
7881: subraster *get_charsubraster(),         /* character subraster */
7882:         *sp=NULL, *prevsp=NULL,         /* raster for current, prev char */
7883:         *expraster = (subraster *)NULL; /* raster returned to caller */
7884: int     delete_subraster();             /* free everything before returning*/
7885: int     family = fontinfo[fontnum].family; /* current font family */
7886: int     isleftscript = 0,               /* true if left-hand term scripted */
7887:         wasscripted = 0,                /* true if preceding token scripted*/
7888:         wasdelimscript = 0;             /* true if preceding delim scripted*/
7889: /*int   pixsz = 1;*/                    /*default #bits per pixel, 1=bitmap*/
7890: char    *strdetex();                    /* detex token for error message */
7891: /* --- global values saved/restored at each recursive iteration --- */
7892: int     wasstring = isstring,           /* initial isstring mode flag */
7893:         wasdisplaystyle = isdisplaystyle, /*initial displaystyle mode flag*/
7894:         oldfontnum = fontnum,           /* initial font family */
7895:         oldfontsize = fontsize,         /* initial fontsize */
7896:         olddisplaysize = displaysize,   /* initial \displaystyle size */
7897:         oldshrinkfactor = shrinkfactor, /* initial shrinkfactor */
7898:         oldsmashmargin = smashmargin,   /* initial smashmargin */
7899:         oldissmashdelta = issmashdelta, /* initial issmashdelta */
7900:         oldisexplicitsmash = isexplicitsmash, /* initial isexplicitsmash */
7901:         oldisscripted = isscripted,     /* initial isscripted */
7902:         *oldworkingparam = workingparam; /* initial working parameter */
7903: subraster *oldworkingbox = workingbox,  /* initial working box */
7904:         *oldleftexpression = leftexpression; /*left half rasterized so far*/
7905: double  oldunitlength = unitlength;     /* initial unitlength */
7906: mathchardef *oldleftsymdef = leftsymdef; /* init oldleftsymdef */
7907: /* -------------------------------------------------------------------------
7908: initialization
7909: -------------------------------------------------------------------------- */
7910: recurlevel++;                           /* wind up one more recursion level*/
7911: leftexpression = NULL;                  /* no leading left half yet */
7912: isreplaceleft = 0;                      /* reset replaceleft flag */
7913: if(1)fraccenterline = NOVALUE;          /* reset \frac baseline signal */
7914: /* shrinkfactor = shrinkfactors[max2(0,min2(size,LARGESTSIZE))];*/ /*set sf*/
7915: shrinkfactor = shrinkfactors[max2(0,min2(size,16))]; /* have 17 sf's */
7916: rastlift = 0;                           /* reset global rastraise() lift */
7917: if ( msgfp!=NULL && msglevel >= 9 ) {   /*display expression for debugging*/
7918:  fprintf(msgfp,
7919:  "rasterize> recursion#%d, size=%d,\n\texpression=\"%s\"\n",
7920:  recurlevel,size,(expression==NULL?"<null>":expression)); fflush(msgfp); }
7921: if ( expression == NULL ) goto end_of_job; /* nothing given to do */
7922: /* -------------------------------------------------------------------------
7923: preocess optional $-terminated preamble preceding expression
7924: -------------------------------------------------------------------------- */
7925: expression = preamble(expression,&size,pretext); /* size may be modified */
7926: if ( *expression == '\000' ) goto end_of_job; /* nothing left to do */
7927: fontsize = size;                        /* start at requested size */
7928: if ( isdisplaystyle == 1 )              /* displaystyle enabled but not set*/
7929:  if ( !ispreambledollars )              /* style fixed by $$...$$'s */
7930:   isdisplaystyle = (fontsize>=displaysize? 2:1); /*force at large fontsize*/
7931: /* -------------------------------------------------------------------------
7932: build up raster one character (or subexpression) at a time
7933: -------------------------------------------------------------------------- */
7934: while ( 1 )
7935:   {
7936:   /* --- kludge for \= cyrillic ligature --- */
7937:   isligature = 0;                       /* no ligature found yet */
7938:   family = fontinfo[fontnum].family;    /* current font family */
7939:   if ( family == CYR10 )                /* may have cyrillic \= ligature */
7940:    if ( (ligdef = get_ligature(expression,family)) /*check for any ligature*/
7941:    >=    0 )                            /* got some ligature */
7942:     if ( memcmp(symtable[ligdef].symbol,"\\=",2) == 0 ) /* starts with \= */
7943:      isligature = 1;                    /* signal \= ligature */
7944:   /* --- get next character/token or subexpression --- */
7945:   subexprptr = expression;              /* ptr within expression to subexpr*/
7946:   expression = texsubexpr(expression,chartoken,0,LEFTBRACES,RIGHTBRACES,1,1);
7947:   subexpr = chartoken;                  /* "local" copy of chartoken ptr */
7948:   leftsymdef = NULL;                    /* no character identified yet */
7949:   sp = NULL;                            /* no subraster yet */
7950:   size = fontsize;                      /* in case reset by \tiny, etc */
7951:   /*isleftscript = isdelimscript;*/     /*was preceding term scripted delim*/
7952:   wasscripted = isscripted;             /* true if preceding token scripted*/
7953:   wasdelimscript = isdelimscript;       /* preceding \right delim scripted */
7954:   if(1)isscripted = 0;                  /* no subscripted expression yet */
7955:   isdelimscript = 0;                    /* reset \right delim scripted flag*/
7956:   /* --- debugging output --- */
7957:   if ( msgfp!=NULL && msglevel >= 9 ) { /* display chartoken for debugging */
7958:    fprintf(msgfp,
7959:    "rasterize> recursion#%d,atom#%d=\"%s\" (isligature=%d,isleftscript=%d)\n",
7960:    recurlevel,natoms+1,chartoken,isligature,isleftscript); fflush(msgfp); }
7961:   if ( expression == NULL               /* no more tokens */
7962:   &&   *subexpr == '\000' ) break;      /* and this token empty */
7963:   if ( *subexpr == '\000' ) break;      /* enough if just this token empty */
7964:   /* --- check for parenthesized subexpression --- */
7965:   if ( isbrace(subexpr,LEFTBRACES,1) )  /* got parenthesized subexpression */
7966:     { if ( (sp=rastparen(&subexpr,size,prevsp)) /* rasterize subexpression */
7967:       ==   NULL )  continue; }          /* flush it if failed to rasterize */
7968:   else /* --- single-character atomic token --- */
7969:    if ( !isthischar(*subexpr,SCRIPTS) ) /* scripts handled below */
7970:     {
7971:     /* --- first check for opening $ in \text{ if $n-m$ even} --- */
7972:     if ( istextmode                     /* we're in \text mode */
7973:     &&   *subexpr=='$' && subexpr[1]=='\000' ) { /* and have an opening $ */
7974:      char *endptr=NULL, mathexpr[MAXSUBXSZ+1]; /* $expression$ in \text{ }*/
7975:      int  exprlen = 0;                  /* length of $expression$ */
7976:      int  textfontnum = fontnum;        /* current text font number */
7977:      /*if ( (endptr=strrchr(expression,'$')) != NULL )*/ /*ptr to closing $*/
7978:      if ( (endptr=strchr(expression,'$')) != NULL ) /* ptr to closing $ */
7979:        exprlen = (int)(endptr-expression); /* #chars preceding closing $ */
7980:      else {                             /* no closing $ found */
7981:        exprlen = strlen(expression);    /* just assume entire expression */
7982:        endptr = expression + (exprlen-1); } /*and push expression to '\000'*/
7983:      exprlen = min2(exprlen,MAXSUBXSZ); /* don't overflow mathexpr[] */
7984:      if ( exprlen > 0 ) {               /* have something between $$ */
7985:        memcpy(mathexpr,expression,exprlen); /*local copy of math expression*/
7986:        mathexpr[exprlen] = '\000';      /* null-terminate it */
7987:        fontnum = 0;                     /* set math mode */
7988:        sp = rasterize(mathexpr,size);   /* and rasterize $expression$ */
7989:        fontnum = textfontnum; }         /* set back to text mode */
7990:      expression = endptr+1;             /* push expression past closing $ */
7991:      } /* --- end-of-if(istextmode&&*subexpr=='$') --- */
7992:     else
7993:      /* --- otherwise, look up mathchardef for atomic token in table --- */
7994:      if ( (leftsymdef=symdef=get_symdef(chartoken)) /*mathchardef for token*/
7995:      ==  NULL )                         /* lookup failed */
7996:       { char literal[512] = "[?]";      /*display for unrecognized literal*/
7997:         int  oldfontnum = fontnum;      /* error display in default mode */
7998:         if ( msgfp!=NULL && msglevel >= 29 ) /* display unrecognized symbol*/
7999:          { fprintf(msgfp,"rasterize> get_symdef() failed for \"%s\"\n",
8000:            chartoken); fflush(msgfp); }
8001:         sp = (subraster *)NULL;         /* init to signal failure */
8002:         if ( warninglevel < 1 ) continue; /* warnings not wanted */
8003:         fontnum = 0;                    /* reset from \mathbb, etc */
8004:         if ( isthischar(*chartoken,ESCAPE) ) /* we got unrecognized \escape*/
8005:          { /* --- so display literal {\rm~[\backslash~chartoken?]} ---  */
8006:            strcpy(literal,"{\\rm~[");   /* init error message token */
8007:            strcat(literal,strdetex(chartoken,0)); /* detex the token */
8008:            strcat(literal,"?]}"); }     /* add closing ? and brace */
8009:         sp = rasterize(literal,size-1); /* rasterize literal token */
8010:         fontnum = oldfontnum;           /* reset font family */
8011:         if ( sp == (subraster *)NULL ) continue; }/*flush if rasterize fails*/
8012:      else /* --- check if we have special handler to process this token --- */
8013:       if ( symdef->handler != NULL )    /* have a handler for this token */
8014:        { int arg1=symdef->charnum, arg2=symdef->family, arg3=symdef->class;
8015:          if ( (sp = (subraster *)       /* returned void* is subraster* */
8016:          (*(symdef->handler))(&expression,size,prevsp,arg1,arg2,arg3))==NULL)
8017:            continue; }                  /* flush token if handler failed */
8018:       else /* --- no handler, so just get subraster for this character --- */
8019:        if ( !isstring )                 /* rasterizing */
8020:         { if ( isligature )             /* found a ligature */
8021:            expression = subexprptr + strlen(symdef->symbol); /*push past it*/
8022:           if ( (sp=get_charsubraster(symdef,size)) /* get subraster */
8023:           ==  NULL )  continue; }       /* flush token if failed */
8024:        else                             /* constructing ascii string */
8025:         { char *symbol = symdef->symbol; /* symbol for ascii string */
8026:           int symlen = (symbol!=NULL?strlen(symbol):0); /*#chars in symbol*/
8027:           if ( symlen < 1 ) continue;   /* no symbol for ascii string */
8028:           if ( (sp=new_subraster(symlen+1,1,8)) /* subraster for symbol */
8029:           ==  NULL )  continue;         /* flush token if malloc failed */
8030:           sp->type = ASCIISTRING;       /* set subraster type */
8031:           sp->symdef = symdef;          /* and set symbol definition */
8032:           sp->baseline = 1;             /* default (should be unused) */
8033:           strcpy((char *)((sp->image)->pixmap),symbol); /* copy symbol */
8034:           /*((char *)((sp->image)->pixmap))[symlen] = '\000';*/ } /*null*/
8035:     } /* --- end-of-if(!isthischar(*subexpr,SCRIPTS)) --- */
8036:   /* --- handle any super/subscripts following symbol or subexpression --- */
8037:   sp = rastlimits(&expression,size,sp);
8038:   isleftscript = (wasscripted||wasdelimscript?1:0);/*preceding term scripted*/
8039:   /* --- debugging output --- */
8040:   if ( msgfp!=NULL && msglevel >= 9 ) { /* display raster for debugging */
8041:    fprintf(msgfp,"rasterize> recursion#%d,atom#%d%s\n",
8042:    recurlevel,natoms+1,(sp==NULL?" = <null>":"..."));
8043:    if ( msglevel >= 9 ) fprintf(msgfp,
8044:     "  isleftscript=%d is/wasscripted=%d,%d is/wasdelimscript=%d,%d\n",
8045:     isleftscript,isscripted,wasscripted,isdelimscript,wasdelimscript);
8046:    if ( msglevel >= 99 )
8047:     if(sp!=NULL) type_raster(sp->image,msgfp); /* display raster */
8048:    fflush(msgfp); }                     /* flush msgfp buffer */
8049:   /* --- accumulate atom or parenthesized subexpression --- */
8050:   if ( natoms < 1                       /* nothing previous to concat */
8051:   ||   expraster == NULL                /* or previous was complete error */
8052:   ||   isreplaceleft )                  /* or we're replacing previous */
8053:     { if ( 1 && expraster!=NULL )       /* probably replacing left */
8054:         delete_subraster(expraster);    /* so first free original left */
8055:       expraster = subrastcpy(sp);       /* copy static CHARASTER or left */
8056:       isreplaceleft = 0; }              /* reset replacement flag */
8057:   else                                  /*we've already built up atoms so...*/
8058:    if ( sp != NULL ) {                  /* ...if we have a new term */
8059:     int prevsmashmargin = smashmargin;  /* save current smash margin */
8060:     if ( isleftscript ) {               /* don't smash against scripts */
8061:      isdelimscript = 0;                 /* reset \right delim scripted flag*/
8062:      if ( !isexplicitsmash ) smashmargin = 0; } /* signal no smash wanted */
8063:     expraster = rastcat(expraster,sp,1); /* concat new term, free previous */
8064:     smashmargin = prevsmashmargin; }    /* restore current smash margin */
8065:   delete_subraster(prevsp);             /* free prev (if not a CHARASTER) */
8066:   prevsp = sp;                          /* current becomes previous */
8067:   leftexpression = expraster;           /* left half rasterized so far */
8068:   /* --- bump count --- */
8069:   natoms++;                             /* bump #atoms count */
8070:   } /* --- end-of-while(expression!=NULL) --- */
8071: /* -------------------------------------------------------------------------
8072: back to caller with rasterized expression
8073: -------------------------------------------------------------------------- */
8074: end_of_job:
8075:   delete_subraster(prevsp);             /* free last (if not a CHARASTER) */
8076:   /* --- debugging output --- */
8077:   if ( msgfp!=NULL && msglevel >= 999 ) /* display raster for debugging */
8078:     { fprintf(msgfp,"rasterize> Final recursion level=%d, atom#%d...\n",
8079:       recurlevel,natoms);
8080:       if ( expraster != (subraster *)NULL ) /* i.e., if natoms>0 */
8081:         type_raster(expraster->image,msgfp); /* display completed raster */
8082:       fflush(msgfp); }                  /* flush msgfp buffer */
8083:   /* --- set final raster buffer --- */
8084:   if ( 1 && expraster != (subraster *)NULL ) /* have an expression */
8085:     { int type = expraster->type;       /* type of constructed image */
8086:       if ( type != FRACRASTER )         /* leave \frac alone */
8087:         expraster->type = IMAGERASTER;  /* set type to constructed image */
8088:       if ( istextmode )                 /* but in text mode */
8089:         expraster->type = blanksignal;  /* set type to avoid smash */
8090:       expraster->size = fontsize; }     /* set original input font size */
8091:   /* --- restore flags/values to original saved values --- */
8092:   isstring = wasstring;                 /* string mode reset */
8093:   isdisplaystyle = wasdisplaystyle;     /* displaystyle mode reset */
8094:   fontnum = oldfontnum;                 /* font family reset */
8095:   fontsize = oldfontsize;               /* fontsize reset */
8096:   displaysize = olddisplaysize;         /* \displaystyle size reset */
8097:   shrinkfactor = oldshrinkfactor;       /* shrinkfactor reset */
8098:   smashmargin = oldsmashmargin;         /* smashmargin reset */
8099:   issmashdelta = oldissmashdelta;       /* issmashdelta reset */
8100:   isexplicitsmash = oldisexplicitsmash; /* isexplicitsmash reset */
8101:   isscripted = oldisscripted;           /* isscripted reset */
8102:   workingparam = oldworkingparam;       /* working parameter reset */
8103:   workingbox = oldworkingbox;           /* working box reset */
8104:   leftexpression = oldleftexpression;   /* leftexpression reset */
8105:   leftsymdef = oldleftsymdef;           /* leftsymdef reset */
8106:   unitlength = oldunitlength;           /* unitlength reset */
8107:   iunitlength = (int)(unitlength+0.5);  /* iunitlength reset */
8108:   recurlevel--;                         /* unwind one recursion level */
8109:   /* --- return final subraster to caller --- */
8110:   return ( expraster );
8111: } /* --- end-of-function rasterize() --- */
8112: 
8113: 
8114: /* ==========================================================================
8115:  * Function:    rastparen ( subexpr, size, basesp )
8116:  * Purpose:     parentheses handler, returns a subraster corresponding to
8117:  *              parenthesized subexpression at font size
8118:  * --------------------------------------------------------------------------
8119:  * Arguments:   subexpr (I)     char **  to first char of null-terminated
8120:  *                              string beginning with a LEFTBRACES
8121:  *                              to be rasterized
8122:  *              size (I)        int containing 0-7 default font size
8123:  *              basesp (I)      subraster *  to character (or subexpression)
8124:  *                              immediately preceding leading left{
8125:  *                              (unused, but passed for consistency)
8126:  * --------------------------------------------------------------------------
8127:  * Returns:     ( subraster * ) ptr to subraster corresponding to subexpr,
8128:  *                              or NULL for any parsing error
8129:  * --------------------------------------------------------------------------
8130:  * Notes:     o This "handler" isn't in the mathchardef symbol table,
8131:  *              but is called directly from rasterize(), as necessary.
8132:  *            o Though subexpr is returned unchanged, it's passed as char **
8133:  *              for consistency with other handlers.  Ditto, basesp is unused
8134:  *              but passed for consistency
8135:  * ======================================================================= */
8136: /* --- entry point --- */
8137: subraster *rastparen ( char **subexpr, int size, subraster *basesp )
8138: {
8139: /* -------------------------------------------------------------------------
8140: Allocations and Declarations
8141: -------------------------------------------------------------------------- */
8142: char    *expression = *subexpr;         /* dereference subexpr to get char* */
8143: int     explen = strlen(expression);    /* total #chars, including parens */
8144: int     isescape = 0,                   /* true if parens \escaped */
8145:         isrightdot = 0,                 /* true if right paren is \right. */
8146:         isleftdot = 0;                  /* true if left paren is \left. */
8147: char    left[32], right[32];            /* parens enclosing expresion */
8148: char    noparens[MAXSUBXSZ+1];          /* get subexpr without parens */
8149: subraster *rasterize(), *sp=NULL;       /* rasterize what's between ()'s */
8150: int     isheight = 1;                   /*true=full height, false=baseline*/
8151: int     height,                         /* height of rasterized noparens[] */
8152:         baseline;                       /* and its baseline */
8153: int     family = /*CMSYEX*/ CMEX10;     /* family for paren chars */
8154: subraster *get_delim(), *lp=NULL, *rp=NULL; /* left and right paren chars */
8155: subraster *rastcat();                   /* concatanate subrasters */
8156: int     delete_subraster();             /*in case of error after allocation*/
8157: /* -------------------------------------------------------------------------
8158: rasterize "interior" of expression, i.e., without enclosing parens
8159: -------------------------------------------------------------------------- */
8160: /* --- first see if enclosing parens are \escaped --- */
8161: if ( isthischar(*expression,ESCAPE) )   /* expression begins with \escape */
8162:   isescape = 1;                         /* so set flag accordingly */
8163: /* --- get expression *without* enclosing parens --- */
8164: strcpy(noparens,expression);            /* get local copy of expression */
8165: noparens[explen-(1+isescape)] = '\000'; /* null-terminate before right} */
8166: strsqueeze(noparens,(1+isescape));      /* and then squeeze out left{ */
8167: /* --- rasterize it --- */
8168: if ( (sp = rasterize(noparens,size))    /*rasterize "interior" of expression*/
8169: ==   NULL ) goto end_of_job;            /* quit if failed */
8170: /* --- no need to add parentheses for unescaped { --- */
8171: if ( !isescape && isthischar(*expression,"{") ) /* don't add parentheses */
8172:   goto end_of_job;                      /* just return sp to caller */
8173: /* -------------------------------------------------------------------------
8174: obtain paren characters to enclose noparens[] raster with
8175: -------------------------------------------------------------------------- */
8176: /* --- first get left and right parens from expression --- */
8177: memset(left,0,16);  memset(right,0,16); /* init parens with nulls */
8178: left[0] = *(expression+isescape);       /* left{ is 1st or 2nd char */
8179: right[0] = *(expression+explen-1);      /* right} is always last char */
8180: isleftdot  = (isescape && isthischar(*left,".")); /* true if \left. */
8181: isrightdot = (isescape && isthischar(*right,".")); /* true if \right. */
8182: /* --- need height of noparens[] raster as minimum parens height --- */
8183: height = (sp->image)->height;           /* height of noparens[] raster */
8184: baseline = sp->baseline;                /* baseline of noparens[] raster */
8185: if ( !isheight ) height = baseline+1;   /* parens only enclose baseline up */
8186: /* --- get best-fit parentheses characters --- */
8187: if ( !isleftdot )                       /* if not \left. */
8188:   lp = get_delim(left,height+1,family); /* get left paren char */
8189: if ( !isrightdot )                      /* and if not \right. */
8190:   rp = get_delim(right,height+1,family); /* get right paren char */
8191: if ( (lp==NULL && !isleftdot)           /* check that we got left( */
8192: ||   (rp==NULL && !isrightdot) )        /* and right) if needed */
8193:   { delete_subraster(sp);               /* if failed, free subraster */
8194:     if ( lp != NULL ) free ((void *)lp);/*free left-paren subraster envelope*/
8195:     if ( rp != NULL ) free ((void *)rp);/*and right-paren subraster envelope*/
8196:     sp = (subraster *)NULL;             /* signal error to caller */
8197:     goto end_of_job; }                  /* and quit */
8198: /* -------------------------------------------------------------------------
8199: set paren baselines to center on noparens[] raster, and concat components
8200: -------------------------------------------------------------------------- */
8201: /* --- set baselines to center paren chars on raster --- */
8202: if ( lp != NULL )                       /* ignore for \left. */
8203:   lp->baseline = baseline + ((lp->image)->height - height)/2;
8204: if ( rp != NULL )                       /* ignore for \right. */
8205:   rp->baseline = baseline + ((rp->image)->height - height)/2;
8206: /* --- concat lp||sp||rp to obtain final result --- */
8207: if ( lp != NULL )                       /* ignore \left. */
8208:   sp = rastcat(lp,sp,3);                /* concat lp||sp and free sp,lp */
8209: if ( sp != NULL )                       /* succeeded or ignored \left. */
8210:   if ( rp != NULL )                     /* ignore \right. */
8211:     sp = rastcat(sp,rp,3);              /* concat sp||rp and free sp,rp */
8212: /* --- back to caller --- */
8213: end_of_job:
8214:   return ( sp );
8215: } /* --- end-of-function rastparen() --- */
8216: 
8217: 
8218: /* ==========================================================================
8219:  * Function:    rastlimits ( expression, size, basesp )
8220:  * Purpose:     \limits, \nolimts, _ and ^ handler,
8221:  *              dispatches call to rastscripts() or to rastdispmath()
8222:  *              as necessary, to handle sub/superscripts following symbol
8223:  * --------------------------------------------------------------------------
8224:  * Arguments:   expression (I)  char **  to first char of null-terminated
8225:  *                              LaTeX expression (unused/unchanged)
8226:  *              size (I)        int containing base font size (not used,
8227:  *                              just stored in subraster)
8228:  *              basesp (I)      subraster *  to current character (or
8229:  *                              subexpression) immediately preceding script
8230:  *                              indicator
8231:  * --------------------------------------------------------------------------
8232:  * Returns:     ( subraster * ) ptr to subraster returned by rastscripts()
8233:  *                              or rastdispmath(), or NULL for any error
8234:  * --------------------------------------------------------------------------
8235:  * Notes:     o
8236:  * ======================================================================= */
8237: /* --- entry point --- */
8238: subraster *rastlimits ( char **expression, int size, subraster *basesp )
8239: {
8240: /* -------------------------------------------------------------------------
8241: Allocations and Declarations
8242: -------------------------------------------------------------------------- */
8243: subraster *rastscripts(), *rastdispmath(), /*one of these will do the work*/
8244:         *rastcat(),                     /* may need to concat scripts */
8245:         *rasterize(),                   /* may need to construct dummy base*/
8246:         *scriptsp = basesp,             /* and this will become the result */
8247:         *dummybase = basesp;            /* for {}_i construct a dummy base */
8248: int     isdisplay = (-1);               /* set 1 for displaystyle, else 0 */
8249: int     oldsmashmargin = smashmargin;   /* save original smashmargin */
8250: int     type_raster();                  /* display debugging output */
8251: int     delete_subraster();             /* free dummybase, if necessary */
8252: int     rastsmashcheck();               /* check if okay to smash scripts */
8253: /* --- to check for \limits or \nolimits preceding scripts --- */
8254: char    *texchar(), *exprptr=*expression, limtoken[255]; /*check for \limits*/
8255: int     toklen=0;                       /* strlen(limtoken) */
8256: mathchardef *tokdef, *get_symdef();     /* mathchardef struct for limtoken */
8257: int     class=(leftsymdef==NULL?NOVALUE:leftsymdef->class); /*base sym class*/
8258: /* -------------------------------------------------------------------------
8259: determine whether or not to use displaymath
8260: -------------------------------------------------------------------------- */
8261: scriptlevel++;                          /* first, increment subscript level*/
8262: *limtoken = '\000';                     /* no token yet */
8263: isscripted = 0;                         /* signal term not (text) scripted */
8264: if ( msgfp!=NULL && msglevel>=999 )
8265:  { fprintf(msgfp,"rastlimits> scriptlevel#%d exprptr=%.48s\n",
8266:    scriptlevel,(exprptr==NULL?"null":exprptr));  fflush(msgfp); }
8267: if ( isstring ) goto end_of_job;        /* no scripts for ascii string */
8268: /* --- check for \limits or \nolimits --- */
8269: skipwhite(exprptr);                     /* skip white space before \limits */
8270: if ( !isempty(exprptr) )                /* non-empty expression supplied */
8271:   exprptr = texchar(exprptr,limtoken);  /* retrieve next token */
8272: if ( *limtoken != '\000' )              /* have token */
8273:  if ( (toklen=strlen(limtoken)) >= 3 )  /* which may be \[no]limits */
8274:   if ( memcmp("\\limits",limtoken,toklen) == 0     /* may be \limits */
8275:   ||   memcmp("\\nolimits",limtoken,toklen) == 0 ) /* or may be \nolimits */
8276:    if ( (tokdef= get_symdef(limtoken))  /* look up token to be sure */
8277:    !=   NULL ) {                        /* found token in table */
8278:     if ( strcmp("\\limits",tokdef->symbol) == 0 )  /* found \limits */
8279:       isdisplay = 1;                    /* so explicitly set displaymath */
8280:     else                                /* wasn't \limits */
8281:       if ( strcmp("\\nolimits",tokdef->symbol) == 0 ) /* found \nolimits */
8282:         isdisplay = 0; }                /* so explicitly reset displaymath */
8283: /* --- see if we found \[no]limits --- */
8284: if ( isdisplay != (-1) )                /* explicit directive found */
8285:   *expression = exprptr;                /* so bump expression past it */
8286: else                                    /* noexplicit directive */
8287:   { isdisplay = 0;                      /* init displaymath flag off */
8288:     if ( isdisplaystyle ) {             /* we're in displaystyle math mode */
8289:       if ( isdisplaystyle >= 5 )        /* and mode irrevocably forced true */
8290:         { if ( class!=OPENING && class!=CLOSING ) /*don't force ('s and )'s*/
8291:             isdisplay = 1; }            /* set flag if mode forced true */
8292:       else
8293:        if ( isdisplaystyle >= 2 )       /*or mode forced conditionally true*/
8294:         { if ( class!=VARIABLE && class!=ORDINARY /*don't force characters*/
8295:           &&   class!=OPENING  && class!=CLOSING  /*don't force ('s and )'s*/
8296:           &&   class!=BINARYOP          /* don't force binary operators */
8297:           &&   class!=NOVALUE )         /* finally, don't force "images" */
8298:             isdisplay = 1; }            /* set flag if mode forced true */
8299:        else                             /* determine mode from base symbol */
8300:         if ( class == DISPOPER )        /* it's a displaystyle operator */
8301:           isdisplay = 1; } }            /* so set flag */
8302: /* -------------------------------------------------------------------------
8303: dispatch call to create sub/superscripts
8304: -------------------------------------------------------------------------- */
8305: if ( isdisplay )                        /* scripts above/below base symbol */
8306:   scriptsp = rastdispmath(expression,size,basesp); /* everything all done */
8307: else {                                  /* scripts alongside base symbol */
8308:   if ( dummybase == NULL )              /* no base symbol preceding scripts*/
8309:     dummybase = rasterize("\\rule0{10}",size); /*guess a typical base symbol*/
8310:   issmashokay = 1;                      /*haven't found a no-smash char yet*/
8311:   if((scriptsp=rastscripts(expression,size,dummybase)) == NULL) /*no scripts*/
8312:     scriptsp = basesp;                  /* so just return unscripted symbol*/
8313:   else {                                /* symbols followed by scripts */
8314:     isscripted = 1;                     /*signal current term text-scripted*/
8315:     if ( basesp != NULL )               /* have base symbol */
8316:      { /*if(0)smashmargin = 0;*/        /*don't smash script (doesn't work)*/
8317:        /*scriptsp = rastcat(basesp,scriptsp,2);*//*concat scripts to base sym*/
8318:        /* --- smash (or just concat) script raster against base symbol --- */
8319:        if ( !issmashokay )              /* don't smash leading - */
8320:          if ( !isexplicitsmash ) scriptsp->type = blanksignal; /*don't smash*/
8321:        scriptsp = rastcat(basesp,scriptsp,3); /*concat scripts to base sym*/
8322:        if(1) scriptsp->type = IMAGERASTER; /* flip type of composite object */
8323:        /* --- smash (or just concat) scripted term to stuff to its left --- */
8324:        issmashokay = 1;                 /* okay to smash base expression */
8325:        if ( 0 && smashcheck > 1 )       /* smashcheck=2 to check base */
8326:          /* note -- we _don't_ have base expression available to check */
8327:          issmashokay = rastsmashcheck(*expression); /*check if okay to smash*/
8328:        if ( !issmashokay )              /* don't smash leading - */
8329:          if ( !isexplicitsmash ) scriptsp->type = blanksignal; /*don't smash*/
8330:        scriptsp->size = size; } } }     /* and set font size */
8331: end_of_job:
8332:   smashmargin = oldsmashmargin;         /* reset original smashmargin */
8333:   if ( dummybase != basesp ) delete_subraster(dummybase); /*free work area*/
8334:   if ( msgfp!=NULL && msglevel>=99 )
8335:     { fprintf(msgfp,"rastlimits> scriptlevel#%d returning %s\n",
8336:         scriptlevel,(scriptsp==NULL?"null":"..."));
8337:       if ( scriptsp != NULL )           /* have a constructed raster */
8338:         type_raster(scriptsp->image,msgfp); /*display constructed raster*/
8339:       fflush(msgfp); }
8340:   scriptlevel--;                        /*lastly, decrement subscript level*/
8341:   return ( scriptsp );
8342: } /* --- end-of-function rastlimits() --- */
8343: 
8344: 
8345: /* ==========================================================================
8346:  * Function:    rastscripts ( expression, size, basesp )
8347:  * Purpose:     super/subscript handler, returns subraster for the leading
8348:  *              scripts in expression, whose base symbol is at font size
8349:  * --------------------------------------------------------------------------
8350:  * Arguments:   expression (I/O) char **  to first char of null-terminated
8351:  *                              string beginning with a super/subscript,
8352:  *                              and returning ptr immediately following
8353:  *                              last script character processed.
8354:  *              size (I)        int containing 0-7 default font size
8355:  *              basesp (I)      subraster *  to character (or subexpression)
8356:  *                              immediately preceding leading script
8357:  *                              (scripts will be placed relative to base)
8358:  * --------------------------------------------------------------------------
8359:  * Returns:     ( subraster * ) ptr to subraster corresponding to scripts,
8360:  *                              or NULL for any parsing error
8361:  * --------------------------------------------------------------------------
8362:  * Notes:     o This "handler" isn't in the mathchardef symbol table,
8363:  *              but is called directly from rasterize(), as necessary.
8364:  * ======================================================================= */
8365: /* --- entry point --- */
8366: subraster *rastscripts ( char **expression, int size, subraster *basesp )
8367: {
8368: /* -------------------------------------------------------------------------
8369: Allocations and Declarations
8370: -------------------------------------------------------------------------- */
8371: char    *texscripts(),                  /* parse expression for scripts */
8372:         subscript[512], supscript[512]; /* scripts parsed from expression */
8373: subraster *rasterize(), *subsp=NULL, *supsp=NULL; /* rasterize scripts */
8374: subraster *new_subraster(), *sp=NULL,   /* super- over subscript subraster */
8375:         *rastack();                     /*sets scripts in displaymath mode*/
8376: raster  *rp=NULL;                       /* image raster embedded in sp */
8377: int     height=0, width=0,  baseline=0, /* height,width,baseline of sp */
8378:         subht=0,  subwidth=0,  subln=0, /* height,width,baseline of sub */
8379:         supht=0,  supwidth=0,  supln=0, /* height,width,baseline of sup */
8380:         baseht=0, baseln=0;             /* height,baseline of base */
8381: int     bdescend=0, sdescend=0;         /* descender of base, subscript */
8382: int     issub=0, issup=0, isboth=0,     /* true if we have sub,sup,both */
8383:         isbase=0;                       /* true if we have base symbol */
8384: int     szval = min2(max2(size,0),LARGESTSIZE), /* 0...LARGESTSIZE */
8385:         vbetween = 2,                   /* vertical space between scripts */
8386:         vabove   = szval+1,             /*sup's top/bot above base's top/bot*/
8387:         vbelow   = szval+1,             /*sub's top/bot below base's top/bot*/
8388:         vbottom  = szval+1;             /*sup's bot above (sub's below) bsln*/
8389: /*int   istweak = 1;*/                  /* true to tweak script positioning */
8390: int     rastput();                      /*put scripts in constructed raster*/
8391: int     delete_subraster();             /* free work areas */
8392: int     rastsmashcheck();               /* check if okay to smash scripts */
8393: int     pixsz = 1;                      /*default #bits per pixel, 1=bitmap*/
8394: /* -------------------------------------------------------------------------
8395: Obtain subscript and/or superscript expressions, and rasterize them/it
8396: -------------------------------------------------------------------------- */
8397: /* --- parse for sub,superscript(s), and bump expression past it(them) --- */
8398: if ( expression == NULL ) goto end_of_job; /* no *ptr given */
8399: if ( *expression == NULL ) goto end_of_job; /* no expression given */
8400: if ( *(*expression) == '\000' ) goto end_of_job; /* nothing in expression */
8401: *expression = texscripts(*expression,subscript,supscript,3);
8402: /* --- rasterize scripts --- */
8403: if ( *subscript != '\000' )             /* have a subscript */
8404:   subsp = rasterize(subscript,size-1);  /* so rasterize it at size-1 */
8405: if ( *supscript != '\000' )             /* have a superscript */
8406:   supsp = rasterize(supscript,size-1);  /* so rasterize it at size-1 */
8407: /* --- set flags for convenience --- */
8408: issub  = (subsp != (subraster *)NULL);  /* true if we have subscript */
8409: issup  = (supsp != (subraster *)NULL);  /* true if we have superscript */
8410: isboth = (issub && issup);              /* true if we have both */
8411: if (!issub && !issup) goto end_of_job;  /* quit if we have neither */
8412: /* --- check for leading no-smash chars (if enabled) --- */
8413: issmashokay = 0;                        /* default, don't smash scripts */
8414: if ( smashcheck > 0 ) {                 /* smash checking wanted */
8415:  issmashokay = 1;                       /*haven't found a no-smash char yet*/
8416:  if ( issub )                           /* got a subscript */
8417:   issmashokay = rastsmashcheck(subscript); /* check if okay to smash */
8418:  if ( issmashokay )                     /* clean sub, so check sup */
8419:   if ( issup )                          /* got a superscript */
8420:    issmashokay = rastsmashcheck(supscript); /* check if okay to smash */
8421:  } /* --- end-of-if(smashcheck>0) --- */
8422: /* -------------------------------------------------------------------------
8423: get height, width, baseline of scripts,  and height, baseline of base symbol
8424: -------------------------------------------------------------------------- */
8425: /* --- get height and width of components --- */
8426: if ( issub )                            /* we have a subscript */
8427:   { subht    = (subsp->image)->height;  /* so get its height */
8428:     subwidth = (subsp->image)->width;   /* and width */
8429:     subln    =  subsp->baseline; }      /* and baseline */
8430: if ( issup )                            /* we have a superscript */
8431:   { supht    = (supsp->image)->height;  /* so get its height */
8432:     supwidth = (supsp->image)->width;   /* and width */
8433:     supln    =  supsp->baseline; }      /* and baseline */
8434: /* --- get height and baseline of base, and descender of base and sub --- */
8435: if ( basesp == (subraster *)NULL )      /* no base symbol for scripts */
8436:   basesp = leftexpression;              /* try using left side thus far */
8437: if ( basesp != (subraster *)NULL )      /* we have base symbol for scripts */
8438:   { baseht   = (basesp->image)->height; /* height of base symbol */
8439:     baseln   =  basesp->baseline;       /* and its baseline */
8440:     bdescend =  baseht-(baseln+1);      /* and base symbol descender */
8441:     sdescend =  bdescend + vbelow;      /*sub must descend by at least this*/
8442:     if ( baseht > 0 ) isbase = 1; }     /* set flag */
8443: /* -------------------------------------------------------------------------
8444: determine width of constructed raster
8445: -------------------------------------------------------------------------- */
8446: width = max2(subwidth,supwidth);        /*widest component is overall width*/
8447: /* -------------------------------------------------------------------------
8448: determine height and baseline of constructed raster
8449: -------------------------------------------------------------------------- */
8450: /* --- both super/subscript --- */
8451: if ( isboth )                           /*we have subscript and superscript*/
8452:   { height = max2(subht+vbetween+supht, /* script heights + space bewteen */
8453:                 vbelow+baseht+vabove);  /*sub below base bot, sup above top*/
8454:     baseline = baseln + (height-baseht)/2; } /*center scripts on base symbol*/
8455: /* --- superscript only --- */
8456: if ( !issub )                           /* we only have a superscript */
8457:   { height = max3(baseln+1+vabove,      /* sup's top above base symbol top */
8458:                 supht+vbottom,          /* sup's bot above baseln */
8459:                 supht+vabove-bdescend); /* sup's bot above base symbol bot */
8460:     baseline = height-1; }              /*sup's baseline at bottom of raster*/
8461: /* --- subscript only --- */
8462: if ( !issup ) {                         /* we only have a subscript */
8463:   if ( subht > sdescend )               /*sub can descend below base bot...*/
8464:     { height = subht;                   /* ...without extra space on top */
8465:       baseline = height-(sdescend+1);   /* sub's bot below base symbol bot */
8466:       baseline = min2(baseline,max2(baseln-vbelow,0)); }/*top below base top*/
8467:   else                                  /* sub's top will be below baseln */
8468:     { height = sdescend+1;              /* sub's bot below base symbol bot */
8469:       baseline = 0; } }                 /* sub's baseline at top of raster */
8470: /* -------------------------------------------------------------------------
8471: construct raster with superscript over subscript
8472: -------------------------------------------------------------------------- */
8473: /* --- allocate subraster containing constructed raster --- */
8474: if ( (sp=new_subraster(width,height,pixsz)) /*allocate subraster and raster*/
8475: ==   NULL )                             /* and if we fail to allocate */
8476:   goto end_of_job;                      /* quit */
8477: /* --- initialize subraster parameters --- */
8478: sp->type  = IMAGERASTER;                /* set type as constructed image */
8479: sp->size  = size;                       /* set given size */
8480: sp->baseline = baseline;                /* composite scripts baseline */
8481: rp = sp->image;                         /* raster embedded in subraster */
8482: /* --- place super/subscripts in new raster --- */
8483: if ( issup )                            /* we have a superscript */
8484:  rastput(rp,supsp->image,0,0,1);        /* it goes in upper-left corner */
8485: if ( issub )                            /* we have a subscript */
8486:  rastput(rp,subsp->image,height-subht,0,1); /*in lower-left corner*/
8487: /* -------------------------------------------------------------------------
8488: free unneeded component subrasters and return final result to caller
8489: -------------------------------------------------------------------------- */
8490: end_of_job:
8491:   if ( issub ) delete_subraster(subsp); /* free unneeded subscript */
8492:   if ( issup ) delete_subraster(supsp); /* and superscript */
8493:   return ( sp );
8494: } /* --- end-of-function rastscripts() --- */
8495: 
8496: 
8497: /* ==========================================================================
8498:  * Function:    rastdispmath ( expression, size, sp )
8499:  * Purpose:     displaymath handler, returns sp along with
8500:  *              its immediately following super/subscripts
8501:  * --------------------------------------------------------------------------
8502:  * Arguments:   expression (I/O) char **  to first char of null-terminated
8503:  *                              string immediately following sp to be
8504:  *                              rasterized along with its super/subscripts,
8505:  *                              and returning ptr immediately following last
8506:  *                              character processed.
8507:  *              size (I)        int containing 0-7 default font size
8508:  *              sp (I)          subraster *  to display math operator
8509:  *                              to which super/subscripts will be added
8510:  * --------------------------------------------------------------------------
8511:  * Returns:     ( subraster * ) ptr to subraster corresponding to sp
8512:  *                              plus its scripts, or NULL for any error
8513:  * --------------------------------------------------------------------------
8514:  * Notes:     o sp returned unchanged if no super/subscript(s) follow it.
8515:  * ======================================================================= */
8516: /* --- entry point --- */
8517: subraster *rastdispmath ( char **expression, int size, subraster *sp )
8518: {
8519: /* -------------------------------------------------------------------------
8520: Allocations and Declarations
8521: -------------------------------------------------------------------------- */
8522: char    *texscripts(),                  /* parse expression for scripts */
8523:         subscript[512], supscript[512]; /* scripts parsed from expression */
8524: int     issub=0, issup=0;               /* true if we have sub,sup */
8525: subraster *rasterize(), *subsp=NULL, *supsp=NULL, /* rasterize scripts */
8526:         *rastack(),                     /* stack operator with scripts */
8527:         *new_subraster();               /* for dummy base sp, if needed */
8528: int     vspace = 1;                     /* vertical space between scripts */
8529: /* -------------------------------------------------------------------------
8530: Obtain subscript and/or superscript expressions, and rasterize them/it
8531: -------------------------------------------------------------------------- */
8532: /* --- parse for sub,superscript(s), and bump expression past it(them) --- */
8533: if ( expression == NULL ) goto end_of_job; /* no *ptr given */
8534: if ( *expression == NULL ) goto end_of_job; /* no expression given */
8535: if ( *(*expression) == '\000' ) goto end_of_job; /* nothing in expression */
8536: *expression = texscripts(*expression,subscript,supscript,3);
8537: /* --- rasterize scripts --- */
8538: if ( *subscript != '\000' )             /* have a subscript */
8539:   subsp = rasterize(subscript,size-1);  /* so rasterize it at size-1 */
8540: if ( *supscript != '\000' )             /* have a superscript */
8541:   supsp = rasterize(supscript,size-1);  /* so rasterize it at size-1 */
8542: /* --- set flags for convenience --- */
8543: issub  = (subsp != (subraster *)NULL);  /* true if we have subscript */
8544: issup  = (supsp != (subraster *)NULL);  /* true if we have superscript */
8545: if (!issub && !issup) goto end_of_job;  /*return operator alone if neither*/
8546: /* -------------------------------------------------------------------------
8547: stack operator and its script(s)
8548: -------------------------------------------------------------------------- */
8549: /* --- stack superscript atop operator --- */
8550: if ( issup ) {                          /* we have a superscript */
8551:  if ( sp == NULL )                      /* but no base expression */
8552:   sp = supsp;                           /* so just use superscript */
8553:  else                                   /* have base and superscript */
8554:   if ( (sp=rastack(sp,supsp,1,vspace,1,3)) /* stack supsp atop base sp */
8555:   ==   NULL ) goto end_of_job; }        /* and quit if failed */
8556: /* --- stack operator+superscript atop subscript --- */
8557: if ( issub ) {                          /* we have a subscript */
8558:  if ( sp == NULL )                      /* but no base expression */
8559:   sp = subsp;                           /* so just use subscript */
8560:  else                                   /* have base and subscript */
8561:   if ( (sp=rastack(subsp,sp,2,vspace,1,3)) /* stack sp atop base subsp */
8562:   ==   NULL ) goto end_of_job; }        /* and quit if failed */
8563: sp->type = IMAGERASTER;                 /* flip type of composite object */
8564: sp->size = size;                        /* and set font size */
8565: /* -------------------------------------------------------------------------
8566: free unneeded component subrasters and return final result to caller
8567: -------------------------------------------------------------------------- */
8568: end_of_job:
8569:   return ( sp );
8570: } /* --- end-of-function rastdispmath() --- */
8571: 
8572: 
8573: /* ==========================================================================
8574:  * Function:    rastleft ( expression, size, basesp, ildelim, arg2, arg3 )
8575:  * Purpose:     \left...\right handler, returns a subraster corresponding to
8576:  *              delimited subexpression at font size
8577:  * --------------------------------------------------------------------------
8578:  * Arguments:   expression (I)  char **  to first char of null-terminated
8579:  *                              string beginning with a \left
8580:  *                              to be rasterized
8581:  *              size (I)        int containing 0-7 default font size
8582:  *              basesp (I)      subraster *  to character (or subexpression)
8583:  *                              immediately preceding leading left{
8584:  *                              (unused, but passed for consistency)
8585:  *              ildelim (I)     int containing ldelims[] index of
8586:  *                              left delimiter
8587:  *              arg2 (I)        int unused
8588:  *              arg3 (I)        int unused
8589:  * --------------------------------------------------------------------------
8590:  * Returns:     ( subraster * ) ptr to subraster corresponding to subexpr,
8591:  *                              or NULL for any parsing error
8592:  * --------------------------------------------------------------------------
8593:  * Notes:     o
8594:  * ======================================================================= */
8595: /* --- entry point --- */
8596: subraster *rastleft ( char **expression, int size, subraster *basesp,
8597:                         int ildelim, int arg2, int arg3 )
8598: {
8599: /* -------------------------------------------------------------------------
8600: Allocations and Declarations
8601: -------------------------------------------------------------------------- */
8602: subraster *rasterize(), *sp=NULL;       /*rasterize between \left...\right*/
8603: subraster *get_delim(), *lp=NULL, *rp=NULL; /* left and right delim chars */
8604: subraster *rastlimits();                /*handle sub/super scripts on lp,rp*/
8605: subraster *rastcat();                   /* concat lp||sp||rp subrasters */
8606: int     family=CMSYEX,                  /* get_delim() family */
8607:         height=0, rheight=0,            /* subexpr, right delim height */
8608:         margin=(size+1),                /* delim height margin over subexpr*/
8609:         opmargin=(5);                   /* extra margin for \int,\sum,\etc */
8610: char    /* *texleft(),*/ subexpr[MAXSUBXSZ+1];/*chars between \left...\right*/
8611: char    *texchar(),                     /* get delims after \left,\right */
8612:         ldelim[256]=".", rdelim[256]="."; /* delims following \left,\right */
8613: char    *strtexchr(), *pleft, *pright;  /*locate \right matching our \left*/
8614: int     isleftdot=0, isrightdot=0;      /* true if \left. or \right. */
8615: int     isleftscript=0, isrightscript=0; /* true if delims are scripted */
8616: int     sublen=0;                       /* strlen(subexpr) */
8617: int     idelim=0;                       /* 1=left,2=right */
8618: /* int  gotldelim = 0; */               /* true if ildelim given by caller */
8619: int     delete_subraster();             /* free subraster if rastleft fails*/
8620: int     wasdisplaystyle = isdisplaystyle; /* save current displaystyle */
8621: int     istextleft=0, istextright=0;    /* true for non-displaystyle delims*/
8622: /* --- recognized delimiters --- */
8623: static  char left[16]="\\left", right[16]="\\right"; /* tex delimiters */
8624: static  char *ldelims[] = {
8625:    "unused", ".",                       /* 1   for \left., \right. */
8626:         "(", ")",                       /* 2,3 for \left(, \right) */
8627:         "\\{","\\}",                    /* 4,5 for \left\{, \right\} */
8628:         "[", "]",                       /* 6,7 for \left[, \right] */
8629:         "<", ">",                       /* 8,9 for \left<, \right> */
8630:         "|", "\\|",                     /* 10,11 for \left,\right |,\|*/
8631:         NULL };
8632: /* --- recognized operator delimiters --- */
8633: static  char *opdelims[] = {            /* operator delims from cmex10 */
8634:      "int",       "sum",        "prod",
8635:      "cup",       "cap",        "dot",
8636:      "plus",      "times",      "wedge",
8637:      "vee",
8638:      NULL }; /* --- end-of-opdelims[] --- */
8639: /* --- delimiter xlation --- */
8640: static  char *xfrom[] =                 /* xlate any delim suffix... */
8641:    { "\\|",                             /* \| */
8642:      "\\{",                             /* \{ */
8643:      "\\}",                             /* \} */
8644:      "\\lbrace",                        /* \lbrace */
8645:      "\\rbrace",                        /* \rbrace */
8646:      "\\langle",                        /* \langle */
8647:      "\\rangle",                        /* \rangle */
8648:      NULL } ; /* --- end-of-xfrom[] --- */
8649: static  char *xto[] =                   /* ...to this instead */
8650:    { "=",                               /* \| to = */
8651:      "{",                               /* \{ to { */
8652:      "}",                               /* \} to } */
8653:      "{",                               /* \lbrace to { */
8654:      "}",                               /* \rbrace to } */
8655:      "<",                               /* \langle to < */
8656:      ">",                               /* \rangle to > */
8657:      NULL } ; /* --- end-of-xto[] --- */
8658: /* --- non-displaystyle delimiters --- */
8659: static  char *textdelims[] =            /* these delims _aren't_ display */
8660:    { "|", "=",
8661:      "(", ")",
8662:      "[", "]",
8663:      "<", ">",
8664:      "{", "}",
8665:      "dbl",                             /* \lbrackdbl and \rbrackdbl */
8666:      NULL } ; /* --- end-of-textdelims[] --- */
8667: /* -------------------------------------------------------------------------
8668: initialization
8669: -------------------------------------------------------------------------- */
8670: /* --- check args --- */
8671: if ( *(*expression) == '\000' ) goto end_of_job; /* nothing after \left */
8672: /* --- determine left delimiter, and set default \right. delimiter --- */
8673: if ( ildelim!=NOVALUE && ildelim>=1 )   /* called with explicit left delim */
8674:  { strcpy(ldelim,ldelims[ildelim]);     /* so just get a local copy */
8675:    /* gotldelim = 1; */ }               /* and set flag that we got it */
8676: else                                    /* trapped \left without delim */
8677:  { skipwhite(*expression);              /* interpret \left ( as \left( */
8678:    if ( *(*expression) == '\000' )      /* end-of-string after \left */
8679:       goto end_of_job;                  /* so return NULL */
8680:    *expression = texchar(*expression,ldelim); /*pull delim from expression*/
8681:    if ( *expression == NULL             /* probably invalid end-of-string */
8682:    ||   *ldelim == '\000' ) goto end_of_job; } /* no delimiter */
8683: strcpy(rdelim,".");                     /* init default \right. delim */
8684: /* -------------------------------------------------------------------------
8685: locate \right balancing our opening \left
8686: -------------------------------------------------------------------------- */
8687: /* --- first \right following \left --- */
8688: if ( (pright=strtexchr(*expression,right)) /* look for \right after \left */
8689: !=   NULL ) {                           /* found it */
8690:  /* --- find matching \right by pushing past any nested \left's --- */
8691:  pleft = *expression;                   /* start after first \left( */
8692:  while ( 1 ) {                          /*break when matching \right found*/
8693:   /* -- locate next nested \left if there is one --- */
8694:   if ( (pleft=strtexchr(pleft,left))    /* find next \left */
8695:   ==   NULL ) break;                    /*no more, so matching \right found*/
8696:   pleft += strlen(left);                /* push ptr past \left token */
8697:   if ( pleft >= pright ) break;         /* not nested if \left after \right*/
8698:   /* --- have nested \left, so push forward to next \right --- */
8699:   if ( (pright=strtexchr(pright+strlen(right),right)) /* find next \right */
8700:   ==   NULL ) break;                    /* ran out of \right's */
8701:   } /* --- end-of-while(1) --- */
8702:  } /* --- end-of-if(pright!=NULL) --- */
8703: /* -------------------------------------------------------------------------
8704: push past \left(_a^b sub/superscripts, if present
8705: -------------------------------------------------------------------------- */
8706: pleft = *expression;                    /*reset pleft after opening \left( */
8707: if ( (lp=rastlimits(expression,size,lp)) /*dummy call push expression past b*/
8708: !=   NULL )                             /* found actual _a^b scripts, too */
8709:   { delete_subraster(lp);               /* but we don't need them */
8710:     lp = NULL; }                        /* reset pointer, too */
8711: /* -------------------------------------------------------------------------
8712: get \right delimiter and subexpression between \left...\right, xlate delims
8713: -------------------------------------------------------------------------- */
8714: /* --- get delimiter following \right --- */
8715: if ( pright == (char *)NULL ) {         /* assume \right. at end of exprssn*/
8716:   strcpy(rdelim,".");                   /* set default \right. */
8717:   sublen = strlen(*expression);         /* use entire remaining expression */
8718:   memcpy(subexpr,*expression,sublen);   /* copy all remaining chars */
8719:   *expression += sublen; }              /* and push expression to its null */
8720: else {                                  /* have explicit matching \right */
8721:   sublen = (int)(pright-(*expression)); /* #chars between \left...\right */
8722:   memcpy(subexpr,*expression,sublen);   /* copy chars preceding \right */
8723:   *expression = pright+strlen(right);   /* push expression past \right */
8724:   skipwhite(*expression);               /* interpret \right ) as \right) */
8725:   *expression = texchar(*expression,rdelim); /*pull delim from expression*/
8726:   if ( *rdelim == '\000' ) strcpy(rdelim,"."); } /* \right. if no rdelim */
8727: /* --- get subexpression between \left...\right --- */
8728: if ( sublen < 1 ) goto end_of_job;      /* nothing between delimiters */
8729: subexpr[sublen] = '\000';               /* and null-terminate it */
8730: /* --- adjust margin for expressions containing \middle's --- */
8731: if ( strtexchr(subexpr,"\\middle") != NULL ) /* have enclosed \middle's */
8732:   margin = 1;                           /* so don't "overwhelm" them */
8733: /* --- check for operator delimiter --- */
8734: for ( idelim=0; opdelims[idelim]!=NULL; idelim++ )
8735:   if ( strstr(ldelim,opdelims[idelim]) != NULL ) /* found operator */
8736:     { margin += opmargin;               /* extra height for operator */
8737:       if ( *ldelim == '\\' )            /* have leading escape */
8738:         {strsqueeze(ldelim,1);}         /* squeeze it out */
8739:       break; }                          /* no need to check rest of table */
8740: /* --- xlate delimiters and check for textstyle --- */
8741: for ( idelim=1; idelim<=2; idelim++ ) { /* 1=left, 2=right */
8742:   char  *lrdelim  = (idelim==1? ldelim:rdelim); /* ldelim or rdelim */
8743:   int   ix;  char *xdelim;              /* xfrom[] and xto[] index, delim */
8744:   for( ix=0; (xdelim=xfrom[ix]) != NULL; ix++ )
8745:     if ( strcmp(lrdelim,xdelim) == 0 )  /* found delim to xlate */
8746:       { strcpy(lrdelim,xto[ix]);        /* replace with corresponding xto[]*/
8747:         break; }                        /* no need to check further */
8748:   for( ix=0; (xdelim=textdelims[ix]) != NULL; ix++ )
8749:     if ( strstr(lrdelim,xdelim) != 0 )  /* found textstyle delim */
8750:       { if ( idelim == 1 )              /* if it's the \left one */
8751:           istextleft = 1;               /* set left textstyle flag */
8752:         else istextright = 1;           /* else set right textstyle flag */
8753:         break; }                        /* no need to check further */
8754:   } /* --- end-of-for(idelim) --- */
8755: /* --- debugging --- */
8756: if ( msgfp!=NULL && msglevel>=99 )
8757:   fprintf(msgfp,"rastleft> left=\"%s\" right=\"%s\" subexpr=\"%s\"\n",
8758:   ldelim,rdelim,subexpr);
8759: /* -------------------------------------------------------------------------
8760: rasterize subexpression
8761: -------------------------------------------------------------------------- */
8762: /* --- rasterize subexpression --- */
8763: if ( (sp = rasterize(subexpr,size))     /* rasterize chars between delims */
8764: ==   NULL ) goto end_of_job;            /* quit if failed */
8765: height = (sp->image)->height;           /* height of subexpr raster */
8766: rheight = height+margin;                /*default rheight as subexpr height*/
8767: /* -------------------------------------------------------------------------
8768: rasterize delimiters, reset baselines, and add  sub/superscripts if present
8769: -------------------------------------------------------------------------- */
8770: /* --- check for dot delimiter --- */
8771: isleftdot  = (strchr(ldelim,'.')!=NULL); /* true if \left. */
8772: isrightdot = (strchr(rdelim,'.')!=NULL); /* true if \right. */
8773: /* --- get rasters for best-fit delim characters, add sub/superscripts --- */
8774: isdisplaystyle = (istextleft?0:9);      /* force \displaystyle */
8775: if ( !isleftdot )                       /* if not \left. */
8776:  { /* --- first get requested \left delimiter --- */
8777:    lp = get_delim(ldelim,rheight,family); /* get \left delim char */
8778:    /* --- reset lp delim baseline to center delim on subexpr raster --- */
8779:    if ( lp != NULL )                    /* if get_delim() succeeded */
8780:     { int lheight = (lp->image)->height; /* actual height of left delim */
8781:       lp->baseline = sp->baseline + (lheight - height)/2;
8782:       if ( lheight > rheight )          /* got bigger delim than requested */
8783:         rheight = lheight-1; }          /* make sure right delim matches */
8784:    /* --- then add on any sub/superscripts attached to \left( --- */
8785:    lp = rastlimits(&pleft,size,lp);     /*\left(_a^b and push pleft past b*/
8786:    isleftscript = isscripted; }         /* check if left delim scripted */
8787: isdisplaystyle = (istextright?0:9);     /* force \displaystyle */
8788: if ( !isrightdot )                      /* and if not \right. */
8789:  { /* --- first get requested \right delimiter --- */
8790:    rp = get_delim(rdelim,rheight,family); /* get \right delim char */
8791:    /* --- reset rp delim baseline to center delim on subexpr raster --- */
8792:    if ( rp != NULL )                    /* if get_delim() succeeded */
8793:      rp->baseline = sp->baseline + ((rp->image)->height - height)/2;
8794:    /* --- then add on any sub/superscripts attached to \right) --- */
8795:    rp = rastlimits(expression,size,rp); /*\right)_c^d, expression past d*/
8796:    isrightscript = isscripted; }        /* check if right delim scripted */
8797: isdisplaystyle = wasdisplaystyle;       /* original \displystyle default */
8798: /* --- check that we got delimiters --- */
8799: if ( 0 )
8800:  if ( (lp==NULL && !isleftdot)          /* check that we got left( */
8801:  ||   (rp==NULL && !isrightdot) )       /* and right) if needed */
8802:   { if ( lp != NULL ) free ((void *)lp); /* free \left-delim subraster */
8803:     if ( rp != NULL ) free ((void *)rp); /* and \right-delim subraster */
8804:     if (0) { delete_subraster(sp);      /* if failed, free subraster */
8805:              sp = (subraster *)NULL; }  /* signal error to caller */
8806:     goto end_of_job; }                  /* and quit */
8807: /* -------------------------------------------------------------------------
8808: concat  lp || sp || rp  components
8809: -------------------------------------------------------------------------- */
8810: /* --- concat lp||sp||rp to obtain final result --- */
8811: if ( lp != NULL )                       /* ignore \left. */
8812:   sp = rastcat(lp,sp,3);                /* concat lp||sp and free sp,lp */
8813: if ( sp != NULL )                       /* succeeded or ignored \left. */
8814:   if ( rp != NULL )                     /* ignore \right. */
8815:     sp = rastcat(sp,rp,3);              /* concat sp||rp and free sp,rp */
8816: /* --- back to caller --- */
8817: end_of_job:
8818:   isdelimscript = isrightscript;        /* signal if right delim scripted */
8819:   return ( sp );
8820: } /* --- end-of-function rastleft() --- */
8821: 
8822: 
8823: /* ==========================================================================
8824:  * Function:    rastright ( expression, size, basesp, ildelim, arg2, arg3 )
8825:  * Purpose:     ...\right handler, intercepts an unexpected/unbalanced \right
8826:  * --------------------------------------------------------------------------
8827:  * Arguments:   expression (I)  char **  to first char of null-terminated
8828:  *                              string beginning with a \right
8829:  *                              to be rasterized
8830:  *              size (I)        int containing 0-7 default font size
8831:  *              basesp (I)      subraster *  to character (or subexpression)
8832:  *                              immediately preceding leading left{
8833:  *                              (unused, but passed for consistency)
8834:  *              ildelim (I)     int containing rdelims[] index of
8835:  *                              right delimiter
8836:  *              arg2 (I)        int unused
8837:  *              arg3 (I)        int unused
8838:  * --------------------------------------------------------------------------
8839:  * Returns:     ( subraster * ) ptr to subraster corresponding to subexpr,
8840:  *                              or NULL for any parsing error
8841:  * --------------------------------------------------------------------------
8842:  * Notes:     o
8843:  * ======================================================================= */
8844: /* --- entry point --- */
8845: subraster *rastright ( char **expression, int size, subraster *basesp,
8846:                         int ildelim, int arg2, int arg3 )
8847: {
8848: /* -------------------------------------------------------------------------
8849: Allocations and Declarations
8850: -------------------------------------------------------------------------- */
8851: subraster /* *rasterize(),*/ *sp=NULL;  /*rasterize \right subexpr's*/
8852:   if ( sp != NULL )                     /* returning entire expression */
8853:     {
8854:       isreplaceleft = 1;                /* set flag to replace left half*/
8855:     }
8856: return ( sp );
8857: } /* --- end-of-function rastright() --- */
8858: 
8859: 
8860: /* ==========================================================================
8861:  * Function:    rastmiddle ( expression, size, basesp,  arg1, arg2, arg3 )
8862:  * Purpose:     \middle handler, returns subraster corresponding to
8863:  *              entire expression with \middle delimiter(s) sized to fit.
8864:  * --------------------------------------------------------------------------
8865:  * Arguments:   expression (I/O) char **  to first char of null-terminated
8866:  *                              string immediately following \middle to be
8867:  *                              rasterized, and returning ptr immediately
8868:  *                              to terminating null.
8869:  *              size (I)        int containing 0-7 default font size
8870:  *              basesp (I)      subraster *  to character (or subexpression)
8871:  *                              immediately preceding \middle
8872:  *                              (unused, but passed for consistency)
8873:  *              arg1 (I)        int unused
8874:  *              arg2 (I)        int unused
8875:  *              arg3 (I)        int unused
8876:  * --------------------------------------------------------------------------
8877:  * Returns:     ( subraster * ) ptr to subraster corresponding to expression,
8878:  *                              or NULL for any parsing error
8879:  *                              (expression ptr unchanged if error occurs)
8880:  * --------------------------------------------------------------------------
8881:  * Notes:     o
8882:  * ======================================================================= */
8883: /* --- entry point --- */
8884: subraster *rastmiddle ( char **expression, int size, subraster *basesp,
8885:                         int arg1, int arg2, int arg3 )
8886: {
8887: /* -------------------------------------------------------------------------
8888: Allocations and Declarations
8889: -------------------------------------------------------------------------- */
8890: subraster *rasterize(), *sp=NULL, *subsp[32]; /*rasterize \middle subexpr's*/
8891: char    *exprptr = *expression,         /* local copy of ptr to expression */
8892:         *texchar(), delim[32][132],     /* delimiters following \middle's */
8893:         *strtexchr(),                   /* locate \middle's */
8894:         subexpr[MAXSUBXSZ+1], *subptr=NULL;/*subexpression between \middle's*/
8895: int     height=0, habove=0, hbelow=0;   /* height, above & below baseline */
8896: int     idelim, ndelims=0,              /* \middle count (max 32) */
8897:         family = CMSYEX;                /* delims from CMSY10 or CMEX10 */
8898: subraster *subrastcpy(),                /* copy subraster */
8899:         *rastcat(),                     /* concatanate subraster */
8900:         *get_delim();                   /* get rasterized delimiter */
8901: int     delete_subraster();             /* free work area subsp[]'s at eoj */
8902: /* -------------------------------------------------------------------------
8903: initialization
8904: -------------------------------------------------------------------------- */
8905: subsp[0] = leftexpression;              /* expressn preceding 1st \middle */
8906: subsp[1] = NULL;                        /* set first null */
8907: /* -------------------------------------------------------------------------
8908: accumulate subrasters between consecutive \middle\delim...\middle\delim...'s
8909: -------------------------------------------------------------------------- */
8910: while ( ndelims < 30 )                  /* max of 31 \middle's */
8911:   {
8912:   /* --- maintain max height above,below baseline --- */
8913:   if ( subsp[ndelims] != NULL )         /*exprssn preceding current \middle*/
8914:    { int baseline = (subsp[ndelims])->baseline;  /* #rows above baseline */
8915:      height = ((subsp[ndelims])->image)->height; /* tot #rows (height) */
8916:      habove = max2(habove,baseline);    /* max #rows above baseline */
8917:      hbelow = max2(hbelow,height-baseline); } /* max #rows below baseline */
8918:   /* --- get delimter after \middle --- */
8919:   skipwhite(exprptr);                   /*skip space betwn \middle & \delim*/
8920:   exprptr = texchar(exprptr,delim[ndelims]); /* \delim after \middle */
8921:   if ( *(delim[ndelims]) == '\000' )    /* \middle at end-of-expression */
8922:     break;                              /* ignore it and consider job done */
8923:   ndelims++;                            /* count another \middle\delim */
8924:   /* --- get subexpression between \delim and next \middle --- */
8925:   subsp[ndelims] = NULL;                /* no subexpresion yet */
8926:   if ( *exprptr == '\000' )             /* end-of-expression after \delim */
8927:     break;                              /* so we have all subexpressions */
8928:   if ( (subptr = strtexchr(exprptr,"\\middle")) /* find next \middle */
8929:   ==   NULL )                           /* no more \middle's */
8930:    { strncpy(subexpr,exprptr,MAXSUBXSZ); /*get entire remaining expression*/
8931:      subexpr[MAXSUBXSZ] = '\000';       /* make sure it's null-terminated */
8932:      exprptr += strlen(exprptr); }      /* push exprptr to terminating '\0'*/
8933:   else                                  /* have another \middle */
8934:    { int sublen = (int)(subptr-exprptr); /* #chars between \delim...\middle*/
8935:      memcpy(subexpr,exprptr,min2(sublen,MAXSUBXSZ)); /* get subexpression */
8936:      subexpr[min2(sublen,MAXSUBXSZ)] = '\000'; /* and null-terminate it */
8937:      exprptr += (sublen+strlen("\\middle")); } /* push exprptr past \middle*/
8938:   /* --- rasterize subexpression --- */
8939:   subsp[ndelims] = rasterize(subexpr,size); /* rasterize subexpresion */
8940:   } /* --- end-of-while(1) --- */
8941: /* -------------------------------------------------------------------------
8942: construct \middle\delim's and concatanate them between subexpressions
8943: -------------------------------------------------------------------------- */
8944: if ( ndelims < 1                        /* no delims */
8945: ||   (height=habove+hbelow) < 1 )       /* or no subexpressions? */
8946:   goto end_of_job;                      /* just flush \middle directive */
8947: for ( idelim=0; idelim<=ndelims; idelim++ )
8948:   {
8949:   /* --- first add on subexpression preceding delim --- */
8950:   if ( subsp[idelim] != NULL ) {        /* have subexpr preceding delim */
8951:     if ( sp == NULL )                   /* this is first piece */
8952:      { sp = subsp[idelim];              /* so just use it */
8953:        if ( idelim == 0 ) sp = subrastcpy(sp); } /* or copy leftexpression */
8954:     else sp = rastcat(sp,subsp[idelim],(idelim>0?3:1)); } /* or concat it */
8955:   /* --- now construct delimiter --- */
8956:   if ( *(delim[idelim]) != '\000' )     /* have delimter */
8957:    { subraster *delimsp = get_delim(delim[idelim],height,family);
8958:      if ( delimsp != NULL )             /* rasterized delim */
8959:       { delimsp->baseline = habove;     /* set baseline */
8960:         if ( sp == NULL )               /* this is first piece */
8961:           sp = delimsp;                 /* so just use it */
8962:         else sp = rastcat(sp,delimsp,3); } } /*or concat to existing pieces*/
8963:   } /* --- end-of-for(idelim) --- */
8964: /* --- back to caller --- */
8965: end_of_job:
8966:   if ( 0 ) /* now handled above */
8967:     for ( idelim=1; idelim<=ndelims; idelim++ ) /* free subsp[]'s (not 0) */
8968:      if ( subsp[idelim] != NULL )       /* have allocated subraster */
8969:       delete_subraster(subsp[idelim]);  /* so free it */
8970:   if ( sp != NULL )                     /* returning entire expression */
8971:     { int newht = (sp->image)->height;  /* height of returned subraster */
8972:       sp->baseline = min2(newht-1,newht/2+5); /* guess new baseline */
8973:       isreplaceleft = 1;                /* set flag to replace left half*/
8974:       *expression += strlen(*expression); } /* and push to terminating null*/
8975:   return ( sp );
8976: } /* --- end-of-function rastmiddle() --- */
8977: 
8978: 
8979: /* ==========================================================================
8980:  * Function:    rastflags ( expression, size, basesp,  flag, value, arg3 )
8981:  * Purpose:     sets an internal flag, e.g., for \rm, or sets an internal
8982:  *              value, e.g., for \unitlength=<value>, and returns NULL
8983:  *              so nothing is displayed
8984:  * --------------------------------------------------------------------------
8985:  * Arguments:   expression (I)  char **  to first char of null-terminated
8986:  *                              LaTeX expression (unused/unchanged)
8987:  *              size (I)        int containing base font size (not used,
8988:  *                              just stored in subraster)
8989:  *              basesp (I)      subraster *  to character (or subexpression)
8990:  *                              immediately preceding "flags" directive
8991:  *                              (unused but passed for consistency)
8992:  *              flag (I)        int containing #define'd symbol specifying
8993:  *                              internal flag to be set
8994:  *              value (I)       int containing new value of flag
8995:  *              arg3 (I)        int unused
8996:  * --------------------------------------------------------------------------
8997:  * Returns:     ( subraster * ) NULL so nothing is displayed
8998:  * --------------------------------------------------------------------------
8999:  * Notes:     o
9000:  * ======================================================================= */
9001: /* --- entry point --- */
9002: subraster *rastflags ( char **expression, int size, subraster *basesp,
9003:                         int flag, int value, int arg3 )
9004: {
9005: /* -------------------------------------------------------------------------
9006: Allocations and Declarations
9007: -------------------------------------------------------------------------- */
9008: char    *texsubexpr(),                  /* parse expression for... */
9009:         valuearg[1024]="NOVALUE";       /* value from expression, if needed */
9010: int     argvalue=NOVALUE,               /* atoi(valuearg) */
9011:         isdelta=0,                      /* true if + or - precedes valuearg */
9012:         valuelen=0;                     /* strlen(valuearg) */
9013: double  dblvalue=(-99.), strtod();      /*convert ascii {valuearg} to double*/
9014: static  int displaystylelevel = (-99);  /* \displaystyle set at recurlevel */
9015: /* -------------------------------------------------------------------------
9016: set flag or value
9017: -------------------------------------------------------------------------- */
9018: switch ( flag )
9019:   {
9020:   default: break;                       /* unrecognized flag */
9021:   case ISFONTFAM:
9022:     if ( isthischar((*(*expression)),WHITEMATH) ) /* \rm followed by white */
9023:       (*expression)++;                  /* skip leading ~ after \rm */
9024:     fontnum = value;                    /* set font family */
9025:     break;
9026:   case ISSTRING: isstring=value; break; /* set string/image mode */
9027:   case ISDISPLAYSTYLE:                  /* set \displaystyle mode */
9028:     displaystylelevel = recurlevel;     /* \displaystyle set at recurlevel */
9029:     isdisplaystyle=value; break;
9030:   case ISOPAQUE:  istransparent=value; break; /* set transparent/opaque */
9031:   case ISREVERSE:                       /* reverse video */
9032:     if ( value==1 || value==NOVALUE )
9033:       { fgred=255-fgred; fggreen=255-fggreen; fgblue=255-fgblue; }
9034:     if ( value==2 || value==NOVALUE )
9035:       { bgred=255-bgred; bggreen=255-bggreen; bgblue=255-bgblue; }
9036:     if ( value==2 || value==NOVALUE )
9037:       isblackonwhite = !isblackonwhite;
9038:     if ( gammacorrection > 0.0001 )     /* have gamma correction */
9039:       gammacorrection = REVERSEGAMMA;   /* use reverse video gamma instead */
9040:     break;
9041:   case ISSUPER:                         /* set supersampling/lowpass flag */
9042:     #ifndef SSFONTS                     /* don't have ss fonts loaded */
9043:       value = 0;                        /* so force lowpass */
9044:     #endif
9045:     isss = issupersampling = value;
9046:     fonttable = (issupersampling?ssfonttable:aafonttable); /* set fonts */
9047:     break;
9048:   case ISFONTSIZE:                      /* set fontsize */
9049:   case ISMAGSTEP:                       /* set magstep */
9050:   case ISDISPLAYSIZE:                   /* set displaysize */
9051:   case ISCONTENTTYPE:                   /*enable/disable content-type lines*/
9052:   case ISCONTENTCACHED:                 /* write content-type to cache file*/
9053:   case ISSHRINK:                        /* set shrinkfactor */
9054:   case ISAAALGORITHM:                   /* set anti-aliasing algorithm */
9055:   case ISWEIGHT:                        /* set font weight */
9056:   case ISCENTERWT:                      /* set lowpass center pixel weight */
9057:   case ISADJACENTWT:                    /* set lowpass adjacent weight */
9058:   case ISCORNERWT:                      /* set lowpass corner weight */
9059:   case ISCOLOR:                         /* set red(1),green(2),blue(3) */
9060:   case ISSMASH:                         /* set (minimum) "smash" margin */
9061:   case ISGAMMA:                         /* set gamma correction */
9062:   case ISPBMPGM:                        /* set pbmpgm output flag and ptype*/
9063:   case ISUTHETA:                        /* set utheta 3d-rotation angle */
9064:     if ( value != NOVALUE )             /* passed a fixed value to be set */
9065:       { argvalue = value;               /* set given fixed int value */
9066:         dblvalue = (double)value; }     /* or maybe interpreted as double */
9067:     else                                /* get value from expression */
9068:       { *expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0);
9069:         if ( *valuearg != '\000' )      /* guard against empty string */
9070:          if ( !isalpha(*valuearg) )     /* and against alpha string args */
9071:           if ( !isthischar(*valuearg,"?") ) /*leading ? is query for value*/
9072:            { isdelta = isthischar(*valuearg,"+-"); /* leading + or - */
9073:              if ( memcmp(valuearg,"--",2) == 0 ) /* leading -- signals...*/
9074:                { isdelta=0; strsqueeze(valuearg,1); } /* ...not delta */
9075:              switch ( flag ) {          /* convert to double or int */
9076:               default: argvalue = atoi(valuearg); break; /* convert to int */
9077:               case ISGAMMA:
9078:               case ISUTHETA:
9079:                 dblvalue = strtod(valuearg,NULL); break; } /* or to double */
9080:            } /* --- end-of-if(*valuearg!='?') --- */
9081:       } /* --- end-of-if(value==NOVALUE) --- */
9082:     switch ( flag )
9083:       {
9084:       default: break;
9085:       case ISCOLOR:                     /* set color */
9086:         slower(valuearg);               /* convert arg to lower case */
9087:         if ( argvalue==1 || strstr(valuearg,"red") )
9088:           { fggreen = fgblue = (isblackonwhite?0:255);
9089:             fgred = (isblackonwhite?255:0); }
9090:         if ( argvalue==2 || strstr(valuearg,"green") )
9091:           { fgred = fgblue = (isblackonwhite?0:255);
9092:             fggreen = (isblackonwhite?255:0); }
9093:         if ( argvalue==3 || strstr(valuearg,"blue") )
9094:           { fgred = fggreen = (isblackonwhite?0:255);
9095:             fgblue = (isblackonwhite?255:0); }
9096:         if ( argvalue==0 || strstr(valuearg,"black") )
9097:             fgred = fggreen = fgblue = (isblackonwhite?0:255);
9098:         if ( argvalue==7 || strstr(valuearg,"white") )
9099:             fgred = fggreen = fgblue = (isblackonwhite?255:0);
9100:         break;
9101:       case ISFONTSIZE:                  /* set fontsize */
9102:         if ( argvalue != NOVALUE )      /* got a value */
9103:           { int largestsize = (issupersampling?16:LARGESTSIZE);
9104:             fontsize = (isdelta? fontsize+argvalue : argvalue);
9105:             fontsize = max2(0,min2(fontsize,largestsize));
9106:             shrinkfactor = shrinkfactors[fontsize];
9107:             if ( isdisplaystyle == 1    /* displaystyle enabled but not set*/
9108:             ||  (1 && isdisplaystyle==2) /* displaystyle enabled and set */
9109:             ||  (0 && isdisplaystyle==0) )/*\textstyle disabled displaystyle*/
9110:              if ( displaystylelevel != recurlevel ) /*respect \displaystyle*/
9111:               if ( !ispreambledollars ) { /* respect $$...$$'s */
9112:                if ( fontsize >= displaysize )
9113:                 isdisplaystyle = 2;     /* forced */
9114:                else isdisplaystyle = 1; }
9115:             /*displaystylelevel = (-99);*/ } /* reset \displaystyle level */
9116:         else                            /* embed font size in expression */
9117:           { sprintf(valuearg,"%d",fontsize); /* convert size */
9118:             valuelen = strlen(valuearg); /* ought to be 1 */
9119:             if ( *expression != '\000' ) /* ill-formed expression */
9120:              { *expression = (char *)(*expression-valuelen); /*back up buff*/
9121:                memcpy(*expression,valuearg,valuelen); } } /*and put in size*/
9122:         break;
9123:       case ISMAGSTEP:                   /* set magstep */
9124:         if ( argvalue != NOVALUE ) {    /* got a value */
9125:           int largestmag = 10;
9126:           magstep = (isdelta? magstep+argvalue : argvalue);
9127:           magstep = max2(1,min2(magstep,largestmag)); }
9128:         break;
9129:       case ISDISPLAYSIZE:               /* set displaysize */
9130:         if ( argvalue != NOVALUE )      /* got a value */
9131:             displaysize = (isdelta? displaysize+argvalue : argvalue);
9132:         break;
9133:       case ISCONTENTTYPE:               /*enable/disable content-type lines*/
9134:         if ( argvalue != NOVALUE )      /* got a value */
9135:             isemitcontenttype = (argvalue>0?1:0);
9136:         break;
9137:       case ISCONTENTCACHED:             /* write content-type to cache file*/
9138:         if ( argvalue != NOVALUE )      /* got a value */
9139:             iscachecontenttype = (argvalue>0?1:0);
9140:         break;
9141:       case ISPBMPGM:                    /* output pbm/pgm rather than gif */
9142:         ispbmpgm = 1;                   /* always set pbm/pgm flag */
9143:         pbmpgmtype = 1;                 /* and init type (1 for pbm) */
9144:         if ( argvalue != NOVALUE )      /* got a value */
9145:             pbmpgmtype = (argvalue<1?1:argvalue);
9146:         break;
9147:       case ISSMASH:                     /* set (minimum) "smash" margin */
9148:         if ( argvalue != NOVALUE )      /* got a value */
9149:           { smashmargin = argvalue;     /* set value */
9150:             if ( arg3 != NOVALUE ) isdelta=arg3; /* hard-coded isdelta */
9151:             issmashdelta = (isdelta?1:0); } /* and set delta flag */
9152:         smashmargin = max2((isdelta?-5:0),min2(smashmargin,32)); /*sanity*/
9153:         isexplicitsmash = 1;            /* signal explicit \smash directive*/
9154:         break;
9155:       case ISSHRINK:                    /* set shrinkfactor */
9156:         if ( argvalue != NOVALUE )      /* got a value */
9157:           shrinkfactor = (isdelta? shrinkfactor+argvalue : argvalue);
9158:         shrinkfactor = max2(1,min2(shrinkfactor,27)); /* sanity check */
9159:         break;
9160:       case ISAAALGORITHM:               /* set anti-aliasing algorithm */
9161:         if ( argvalue != NOVALUE ) {    /* got a value */
9162:           if ( argvalue >= 0 ) {        /* non-negative to set algorithm */
9163:               aaalgorithm = argvalue;   /* set algorithm number */
9164:             aaalgorithm = max2(0,min2(aaalgorithm,4)); } /* bounds check */
9165:           else maxfollow = abs(argvalue); } /* or maxfollow=abs(negative#) */
9166:         break;
9167:       case ISWEIGHT:                    /* set font weight number */
9168:         value = (argvalue==NOVALUE? NOVALUE : /* don't have a value */
9169:                 (isdelta? weightnum+argvalue : argvalue));
9170:         if ( value>=0 && value<maxaaparams ) /* in range */
9171:           { weightnum   = value;        /* reset weightnum index */
9172:             minadjacent = aaparams[weightnum].minadjacent;
9173:             maxadjacent = aaparams[weightnum].maxadjacent;
9174:             cornerwt    = aaparams[weightnum].cornerwt;
9175:             adjacentwt  = aaparams[weightnum].adjacentwt;
9176:             centerwt    = aaparams[weightnum].centerwt;
9177:             fgalias     = aaparams[weightnum].fgalias;
9178:             fgonly      = aaparams[weightnum].fgonly;
9179:             bgalias     = aaparams[weightnum].bgalias;
9180:             bgonly      = aaparams[weightnum].bgonly; }
9181:         break;
9182:       case ISCENTERWT:                  /* set lowpass center pixel weight */
9183:         if ( argvalue != NOVALUE )      /* got a value */
9184:           centerwt = argvalue;          /* set lowpass center weight */
9185:         break;
9186:       case ISADJACENTWT:                /* set lowpass adjacent weight */
9187:         if ( argvalue != NOVALUE )      /* got a value */
9188:           adjacentwt = argvalue;        /* set lowpass adjacent weight */
9189:         break;
9190:       case ISCORNERWT:                  /* set lowpass corner weight */
9191:         if ( argvalue != NOVALUE )      /* got a value */
9192:           cornerwt = argvalue;          /* set lowpass corner weight */
9193:         break;
9194:       case ISGAMMA:                     /* set gamma correction */
9195:         if ( dblvalue >= 0.0 )          /* got a value */
9196:           gammacorrection = dblvalue;   /* set gamma correction */
9197:         break;
9198:       case ISUTHETA:                    /* set 3d-rotation angle for chars */
9199:         if ( absval(dblvalue) >= 1.0e-6 ) /* got a value */
9200:           utheta = dblvalue;            /* set utheta */
9201:         break;
9202:       } /* --- end-of-switch() --- */
9203:     break;
9204:   case PNMPARAMS:                       /*set fgalias,fgonly,bgalias,bgonly*/
9205:     *expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0);
9206:     valuelen = strlen(valuearg);        /* ought to be 1-4 */
9207:     if ( valuelen>0 && isthischar(toupper(valuearg[0]),"TY1") ) fgalias=1;
9208:     if ( valuelen>0 && isthischar(toupper(valuearg[0]),"FN0") ) fgalias=0;
9209:     if ( valuelen>1 && isthischar(toupper(valuearg[1]),"TY1") ) fgonly =1;
9210:     if ( valuelen>1 && isthischar(toupper(valuearg[1]),"FN0") ) fgonly =0;
9211:     if ( valuelen>2 && isthischar(toupper(valuearg[2]),"TY1") ) bgalias=1;
9212:     if ( valuelen>2 && isthischar(toupper(valuearg[2]),"FN0") ) bgalias=0;
9213:     if ( valuelen>3 && isthischar(toupper(valuearg[3]),"TY1") ) bgonly =1;
9214:     if ( valuelen>3 && isthischar(toupper(valuearg[3]),"FN0") ) bgonly =0;
9215:     break;
9216:   case UNITLENGTH:
9217:     if ( value != NOVALUE )             /* passed a fixed value to be set */
9218:         unitlength = (double)(value);   /* set given fixed value */
9219:     else                                /* get value from expression */
9220:       { *expression = texsubexpr(*expression,valuearg,1023,"{","}",0,0);
9221:         if ( *valuearg != '\000' )      /* guard against empty string */
9222:           unitlength = strtod(valuearg,NULL); } /* convert to double */
9223:     iunitlength = (int)(unitlength+0.5); /* iunitlength reset */
9224:     break;
9225:   } /* --- end-of-switch(flag) --- */
9226: return ( NULL );                        /*just set value, nothing to display*/
9227: } /* --- end-of-function rastflags() --- */
9228: 
9229: 
9230: /* ==========================================================================
9231:  * Function:    rastspace(expression, size, basesp,  width, isfill, isheight)
9232:  * Purpose:     returns a blank/space subraster width wide,
9233:  *              with baseline and height corresponding to basep
9234:  * --------------------------------------------------------------------------
9235:  * Arguments:   expression (I)  char **  to first char of null-terminated
9236:  *                              LaTeX expression (unused/unchanged)
9237:  *              size (I)        int containing base font size (not used,
9238:  *                              just stored in subraster)
9239:  *              basesp (I)      subraster *  to character (or subexpression)
9240:  *                              immediately preceding space, whose baseline
9241:  *                              and height params are transferred to space
9242:  *              width (I)       int containing #bits/pixels for space width
9243:  *              isfill (I)      int containing true to \hfill complete
9244:  *                              expression out to width
9245:  *                              (Kludge: isfill=99 signals \hspace*
9246:  *                              for negative space)
9247:  *              isheight (I)    int containing true (but not NOVALUE)
9248:  *                              to treat width arg as height
9249:  * --------------------------------------------------------------------------
9250:  * Returns:     ( subraster * ) ptr to empty/blank subraster
9251:  *                              or NULL for any error
9252:  * --------------------------------------------------------------------------
9253:  * Notes:     o
9254:  * ======================================================================= */
9255: /* --- entry point --- */
9256: subraster *rastspace ( char **expression, int size, subraster *basesp,
9257:                         int width, int isfill, int isheight )
9258: {
9259: /* -------------------------------------------------------------------------
9260: Allocations and Declarations
9261: -------------------------------------------------------------------------- */
9262: subraster *new_subraster(), *spacesp=NULL; /* subraster for space */
9263: raster  *bp=NULL, *backspace_raster();  /* for negative space */
9264: int     delete_subraster();             /* if fail, free unneeded subraster*/
9265: int     baseht=1, baseln=0;             /* height,baseline of base symbol */
9266: int     pixsz = 1;                      /*default #bits per pixel, 1=bitmap*/
9267: int     isstar=0, minspace=0;           /* defaults for negative hspace */
9268: int     maxwidth = 2047;                /* max width of expression in pixels*/
9269: char    *texsubexpr(), widtharg[256];   /* parse for optional {width} */
9270: int     evalterm(), evalue=0;           /* evaluate [args], {args} */
9271: subraster *rasterize(), *rightsp=NULL;  /*rasterize right half of expression*/
9272: subraster *rastcat();                   /* cat rightsp after \hfill */
9273: /* -------------------------------------------------------------------------
9274: initialization
9275: -------------------------------------------------------------------------- */
9276: if ( isfill > 1 ) { isstar=1; isfill=0; } /* large fill signals \hspace* */
9277: if ( isfill == NOVALUE ) isfill=0;      /* novalue means false */
9278: if ( isheight == NOVALUE ) isheight=0;  /* novalue means false */
9279: minspace = (isstar?(-1):0);             /* reset default minspace */
9280: /* -------------------------------------------------------------------------
9281: determine width if not given (e.g., \hspace{width}, \hfill{width})
9282: -------------------------------------------------------------------------- */
9283: if ( width == 0 ) {                     /* width specified in expression */
9284:   double dwidth;  int widthval;         /* test {width} before using it */
9285:   int minwidth = (isfill||isheight?1:-maxwidth); /* \hspace allows negative */
9286:   /* --- check if optional [minspace] given for negative \hspace --- */
9287:   if ( *(*expression) == '[' ) {        /* [minspace] if leading char is [ */
9288:     /* ---parse [minspace], bump expression past it, evaluate expression--- */
9289:     *expression = texsubexpr(*expression,widtharg,127,"[","]",0,0);
9290:     if ( !isempty(widtharg) ) {         /* got [minspace] */
9291:       evalue = evalterm(mimestore,widtharg); /* evaluate widtharg expr */
9292:       minspace = iround(unitlength*((double)evalue)); } /* in pixels */
9293:     } /* --- end-of-if(*(*expression)=='[') --- */
9294:   width = 1;                            /* set default width */
9295:   *expression = texsubexpr(*expression,widtharg,255,"{","}",0,0);
9296:   dwidth = unitlength*((double)evalterm(mimestore,widtharg)); /* scaled */
9297:   widthval =                            /* convert {width} to integer */
9298:                 (int)( dwidth + (dwidth>=0.0?0.5:(-0.5)) );
9299:   if ( widthval>=minwidth && widthval<=maxwidth ) /* sanity check */
9300:     width = widthval;                   /* replace deafault width */
9301:   } /* --- end-of-if(width==0) --- */
9302: /* -------------------------------------------------------------------------
9303: first check for negative space
9304: -------------------------------------------------------------------------- */
9305: if ( width < 0 ) {                      /* have negative hspace */
9306:  if ( leftexpression != (subraster *)NULL ) /* can't backspace */
9307:   if ( (spacesp=new_subraster(0,0,0))   /* get new subraster for backspace */
9308:   !=   NULL ) {                         /* and if we succeed... */
9309:    int nback=(-width), pback;           /*#pixels wanted,actually backspaced*/
9310:    if ( (bp=backspace_raster(leftexpression->image,nback,&pback,minspace,0))
9311:    !=    NULL ) {                       /* and if backspace succeeds... */
9312:      spacesp->image = bp;               /* save backspaced image */
9313:      /*spacesp->type = leftexpression->type;*/ /* copy original type */
9314:      spacesp->type = blanksignal;       /* need to propagate blanks */
9315:      spacesp->size = leftexpression->size; /* copy original font size */
9316:      spacesp->baseline = leftexpression->baseline; /* and baseline */
9317:      blanksymspace += -(nback-pback);   /* wanted more than we got */
9318:      isreplaceleft = 1; }               /*signal to replace entire expressn*/
9319:    else {                               /* backspace failed */
9320:      delete_subraster(spacesp);         /* free unneeded envelope */
9321:      spacesp = (subraster *)NULL; } }   /* and signal failure */
9322:  goto end_of_job;
9323:  } /* --- end-of-if(width<0) --- */
9324: /* -------------------------------------------------------------------------
9325: see if width is "absolute" or fill width
9326: -------------------------------------------------------------------------- */
9327: if ( isfill                             /* called as \hfill{} */
9328: &&   !isheight )                        /* parameter conflict */
9329:  { if ( leftexpression != NULL )        /* if we have left half */
9330:     width -= (leftexpression->image)->width; /*reduce left width from total*/
9331:    if ( (rightsp=rasterize(*expression,size)) /* rasterize right half */
9332:    != NULL )                            /* succeeded */
9333:     width -= (rightsp->image)->width; } /* reduce right width from total */
9334: /* -------------------------------------------------------------------------
9335: construct blank subraster, and return it to caller
9336: -------------------------------------------------------------------------- */
9337: /* --- get parameters from base symbol --- */
9338: if ( basesp != (subraster *)NULL )      /* we have base symbol for space */
9339:   { baseht = (basesp->image)->height;   /* height of base symbol */
9340:     baseln =  basesp->baseline; }       /* and its baseline */
9341: /* --- flip params for height --- */
9342: if ( isheight )                         /* width is actually height */
9343:   { baseht = width;                     /* use given width as height */
9344:     width = 1; }                        /* and set default width */
9345: /* --- generate and init space subraster --- */
9346: if ( width > 0 )                        /*make sure we have positive width*/
9347:  if ( (spacesp=new_subraster(width,baseht,pixsz)) /*generate space subraster*/
9348:  !=   NULL )                            /* and if we succeed... */
9349:   { /* --- ...re-init subraster parameters --- */
9350:     spacesp->size = size;               /*propagate base font size forward*/
9351:     if(1)spacesp->type = blanksignal;   /* need to propagate blanks (???) */
9352:     spacesp->baseline = baseln; }       /* ditto baseline */
9353: /* -------------------------------------------------------------------------
9354: concat right half if \hfill-ing
9355: -------------------------------------------------------------------------- */
9356: if ( rightsp != NULL )                  /* we have a right half after fill */
9357:   { spacesp = (spacesp==NULL? rightsp:  /* no space, so just use right half*/
9358:         rastcat(spacesp,rightsp,3));    /* or cat right half after space */
9359:     spacesp->type = blanksignal;        /* need to propagate blanks */
9360:     *expression += strlen((*expression)); } /* push expression to its null */
9361: end_of_job:
9362:   return ( spacesp );
9363: } /* --- end-of-function rastspace() --- */
9364: 
9365: 
9366: /* ==========================================================================
9367:  * Function:    rastnewline ( expression, size, basesp,  arg1, arg2, arg3 )
9368:  * Purpose:     \\ handler, returns subraster corresponding to
9369:  *              left-hand expression preceding \\ above right-hand expression
9370:  * --------------------------------------------------------------------------
9371:  * Arguments:   expression (I/O) char **  to first char of null-terminated
9372:  *                              string immediately following \\ to be
9373:  *                              rasterized, and returning ptr immediately
9374:  *                              to terminating null.
9375:  *              size (I)        int containing 0-7 default font size
9376:  *              basesp (I)      subraster *  to character (or subexpression)
9377:  *                              immediately preceding \\
9378:  *                              (unused, but passed for consistency)
9379:  *              arg1 (I)        int unused
9380:  *              arg2 (I)        int unused
9381:  *              arg3 (I)        int unused
9382:  * --------------------------------------------------------------------------
9383:  * Returns:     ( subraster * ) ptr to subraster corresponding to expression,
9384:  *                              or NULL for any parsing error
9385:  *                              (expression ptr unchanged if error occurs)
9386:  * --------------------------------------------------------------------------
9387:  * Notes:     o
9388:  * ======================================================================= */
9389: /* --- entry point --- */
9390: subraster *rastnewline ( char **expression, int size, subraster *basesp,
9391:                         int arg1, int arg2, int arg3 )
9392: {
9393: /* -------------------------------------------------------------------------
9394: Allocations and Declarations
9395: -------------------------------------------------------------------------- */
9396: subraster *rastack(), *newlsp=NULL;     /* subraster for both lines */
9397: subraster *rasterize(), *rightsp=NULL;  /*rasterize right half of expression*/
9398: char    *texsubexpr(), spacexpr[129]/*, *xptr=spacexpr*/; /*for \\[vspace]*/
9399: int     evalterm(), evalue=0;           /* evaluate [arg], {arg} */
9400: int     vspace = size+2;                /* #pixels between lines */
9401: /* -------------------------------------------------------------------------
9402: obtain optional [vspace] argument immediately following \\ command
9403: -------------------------------------------------------------------------- */
9404: /* --- check if [vspace] given --- */
9405: if ( *(*expression) == '[' )            /*have [vspace] if leading char is [*/
9406:   {
9407:   /* ---parse [vspace] and bump expression past it, interpret as double--- */
9408:   *expression = texsubexpr(*expression,spacexpr,127,"[","]",0,0);
9409:   if ( *spacexpr == '\000' ) goto end_of_job; /* couldn't get [vspace] */
9410:   evalue = evalterm(mimestore,spacexpr); /* evaluate [space] arg */
9411:   vspace = iround(unitlength*((double)evalue)); /* vspace in pixels */
9412:   } /* --- end-of-if(*(*expression)=='[') --- */
9413: if ( leftexpression == NULL ) goto end_of_job; /* nothing preceding \\ */
9414: /* -------------------------------------------------------------------------
9415: rasterize right half of expression and stack left half above it
9416: -------------------------------------------------------------------------- */
9417: /* --- rasterize right half --- */
9418: if ( (rightsp=rasterize(*expression,size)) /* rasterize right half */
9419: == NULL ) goto end_of_job;              /* quit if failed */
9420: /* --- stack left half above it --- */
9421: /*newlsp = rastack(rightsp,leftexpression,1,vspace,0,3);*//*right under left*/
9422: newlsp = rastack(rightsp,leftexpression,1,vspace,0,1); /*right under left*/
9423: /* --- back to caller --- */
9424: end_of_job:
9425:   if ( newlsp != NULL )                 /* returning entire expression */
9426:     { int newht = (newlsp->image)->height; /* height of returned subraster */
9427:       newlsp->baseline = min2(newht-1,newht/2+5); /* guess new baseline */
9428:       isreplaceleft = 1;                /* so set flag to replace left half*/
9429:       *expression += strlen(*expression); } /* and push to terminating null*/
9430:   return ( newlsp );                    /* 1st line over 2nd, or null=error*/
9431: } /* --- end-of-function rastnewline() --- */
9432: 
9433: 
9434: /* ==========================================================================
9435:  * Function:    rastarrow ( expression, size, basesp,  drctn, isBig, arg3 )
9436:  * Purpose:     returns left/right arrow subraster (e.g., for \longrightarrow)
9437:  * --------------------------------------------------------------------------
9438:  * Arguments:   expression (I)  char **  to first char of null-terminated
9439:  *                              LaTeX expression (unused/unchanged)
9440:  *              size (I)        int containing base font size (not used,
9441:  *                              just stored in subraster)
9442:  *              basesp (I)      subraster *  to character (or subexpression)
9443:  *                              immediately preceding space, whose baseline
9444:  *                              and height params are transferred to space
9445:  *              drctn (I)       int containing +1 for right, -1 for left,
9446:  *                              or 0 for leftright
9447:  *              isBig (I)       int containing 0 for ---> or 1 for ===>
9448:  *              arg3 (I)        int unused
9449:  * --------------------------------------------------------------------------
9450:  * Returns:     ( subraster * ) ptr to left/right arrow subraster
9451:  *                              or NULL for any error
9452:  * --------------------------------------------------------------------------
9453:  * Notes:     o An optional argument [width] may *immediately* follow
9454:  *              the \longxxx to explicitly set the arrow's width in pixels.
9455:  *              For example, \longrightarrow calculates a default width
9456:  *              (as usual in LaTeX), whereas \longrightarrow[50] explicitly
9457:  *              draws a 50-pixel long arrow.  This can be used, e.g.,
9458:  *              to draw commutative diagrams in conjunction with
9459:  *              \array (and maybe with \stackrel and/or \relstack, too).
9460:  *            o In case you really want to render, say, [f]---->[g], just
9461:  *              use an intervening space, i.e., [f]\longrightarrow~[g].
9462:  *              In text mode use two spaces {\rm~[f]\longrightarrow~~[g]}.
9463:  * ======================================================================= */
9464: /* --- entry point --- */
9465: subraster *rastarrow ( char **expression, int size, subraster *basesp,
9466:                         int drctn, int isBig, int arg3 )
9467: {
9468: /* -------------------------------------------------------------------------
9469: Allocations and Declarations
9470: -------------------------------------------------------------------------- */
9471: subraster *arrow_subraster(), *arrowsp=NULL; /* subraster for arrow */
9472: char    *texsubexpr(), widtharg[256];   /* parse for optional [width] */
9473: char    *texscripts(), sub[1024],super[1024]; /* and _^limits after [width]*/
9474: subraster *rasterize(), *subsp=NULL,*supsp=NULL; /*rasterize limits*/
9475: subraster *new_subraster(), *rastack(), *spacesp=NULL; /*space below arrow*/
9476: int     delete_subraster();             /*free work areas in case of error*/
9477: int     evalterm();                     /* evaluate [arg], {arg} */
9478: int     width = 10 + 8*size,  height;   /* width, height for \longxxxarrow */
9479: int     maxwidth = 1024;                /* max arrow width in pixels */
9480: int     islimits = 1;                   /*true to handle limits internally*/
9481: int     limsize = size-1;               /* font size for limits */
9482: int     vspace = 1;                     /* #empty rows below arrow */
9483: int     pixsz = 1;                      /*default #bits per pixel, 1=bitmap*/
9484: /* -------------------------------------------------------------------------
9485: construct longleft/rightarrow subraster, with limits, and return it to caller
9486: -------------------------------------------------------------------------- */
9487: /* --- check for optional width arg and replace default width --- */
9488: if ( *(*expression) == '[' )            /*check for []-enclosed optional arg*/
9489:   { int widthval;                       /* test [width] before using it */
9490:     *expression = texsubexpr(*expression,widtharg,255,"[","]",0,0);
9491:     widthval =                          /* convert [width] to integer */
9492:         (int)((unitlength*((double)evalterm(mimestore,widtharg)))+0.5);
9493:     if ( widthval>=2 && widthval<=maxwidth )    /* sanity check */
9494:       width = widthval; }               /* replace deafault width */
9495: /* --- now parse for limits, and bump expression past it(them) --- */
9496: if ( islimits )                         /* handling limits internally */
9497:   { *expression = texscripts(*expression,sub,super,3); /* parse for limits */
9498:     if ( *sub != '\000' )               /*have a subscript following arrow*/
9499:       subsp = rasterize(sub,limsize);   /* so try to rasterize subscript */
9500:     if ( *super != '\000' )             /*have superscript following arrow*/
9501:       supsp = rasterize(super,limsize); } /*so try to rasterize superscript*/
9502: /* --- set height based on width --- */
9503: height = min2(17,max2(9,(width+2)/6));  /* height based on width */
9504: height = 1 + (height/2)*2;              /* always force odd height */
9505: /* --- generate arrow subraster --- */
9506: if ( (arrowsp=arrow_subraster(width,height,pixsz,drctn,isBig)) /*build arrow*/
9507: ==   NULL ) goto end_of_job;            /* and quit if we failed */
9508: /* --- add space below arrow --- */
9509: if ( vspace > 0 )                       /* if we have space below arrow */
9510:   if ( (spacesp=new_subraster(width,vspace,pixsz)) /*allocate required space*/
9511:   !=   NULL )                           /* and if we succeeded */
9512:     if ( (arrowsp = rastack(spacesp,arrowsp,2,0,1,3)) /* space below arrow */
9513:     ==   NULL ) goto end_of_job;        /* and quit if we failed */
9514: /* --- init arrow subraster parameters --- */
9515: arrowsp->size = size;                   /*propagate base font size forward*/
9516: arrowsp->baseline = height+vspace-1;    /* set baseline at bottom of arrow */
9517: /* --- add limits above/below arrow, as necessary --- */
9518: if ( subsp != NULL )                    /* stack subscript below arrow */
9519:   if ( (arrowsp = rastack(subsp,arrowsp,2,0,1,3)) /* subscript below arrow */
9520:   ==   NULL ) goto end_of_job;          /* quit if failed */
9521: if ( supsp != NULL )                    /* stack superscript above arrow */
9522:   if ( (arrowsp = rastack(arrowsp,supsp,1,vspace,1,3)) /*supsc above arrow*/
9523:   ==   NULL ) goto end_of_job;          /* quit if failed */
9524: /* --- return arrow (or NULL) to caller --- */
9525: end_of_job:
9526:   return ( arrowsp );
9527: } /* --- end-of-function rastarrow() --- */
9528: 
9529: 
9530: /* ==========================================================================
9531:  * Function:    rastuparrow ( expression, size, basesp,  drctn, isBig, arg3 )
9532:  * Purpose:     returns an up/down arrow subraster (e.g., for \longuparrow)
9533:  * --------------------------------------------------------------------------
9534:  * Arguments:   expression (I)  char **  to first char of null-terminated
9535:  *                              LaTeX expression (unused/unchanged)
9536:  *              size (I)        int containing base font size (not used,
9537:  *                              just stored in subraster)
9538:  *              basesp (I)      subraster *  to character (or subexpression)
9539:  *                              immediately preceding space, whose baseline
9540:  *                              and height params are transferred to space
9541:  *              drctn (I)       int containing +1 for up, -1 for down,
9542:  *                              or 0 for updown
9543:  *              isBig (I)       int containing 0 for ---> or 1 for ===>
9544:  *              arg3 (I)        int unused
9545:  * --------------------------------------------------------------------------
9546:  * Returns:     ( subraster * ) ptr to up/down arrow subraster
9547:  *                              or NULL for any error
9548:  * --------------------------------------------------------------------------
9549:  * Notes:     o An optional argument [height] may *immediately* follow
9550:  *              the \longxxx to explicitly set the arrow's height in pixels.
9551:  *              For example, \longuparrow calculates a default height
9552:  *              (as usual in LaTeX), whereas \longuparrow[25] explicitly
9553:  *              draws a 25-pixel high arrow.  This can be used, e.g.,
9554:  *              to draw commutative diagrams in conjunction with
9555:  *              \array (and maybe with \stackrel and/or \relstack, too).
9556:  *            o In case you really want to render, say, [f]---->[g], just
9557:  *              use an intervening space, i.e., [f]\longuparrow~[g].
9558:  *              In text use two spaces {\rm~[f]\longuparrow~~[g]}.
9559:  * ======================================================================= */
9560: /* --- entry point --- */
9561: subraster *rastuparrow ( char **expression, int size, subraster *basesp,
9562:                         int drctn, int isBig, int arg3 )
9563: {
9564: /* -------------------------------------------------------------------------
9565: Allocations and Declarations
9566: -------------------------------------------------------------------------- */
9567: subraster *uparrow_subraster(), *arrowsp=NULL; /* subraster for arrow */
9568: char    *texsubexpr(), heightarg[256];  /* parse for optional [height] */
9569: char    *texscripts(), sub[1024],super[1024]; /* and _^limits after [width]*/
9570: subraster *rasterize(), *subsp=NULL,*supsp=NULL; /*rasterize limits*/
9571: subraster *rastcat();                   /* cat superscript left, sub right */
9572: int     evalterm();                     /* evaluate [arg], {arg} */
9573: int     height = 8 + 2*size,  width;    /* height, width for \longxxxarrow */
9574: int     maxheight = 800;                /* max arrow height in pixels */
9575: int     islimits = 1;                   /*true to handle limits internally*/
9576: int     limsize = size-1;               /* font size for limits */
9577: int     pixsz = 1;                      /*default #bits per pixel, 1=bitmap*/
9578: /* -------------------------------------------------------------------------
9579: construct blank subraster, and return it to caller
9580: -------------------------------------------------------------------------- */
9581: /* --- check for optional height arg and replace default height --- */
9582: if ( *(*expression) == '[' )            /*check for []-enclosed optional arg*/
9583:   { int heightval;                      /* test height before using it */
9584:     *expression = texsubexpr(*expression,heightarg,255,"[","]",0,0);
9585:     heightval =                         /* convert [height] to integer */
9586:         (int)((unitlength*((double)evalterm(mimestore,heightarg)))+0.5);
9587:     if ( heightval>=2 && heightval<=maxheight ) /* sanity check */
9588:       height = heightval; }             /* replace deafault height */
9589: /* --- now parse for limits, and bump expression past it(them) --- */
9590: if ( islimits )                         /* handling limits internally */
9591:   { *expression = texscripts(*expression,sub,super,3); /* parse for limits */
9592:     if ( *sub != '\000' )               /*have a subscript following arrow*/
9593:       subsp = rasterize(sub,limsize);   /* so try to rasterize subscript */
9594:     if ( *super != '\000' )             /*have superscript following arrow*/
9595:       supsp = rasterize(super,limsize); } /*so try to rasterize superscript*/
9596: /* --- set width based on height --- */
9597: width = min2(17,max2(9,(height+2)/4));  /* width based on height */
9598: width = 1 + (width/2)*2;                /* always force odd width */
9599: /* --- generate arrow subraster --- */
9600: if ( (arrowsp=uparrow_subraster(width,height,pixsz,drctn,isBig)) /*build arr*/
9601: ==   NULL ) goto end_of_job;            /* and quit if we failed */
9602: /* --- init arrow subraster parameters --- */
9603: arrowsp->size = size;                   /*propagate base font size forward*/
9604: arrowsp->baseline = height-1;           /* set baseline at bottom of arrow */
9605: /* --- add limits above/below arrow, as necessary --- */
9606: if ( supsp != NULL )                    /* cat superscript to left of arrow*/
9607:   { int supht = (supsp->image)->height, /* superscript height */
9608:         deltab = (1+abs(height-supht))/2; /* baseline difference to center */
9609:   supsp->baseline = supht-1;            /* force script baseline to bottom */
9610:   if ( supht <= height )                /* arrow usually taller than script*/
9611:         arrowsp->baseline -= deltab;    /* so bottom of script goes here */
9612:   else  supsp->baseline -= deltab;      /* else bottom of arrow goes here */
9613:   if ( (arrowsp = rastcat(supsp,arrowsp,3)) /* superscript left of arrow */
9614:     ==   NULL ) goto end_of_job; }      /* quit if failed */
9615: if ( subsp != NULL )                    /* cat subscript to right of arrow */
9616:   { int subht = (subsp->image)->height, /* subscript height */
9617:         deltab = (1+abs(height-subht))/2; /* baseline difference to center */
9618:   arrowsp->baseline = height-1;         /* reset arrow baseline to bottom */
9619:   subsp->baseline = subht-1;            /* force script baseline to bottom */
9620:   if ( subht <= height )                /* arrow usually taller than script*/
9621:         arrowsp->baseline -= deltab;    /* so bottom of script goes here */
9622:   else  subsp->baseline -= deltab;      /* else bottom of arrow goes here */
9623:   if ( (arrowsp = rastcat(arrowsp,subsp,3)) /* subscript right of arrow */
9624:     ==   NULL ) goto end_of_job; }      /* quit if failed */
9625: /* --- return arrow (or NULL) to caller --- */
9626: end_of_job:
9627:   arrowsp->baseline = height-1;         /* reset arrow baseline to bottom */
9628:   return ( arrowsp );
9629: } /* --- end-of-function rastuparrow() --- */
9630: 
9631: 
9632: /* ==========================================================================
9633:  * Function:    rastoverlay (expression, size, basesp, overlay, offset2, arg3)
9634:  * Purpose:     overlays one raster on another
9635:  * --------------------------------------------------------------------------
9636:  * Arguments:   expression (I/O) char **  to first char of null-terminated
9637:  *                              string immediately following overlay \cmd to
9638:  *                              be rasterized, and returning ptr immediately
9639:  *                              following last character processed.
9640:  *              size (I)        int containing 0-7 default font size
9641:  *              basesp (I)      subraster *  to character (or subexpression)
9642:  *                              immediately preceding overlay \cmd
9643:  *                              (unused, but passed for consistency)
9644:  *              overlay (I)     int containing 1 to overlay / (e.g., \not)
9645:  *                              or NOVALUE to pick up 2nd arg from expression
9646:  *              offset2 (I)     int containing #pixels to horizontally offset
9647:  *                              overlay relative to underlying symbol,
9648:  *                              positive(right) or negative or 0,
9649:  *                              or NOVALUE to pick up optional [offset] arg
9650:  *              arg3 (I)        int unused
9651:  * --------------------------------------------------------------------------
9652:  * Returns:     ( subraster * ) ptr to subraster corresponding to composite,
9653:  *                              or NULL for any parsing error
9654:  * --------------------------------------------------------------------------
9655:  * Notes:     o
9656:  * ======================================================================= */
9657: /* --- entry point --- */
9658: subraster *rastoverlay ( char **expression, int size, subraster *basesp,
9659:                         int overlay, int offset2, int arg3 )
9660: {
9661: /* -------------------------------------------------------------------------
9662: Allocations and Declarations
9663: -------------------------------------------------------------------------- */
9664: char    *texsubexpr(),                  /*parse expression for base,overlay*/
9665:         expr1[512], expr2[512];         /* base, overlay */
9666: subraster *rasterize(), *sp1=NULL, *sp2=NULL, /*rasterize 1=base, 2=overlay*/
9667:         *new_subraster();               /*explicitly alloc sp2 if necessary*/
9668: subraster *rastcompose(), *overlaysp=NULL; /*subraster for composite overlay*/
9669: int     isalign = 0;                    /* true to align baselines */
9670: int     line_raster();                  /* draw diagonal for \Not */
9671: int     evalterm();                     /* evaluate [arg], {arg} */
9672: /* -------------------------------------------------------------------------
9673: Obtain base, and maybe overlay, and rasterize them
9674: -------------------------------------------------------------------------- */
9675: /* --- check for optional offset2 arg  --- */
9676: if ( offset2 == NOVALUE )               /* only if not explicitly specified*/
9677:  if ( *(*expression) == '[' )           /*check for []-enclosed optional arg*/
9678:   { int offsetval;                      /* test before using it */
9679:     *expression = texsubexpr(*expression,expr2,511,"[","]",0,0);
9680:     offsetval =                         /* convert [offset2] to int */
9681:         (int)(((double)evalterm(mimestore,expr2))+0.5);
9682:     if ( abs(offsetval) <= 25 )         /* sanity check */
9683:       offset2 = offsetval; }            /* replace deafault */
9684: if ( offset2 == NOVALUE ) offset2 = 0;  /* novalue means no offset */
9685: /* --- parse for base, bump expression past it, and rasterize it --- */
9686: *expression = texsubexpr(*expression,expr1,511,"{","}",0,0);
9687: if ( isempty(expr1) ) goto end_of_job;  /* nothing to overlay, so quit */
9688: rastlift1 = rastlift = 0;               /* reset all raisebox() lifts */
9689: if ( strstr(expr1,"\\raise") != NULL )  /* have a \raisebox */
9690:   isalign = 2;                          /* so align baselines */
9691: if ( (sp1=rasterize(expr1,size))        /* rasterize base expression */
9692: ==   NULL ) goto end_of_job;            /* quit if failed to rasterize */
9693: overlaysp = sp1;                        /*in case we return with no overlay*/
9694: rastlift1 = rastlift;                   /* save lift for base expression */
9695: /* --- get overlay expression, and rasterize it --- */
9696: if ( overlay == NOVALUE )               /* get overlay from input stream */
9697:   { *expression = texsubexpr(*expression,expr2,511,"{","}",0,0);
9698:     if ( !isempty(expr2) ) {            /* have an overlay */
9699:       if ( strstr(expr2,"\\raise") != NULL ) /* have a \raisebox */
9700:         isalign = 2;                    /* so align baselines */
9701:       sp2 = rasterize(expr2,size); } }  /* rasterize overlay expression */
9702: else                                    /* use specific built-in overlay */
9703:   switch ( overlay )
9704:     {
9705:     default: break;
9706:     case 1:                             /* e.g., \not overlays slash */
9707:       sp2 = rasterize("/",size+1);      /* rasterize overlay expression */
9708:       isalign = 0;                      /* automatically handled correctly */
9709:       offset2 = max2(1,size-3);         /* push / right a bit */
9710:       offset2 = 0;
9711:       break;
9712:     case 2:                             /* e.g., \Not draws diagonal */
9713:       sp2 = NULL;                       /* no overlay required */
9714:       isalign = 0;                      /* automatically handled correctly */
9715:       if ( overlaysp != NULL )          /* check that we have raster */
9716:         { raster *rp = overlaysp->image; /* raster to be \Not-ed */
9717:           int width=rp->width, height=rp->height; /* raster dimensions */
9718:           if ( 0 )                      /* diagonal within bounding box */
9719:            line_raster(rp,0,width-1,height-1,0,1); /* just draw diagonal */
9720:           else                          /* construct "wide" diagonal */
9721:            { int margin=3;              /* desired extra margin width */
9722:              sp2 = new_subraster(width+margin,height+margin,1); /*alloc it*/
9723:              if ( sp2 != NULL )         /* allocated successfully */
9724:               line_raster(sp2->image,0,width+margin-1,height+margin-1,0,1);}}
9725:       break;
9726:     case 3:                             /* e.g., \sout for strikeout */
9727:       sp2 = NULL;                       /* no overlay required */
9728:       if ( overlaysp != NULL )          /* check that we have raster */
9729:         { raster *rp = overlaysp->image; /* raster to be \sout-ed */
9730:           int width=rp->width, height=rp->height; /* raster dimensions */
9731:           int baseline = (overlaysp->baseline)-rastlift; /*skip descenders*/
9732:           int midrow = max2(0,min2(height-1,offset2+((baseline+1)/2)));
9733:           if ( 1 )                      /* strikeout within bounding box */
9734:             line_raster(rp,midrow,0,midrow,width-1,1); } /*draw strikeout*/
9735:       break;
9736:     } /* --- end-of-switch(overlay) --- */
9737: if ( sp2 == NULL ) goto end_of_job;     /*return sp1 if failed to rasterize*/
9738: /* -------------------------------------------------------------------------
9739: construct composite overlay
9740: -------------------------------------------------------------------------- */
9741: overlaysp = rastcompose(sp1,sp2,offset2,isalign,3);
9742: end_of_job:
9743:   return ( overlaysp );
9744: } /* --- end-of-function rastoverlay() --- */
9745: 
9746: 
9747: /* ==========================================================================
9748:  * Function:    rastfrac ( expression, size, basesp,  isfrac, arg2, arg3 )
9749:  * Purpose:     \frac,\atop handler, returns a subraster corresponding to
9750:  *              expression (immediately following \frac,\atop) at font size
9751:  * --------------------------------------------------------------------------
9752:  * Arguments:   expression (I/O) char **  to first char of null-terminated
9753:  *                              string immediately following \frac to be
9754:  *                              rasterized, and returning ptr immediately
9755:  *                              following last character processed.
9756:  *              size (I)        int containing 0-7 default font size
9757:  *              basesp (I)      subraster *  to character (or subexpression)
9758:  *                              immediately preceding \frac
9759:  *                              (unused, but passed for consistency)
9760:  *              isfrac (I)      int containing true to draw horizontal line
9761:  *                              between numerator and denominator,
9762:  *                              or false not to draw it (for \atop).
9763:  *              arg2 (I)        int unused
9764:  *              arg3 (I)        int unused
9765:  * --------------------------------------------------------------------------
9766:  * Returns:     ( subraster * ) ptr to subraster corresponding to fraction,
9767:  *                              or NULL for any parsing error
9768:  * --------------------------------------------------------------------------
9769:  * Notes:     o
9770:  * ======================================================================= */
9771: /* --- entry point --- */
9772: subraster *rastfrac ( char **expression, int size, subraster *basesp,
9773:                         int isfrac, int arg2, int arg3 )
9774: {
9775: /* -------------------------------------------------------------------------
9776: Allocations and Declarations
9777: -------------------------------------------------------------------------- */
9778: char    *texsubexpr(),                  /*parse expression for numer,denom*/
9779:         numer[MAXSUBXSZ+1], denom[MAXSUBXSZ+1]; /* parsed numer, denom */
9780: subraster *rasterize(), *numsp=NULL, *densp=NULL; /*rasterize numer, denom*/
9781: subraster *rastack(), *fracsp=NULL;     /* subraster for numer/denom */
9782: subraster *new_subraster()/*, *spacesp=NULL*/; /* space for num or den */
9783: int     width=0,                        /* width of constructed raster */
9784:         numheight=0;                    /* height of numerator */
9785: int     baseht=0, baseln=0;             /* height,baseline of base symbol */
9786: /*int   istweak = 1;*/                  /*true to tweak baseline alignment*/
9787: int     rule_raster(),                  /* draw horizontal line for frac */
9788:         lineheight = (isfrac?1:0);      /* thickness of fraction line */
9789: int     vspace = (size>2?2:1);          /*vertical space between components*/
9790: int     delete_subraster();             /*free work areas in case of error*/
9791: int     type_raster();                  /* display debugging output */
9792: /* -------------------------------------------------------------------------
9793: Obtain numerator and denominator, and rasterize them
9794: -------------------------------------------------------------------------- */
9795: /* --- first parse out optional \frac[] argument(s) --- */
9796: if ( *(*expression) == '[' ) {          /* check for []-enclosed arg(s) */
9797:   char optarg[512];                     /* buffer for optional \frac[arg] */
9798:   *expression = texsubexpr(*expression,optarg,511,"[","]",0,0);
9799:   if ( !isempty(optarg) ) {             /* got optional arg */
9800:     char *semi = strchr(optarg,';');    /* optionally followed by ; */
9801:     if ( semi != NULL ) {               /* found further optional ; */
9802:       char *comma = strchr(semi+1,','); /* further optionally followed by , */
9803:       *semi = '\000';                   /* null-terminate optarg at ; */
9804:       if ( comma != NULL ) *comma = '\000'; /* and that arg at (first) , */
9805:       /* nextarg=atoi(semi+1)); */      /* convert argument after ; */
9806:       isfrac = atoi(semi+1);            /* kludge for \frac[;0] = \atop */
9807:       lineheight = (isfrac?1:0);        /* and reset lineheight */
9808:       if ( comma != NULL ) {            /* have , and arg */
9809:         /* nextarg=atoi(comma+1); */    /* convert argument after , */
9810:         } /* --- end-of-if(comma!=NULL) --- */
9811:       } /* --- end-of-if(semi!=NULL) --- */
9812:     trimwhite(optarg);                  /* remove leading/trailing whitespace*/
9813:     if ( !isempty(optarg) )             /* wasn't just ";other,args" */
9814:       vspace = atoi(optarg);            /* convert optarg to vspace int */
9815:     if ( vspace<0 || vspace>999 )       /* sanity check (revert to default) */
9816:       vspace = (size>2?2:1);            /* back to original default */
9817:     } /* --- end-of-if(!isempty(optarg)) --- */
9818:   } /* --- end-of-if(**expression=='[') --- */
9819: /* ---then parse for numerator,denominator and bump expression past them--- */
9820: *expression = texsubexpr(*expression,numer,0,"{","}",0,0);
9821: *expression = texsubexpr(*expression,denom,0,"{","}",0,0);
9822: if ( *numer=='\000' && *denom=='\000' ) /* missing both components of frac */
9823:   goto end_of_job;                      /* nothing to do, so quit */
9824: /* --- rasterize numerator, denominator --- */
9825: if ( *numer != '\000' )                 /* have a numerator */
9826:  if ( (numsp = rasterize(numer,size-1)) /* so rasterize numer at size-1 */
9827:  ==   NULL ) goto end_of_job;           /* and quit if failed */
9828: if ( *denom != '\000' )                 /* have a denominator */
9829:  if ( (densp = rasterize(denom,size-1)) /* so rasterize denom at size-1 */
9830:  ==   NULL )                            /* failed */
9831:   { if ( numsp != NULL )                /* already rasterized numerator */
9832:       delete_subraster(numsp);          /* so free now-unneeded numerator */
9833:     goto end_of_job; }                  /* and quit */
9834: /* --- if one component missing, use a blank space for it --- */
9835: if ( numsp == NULL )                    /* no numerator given */
9836:   numsp = rasterize("[?]",size-1);      /* missing numerator */
9837: if ( densp == NULL )                    /* no denominator given */
9838:   densp = rasterize("[?]",size-1);      /* missing denominator */
9839: /* --- check that we got both components --- */
9840: if ( numsp==NULL || densp==NULL )       /* some problem */
9841:   { delete_subraster(numsp);            /*delete numerator (if it existed)*/
9842:     delete_subraster(densp);            /*delete denominator (if it existed)*/
9843:     goto end_of_job; }                  /* and quit */
9844: /* --- get height of numerator (to determine where line belongs) --- */
9845: numheight = (numsp->image)->height;     /* get numerator's height */
9846: /* -------------------------------------------------------------------------
9847: construct raster with numerator stacked over denominator
9848: -------------------------------------------------------------------------- */
9849: /* --- construct raster with numer/denom --- */
9850: if ( (fracsp = rastack(densp,numsp,0,(2*vspace)+lineheight,1,3))/*numer/denom*/
9851: ==  NULL )                              /* failed to construct numer/denom */
9852:   { delete_subraster(numsp);            /* so free now-unneeded numerator */
9853:     delete_subraster(densp);            /* and now-unneeded denominator */
9854:     goto end_of_job; }                  /* and then quit */
9855: /* --- determine width of constructed raster --- */
9856: width = (fracsp->image)->width;         /*just get width of embedded image*/
9857: /* --- initialize subraster parameters --- */
9858: fracsp->size = size;                    /* propagate font size forward */
9859: fracsp->baseline = (numheight+vspace+lineheight)+(size+2);/*default baseline*/
9860: fracsp->type = FRACRASTER;              /* signal \frac image */
9861: if ( basesp != (subraster *)NULL )      /* we have base symbol for frac */
9862:   { baseht = (basesp->image)->height;   /* height of base symbol */
9863:     baseln =  basesp->baseline;         /* and its baseline */
9864:   } /* --- end-of-if(basesp!=NULL) --- */
9865: /* -------------------------------------------------------------------------
9866: draw horizontal line between numerator and denominator
9867: -------------------------------------------------------------------------- */
9868: fraccenterline = numheight+vspace;      /* signal that we have a \frac */
9869: if ( isfrac )                           /*line for \frac, but not for \atop*/
9870:   rule_raster(fracsp->image,fraccenterline,0,width,lineheight,0);
9871: /* -------------------------------------------------------------------------
9872: return final result to caller
9873: -------------------------------------------------------------------------- */
9874: end_of_job:
9875:   if ( msgfp!=NULL && msglevel>=99 )
9876:     { fprintf(msgfp,"rastfrac> returning %s\n",(fracsp==NULL?"null":"..."));
9877:       if ( fracsp != NULL )             /* have a constructed raster */
9878:         type_raster(fracsp->image,msgfp); } /* display constructed raster */
9879:   return ( fracsp );
9880: } /* --- end-of-function rastfrac() --- */
9881: 
9882: 
9883: /* ==========================================================================
9884:  * Function:    rastackrel ( expression, size, basesp,  base, arg2, arg3 )
9885:  * Purpose:     \stackrel handler, returns a subraster corresponding to
9886:  *              stacked relation
9887:  * --------------------------------------------------------------------------
9888:  * Arguments:   expression (I/O) char **  to first char of null-terminated
9889:  *                              string immediately following \stackrel to be
9890:  *                              rasterized, and returning ptr immediately
9891:  *                              following last character processed.
9892:  *              size (I)        int containing 0-7 default font size
9893:  *              basesp (I)      subraster *  to character (or subexpression)
9894:  *                              immediately preceding \stackrel
9895:  *                              (unused, but passed for consistency)
9896:  *              base (I)        int containing 1 if upper/first subexpression
9897:  *                              is base relation, or 2 if lower/second is
9898:  *              arg2 (I)        int unused
9899:  *              arg3 (I)        int unused
9900:  * --------------------------------------------------------------------------
9901:  * Returns:     ( subraster * ) ptr to subraster corresponding to stacked
9902:  *                              relation, or NULL for any parsing error
9903:  * --------------------------------------------------------------------------
9904:  * Notes:     o
9905:  * ======================================================================= */
9906: /* --- entry point --- */
9907: subraster *rastackrel ( char **expression, int size, subraster *basesp,
9908:                         int base, int arg2, int arg3 )
9909: {
9910: /* -------------------------------------------------------------------------
9911: Allocations and Declarations
9912: -------------------------------------------------------------------------- */
9913: char    *texsubexpr(),                  /*parse expression for upper,lower*/
9914:         upper[MAXSUBXSZ+1], lower[MAXSUBXSZ+1]; /* parsed upper, lower */
9915: subraster *rasterize(), *upsp=NULL, *lowsp=NULL; /* rasterize upper, lower */
9916: subraster *rastack(), *relsp=NULL;      /* subraster for upper/lower */
9917: int     upsize  = (base==1? size:size-1), /* font size for upper component */
9918:         lowsize = (base==2? size:size-1); /* font size for lower component */
9919: int     vspace = 1;                     /*vertical space between components*/
9920: int     delete_subraster();             /*free work areas in case of error*/
9921: /* -------------------------------------------------------------------------
9922: Obtain numerator and denominator, and rasterize them
9923: -------------------------------------------------------------------------- */
9924: /* --- parse for numerator,denominator and bump expression past them --- */
9925: *expression = texsubexpr(*expression,upper,0,"{","}",0,0);
9926: *expression = texsubexpr(*expression,lower,0,"{","}",0,0);
9927: if ( *upper=='\000' || *lower=='\000' ) /* missing either component */
9928:   goto end_of_job;                      /* nothing to do, so quit */
9929: /* --- rasterize upper, lower --- */
9930: if ( *upper != '\000' )                 /* have upper component */
9931:  if ( (upsp = rasterize(upper,upsize))  /* so rasterize upper component */
9932:  ==   NULL ) goto end_of_job;           /* and quit if failed */
9933: if ( *lower != '\000' )                 /* have lower component */
9934:  if ( (lowsp = rasterize(lower,lowsize)) /* so rasterize lower component */
9935:  ==   NULL )                            /* failed */
9936:   { if ( upsp != NULL )                 /* already rasterized upper */
9937:       delete_subraster(upsp);           /* so free now-unneeded upper */
9938:     goto end_of_job; }                  /* and quit */
9939: /* -------------------------------------------------------------------------
9940: construct stacked relation raster
9941: -------------------------------------------------------------------------- */
9942: /* --- construct stacked relation --- */
9943: if ( (relsp = rastack(lowsp,upsp,3-base,vspace,1,3)) /* stacked relation */
9944: ==   NULL ) goto end_of_job;            /* quit if failed */
9945: /* --- initialize subraster parameters --- */
9946: relsp->size = size;                     /* propagate font size forward */
9947: /* -------------------------------------------------------------------------
9948: return final result to caller
9949: -------------------------------------------------------------------------- */
9950: end_of_job:
9951:   return ( relsp );
9952: } /* --- end-of-function rastackrel() --- */
9953: 
9954: 
9955: /* ==========================================================================
9956:  * Function:    rastmathfunc ( expression, size, basesp,  base, arg2, arg3 )
9957:  * Purpose:     \log, \lim, etc handler, returns a subraster corresponding
9958:  *              to math functions
9959:  * --------------------------------------------------------------------------
9960:  * Arguments:   expression (I/O) char **  to first char of null-terminated
9961:  *                              string immediately following \mathfunc to be
9962:  *                              rasterized, and returning ptr immediately
9963:  *                              following last character processed.
9964:  *              size (I)        int containing 0-7 default font size
9965:  *              basesp (I)      subraster *  to character (or subexpression)
9966:  *                              immediately preceding \mathfunc
9967:  *                              (unused, but passed for consistency)
9968:  *              mathfunc (I)    int containing 1=arccos, 2=arcsin, etc.
9969:  *              islimits (I)    int containing 1 if function may have
9970:  *                              limits underneath, e.g., \lim_{n\to\infty}
9971:  *              arg3 (I)        int unused
9972:  * --------------------------------------------------------------------------
9973:  * Returns:     ( subraster * ) ptr to subraster corresponding to mathfunc,
9974:  *                              or NULL for any parsing error
9975:  * --------------------------------------------------------------------------
9976:  * Notes:     o
9977:  * ======================================================================= */
9978: /* --- entry point --- */
9979: subraster *rastmathfunc ( char **expression, int size, subraster *basesp,
9980:                         int mathfunc, int islimits, int arg3 )
9981: {
9982: /* -------------------------------------------------------------------------
9983: Allocations and Declarations
9984: -------------------------------------------------------------------------- */
9985: char    *texscripts(),                  /* parse expression for _limits */
9986:         func[MAXTOKNSZ+1], limits[MAXSUBXSZ+1]; /*func as {\rm func}, limits*/
9987: char    *texsubexpr(),                  /* parse expression for arg */
9988:         funcarg[MAXTOKNSZ+1];           /* optional func arg */
9989: subraster *rasterize(), *funcsp=NULL, *limsp=NULL; /*rasterize func,limits*/
9990: subraster *rastack(), *mathfuncsp=NULL; /* subraster for mathfunc/limits */
9991: int     limsize = size-1;               /* font size for limits */
9992: int     vspace = 1;                     /*vertical space between components*/
9993: int     delete_subraster();             /*free work areas in case of error*/
9994: /* --- table of function names by mathfunc number --- */
9995: static  int  numnames = 34;             /* number of names in table */
9996: static  char *funcnames[] = {
9997:         "error",                        /*  0 index is illegal/error bucket*/
9998:         "arccos",  "arcsin",  "arctan", /*  1 -  3 */
9999:         "arg",     "cos",     "cosh",   /*  4 -  6 */
10000:         "cot",     "coth",    "csc",    /*  7 -  9 */
10001:         "deg",     "det",     "dim",    /* 10 - 12 */
10002:         "exp",     "gcd",     "hom",    /* 13 - 15 */
10003:         "inf",     "ker",     "lg",     /* 16 - 18 */
10004:         "lim",     "liminf",  "limsup", /* 19 - 21 */
10005:         "ln",      "log",     "max",    /* 22 - 24 */
10006:         "min",     "Pr",      "sec",    /* 25 - 27 */
10007:         "sin",     "sinh",    "sup",    /* 28 - 30 */
10008:         "tan",     "tanh",              /* 31 - 32 */
10009:         /* --- extra mimetex funcnames --- */
10010:         "tr",                           /* 33 */
10011:         "pmod"                          /* 34 */
10012:         } ;
10013: /* -------------------------------------------------------------------------
10014: set up and rasterize function name in \rm
10015: -------------------------------------------------------------------------- */
10016: if ( mathfunc<0 || mathfunc>numnames ) mathfunc=0; /* check index bounds */
10017: switch ( mathfunc )                     /* check for special processing */
10018:   {
10019:   default:                              /* no special processing */
10020:     strcpy(func,"{\\rm~");              /* init string with {\rm~ */
10021:     strcat(func,funcnames[mathfunc]);   /* concat function name */
10022:     strcat(func,"}");                   /* and add terminating } */
10023:     break;
10024:   case 34:                              /* \pmod{x} --> (mod x) */
10025:     /* --- parse for \pmod{arg} argument --- */
10026:     *expression = texsubexpr(*expression,funcarg,2047,"{","}",0,0);
10027:     strcpy(func,"{\\({\\rm~mod}");      /* init with {\left({\rm~mod} */
10028:     strcat(func,"\\hspace2");           /* concat space */
10029:     strcat(func,funcarg);               /* and \pmodargument */
10030:     strcat(func,"\\)}");                /* and add terminating \right)} */
10031:     break;
10032:   } /* --- end-of-switch(mathfunc) --- */
10033: if ( (funcsp = rasterize(func,size))    /* rasterize function name */
10034: ==   NULL ) goto end_of_job;            /* and quit if failed */
10035: mathfuncsp = funcsp;                    /* just return funcsp if no limits */
10036: if ( !islimits ) goto end_of_job;       /* treat any subscript normally */
10037: /* -------------------------------------------------------------------------
10038: Obtain limits, if permitted and if provided, and rasterize them
10039: -------------------------------------------------------------------------- */
10040: /* --- parse for subscript limits, and bump expression past it(them) --- */
10041: *expression = texscripts(*expression,limits,limits,1);
10042: if ( *limits=='\000') goto end_of_job;  /* no limits, nothing to do, quit */
10043: /* --- rasterize limits --- */
10044: if ( (limsp = rasterize(limits,limsize)) /* rasterize limits */
10045: ==   NULL ) goto end_of_job;            /* and quit if failed */
10046: /* -------------------------------------------------------------------------
10047: construct func atop limits
10048: -------------------------------------------------------------------------- */
10049: /* --- construct func atop limits --- */
10050: if ( (mathfuncsp = rastack(limsp,funcsp,2,vspace,1,3)) /* func atop limits */
10051: ==   NULL ) goto end_of_job;            /* quit if failed */
10052: /* --- initialize subraster parameters --- */
10053: mathfuncsp->size = size;                /* propagate font size forward */
10054: /* -------------------------------------------------------------------------
10055: return final result to caller
10056: -------------------------------------------------------------------------- */
10057: end_of_job:
10058:   return ( mathfuncsp );
10059: } /* --- end-of-function rastmathfunc() --- */
10060: 
10061: 
10062: /* ==========================================================================
10063:  * Function:    rastsqrt ( expression, size, basesp,  arg1, arg2, arg3 )
10064:  * Purpose:     \sqrt handler, returns a subraster corresponding to
10065:  *              expression (immediately following \sqrt) at font size
10066:  * --------------------------------------------------------------------------
10067:  * Arguments:   expression (I/O) char **  to first char of null-terminated
10068:  *                              string immediately following \sqrt to be
10069:  *                              rasterized, and returning ptr immediately
10070:  *                              following last character processed.
10071:  *              size (I)        int containing 0-7 default font size
10072:  *              basesp (I)      subraster *  to character (or subexpression)
10073:  *                              immediately preceding \accent
10074:  *                              (unused, but passed for consistency)
10075:  *              arg1 (I)        int unused
10076:  *              arg2 (I)        int unused
10077:  *              arg3 (I)        int unused
10078:  * --------------------------------------------------------------------------
10079:  * Returns:     ( subraster * ) ptr to subraster corresponding to expression,
10080:  *                              or NULL for any parsing error
10081:  *                              (expression ptr unchanged if error occurs)
10082:  * --------------------------------------------------------------------------
10083:  * Notes:     o
10084:  * ======================================================================= */
10085: /* --- entry point --- */
10086: subraster *rastsqrt ( char **expression, int size, subraster *basesp,
10087:                         int arg1, int arg2, int arg3 )
10088: {
10089: /* -------------------------------------------------------------------------
10090: Allocations and Declarations
10091: -------------------------------------------------------------------------- */
10092: char    *texsubexpr(), subexpr[MAXSUBXSZ+1], /*parse subexpr to be sqrt-ed*/
10093:         rootarg[MAXSUBXSZ+1];           /* optional \sqrt[rootarg]{...} */
10094: subraster *rasterize(), *subsp=NULL;    /* rasterize subexpr */
10095: subraster *accent_subraster(), *sqrtsp=NULL, /* subraster with the sqrt */
10096:         *new_subraster(), *rootsp=NULL; /* optionally preceded by [rootarg]*/
10097: int     sqrtheight=0, sqrtwidth=0, surdwidth=0, /* height,width of sqrt */
10098:         rootheight=0, rootwidth=0,      /* height,width of rootarg raster */
10099:         subheight=0, subwidth=0, pixsz=0; /* height,width,pixsz of subexpr */
10100: int     rastput();                      /* put subexpr in constructed sqrt */
10101: int     overspace = 2;                  /*space between subexpr and overbar*/
10102: int     delete_subraster();             /* free work areas */
10103: /* -------------------------------------------------------------------------
10104: Obtain subexpression to be sqrt-ed, and rasterize it
10105: -------------------------------------------------------------------------- */
10106: /* --- first check for optional \sqrt[rootarg]{...} --- */
10107: if ( *(*expression) == '[' )            /*check for []-enclosed optional arg*/
10108:   { *expression = texsubexpr(*expression,rootarg,0,"[","]",0,0);
10109:     if ( *rootarg != '\000' )           /* got rootarg */
10110:      if ( (rootsp=rasterize(rootarg,size-1)) /*rasterize it at smaller size*/
10111:      != NULL )                          /* rasterized successfully */
10112:       { rootheight = (rootsp->image)->height;  /* get height of rootarg */
10113:         rootwidth  = (rootsp->image)->width; } /* and its width */
10114:   } /* --- end-of-if(**expression=='[') --- */
10115: /* --- parse for subexpr to be sqrt-ed, and bump expression past it --- */
10116: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
10117: if ( *subexpr == '\000' )               /* couldn't get subexpression */
10118:   goto end_of_job;                      /* nothing to do, so quit */
10119: /* --- rasterize subexpression to be accented --- */
10120: if ( (subsp = rasterize(subexpr,size))  /*rasterize subexpr at original size*/
10121: ==   NULL ) goto end_of_job;            /* quit if failed */
10122: /* -------------------------------------------------------------------------
10123: determine height and width of sqrt raster to be constructed
10124: -------------------------------------------------------------------------- */
10125: /* --- first get height and width of subexpr --- */
10126: subheight = (subsp->image)->height;     /* height of subexpr */
10127: subwidth  = (subsp->image)->width;      /* and its width */
10128: pixsz     = (subsp->image)->pixsz;      /* pixsz remains constant */
10129: /* --- determine height and width of sqrt to contain subexpr --- */
10130: sqrtheight = subheight + overspace;     /* subexpr + blank line + overbar */
10131: surdwidth  = SQRTWIDTH(sqrtheight,(rootheight<1?2:1)); /* width of surd */
10132: sqrtwidth  = subwidth + surdwidth + 1;  /* total width */
10133: /* -------------------------------------------------------------------------
10134: construct sqrt (with room to move in subexpr) and embed subexpr in it
10135: -------------------------------------------------------------------------- */
10136: /* --- construct sqrt --- */
10137: if ( (sqrtsp=accent_subraster(SQRTACCENT,
10138: (rootheight<1?sqrtwidth:(-sqrtwidth)),sqrtheight,0,pixsz))
10139: ==   NULL ) goto end_of_job;            /* quit if failed to build sqrt */
10140: /* --- embed subexpr in sqrt at lower-right corner--- */
10141: rastput(sqrtsp->image,subsp->image,overspace,sqrtwidth-subwidth,1);
10142: sqrtsp->baseline = subsp->baseline + overspace; /* adjust baseline */
10143: /* --- "embed" rootarg at upper-left --- */
10144: if ( rootsp != NULL )                   /*have optional \sqrt[rootarg]{...}*/
10145:   {
10146:   /* --- allocate full raster to contain sqrtsp and rootsp --- */
10147:   int fullwidth = sqrtwidth +rootwidth - min2(rootwidth,max2(0,surdwidth-4)),
10148:       fullheight= sqrtheight+rootheight- min2(rootheight,3+size);
10149:   subraster *fullsp = new_subraster(fullwidth,fullheight,pixsz);
10150:   if ( fullsp != NULL )                 /* allocated successfully */
10151:     { /* --- embed sqrtsp exactly at lower-right corner --- */
10152:       rastput(fullsp->image,sqrtsp->image, /* exactly at lower-right corner*/
10153:         fullheight-sqrtheight,fullwidth-sqrtwidth,1);
10154:       /* --- embed rootsp near upper-left, nestled above leading surd --- */
10155:       rastput(fullsp->image,rootsp->image,
10156:         0,max2(0,surdwidth-rootwidth-2-size),0);
10157:       /* --- replace sqrtsp with fullsp --- */
10158:       delete_subraster(sqrtsp);         /* free original sqrtsp */
10159:       sqrtsp = fullsp;                  /* and repoint it to fullsp instead*/
10160:       sqrtsp->baseline = fullheight - (subheight - subsp->baseline); }
10161:   } /* --- end-of-if(rootsp!=NULL) --- */
10162: /* --- initialize subraster parameters --- */
10163: sqrtsp->size = size;                    /* propagate font size forward */
10164: /* -------------------------------------------------------------------------
10165: free unneeded component subrasters and return final result to caller
10166: -------------------------------------------------------------------------- */
10167: end_of_job:
10168:   if ( subsp != NULL ) delete_subraster(subsp); /* free unneeded subexpr */
10169:   return ( sqrtsp );
10170: } /* --- end-of-function rastsqrt() --- */
10171: 
10172: 
10173: /* ==========================================================================
10174:  * Function:    rastaccent (expression,size,basesp,accent,isabove,isscript)
10175:  * Purpose:     \hat, \vec, \etc handler, returns a subraster corresponding
10176:  *              to expression (immediately following \accent) at font size
10177:  * --------------------------------------------------------------------------
10178:  * Arguments:   expression (I/O) char **  to first char of null-terminated
10179:  *                              string immediately following \accent to be
10180:  *                              rasterized, and returning ptr immediately
10181:  *                              following last character processed.
10182:  *              size (I)        int containing 0-7 default font size
10183:  *              basesp (I)      subraster *  to character (or subexpression)
10184:  *                              immediately preceding \accent
10185:  *                              (unused, but passed for consistency)
10186:  *              accent (I)      int containing HATACCENT or VECACCENT, etc,
10187:  *                              between numerator and denominator,
10188:  *                              or false not to draw it (for \over).
10189:  *              isabove (I)     int containing true if accent is above
10190:  *                              expression to be accented, or false
10191:  *                              if accent is below (e.g., underbrace)
10192:  *              isscript (I)    int containing true if sub/superscripts
10193:  *                              allowed (for under/overbrace), or 0 if not.
10194:  * --------------------------------------------------------------------------
10195:  * Returns:     ( subraster * ) ptr to subraster corresponding to expression,
10196:  *                              or NULL for any parsing error
10197:  *                              (expression ptr unchanged if error occurs)
10198:  * --------------------------------------------------------------------------
10199:  * Notes:     o Also handles \overbrace{}^{} and \underbrace{}_{} by way
10200:  *              of isabove and isscript args.
10201:  * ======================================================================= */
10202: /* --- entry point --- */
10203: subraster *rastaccent ( char **expression, int size, subraster *basesp,
10204:                         int accent, int isabove, int isscript )
10205: {
10206: /* -------------------------------------------------------------------------
10207: Allocations and Declarations
10208: -------------------------------------------------------------------------- */
10209: char    *texsubexpr(), subexpr[MAXSUBXSZ+1]; /*parse subexpr to be accented*/
10210: char    *texscripts(), *script=NULL,    /* \under,overbrace allow scripts */
10211:         subscript[MAXTOKNSZ+1], supscript[MAXTOKNSZ+1]; /* parsed scripts */
10212: subraster *rasterize(), *subsp=NULL, *scrsp=NULL; /*rasterize subexpr,script*/
10213: subraster *rastack(), *accsubsp=NULL;   /* stack accent, subexpr, script */
10214: subraster *accent_subraster(), *accsp=NULL; /*raster for the accent itself*/
10215: int     accheight=0, accwidth=0, accdir=0,/*accent height, width, direction*/
10216:         subheight=0, subwidth=0, pixsz=0; /* height,width,pixsz of subexpr */
10217: int     delete_subraster();             /*free work areas in case of error*/
10218: int     vspace = 0;                     /*vertical space between accent,sub*/
10219: /* -------------------------------------------------------------------------
10220: Obtain subexpression to be accented, and rasterize it
10221: -------------------------------------------------------------------------- */
10222: /* --- parse for subexpr to be accented, and bump expression past it --- */
10223: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
10224: if ( *subexpr=='\000' )                 /* couldn't get subexpression */
10225:   goto end_of_job;                      /* nothing to do, so quit */
10226: /* --- rasterize subexpression to be accented --- */
10227: if ( (subsp = rasterize(subexpr,size))  /*rasterize subexpr at original size*/
10228: ==   NULL ) goto end_of_job;            /* quit if failed */
10229: /* -------------------------------------------------------------------------
10230: determine desired accent width and height
10231: -------------------------------------------------------------------------- */
10232: /* --- first get height and width of subexpr --- */
10233: subheight = (subsp->image)->height;     /* height of subexpr */
10234: subwidth  = (subsp->image)->width;      /* and its width is overall width */
10235: pixsz     = (subsp->image)->pixsz;      /* original pixsz remains constant */
10236: /* --- determine desired width, height of accent --- */
10237: accwidth = subwidth;                    /* same width as subexpr */
10238: accheight = 4;                          /* default for bars */
10239: switch ( accent )
10240:   { default: break;                     /* default okay */
10241:   case DOTACCENT: case DDOTACCENT:
10242:     accheight = (size<4? 3:4);          /* default for dots */
10243:     break;
10244:   case VECACCENT:
10245:     vspace = 1;                         /* set 1-pixel vertical space */
10246:     accdir = isscript;                  /* +1=right,-1=left,0=lr; +10for==>*/
10247:     isscript = 0;                       /* >>don't<< signal sub/supscript */
10248:   case HATACCENT:
10249:     accheight = 7;                      /* default */
10250:     if ( subwidth < 10 ) accheight = 5; /* unless small width */
10251:       else if ( subwidth > 25 ) accheight = 9; /* or large */
10252:     break;
10253:   } /* --- end-of-switch(accent) --- */
10254: accheight = min2(accheight,subheight);  /*never higher than accented subexpr*/
10255: /* -------------------------------------------------------------------------
10256: construct accent, and construct subraster with accent over (or under) subexpr
10257: -------------------------------------------------------------------------- */
10258: /* --- first construct accent --- */
10259: if ( (accsp = accent_subraster(accent,accwidth,accheight,accdir,pixsz))
10260: ==   NULL ) goto end_of_job;            /* quit if failed to build accent */
10261: /* --- now stack accent above (or below) subexpr, and free both args --- */
10262: accsubsp = (isabove? rastack(subsp,accsp,1,vspace,1,3)/*accent above subexpr*/
10263:            : rastack(accsp,subsp,2,vspace,1,3));      /*accent below subexpr*/
10264: if ( accsubsp == NULL )                 /* failed to stack accent */
10265:   { delete_subraster(subsp);            /* free unneeded subsp */
10266:     delete_subraster(accsp);            /* and unneeded accsp */
10267:     goto end_of_job; }                  /* and quit */
10268: /* -------------------------------------------------------------------------
10269: look for super/subscript (annotation for over/underbrace)
10270: -------------------------------------------------------------------------- */
10271: /* --- first check whether accent permits accompanying annotations --- */
10272: if ( !isscript ) goto end_of_job;       /* no annotations for this accent */
10273: /* --- now get scripts if there actually are any --- */
10274: *expression = texscripts(*expression,subscript,supscript,(isabove?2:1));
10275: script = (isabove? supscript : subscript); /*select above^ or below_ script*/
10276: if ( *script == '\000' ) goto end_of_job; /* no accompanying script */
10277: /* --- rasterize script annotation at size-2 --- */
10278: if ( (scrsp = rasterize(script,size-2)) /* rasterize script at size-2 */
10279: ==   NULL ) goto end_of_job;            /* quit if failed */
10280: /* --- stack annotation above (or below) accent, and free both args --- */
10281: accsubsp = (isabove? rastack(accsubsp,scrsp,1,0,1,3) /* accent above base */
10282:            : rastack(scrsp,accsubsp,2,0,1,3));       /* accent below base */
10283: /* -------------------------------------------------------------------------
10284: return final result to caller
10285: -------------------------------------------------------------------------- */
10286: end_of_job:
10287:   if ( accsubsp != NULL )               /* initialize subraster parameters */
10288:     accsubsp->size = size;              /* propagate font size forward */
10289:   return ( accsubsp );
10290: } /* --- end-of-function rastaccent() --- */
10291: 
10292: 
10293: /* ==========================================================================
10294:  * Function:    rastfont (expression,size,basesp,ifontnum,arg2,arg3)
10295:  * Purpose:     \cal{}, \scr{}, \etc handler, returns subraster corresponding
10296:  *              to char(s) within {}'s rendered at size
10297:  * --------------------------------------------------------------------------
10298:  * Arguments:   expression (I/O) char **  to first char of null-terminated
10299:  *                              string immediately following \font to be
10300:  *                              rasterized, and returning ptr immediately
10301:  *                              following last character processed.
10302:  *              size (I)        int containing 0-7 default font size
10303:  *              basesp (I)      subraster *  to character (or subexpression)
10304:  *                              immediately preceding \accent
10305:  *                              (unused, but passed for consistency)
10306:  *              ifontnum (I)    int containing 1 for \cal{}, 2 for \scr{}
10307:  *              arg2 (I)        int unused
10308:  *              arg3 (I)        int unused
10309:  * --------------------------------------------------------------------------
10310:  * Returns:     ( subraster * ) ptr to subraster corresponding to chars
10311:  *                              between {}'s, or NULL for any parsing error
10312:  * --------------------------------------------------------------------------
10313:  * Notes:     o
10314:  * ======================================================================= */
10315: /* --- entry point --- */
10316: subraster *rastfont ( char **expression, int size, subraster *basesp,
10317:                         int ifontnum, int arg2, int arg3 )
10318: {
10319: /* -------------------------------------------------------------------------
10320: Allocations and Declarations
10321: -------------------------------------------------------------------------- */
10322: char    *texsubexpr(), fontchars[MAXSUBXSZ+1], /* chars to render in font */
10323:         subexpr[MAXSUBXSZ+1];           /* turn \cal{AB} into \calA\calB */
10324: char    *pfchars=fontchars, fchar='\0'; /* run thru fontchars one at a time*/
10325: char    *name = NULL;                   /* fontinfo[ifontnum].name */
10326: int     family = 0,                     /* fontinfo[ifontnum].family */
10327:         istext = 0,                     /* fontinfo[ifontnum].istext */
10328:         class = 0;                      /* fontinfo[ifontnum].class */
10329: subraster *rasterize(), *fontsp=NULL,   /* rasterize chars in font */
10330:         *rastflags();                   /* or just set flag to switch font */
10331: int     oldsmashmargin = smashmargin;   /* turn off smash in text mode */
10332: #if 0
10333: /* --- fonts recognized by rastfont --- */
10334: static  int  nfonts = 11;               /* legal font #'s are 1...nfonts */
10335: static  struct {char *name; int class;}
10336:   fonts[] =
10337:     { /* --- name  class 1=upper,2=alpha,3=alnum,4=lower,5=digit,9=all --- */
10338:         { "\\math",     0 },
10339:         { "\\mathcal",  1 },            /*(1) calligraphic, uppercase */
10340:         { "\\mathscr",  1 },            /*(2) rsfs/script, uppercase */
10341:         { "\\textrm",   -1 },           /*(3) \rm,\text{abc} --> {\rm~abc} */
10342:         { "\\textit",   -1 },           /*(4) \it,\textit{abc}-->{\it~abc} */
10343:         { "\\mathbb",   -1 },           /*(5) \bb,\mathbb{abc}-->{\bb~abc} */
10344:         { "\\mathbf",   -1 },           /*(6) \bf,\mathbf{abc}-->{\bf~abc} */
10345:         { "\\mathrm",   -1 },           /*(7) \mathrm */
10346:         { "\\cyr",      -1 },           /*(8) \cyr */
10347:         { "\\textgreek",-1 },           /*(9) \textgreek */
10348:         { "\\textbfgreek",CMMI10BGR,1,-1 },/*(10) \textbfgreek{ab} */
10349:         { "\\textbbgreek",BBOLD10GR,1,-1 },/*(11) \textbbgreek{ab} */
10350:         { NULL,         0 }
10351:     } ; /* --- end-of-fonts[] --- */
10352: #endif
10353: /* -------------------------------------------------------------------------
10354: first get font name and class to determine type of conversion desired
10355: -------------------------------------------------------------------------- */
10356: if (ifontnum<=0 || ifontnum>nfontinfo) ifontnum=0; /*math if out-of-bounds*/
10357: name   = fontinfo[ifontnum].name;       /* font name */
10358: family = fontinfo[ifontnum].family;     /* font family */
10359: istext = fontinfo[ifontnum].istext;     /*true in text mode (respect space)*/
10360: class  = fontinfo[ifontnum].class;      /* font class */
10361: if ( istext )                           /* text (respect blanks) */
10362:  { mathsmashmargin = smashmargin;       /* needed for \text{if $n-m$ even} */
10363:    smashmargin = 0; }                   /* don't smash internal blanks */
10364: /* -------------------------------------------------------------------------
10365: now convert \font{abc} --> {\font~abc}, or convert ABC to \calA\calB\calC
10366: -------------------------------------------------------------------------- */
10367: if ( 1 || class<0 )                     /* not character-by-character */
10368:  {
10369:  /* ---
10370:  if \font not immediately followed by { then it has no arg, so just set flag
10371:  ------------------------------------------------------------------------ */
10372:  if ( *(*expression) != '{' )           /* no \font arg, so just set flag */
10373:     {
10374:     if ( msgfp!=NULL && msglevel>=99 )
10375:      fprintf(msgfp,"rastfont> \\%s rastflags() for font#%d\n",name,ifontnum);
10376:     fontsp = rastflags(expression,size,basesp,ISFONTFAM,ifontnum,arg3);
10377:     goto end_of_job;
10378:     } /* --- end-of-if(*(*expression)!='{') --- */
10379:  /* ---
10380:  convert \font{abc} --> {\font~abc}
10381:  ---------------------------------- */
10382:  /* --- parse for {fontchars} arg, and bump expression past it --- */
10383:  *expression = texsubexpr(*expression,fontchars,0,"{","}",0,0);
10384:  if ( msgfp!=NULL && msglevel>=99 )
10385:   fprintf(msgfp,"rastfont> \\%s fontchars=\"%s\"\n",name,fontchars);
10386:  /* --- convert all fontchars at the same time --- */
10387:  strcpy(subexpr,"{");                   /* start off with opening { */
10388:  strcat(subexpr,name);                  /* followed by font name */
10389:  strcat(subexpr,"~");                   /* followed by whitespace */
10390:  strcat(subexpr,fontchars);             /* followed by all the chars */
10391:  strcat(subexpr,"}");                   /* terminate with closing } */
10392:  } /* --- end-of-if(class<0) --- */
10393: else                                    /* character-by-character */
10394:  {
10395:  /* ---
10396:  convert ABC to \calA\calB\calC
10397:  ------------------------------ */
10398:  int    isprevchar=0;                   /* true if prev char converted */
10399:  /* --- parse for {fontchars} arg, and bump expression past it --- */
10400:  *expression = texsubexpr(*expression,fontchars,0,"{","}",0,0);
10401:  if ( msgfp!=NULL && msglevel>=99 )
10402:   fprintf(msgfp,"rastfont> \\%s fontchars=\"%s\"\n",name,fontchars);
10403:  /* --- convert fontchars one at a time --- */
10404:  strcpy(subexpr,"{\\rm~");              /* start off with opening {\rm */
10405:  strcpy(subexpr,"{");                   /* nope, just start off with { */
10406:  for ( pfchars=fontchars; (fchar= *pfchars)!='\000'; pfchars++ )
10407:   {
10408:   if ( isthischar(fchar,WHITEMATH) )    /* some whitespace */
10409:     { if ( 0 || istext )                /* and we're in a text mode font */
10410:         strcat(subexpr,"\\;"); }        /* so respect whitespace */
10411:   else                                  /* char to be displayed in font */
10412:     { int exprlen = 0;                  /* #chars in subexpr before fchar */
10413:       int isinclass = 0;                /* set true if fchar in font class */
10414:       /* --- class: 1=upper, 2=alpha, 3=alnum, 4=lower, 5=digit, 9=all --- */
10415:       switch ( class )                  /* check if fchar is in font class */
10416:         { default: break;               /* no chars in unrecognized class */
10417:           case 1: if ( isupper((int)fchar) ) isinclass=1; break;
10418:           case 2: if ( isalpha((int)fchar) ) isinclass=1; break;
10419:           case 3: if ( isalnum((int)fchar) ) isinclass=1; break;
10420:           case 4: if ( islower((int)fchar) ) isinclass=1; break;
10421:           case 5: if ( isdigit((int)fchar) ) isinclass=1; break;
10422:           case 9: isinclass=1; break; }
10423:       if ( isinclass )                  /* convert current char to \font */
10424:         { strcat(subexpr,name);         /* by prefixing it with font name */
10425:           isprevchar = 1; }             /* and set flag to signal separator*/
10426:       else                              /* current char not in \font */
10427:         { if ( isprevchar )             /* extra separator only after \font*/
10428:            if ( isalpha(fchar) )        /* separator only before alpha */
10429:             strcat(subexpr,"~");        /* need separator after \font */
10430:           isprevchar = 0; }             /* reset flag for next char */
10431:       exprlen = strlen(subexpr);        /* #chars so far */
10432:       subexpr[exprlen] = fchar;         /*fchar immediately after \fontname*/
10433:       subexpr[exprlen+1] = '\000'; }    /* replace terminating '\0' */
10434:   } /* --- end-of-for(pfchars) --- */
10435:  strcat(subexpr,"}");                   /* add closing } */
10436:  } /* --- end-of-if/else(class<0) --- */
10437: /* -------------------------------------------------------------------------
10438: rasterize subexpression containing chars to be rendered at font
10439: -------------------------------------------------------------------------- */
10440: if ( msgfp!=NULL && msglevel>=99 )
10441:   fprintf(msgfp,"rastfont> subexpr=\"%s\"\n",subexpr);
10442: if ( (fontsp = rasterize(subexpr,size)) /* rasterize chars in font */
10443: ==   NULL ) goto end_of_job;            /* and quit if failed */
10444: /* -------------------------------------------------------------------------
10445: back to caller with chars rendered in font
10446: -------------------------------------------------------------------------- */
10447: end_of_job:
10448:   smashmargin = oldsmashmargin;         /* restore smash */
10449:   mathsmashmargin = SMASHMARGIN;        /* this one probably not necessary */
10450:   if ( istext && fontsp!=NULL )         /* raster contains text mode font */
10451:     fontsp->type = blanksignal;         /* signal nosmash */
10452:   return ( fontsp );                    /* chars rendered in font */
10453: } /* --- end-of-function rastfont() --- */
10454: 
10455: 
10456: /* ==========================================================================
10457:  * Function:    rastbegin ( expression, size, basesp, arg1, arg2, arg3 )
10458:  * Purpose:     \begin{}...\end{}  handler, returns a subraster corresponding
10459:  *              to array expression within environment, i.e., rewrites
10460:  *              \begin{}...\end{} as mimeTeX equivalent, and rasterizes that.
10461:  * --------------------------------------------------------------------------
10462:  * Arguments:   expression (I/O) char **  to first char of null-terminated
10463:  *                              string immediately following \begin to be
10464:  *                              rasterized, and returning ptr immediately
10465:  *                              following last character processed.
10466:  *              size (I)        int containing 0-7 default font size
10467:  *              basesp (I)      subraster *  to character (or subexpression)
10468:  *                              immediately preceding \begin
10469:  *                              (unused, but passed for consistency)
10470:  *              arg1 (I)        int unused
10471:  *              arg2 (I)        int unused
10472:  *              arg3 (I)        int unused
10473:  * --------------------------------------------------------------------------
10474:  * Returns:     ( subraster * ) ptr to subraster corresponding to array
10475:  *                              expression, or NULL for any parsing error
10476:  * --------------------------------------------------------------------------
10477:  * Notes:     o
10478:  * ======================================================================= */
10479: /* --- entry point --- */
10480: subraster *rastbegin ( char **expression, int size, subraster *basesp,
10481:                         int arg1, int arg2, int arg3 )
10482: {
10483: /* -------------------------------------------------------------------------
10484: Allocations and Declarations
10485: -------------------------------------------------------------------------- */
10486: char    *texsubexpr(), subexpr[MAXSUBXSZ+1], /* \begin{} environment params*/
10487:         *exprptr=NULL,*begptr=NULL,*endptr=NULL,*braceptr=NULL; /* ptrs */
10488: char    *begtoken="\\begin{", *endtoken="\\end{"; /*tokens we're looking for*/
10489: int     strreplace();                   /* replace substring in string */
10490: char    *strchange();                   /*\begin...\end --> {\begin...\end}*/
10491: char    *delims = (char *)NULL;         /* mdelims[ienviron] */
10492: subraster *rasterize(), *sp=NULL;       /* rasterize environment */
10493: int     ienviron = 0;                   /* environs[] index */
10494: int     nbegins = 0;                    /* #\begins nested beneath this one*/
10495: int     envlen=0, sublen=0;             /* #chars in environ, subexpr */
10496: static  int blevel = 0;                 /* \begin...\end nesting level */
10497: static  char *mdelims[] = { NULL, NULL, NULL, NULL,
10498:         "()","[]","{}","||","==",       /* for pbBvVmatrix */
10499:         NULL, NULL, NULL, NULL, "{.", NULL, NULL, NULL, NULL, NULL, NULL,
10500:         NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
10501: static  char *environs[] = {            /* types of environments we process*/
10502:         "eqnarray",                     /* 0 eqnarray environment */
10503:         "array",                        /* 1 array environment */
10504:         "matrix",                       /* 2 array environment */
10505:         "tabular",                      /* 3 array environment */
10506:         "pmatrix",                      /* 4 ( ) */
10507:         "bmatrix",                      /* 5 [ ] */
10508:         "Bmatrix",                      /* 6 { } */
10509:         "vmatrix",                      /* 7 | | */
10510:         "Vmatrix",                      /* 8 || || */
10511:         "gather",                       /* 9 gather environment */
10512:         "align",                        /* 10 align environment */
10513:         "verbatim",                     /* 11 verbatim environment */
10514:         "picture",                      /* 12 picture environment */
10515:         "cases",                        /* 13 cases environment */
10516:         "equation",                     /* 14 for \begin{equation} */
10517:         NULL };                         /* trailer */
10518: /* -------------------------------------------------------------------------
10519: determine type of environment we're beginning
10520: -------------------------------------------------------------------------- */
10521: /* --- first bump nesting level --- */
10522: blevel++;                               /* count \begin...\begin...'s */
10523: /* --- \begin must be followed by {type_of_environment} --- */
10524: exprptr = texsubexpr(*expression,subexpr,0,"{","}",0,0);
10525: if ( *subexpr == '\000' ) goto end_of_job; /* no environment given */
10526: while ( (delims=strchr(subexpr,'*')) != NULL ) /* have environment* */
10527:   {strsqueeze(delims,1);}               /* treat it as environment */
10528: /* --- look up environment in our table --- */
10529: for ( ienviron=0; ;ienviron++ )         /* search table till NULL */
10530:   if ( environs[ienviron] == NULL )     /* found NULL before match */
10531:     goto end_of_job;                    /* so quit */
10532:   else                                  /* see if we have an exact match */
10533:     if ( memcmp(environs[ienviron],subexpr,strlen(subexpr)) == 0 ) /*match*/
10534:       break;                            /* leave loop with ienviron index */
10535: /* --- accumulate any additional params for this environment --- */
10536: *subexpr = '\000';                      /* reset subexpr to empty string */
10537: delims = mdelims[ienviron];             /* mdelims[] string for ienviron */
10538: if ( delims != NULL )                   /* add appropriate opening delim */
10539:   { strcpy(subexpr,"\\");               /* start with \ for (,[,{,|,= */
10540:     strcat(subexpr,delims);             /* then add opening delim */
10541:     subexpr[2] = '\000'; }              /* remove extraneous closing delim */
10542: switch ( ienviron )
10543:   {
10544:   default: goto end_of_job;             /* environ not implemented yet */
10545:   case 0:                               /* \begin{eqnarray} */
10546:     strcpy(subexpr,"\\array{rcl$");     /* set default rcl for eqnarray */
10547:     break;
10548:   case 1:  case 2:  case 3:             /* \begin{array} followed by {lcr} */
10549:     strcpy(subexpr,"\\array{");         /*start with mimeTeX \array{ command*/
10550:     skipwhite(exprptr);                 /* bump to next non-white char */
10551:     if ( *exprptr == '{' )              /* assume we have {lcr} argument */
10552:       { exprptr = texsubexpr(exprptr,subexpr+7,0,"{","}",0,0); /*add on lcr*/
10553:         if ( *(subexpr+7) == '\000' ) goto end_of_job; /* quit if no lcr */
10554:         strcat(subexpr,"$"); }          /* add terminating $ to lcr */
10555:     break;
10556:   case 4:  case 5:  case 6:             /* \begin{pmatrix} or b,B,v,Vmatrix */
10557:   case 7:  case 8:
10558:     strcat(subexpr,"\\array{");         /*start with mimeTeX \array{ command*/
10559:     break;
10560:   case 9:                               /* gather */
10561:     strcat(subexpr,"\\array{c$");       /* center equations */
10562:     break;
10563:   case 10:                              /* align */
10564:     strcat(subexpr,"\\array{rclrclrclrclrclrcl$"); /* a&=b & c&=d & etc */
10565:     break;
10566:   case 11:                              /* verbatim */
10567:     strcat(subexpr,"{\\rm ");           /* {\rm ...} */
10568:     /*strcat(subexpr,"\\\\{\\rm ");*/   /* \\{\rm } doesn't work in context */
10569:     break;
10570:   case 12:                              /* picture */
10571:     strcat(subexpr,"\\picture");        /* picture environment */
10572:     skipwhite(exprptr);                 /* bump to next non-white char */
10573:     if ( *exprptr == '(' )              /*assume we have (width,height) arg*/
10574:       { exprptr = texsubexpr(exprptr,subexpr+8,0,"(",")",0,1); /*add on arg*/
10575:         if ( *(subexpr+8) == '\000' ) goto end_of_job; } /* quit if no arg */
10576:     strcat(subexpr,"{");                /* opening {  after (width,height) */
10577:     break;
10578:   case 13:                              /* cases */
10579:     strcat(subexpr,"\\array{ll$");      /* a&b \\ c&d etc */
10580:     break;
10581:   case 14:                              /* \begin{equation} */
10582:     strcat(subexpr,"{");                /* just enclose expression in {}'s */
10583:     break;
10584:   } /* --- end-of-switch(ienviron) --- */
10585: /* -------------------------------------------------------------------------
10586: locate matching \end{...}
10587: -------------------------------------------------------------------------- */
10588: /* --- first \end following \begin --- */
10589: if ( (endptr=strstr(exprptr,endtoken))  /* find 1st \end following \begin */
10590: ==   NULL ) goto end_of_job;            /* and quit if no \end found */
10591: /* --- find matching endptr by pushing past any nested \begin's --- */
10592: begptr = exprptr;                       /* start after first \begin{...} */
10593: while ( 1 )                             /*break when we find matching \end*/
10594:   {
10595:   /* --- first, set ptr to closing } terminating current \end{...} --- */
10596:   if ( (braceptr=strchr(endptr+1,'}'))  /* find 1st } following \end{ */
10597:   ==   NULL ) goto end_of_job;          /* and quit if no } found */
10598:   /* -- locate next nested \begin --- */
10599:   if ( (begptr=strstr(begptr,begtoken)) /* find next \begin{...} */
10600:   ==   NULL ) break;                    /*no more, so we have matching \end*/
10601:   begptr += strlen(begtoken);           /* push ptr past token */
10602:   if ( begptr >= endptr ) break;        /* past endptr, so not nested */
10603:   /* --- have nested \begin, so push forward to next \end --- */
10604:   nbegins++;                            /* count another nested \begin */
10605:   if ( (endptr=strstr(endptr+strlen(endtoken),endtoken)) /* find next \end */
10606:   ==   NULL ) goto end_of_job;          /* and quit if none found */
10607:   } /* --- end-of-while(1) --- */
10608: /* --- push expression past closing } of \end{} --- */
10609: *expression = braceptr+1;               /* resume processing after } */
10610: /* -------------------------------------------------------------------------
10611: add on everything (i.e., the ...'s) between \begin{}[{}] ... \end{}
10612: -------------------------------------------------------------------------- */
10613: /* --- add on everything, completing subexpr for \begin{}...\end{} --- */
10614: sublen = strlen(subexpr);               /* #chars in "preamble" */
10615: envlen = (int)(endptr-exprptr);         /* #chars between \begin{}{}...\end */
10616: memcpy(subexpr+sublen,exprptr,envlen);  /*concatanate environ after subexpr*/
10617: subexpr[sublen+envlen] = '\000';        /* and null-terminate */
10618: if ( 2 > 1 )                            /* always... */
10619:   strcat(subexpr,"}");                  /* ...followed by terminating } */
10620: /* --- add terminating \right), etc, if necessary --- */
10621: if ( delims != (char *)NULL )           /* need closing delim */
10622:  { strcat(subexpr,"\\");                /* start with \ for ),],},|,= */
10623:    strcat(subexpr,delims+1); }          /* add appropriate closing delim */
10624: /* -------------------------------------------------------------------------
10625: change nested \begin...\end to {\begin...\end} so \array{} can handle them
10626: -------------------------------------------------------------------------- */
10627: if ( nbegins > 0 )                      /* have nested begins */
10628:  if ( blevel < 2 )                      /* only need to do this once */
10629:   {
10630:   begptr = subexpr;                     /* start at beginning of subexpr */
10631:   while( (begptr=strstr(begptr,begtoken)) != NULL ) /* have \begin{...} */
10632:     { strchange(0,begptr,"{");          /* \begin --> {\begin */
10633:       begptr += strlen(begtoken); }     /* continue past {\begin */
10634:   endptr = subexpr;                     /* start at beginning of subexpr */
10635:   while( (endptr=strstr(endptr,endtoken)) != NULL ) /* have \end{...} */
10636:     if ( (braceptr=strchr(endptr+1,'}')) /* find 1st } following \end{ */
10637:     ==   NULL ) goto end_of_job;        /* and quit if no } found */
10638:     else                                /* found terminating } */
10639:      { strchange(0,braceptr,"}");       /* \end{...} --> \end{...}} */
10640:        endptr = braceptr+1; }           /* continue past \end{...} */
10641:   } /* --- end-of-if(nbegins>0) --- */
10642: /* -------------------------------------------------------------------------
10643: post process as necessary
10644: -------------------------------------------------------------------------- */
10645: switch ( ienviron )
10646:   {
10647:   default: break;                       /* no post-processing required */
10648:   case 10:                              /* align */
10649:     strreplace(subexpr,"&=","#*@*#=",0); /* tag all &='s */
10650:     strreplace(subexpr,"&<","#*@*#<",0); /* tag all &<'s */
10651:     strreplace(subexpr,"&\\lt","#*@*#<",0); /* tag all &\lt's */
10652:     strreplace(subexpr,"&\\leq","#*@*#\\leq",0); /* tag all &\leq's */
10653:     strreplace(subexpr,"&>","#*@*#>",0); /* tag all &>'s */
10654:     strreplace(subexpr,"&\\gt","#*@*#>",0); /* tag all &\gt's */
10655:     strreplace(subexpr,"&\\geq","#*@*#\\geq",0); /* tag all &\geq's */
10656:     if ( nbegins < 1 )                  /* don't modify nested arrays */
10657:       strreplace(subexpr,"&","\\hspace{10}&\\hspace{10}",0); /* add space */
10658:     strreplace(subexpr,"#*@*#=","& = &",0); /*restore and xlate tagged &='s*/
10659:     strreplace(subexpr,"#*@*#<","& \\lt &",0); /*restore, xlate tagged &<'s*/
10660:     strreplace(subexpr,"#*@*#\\leq","& \\leq &",0); /*xlate tagged &\leq's*/
10661:     strreplace(subexpr,"#*@*#>","& \\gt &",0); /*restore, xlate tagged &>'s*/
10662:     strreplace(subexpr,"#*@*#\\geq","& \\geq &",0); /*xlate tagged &\geq's*/
10663:     break;
10664:   case 11:                              /* verbatim */
10665:     strreplace(subexpr,"\n","\\\\",0);  /* xlate \n newline to latex \\ */
10666:     /*strcat(subexpr,"\\\\");*/         /* add final latex \\ newline */
10667:     break;
10668:   case 12:                              /* picture */
10669:     strreplace(subexpr,"\\put "," ",0); /*remove \put's (not really needed)*/
10670:     strreplace(subexpr,"\\put(","(",0); /*remove \put's (not really needed)*/
10671:     strreplace(subexpr,"\\oval","\\circle",0); /* actually an ellipse */
10672:     break;
10673:   } /* --- end-of-switch(ienviron) --- */
10674: /* -------------------------------------------------------------------------
10675: return rasterized mimeTeX equivalent of \begin{}...\end{} environment
10676: -------------------------------------------------------------------------- */
10677: /* --- debugging output --- */
10678: if ( msgfp!=NULL && msglevel>=99 )
10679:   fprintf(msgfp,"rastbegin> subexpr=%s\n",subexpr);
10680: /* --- rasterize mimeTeX equivalent of \begin{}...\end{} environment --- */
10681: sp = rasterize(subexpr,size);           /* rasterize subexpr */
10682: end_of_job:
10683:   blevel--;                             /* decrement \begin nesting level */
10684:   return ( sp );                        /* back to caller with sp or NULL */
10685: } /* --- end-of-function rastbegin() --- */
10686: 
10687: 
10688: /* ==========================================================================
10689:  * Function:    rastarray ( expression, size, basesp, arg1, arg2, arg3 )
10690:  * Purpose:     \array handler, returns a subraster corresponding to array
10691:  *              expression (immediately following \array) at font size
10692:  * --------------------------------------------------------------------------
10693:  * Arguments:   expression (I/O) char **  to first char of null-terminated
10694:  *                              string immediately following \array to be
10695:  *                              rasterized, and returning ptr immediately
10696:  *                              following last character processed.
10697:  *              size (I)        int containing 0-7 default font size
10698:  *              basesp (I)      subraster *  to character (or subexpression)
10699:  *                              immediately preceding \array
10700:  *                              (unused, but passed for consistency)
10701:  *              arg1 (I)        int unused
10702:  *              arg2 (I)        int unused
10703:  *              arg3 (I)        int unused
10704:  * --------------------------------------------------------------------------
10705:  * Returns:     ( subraster * ) ptr to subraster corresponding to array
10706:  *                              expression, or NULL for any parsing error
10707:  * --------------------------------------------------------------------------
10708:  * Notes:     o Summary of syntax...
10709:  *                      \array{3,lcrBC$a&b&c\\d&e&f\\etc}
10710:  *            o The 3,lcrBC$ part is an optional "preamble".  The lcr means
10711:  *              what you think, i.e., "horizontal" left,center,right
10712:  *              justification down corresponding column.  The new BC means
10713:  *              "vertical" baseline,center justification across corresponding
10714:  *              row.  The leading 3 specifies the font size 0-4 to be used.
10715:  *              You may also specify +1,-1,+2,-2, etc, which is used as an
10716:  *              increment to the current font size, e.g., -1,lcr$ uses
10717:  *              one font size smaller than current.  Without a leading
10718:  *              + or -, the font size is "absolute".
10719:  *            o The preamble can also be just lcrBC$ without a leading
10720:  *              size-part, or just 3$ without a trailing lcrBC-part.
10721:  *              The default size is whatever is current, and the
10722:  *              default justification is c(entered) and B(aseline).
10723:  * ======================================================================= */
10724: /* --- entry point --- */
10725: subraster *rastarray ( char **expression, int size, subraster *basesp,
10726:                         int arg1, int arg2, int arg3 )
10727: {
10728: /* -------------------------------------------------------------------------
10729: Allocations and Declarations
10730: -------------------------------------------------------------------------- */
10731: char    *texsubexpr(), subexpr[MAXSUBXSZ+1], *exprptr, /*parse array subexpr*/
10732:          subtok[MAXTOKNSZ+1], *subptr=subtok, /* &,\\ inside { } not a delim*/
10733:          token[MAXTOKNSZ+1],  *tokptr=token, /* subexpr token to rasterize */
10734:         *preamble(),   *preptr=token;   /*process optional size,lcr preamble*/
10735: char    *coldelim="&", *rowdelim="\\";  /* need escaped rowdelim */
10736: int     maxarraysz = 63;                /* max #rows, cols */
10737: 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 */
10738:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10739:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10740:           hline[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* hline above row? */
10741:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10742:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10743:           vline[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*vline left of col?*/
10744:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10745:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10746:        colwidth[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*widest tokn in col*/
10747:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10748:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10749:       rowheight[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* "highest" in row */
10750:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10751:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10752:      fixcolsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed col width*/
10753:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10754:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10755:      fixrowsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed row height*/
10756:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10757:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10758:       rowbaseln[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* baseline for row */
10759:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10760:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10761:       vrowspace[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*extra //[len]space*/
10762:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10763:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
10764:       rowcenter[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*true = vcenter row*/
10765:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
10766:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
10767: static int /* --- propagate global values across arrays --- */
10768:        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 */
10769:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
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:       gcolwidth[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*widest tokn in col*/
10772:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
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:      growheight[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* "highest" in row */
10775:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
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:     gfixcolsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed col width*/
10778:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
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:     gfixrowsize[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*1=fixed row height*/
10781:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
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:      growcenter[65]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /*true = vcenter row*/
10784:                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
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: int     rowglobal=0, colglobal=0,       /* true to set global values */
10787:         rowpropagate=0, colpropagate=0; /* true if propagating values */
10788: int     irow,nrows=0, icol,ncols[65],   /*#rows in array, #cols in each row*/
10789:         maxcols=0;                      /* max# cols in any single row */
10790: int     itoken, ntokens=0,              /* index, total #tokens in array */
10791:         subtoklen=0,                    /* strlen of {...} subtoken */
10792:         istokwhite=1,                   /* true if token all whitespace */
10793:         nnonwhite=0;                    /* #non-white tokens */
10794: int     isescape=0,wasescape=0,         /* current,prev chars escape? */
10795:         ischarescaped=0,                /* is current char escaped? */
10796:         nescapes=0;                     /* #consecutive escapes */
10797: subraster *rasterize(), *toksp[1025],   /* rasterize tokens */
10798:         *new_subraster(), *arraysp=NULL; /* subraster for entire array */
10799: raster  *arrayrp=NULL;                  /* raster for entire array */
10800: int     delete_subraster();             /* free toksp[] workspace at eoj */
10801: int     rowspace=2, colspace=4,         /* blank space between rows, cols */
10802:         hspace=1, vspace=1;             /*space to accommodate hline,vline*/
10803: int     width=0, height=0,              /* width,height of array */
10804:         leftcol=0, toprow=0;            /*upper-left corner for cell in it*/
10805: int     rastput();                      /* embed tokens/cells in array */
10806: int     rule_raster();                  /* draw hlines and vlines in array */
10807: char    *hlchar="\\hline", *hdchar="\\hdash"; /* token signals hline */
10808: char    *texchar(), hltoken[1025];      /* extract \hline from token */
10809: int     ishonly=0, hltoklen, minhltoklen=3; /*flag, token must be \hl or \hd*/
10810: int     isnewrow=1;                     /* true for new row */
10811: int     pixsz = 1;                      /*default #bits per pixel, 1=bitmap*/
10812: int     evalterm(), evalue=0;           /* evaluate [arg], {arg} */
10813: static  int mydaemonlevel = 0;          /* check against global daemonlevel*/
10814: /* -------------------------------------------------------------------------
10815: Macros to determine extra raster space required for vline/hline
10816: -------------------------------------------------------------------------- */
10817: #define vlinespace(icol) \
10818:         ( vline[icol] == 0?  0 :        /* no vline so no space needed */   \
10819:           ( icol<1 || icol>=maxcols? vspace+(colspace+1)/2 : vspace ) )
10820: #define hlinespace(irow) \
10821:         ( hline[irow] == 0?  0 :        /* no hline so no space needed */   \
10822:           ( irow<1 || irow>=nrows? hspace+(rowspace+1)/2 : hspace ) )
10823: /* -------------------------------------------------------------------------
10824: Obtain array subexpression
10825: -------------------------------------------------------------------------- */
10826: /* --- parse for array subexpression, and bump expression past it --- */
10827: subexpr[1] = *subexpr = ' ';            /* set two leading blanks */
10828: *expression = texsubexpr(*expression,subexpr+2,0,"{","}",0,0);
10829: if ( msglevel>=29 && msgfp!=NULL )      /* debugging, display array */
10830:   fprintf(msgfp,"rastarray> %.256s\n",subexpr+2);
10831: if ( *(subexpr+2)=='\000' )             /* couldn't get subexpression */
10832:   goto end_of_job;                      /* nothing to do, so quit */
10833: /* -------------------------------------------------------------------------
10834: reset static arrays if main re-entered as daemon (or dll)
10835: -------------------------------------------------------------------------- */
10836: if ( mydaemonlevel != daemonlevel ) {   /* main re-entered */
10837:   for ( icol=0; icol<=maxarraysz; icol++ ) /* for each array[] index */
10838:     gjustify[icol]    = gcolwidth[icol]   = growheight[icol] =
10839:     gfixcolsize[icol] = gfixrowsize[icol] = growcenter[icol] = 0;
10840:   mydaemonlevel = daemonlevel; }        /* update mydaemonlevel */
10841: /* -------------------------------------------------------------------------
10842: process optional size,lcr preamble if present
10843: -------------------------------------------------------------------------- */
10844: /* --- reset size, get lcr's, and push exprptr past preamble --- */
10845: exprptr = preamble(subexpr+2,&size,preptr); /* reset size and get lcr's */
10846: /* --- init with global values --- */
10847: for(icol=0; icol<=maxarraysz; icol++) { /* propagate global values... */
10848:   justify[icol] = gjustify[icol];       /* -1,0,+1 = l,c,r */
10849:   colwidth[icol] = gcolwidth[icol];     /* column width */
10850:   rowheight[icol] = growheight[icol];   /* row height */
10851:   fixcolsize[icol] = gfixcolsize[icol]; /* 1=fixed col width */
10852:   fixrowsize[icol] = gfixrowsize[icol]; /* 1=fixed row height */
10853:   rowcenter[icol] = growcenter[icol]; } /* true = vcenter row */
10854: /* --- process lcr's, etc in preamble --- */
10855: itoken = 0;                             /* debugging flag */
10856: if ( msglevel>=29 && msgfp!=NULL )      /* debugging, display preamble */
10857:  if ( *preptr != '\000' )               /* if we have one */
10858:   fprintf(msgfp,"rastarray> preamble= \"%.256s\"\nrastarray> preamble: ",
10859:   preptr);
10860: irow = icol = 0;                        /* init lcr counts */
10861: while (  *preptr != '\000' )            /* check preamble text for lcr */
10862:   {
10863:   char  prepchar = *preptr;             /* current preamble character */
10864:   int   prepcase = (islower(prepchar)?1:(isupper(prepchar)?2:0)); /*1,2,or 0*/
10865:   if ( irow<maxarraysz && icol<maxarraysz )
10866:    switch ( /*tolower*/(prepchar) )
10867:     {  default: break;                  /* just flush unrecognized chars */
10868:       case 'l': justify[icol] = (-1);           /*left-justify this column*/
10869:                 if (colglobal) gjustify[irow] = justify[irow]; break;
10870:       case 'c': justify[icol] = (0);            /* center this column */
10871:                 if (colglobal) gjustify[irow] = justify[irow]; break;
10872:       case 'r': justify[icol] = (+1);           /* right-justify this col */
10873:                 if (colglobal) gjustify[irow] = justify[irow]; break;
10874:       case '|': vline[icol] += 1;   break;      /* solid vline left of col */
10875:       case '.': vline[icol] = (-1); break;      /*dashed vline left of col */
10876:       case 'b': prepchar='B'; prepcase=2;       /* alias for B */
10877:       case 'B': break;                          /* baseline-justify row */
10878:       case 'v': prepchar='C'; prepcase=2;       /* alias for C */
10879:       case 'C': rowcenter[irow] = 1;            /* vertically center row */
10880:                 if (rowglobal) growcenter[irow] = rowcenter[irow]; break;
10881:       case 'g': colglobal=1; prepcase=0; break; /* set global col values */
10882:       case 'G': rowglobal=1; prepcase=0; break; /* set global row values */
10883:       case '#': colglobal=rowglobal=1; break; } /* set global col,row vals */
10884:   if ( msglevel>=29 && msgfp!=NULL )    /* debugging */
10885:     fprintf(msgfp," %c[%d]",prepchar,
10886:     (prepcase==1?icol+1:(prepcase==2?irow+1:0)));
10887:   preptr++;                             /* check next char for lcr */
10888:   itoken++;                             /* #lcr's processed (debugging only)*/
10889:   /* --- check for number or +number specifying colwidth or rowheight --- */
10890:   if ( prepcase != 0 )                  /* only check upper,lowercase */
10891:    {
10892:    int  ispropagate = (*preptr=='+'?1:0); /* leading + propagates width/ht */
10893:    if ( ispropagate ) {                 /* set row or col propagation */
10894:      if ( prepcase == 1 ) colpropagate = 1; /* propagating col values */
10895:      else if ( prepcase == 2 ) rowpropagate = 1; } /*propagating row values*/
10896:    if ( !colpropagate && prepcase == 1 )
10897:       { colwidth[icol] = 0;             /* reset colwidth */
10898:         fixcolsize[icol] = 0; }         /* reset width flag */
10899:    if ( !rowpropagate && prepcase == 2 )
10900:       { rowheight[irow] = 0;            /* reset row height */
10901:         fixrowsize[irow] = 0; }         /* reset height flag */
10902:    if ( ispropagate ) preptr++;         /* bump past leading + */
10903:    if ( isdigit(*preptr) )              /* digit follows character */
10904:      { char *endptr = NULL;             /* preptr set to 1st char after num*/
10905:        int size = (int)(strtol(preptr,&endptr,10)); /* interpret number */
10906:        char *whchars="?wh";             /* debugging width/height labels */
10907:        preptr = endptr;                 /* skip over all digits */
10908:        if ( size==0 || (size>=3&&size<=500) ) { /* sanity check */
10909:         int index;                      /* icol,irow...maxarraysz index */
10910:         if ( prepcase == 1 )            /* lowercase signifies colwidth */
10911:          for(index=icol; index<=maxarraysz; index++) { /*propagate col size*/
10912:           colwidth[index] = size;       /* set colwidth to fixed size */
10913:           fixcolsize[index] = (size>0?1:0); /* set fixed width flag */
10914:           justify[index] = justify[icol]; /* and propagate justification */
10915:           if ( colglobal ) {            /* set global values */
10916:             gcolwidth[index] = colwidth[index]; /* set global col width */
10917:             gfixcolsize[index] = fixcolsize[index]; /*set global width flag*/
10918:             gjustify[index] = justify[icol]; } /* set global col justify */
10919:           if ( !ispropagate ) break; }  /* don't propagate */
10920:         else                            /* uppercase signifies rowheight */
10921:          for(index=irow; index<=maxarraysz; index++) { /*propagate row size*/
10922:           rowheight[index] = size;      /* set rowheight to size */
10923:           fixrowsize[index] = (size>0?1:0); /* set fixed height flag */
10924:           rowcenter[index] = rowcenter[irow]; /* and propagate row center */
10925:           if ( rowglobal ) {            /* set global values */
10926:             growheight[index] = rowheight[index]; /* set global row height */
10927:             gfixrowsize[index] = fixrowsize[index]; /*set global height flag*/
10928:             growcenter[index] = rowcenter[irow]; } /*set global row center*/
10929:           if ( !ispropagate ) break; }  /* don't propagate */
10930:         } /* --- end-of-if(size>=3&&size<=500) --- */
10931:        if ( msglevel>=29 && msgfp!=NULL ) /* debugging */
10932:          fprintf(msgfp,":%c=%d/fix#%d",whchars[prepcase],
10933:          (prepcase==1?colwidth[icol]:rowheight[irow]),
10934:          (prepcase==1?fixcolsize[icol]:fixrowsize[irow]));
10935:      } /* --- end-of-if(isdigit()) --- */
10936:    } /* --- end-of-if(prepcase!=0) --- */
10937:   if ( prepcase == 1 ) icol++;          /* bump col if lowercase lcr */
10938:     else if ( prepcase == 2 ) irow++;   /* bump row if uppercase BC */
10939:   } /* --- end-of-while(*preptr!='\000') --- */
10940: if ( msglevel>=29 && msgfp!=NULL )      /* debugging, emit final newline */
10941:  if ( itoken > 0 )                      /* if we have preamble */
10942:   fprintf(msgfp,"\n");
10943: /* -------------------------------------------------------------------------
10944: tokenize and rasterize components  a & b \\ c & d \\ etc  of subexpr
10945: -------------------------------------------------------------------------- */
10946: /* --- rasterize tokens one at a time, and maintain row,col counts --- */
10947: nrows = 0;                              /* start with top row */
10948: ncols[nrows] = 0;                       /* no tokens/cols in top row yet */
10949: while ( 1 )                             /* scan chars till end */
10950:   {
10951:   /* --- local control flags --- */
10952:   int   iseox = (*exprptr == '\000'),   /* null signals end-of-expression */
10953:         iseor = iseox,                  /* \\ or eox signals end-of-row */
10954:         iseoc = iseor;                  /* & or eor signals end-of-col */
10955:   /* --- check for escapes --- */
10956:   isescape = isthischar(*exprptr,ESCAPE); /* is current char escape? */
10957:   wasescape= (!isnewrow&&isthischar(*(exprptr-1),ESCAPE)); /*prev char esc?*/
10958:   nescapes = (wasescape?nescapes+1:0);  /* # preceding consecutive escapes */
10959:   ischarescaped = (nescapes%2==0?0:1);  /* is current char escaped? */
10960:   /* -----------------------------------------------------------------------
10961:   check for {...} subexpression starting from where we are now
10962:   ------------------------------------------------------------------------ */
10963:   if ( *exprptr == '{'                  /* start of {...} subexpression */
10964:   &&   !ischarescaped )                 /* if not escaped \{ */
10965:     {
10966:     subptr = texsubexpr(exprptr,subtok,4095,"{","}",1,1); /*entire subexpr*/
10967:     subtoklen = strlen(subtok);         /* #chars in {...} */
10968:     memcpy(tokptr,exprptr,subtoklen);   /* copy {...} to accumulated token */
10969:     tokptr  += subtoklen;               /* bump tokptr to end of token */
10970:     exprptr += subtoklen;               /* and bump exprptr past {...} */
10971:     istokwhite = 0;                     /* signal non-empty token */
10972:     continue;                           /* continue with char after {...} */
10973:     } /* --- end-of-if(*exprptr=='{') --- */
10974:   /* -----------------------------------------------------------------------
10975:   check for end-of-row(\\) and/or end-of-col(&)
10976:   ------------------------------------------------------------------------ */
10977:   /* --- check for (escaped) end-of-row delimiter --- */
10978:   if ( isescape && !ischarescaped )     /* current char is escaped */
10979:     if ( isthischar(*(exprptr+1),rowdelim) /* next char is rowdelim */
10980:     ||   *(exprptr+1) == '\000' )       /* or a pathological null */
10981:       { iseor = 1;                      /* so set end-of-row flag */
10982:         wasescape=isescape=nescapes = 0; } /* reset flags for new row */
10983:   /* --- check for end-of-col delimiter --- */
10984:   if (iseor                             /* end-of-row signals end-of-col */
10985:   ||  (!ischarescaped&&isthischar(*exprptr,coldelim))) /*or unescaped coldel*/
10986:       iseoc = 1;                        /* so set end-of-col flag */
10987:   /* -----------------------------------------------------------------------
10988:   rasterize completed token
10989:   ------------------------------------------------------------------------ */
10990:   if ( iseoc )                          /* we have a completed token */
10991:     {
10992:     *tokptr = '\000';                   /* first, null-terminate token */
10993:     /* --- check first token in row for [len] and/or \hline or \hdash --- */
10994:     ishonly = 0;                        /*init for token not only an \hline*/
10995:     if ( ncols[nrows] == 0 )            /*\hline must be first token in row*/
10996:       {
10997:       tokptr=token; skipwhite(tokptr);  /* skip whitespace after // */
10998:       /* --- first check for optional [len] --- */
10999:       if ( *tokptr == '[' ) {           /* have [len] if leading char is [ */
11000:         /* ---parse [len] and bump tokptr past it, interpret as double--- */
11001:         char lenexpr[128];  int len;    /* chars between [...] as int */
11002:         tokptr = texsubexpr(tokptr,lenexpr,127,"[","]",0,0);
11003:         if ( *lenexpr != '\000' ) {     /* got [len] expression */
11004:           evalue = evalterm(mimestore,lenexpr); /* evaluate len expression */
11005:           len = iround(unitlength*((double)evalue)); /* len in pixels */
11006:           if ( len>=(-63) && len<=255 ) { /* sanity check */
11007:             vrowspace[nrows] = len;     /* extra vspace before this row */
11008:             strsqueezep(token,tokptr);  /* flush [len] from token */
11009:             tokptr=token; skipwhite(tokptr); } } /* reset ptr, skip white */
11010:         } /* --- end-of-if(*tokptr=='[') --- */
11011:       /* --- now check for \hline or \hdash --- */
11012:       tokptr = texchar(tokptr,hltoken); /* extract first char from token */
11013:       hltoklen = strlen(hltoken);       /* length of first char */
11014:       if ( hltoklen >= minhltoklen ) {  /*token must be at least \hl or \hd*/
11015:         if ( memcmp(hlchar,hltoken,hltoklen) == 0 ) /* we have an \hline */
11016:            hline[nrows] += 1;           /* bump \hline count for row */
11017:         else if ( memcmp(hdchar,hltoken,hltoklen) == 0 ) /*we have an \hdash*/
11018:            hline[nrows] = (-1); }       /* set \hdash flag for row */
11019:       if ( hline[nrows] != 0 )          /* \hline or \hdash prefixes token */
11020:         { skipwhite(tokptr);            /* flush whitespace after \hline */
11021:           if ( *tokptr == '\000'        /* end-of-expression after \hline */
11022:           ||   isthischar(*tokptr,coldelim) ) /* or unescaped coldelim */
11023:             { istokwhite = 1;           /* so token contains \hline only */
11024:               if ( iseox ) ishonly = 1; } /* ignore entire row at eox */
11025:           else                          /* token contains more than \hline */
11026:             {strsqueezep(token,tokptr);} } /* so flush \hline */
11027:       } /* --- end-of-if(ncols[nrows]==0) --- */
11028:     /* --- rasterize completed token --- */
11029:     toksp[ntokens] = (istokwhite? NULL : /* don't rasterize empty token */
11030:       rasterize(token,size));           /* rasterize non-empty token */
11031:     if ( toksp[ntokens] != NULL )       /* have a rasterized token */
11032:       nnonwhite++;                      /* bump rasterized token count */
11033:     /* --- maintain colwidth[], rowheight[] max, and rowbaseln[] --- */
11034:     if ( toksp[ntokens] != NULL )       /* we have a rasterized token */
11035:       {
11036:       /* --- update max token "height" in current row, and baseline --- */
11037:       int twidth = ((toksp[ntokens])->image)->width,  /* width of token */
11038:         theight = ((toksp[ntokens])->image)->height, /* height of token */
11039:         tbaseln =  (toksp[ntokens])->baseline, /* baseline of token */
11040:         rheight = rowheight[nrows],     /* current max height for row */
11041:         rbaseln = rowbaseln[nrows];     /* current baseline for max height */
11042:       if ( 0 || fixrowsize[nrows]==0 )  /* rowheight not fixed */
11043:        rowheight[nrows] = /*max2( rheight,*/( /* current (max) rowheight */
11044:         max2(rbaseln+1,tbaseln+1)       /* max height above baseline */
11045:         + max2(rheight-rbaseln-1,theight-tbaseln-1) ); /* plus max below */
11046:       rowbaseln[nrows] = max2(rbaseln,tbaseln); /*max space above baseline*/
11047:       /* --- update max token width in current column --- */
11048:       icol = ncols[nrows];              /* current column index */
11049:       if ( 0 || fixcolsize[icol]==0 )   /* colwidth not fixed */
11050:        colwidth[icol] = max2(colwidth[icol],twidth); /*widest token in col*/
11051:       } /* --- end-of-if(toksp[]!=NULL) --- */
11052:     /* --- bump counters --- */
11053:     if ( !ishonly )                     /* don't count only an \hline */
11054:       if ( ncols[nrows] < maxarraysz )  /* don't overflow arrays */
11055:         { ntokens++;                    /* bump total token count */
11056:           ncols[nrows] += 1; }          /* and bump #cols in current row */
11057:     /* --- get ready for next token --- */
11058:     tokptr = token;                     /* reset ptr for next token */
11059:     istokwhite = 1;                     /* next token starts all white */
11060:     } /* --- end-of-if(iseoc) --- */
11061:   /* -----------------------------------------------------------------------
11062:   bump row as necessary
11063:   ------------------------------------------------------------------------ */
11064:   if ( iseor )                          /* we have a completed row */
11065:     {
11066:     maxcols = max2(maxcols,ncols[nrows]); /* max# cols in array */
11067:     if ( ncols[nrows]>0 || hline[nrows]==0 ) /*ignore row with only \hline*/
11068:       if ( nrows < maxarraysz )         /* don't overflow arrays */
11069:         nrows++;                        /* bump row count */
11070:     ncols[nrows] = 0;                   /* no cols in this row yet */
11071:     if ( !iseox )                       /* don't have a null yet */
11072:       { exprptr++;                      /* bump past extra \ in \\ delim */
11073:         iseox = (*exprptr == '\000'); } /* recheck for pathological \null */
11074:     isnewrow = 1;                       /* signal start of new row */
11075:     } /* --- end-of-if(iseor) --- */
11076:   else
11077:     isnewrow = 0;                       /* no longer first col of new row */
11078:   /* -----------------------------------------------------------------------
11079:   quit when done, or accumulate char in token and proceed to next char
11080:   ------------------------------------------------------------------------ */
11081:   /* --- quit when done --- */
11082:   if ( iseox ) break;                   /* null terminator signalled done */
11083:   /* --- accumulate chars in token --- */
11084:   if ( !iseoc )                         /* don't accumulate delimiters */
11085:     { *tokptr++ = *exprptr;             /* accumulate non-delim char */
11086:       if ( !isthischar(*exprptr,WHITESPACE) ) /* this token isn't empty */
11087:         istokwhite = 0; }               /* so reset flag to rasterize it */
11088:   /* --- ready for next char --- */
11089:   exprptr++;                            /* bump ptr */
11090:   } /* --- end-of-while(*exprptr!='\000') --- */
11091: /* --- make sure we got something to do --- */
11092: if ( nnonwhite < 1 )                    /* completely empty array */
11093:   goto end_of_job;                      /* NULL back to caller */
11094: /* -------------------------------------------------------------------------
11095: determine dimensions of array raster and allocate it
11096: -------------------------------------------------------------------------- */
11097: /* --- adjust colspace --- */
11098: colspace = 2 + 2*size;                  /* temp kludge */
11099: /* --- reset propagated sizes at boundaries of array --- */
11100: colwidth[maxcols] = rowheight[nrows] = 0; /* reset explicit 0's at edges */
11101: /* --- determine width of array raster --- */
11102: width = colspace*(maxcols-1);           /* empty space between cols */
11103: if ( msglevel>=29 && msgfp!=NULL )      /* debugging */
11104:   fprintf(msgfp,"rastarray> %d cols,  widths: ",maxcols);
11105: for ( icol=0; icol<=maxcols; icol++ )   /* and for each col */
11106:   { width += colwidth[icol];            /*width of this col (0 for maxcols)*/
11107:     width += vlinespace(icol);          /*plus space for vline, if present*/
11108:     if ( msglevel>=29 && msgfp!=NULL )  /* debugging */
11109:      fprintf(msgfp," %d=%2d+%d",icol+1,colwidth[icol],(vlinespace(icol))); }
11110: /* --- determine height of array raster --- */
11111: height = rowspace*(nrows-1);            /* empty space between rows */
11112: if ( msglevel>=29 && msgfp!=NULL )      /* debugging */
11113:   fprintf(msgfp,"\nrastarray> %d rows, heights: ",nrows);
11114: for ( irow=0; irow<=nrows; irow++ )     /* and for each row */
11115:   { height += rowheight[irow];          /*height of this row (0 for nrows)*/
11116:     height += vrowspace[irow];          /*plus extra //[len], if present*/
11117:     height += hlinespace(irow);         /*plus space for hline, if present*/
11118:     if ( msglevel>=29 && msgfp!=NULL )  /* debugging */
11119:      fprintf(msgfp," %d=%2d+%d",irow+1,rowheight[irow],(hlinespace(irow))); }
11120: /* --- allocate subraster and raster for array --- */
11121: if ( msglevel>=29 && msgfp!=NULL )      /* debugging */
11122:   fprintf(msgfp,"\nrastarray> tot width=%d(colspc=%d) height=%d(rowspc=%d)\n",
11123:   width,colspace, height,rowspace);
11124: if ( (arraysp=new_subraster(width,height,pixsz)) /* allocate new subraster */
11125: ==   NULL )  goto end_of_job;           /* quit if failed */
11126: /* --- initialize subraster parameters --- */
11127: arraysp->type = IMAGERASTER;            /* image */
11128: arraysp->symdef = NULL;                 /* not applicable for image */
11129: arraysp->baseline=min2(height/2+5,height-1); /*is a little above center good?*/
11130: arraysp->size = size;                   /* size (probably unneeded) */
11131: arrayrp = arraysp->image;               /* raster embedded in subraster */
11132: /* -------------------------------------------------------------------------
11133: embed tokens/cells in array
11134: -------------------------------------------------------------------------- */
11135: itoken = 0;                             /* start with first token */
11136: toprow = 0;                             /* start at top row of array */
11137: for ( irow=0; irow<=nrows; irow++ )     /*tokens were accumulated row-wise*/
11138:   {
11139:   /* --- initialization for row --- */
11140:   int   baseline = rowbaseln[irow];     /* baseline for this row */
11141:   if ( hline[irow] != 0 )               /* need hline above this row */
11142:     { int hrow = (irow<1? 0 : toprow - rowspace/2); /* row for hline */
11143:       if ( irow >= nrows ) hrow = height-1; /* row for bottom hline */
11144:       rule_raster(arrayrp,hrow,0,width,1,(hline[irow]<0?1:0)); } /* hline */
11145:   if ( irow >= nrows ) break;           /*just needed \hline for irow=nrows*/
11146:   toprow += vrowspace[irow];            /* extra //[len] space above irow */
11147:   if ( toprow < 0 ) toprow = 0;         /* check for large negative [-len] */
11148:   toprow += hlinespace(irow);           /* space for hline above irow */
11149:   leftcol = 0;                          /* start at leftmost column */
11150:   for ( icol=0; icol<ncols[irow]; icol++ ) /* go through cells in this row */
11151:     {
11152:     subraster *tsp = toksp[itoken];     /* token that belongs in this cell */
11153:     /* --- first adjust leftcol for vline to left of icol, if present ---- */
11154:     leftcol += vlinespace(icol);        /* space for vline to left of col */
11155:     /* --- now rasterize cell ---- */
11156:     if ( tsp != NULL )                  /* have a rasterized cell token */
11157:       {
11158:       /* --- local parameters --- */
11159:       int cwidth = colwidth[icol],      /* total column width */
11160:           twidth = (tsp->image)->width, /* token width */
11161:           theight= (tsp->image)->height, /* token height */
11162:           tokencol = 0,                 /*H offset (init for left justify)*/
11163:           tokenrow = baseline - tsp->baseline;/*V offset (init for baseline)*/
11164:       /* --- adjust leftcol for vline to left of icol, if present ---- */
11165:       /*leftcol += vlinespace(icol);*/  /* space for vline to left of col */
11166:       /* --- reset justification (if not left-justified) --- */
11167:       if ( justify[icol] == 0 )         /* but user wants it centered */
11168:           tokencol = (cwidth-twidth+1)/2; /* so split margin left/right */
11169:       else if ( justify[icol] == 1 )    /* or user wants right-justify */
11170:           tokencol = cwidth-twidth;     /* so put entire margin at left */
11171:       /* --- reset vertical centering (if not baseline-aligned) --- */
11172:       if ( rowcenter[irow] )            /* center cells in row vertically */
11173:           tokenrow = (rowheight[irow]-theight)/2; /* center row */
11174:       /* --- embed token raster at appropriate place in array raster --- */
11175:       rastput(arrayrp,tsp->image,       /* overlay cell token in array */
11176:           toprow+ tokenrow,             /*with aligned baseline or centered*/
11177:           leftcol+tokencol, 1);         /* and justified as requested */
11178:       } /* --- end-of-if(tsp!=NULL) --- */
11179:     itoken++;                           /* bump index for next cell */
11180:     leftcol += colwidth[icol] + colspace /*move leftcol right for next col*/
11181:       /* + vlinespace(icol) */ ; /*don't add space for vline to left of col*/
11182:     } /* --- end-of-for(icol) --- */
11183:   toprow += rowheight[irow] + rowspace; /* move toprow down for next row */
11184:   } /* --- end-of-for(irow) --- */
11185: /* -------------------------------------------------------------------------
11186: draw vlines as necessary
11187: -------------------------------------------------------------------------- */
11188: leftcol = 0;                            /* start at leftmost column */
11189: for ( icol=0; icol<=maxcols; icol++ )   /* check each col for a vline */
11190:   {
11191:   if ( vline[icol] != 0 )               /* need vline to left of this col */
11192:     { int vcol = (icol<1? 0 : leftcol - colspace/2); /* column for vline */
11193:       if ( icol >= maxcols ) vcol = width-1; /*column for right edge vline*/
11194:       rule_raster(arrayrp,0,vcol,1,height,(vline[icol]<0?2:0)); } /* vline */
11195:   leftcol += vlinespace(icol);          /* space for vline to left of col */
11196:   if ( icol < maxcols )                 /* don't address past end of array */
11197:     leftcol += colwidth[icol] + colspace; /*move leftcol right for next col*/
11198:   } /* --- end-of-for(icol) --- */
11199: /* -------------------------------------------------------------------------
11200: free workspace and return final result to caller
11201: -------------------------------------------------------------------------- */
11202: end_of_job:
11203:   /* --- free workspace --- */
11204:   if ( ntokens > 0 )                    /* if we have workspace to free */
11205:     while ( --ntokens >= 0 )            /* free each token subraster */
11206:       if ( toksp[ntokens] != NULL )     /* if we rasterized this cell */
11207:         delete_subraster(toksp[ntokens]); /* then free it */
11208:   /* --- return final result to caller --- */
11209:   return ( arraysp );
11210: } /* --- end-of-function rastarray() --- */
11211: 
11212: 
11213: /* ==========================================================================
11214:  * Function:    rastpicture ( expression, size, basesp, arg1, arg2, arg3 )
11215:  * Purpose:     \picture handler, returns subraster corresponding to picture
11216:  *              expression (immediately following \picture) at font size
11217:  * --------------------------------------------------------------------------
11218:  * Arguments:   expression (I/O) char **  to first char of null-terminated
11219:  *                              string immediately following \picture to be
11220:  *                              rasterized, and returning ptr immediately
11221:  *                              following last character processed.
11222:  *              size (I)        int containing 0-7 default font size
11223:  *              basesp (I)      subraster *  to character (or subexpression)
11224:  *                              immediately preceding \picture
11225:  *                              (unused, but passed for consistency)
11226:  *              arg1 (I)        int unused
11227:  *              arg2 (I)        int unused
11228:  *              arg3 (I)        int unused
11229:  * --------------------------------------------------------------------------
11230:  * Returns:     ( subraster * ) ptr to subraster corresponding to picture
11231:  *                              expression, or NULL for any parsing error
11232:  * --------------------------------------------------------------------------
11233:  * Notes:     o Summary of syntax...
11234:  *                \picture(width,height){(x,y){pic_elem}~(x,y){pic_elem}~etc}
11235:  *            o
11236:  * ======================================================================= */
11237: /* --- entry point --- */
11238: subraster *rastpicture ( char **expression, int size, subraster *basesp,
11239:                         int arg1, int arg2, int arg3 )
11240: {
11241: /* -------------------------------------------------------------------------
11242: Allocations and Declarations
11243: -------------------------------------------------------------------------- */
11244: char    *texsubexpr(), picexpr[2049], *picptr=picexpr, /* picture {expre} */
11245:         putexpr[256], *putptr,*multptr, /*[multi]put (x,y[;xinc,yinc;num])*/
11246:         pream[96], *preptr,             /* optional put preamble */
11247:         picelem[1025];                  /* picture element following put */
11248: subraster   *rasterize(), *picelemsp=NULL, /* rasterize picture elements */
11249:         *new_subraster(), *picturesp=NULL, /* subraster for entire picture */
11250:         *oldworkingbox = workingbox;    /* save working box on entry */
11251: raster  *picturerp=NULL;                /* raster for entire picture */
11252: int     delete_subraster();             /* free picelemsp[] workspace */
11253: int     pixsz = 1;                      /* pixels are one bit each */
11254: double  x=0.0,y=0.0,                    /* x,y-coords for put,multiput*/
11255:         xinc=0.0,yinc=0.0;              /* x,y-incrementss for multiput*/
11256: int     width=0,  height=0,             /* #pixels width,height of picture */
11257:         ewidth=0, eheight=0,            /* pic element width,height */
11258:         maxwidth=1600, maxheight=1600,  /* max width,height in pixels */
11259:         ix=0,xpos=0, iy=0,ypos=0,       /* mimeTeX x,y pixel coords */
11260:         num=1, inum;                    /* number reps, index of element */
11261: int     evalterm();                     /* evaluate [arg] and {arg}'s */
11262: int     iscenter=0;                     /* center or lowerleft put position*/
11263: int     *oldworkingparam = workingparam, /* save working param on entry */
11264:         origin = 0;                     /* x,yinc ++=00 +-=01 -+=10 --=11 */
11265: int     rastput();                      /* embed elements in picture */
11266: int     type_raster();                  /* display debugging output */
11267: /* -------------------------------------------------------------------------
11268: First obtain (width,height) arguments immediately following \picture command
11269: -------------------------------------------------------------------------- */
11270: /* --- parse for (width,height) arguments, and bump expression past it --- */
11271: *expression = texsubexpr(*expression,putexpr,254,"(",")",0,0);
11272: if ( *putexpr == '\000' ) goto end_of_job; /* couldn't get (width,height) */
11273: /* --- now interpret width,height returned in putexpr --- */
11274: if ( (putptr=strchr(putexpr,',')) != NULL ) /* look for ',' in width,height*/
11275:   *putptr = '\000';                     /* found it, so replace ',' by '\0'*/
11276: width=height = eround(putexpr);         /*width pixels*/
11277: if ( putptr != NULL )                   /* 2nd arg, if present, is height */
11278:   height = eround(putptr+1);            /*in pixels*/
11279: /* -------------------------------------------------------------------------
11280: Then obtain entire picture {...} subexpression following (width,height)
11281: -------------------------------------------------------------------------- */
11282: /* --- parse for picture subexpression, and bump expression past it --- */
11283: *expression = texsubexpr(*expression,picexpr,2047,"{","}",0,0);
11284: if ( *picexpr == '\000' ) goto end_of_job; /* couldn't get {pic_elements} */
11285: /* -------------------------------------------------------------------------
11286: allocate subraster and raster for complete picture
11287: -------------------------------------------------------------------------- */
11288: /* --- sanity check on width,height args --- */
11289: if ( width < 2 ||  width > maxwidth
11290: ||  height < 2 || height > maxheight ) goto end_of_job;
11291: /* --- allocate and initialize subraster for constructed picture --- */
11292: if ( (picturesp=new_subraster(width,height,pixsz)) /*allocate new subraster*/
11293: ==   NULL )  goto end_of_job;           /* quit if failed */
11294: workingbox = picturesp;                 /* set workingbox to our picture */
11295: /* --- initialize picture subraster parameters --- */
11296: picturesp->type = IMAGERASTER;          /* image */
11297: picturesp->symdef = NULL;               /* not applicable for image */
11298: picturesp->baseline = height/2 + 2;     /* is a little above center good? */
11299: picturesp->size = size;                 /* size (probably unneeded) */
11300: picturerp = picturesp->image;           /* raster embedded in subraster */
11301: if ( msgfp!=NULL && msglevel>=29 )      /* debugging */
11302:   fprintf(msgfp,"picture> width,height=%d,%d\n",width,height);
11303: /* -------------------------------------------------------------------------
11304: parse out each picture element, rasterize it, and place it in picture
11305: -------------------------------------------------------------------------- */
11306: while ( *picptr != '\000' )             /* until we run out of pic_elems */
11307:   {
11308:   /* -----------------------------------------------------------------------
11309:   first obtain leading \[multi]put(x,y[;xinc,yinc;num]) args for pic_elem
11310:   ------------------------------------------------------------------------ */
11311:   /* --- init default values in case not explicitly supplied in args --- */
11312:   x=y=0.0;  xinc=yinc=0.0;  num=1;      /* init default values */
11313:   iscenter = origin = 0;                /* center, origin */
11314:   /* --- get (pream$x,y;xinc,yinc;num ) args and bump picptr past it --- */
11315:   while ( *picptr != '\000' )           /* skip invalid chars preceding ( */
11316:     if ( *picptr == '(' ) break;        /* found opening ( */
11317:     else picptr++;                      /* else skip invalid char */
11318:   picptr = texsubexpr(picptr,putexpr,254,"(",")",0,0);
11319:   if ( *putexpr == '\000' ) goto end_of_job; /* couldn't get (x,y) */
11320:   /* --- first look for $-terminated or for any non-digit preamble --- */
11321:   *pream = '\000';                      /* init preamble as empty string */
11322:   if ( (putptr=strchr(putexpr,'$')) != NULL ) /*check for $ pream terminator*/
11323:     { *putptr++ = '\000';               /* replace $ by '\0', bump past $ */
11324:       strninit(pream,putexpr,92); }     /* copy leading preamble from put */
11325:   else                                  /* look for any non-digit preamble */
11326:     { int npream = 0;                   /* #chars in preamble */
11327:       for ( preptr=pream,putptr=putexpr; ; npream++,putptr++ )
11328:         if ( *putptr == '\000'          /* end-of-putdata signalled */
11329:         ||   !isalpha((int)(*putptr))   /* or found non-alpha char */
11330:         ||   npream > 92 ) break;       /* or preamble too long */
11331:         else *preptr++ = *putptr;       /* copy alpha char to preamble */
11332:       *preptr = '\000'; }               /* null-terminate preamble */
11333:   /* --- interpret preamble --- */
11334:   for ( preptr=pream; ; preptr++ )      /* examine each preamble char */
11335:     if ( *preptr == '\000' ) break;     /* end-of-preamble signalled */
11336:     else switch ( tolower(*preptr) )    /* check lowercase preamble char */
11337:       {
11338:       default: break;                   /* unrecognized flag */
11339:       case 'c': iscenter=1; break;      /* center pic_elem at x,y coords */
11340:       } /* --- end-of-switch --- */
11341:   /* --- interpret x,y;xinc,yinc;num following preamble --- */
11342:   if ( *putptr != '\000' )              /*check for put data after preamble*/
11343:    {
11344:    /* --- first squeeze preamble out of put expression --- */
11345:    if ( *pream != '\000' )              /* have preamble */
11346:      {strsqueezep(putexpr,putptr);}     /* squeeze it out */
11347:    /* --- interpret x,y --- */
11348:    if ( (multptr=strchr(putexpr,';')) != NULL ) /*semicolon signals multiput*/
11349:      *multptr = '\000';                 /* replace semicolon by '\0' */
11350:    if ( (putptr=strchr(putexpr,',')) != NULL ) /* comma separates x,y */
11351:      *putptr = '\000';                  /* replace comma by '\0'  */
11352:    if ( *putexpr != '\000' )            /* leading , may be placeholder */
11353:      x = (double)(eround(putexpr));     /* x coord in pixels*/
11354:    if ( putptr != NULL )                /* 2nd arg, if present, is y coord */
11355:      y = (double)(eround(putptr+1));    /* in pixels */
11356:    /* --- interpret xinc,yinc,num if we have a multiput --- */
11357:    if ( multptr != NULL )               /* found ';' signalling multiput */
11358:      {
11359:      if ( (preptr=strchr(multptr+1,';')) != NULL ) /* ';' preceding num arg*/
11360:        *preptr = '\000';                /* replace ';' by '\0' */
11361:      if ( (putptr=strchr(multptr+1,',')) != NULL ) /* ',' between xinc,yinc*/
11362:        *putptr = '\000';                /* replace ',' by '\0' */
11363:      if ( *(multptr+1) != '\000' )      /* leading , may be placeholder */
11364:        xinc = (double)(eround(multptr+1)); /* xinc in pixels */
11365:      if ( putptr != NULL )              /* 2nd arg, if present, is yinc */
11366:        yinc = (double)(eround(putptr+1)); /* in user pixels */
11367:      num = (preptr==NULL? 999 : atoi(preptr+1)); /*explicit num val or 999*/
11368:      } /* --- end-of-if(multptr!=NULL) --- */
11369:    } /* --- end-of-if(*preptr!='\000') --- */
11370:   if ( msgfp!=NULL && msglevel>=29 )    /* debugging */
11371:     fprintf(msgfp,
11372:     "picture> pream;x,y;xinc,yinc;num=\"%s\";%.2f,%.2f;%.2f,%.2f;%d\n",
11373:     pream,x,y,xinc,yinc,num);
11374:   /* -----------------------------------------------------------------------
11375:   now obtain {...} picture element following [multi]put, and rasterize it
11376:   ------------------------------------------------------------------------ */
11377:   /* --- parse for {...} picture element and bump picptr past it --- */
11378:   picptr = texsubexpr(picptr,picelem,1023,"{","}",0,0);
11379:   if ( *picelem == '\000' ) goto end_of_job; /* couldn't get {pic_elem} */
11380:   if ( msgfp!=NULL && msglevel>=29 )    /* debugging */
11381:     fprintf(msgfp, "picture> picelem=\"%.50s\"\n",picelem);
11382:   /* --- rasterize picture element --- */
11383:   origin = 0;                           /* init origin as working param */
11384:   workingparam = &origin;               /* and point working param to it */
11385:   picelemsp = rasterize(picelem,size);  /* rasterize picture element */
11386:   if ( picelemsp == NULL ) continue;    /* failed to rasterize, skip elem */
11387:   ewidth  = (picelemsp->image)->width;  /* width of element, in pixels */
11388:   eheight = (picelemsp->image)->height; /* height of element, in pixels */
11389:   if ( origin == 55 ) iscenter = 1;     /* origin set to (.5,.5) for center*/
11390:   if ( msgfp!=NULL && msglevel>=29 )    /* debugging */
11391:     { fprintf(msgfp, "picture> ewidth,eheight,origin,num=%d,%d,%d,%d\n",
11392:       ewidth,eheight,origin,num);
11393:       if ( msglevel >= 999 ) type_raster(picelemsp->image,msgfp); }
11394:   /* -----------------------------------------------------------------------
11395:   embed element in picture (once, or multiple times if requested)
11396:   ------------------------------------------------------------------------ */
11397:   for ( inum=0; inum<num; inum++ )      /* once, or for num repetitions */
11398:     {
11399:     /* --- set x,y-coords for this iteration --- */
11400:     ix = iround(x);  iy = iround(y);    /* round x,y to nearest integer */
11401:     if ( iscenter )                     /* place center of element at x,y */
11402:       { xpos = ix - ewidth/2;           /* x picture coord to center elem */
11403:         ypos = height - iy - eheight/2; } /* y pixel coord to center elem */
11404:     else                                /* default places lower-left at x,y*/
11405:       { xpos = ix;                      /* set x pixel coord for left */
11406:         if ( origin==10 || origin==11 ) /* x,yinc's are -+ or -- */
11407:           xpos = ix - ewidth;           /* so set for right instead */
11408:         ypos = height - iy - eheight;   /* set y pixel coord for lower */
11409:         if ( origin==1 || origin==11 )  /* x,yinc's are +- or -- */
11410:           ypos = height - iy; }         /* so set for upper instead */
11411:     if ( msgfp!=NULL && msglevel>=29 )  /* debugging */
11412:       fprintf(msgfp,
11413:       "picture> inum,x,y,ix,iy,xpos,ypos=%d,%.2f,%.2f,%d,%d,%d,%d\n",
11414:       inum,x,y,ix,iy,xpos,ypos);
11415:     /* --- embed token raster at xpos,ypos, and quit if out-of-bounds --- */
11416:     if ( !rastput(picturerp,picelemsp->image,ypos,xpos,0) ) break;
11417:     /* --- apply increment --- */
11418:     if ( xinc==0. && yinc==0. ) break;  /* quit if both increments zero */
11419:     x += xinc;  y += yinc;              /* increment coords for next iter */
11420:     } /* --- end-of-for(inum) --- */
11421:   /* --- free picture element subraster after embedding it in picture --- */
11422:   delete_subraster(picelemsp);          /* done with subraster, so free it */
11423:   } /* --- end-of-while(*picptr!=0) --- */
11424: /* -------------------------------------------------------------------------
11425: return picture constructed from pic_elements to caller
11426: -------------------------------------------------------------------------- */
11427: end_of_job:
11428:   workingbox = oldworkingbox;           /* restore original working box */
11429:   workingparam = oldworkingparam;       /* restore original working param */
11430:   return ( picturesp );                 /* return our picture to caller */
11431: } /* --- end-of-function rastpicture() --- */
11432: 
11433: 
11434: /* ==========================================================================
11435:  * Function:    rastline ( expression, size, basesp, arg1, arg2, arg3 )
11436:  * Purpose:     \line handler, returns subraster corresponding to line
11437:  *              parameters (xinc,yinc){xlen}
11438:  * --------------------------------------------------------------------------
11439:  * Arguments:   expression (I/O) char **  to first char of null-terminated
11440:  *                              string immediately following \line to be
11441:  *                              rasterized, and returning ptr immediately
11442:  *                              following last character processed.
11443:  *              size (I)        int containing 0-7 default font size
11444:  *              basesp (I)      subraster *  to character (or subexpression)
11445:  *                              immediately preceding \line
11446:  *                              (unused, but passed for consistency)
11447:  *              arg1 (I)        int unused
11448:  *              arg2 (I)        int unused
11449:  *              arg3 (I)        int unused
11450:  * --------------------------------------------------------------------------
11451:  * Returns:     ( subraster * ) ptr to subraster corresponding to line
11452:  *                              requested, or NULL for any parsing error
11453:  * --------------------------------------------------------------------------
11454:  * Notes:     o Summary of syntax...
11455:  *                \line(xinc,yinc){xlen}
11456:  *            o if {xlen} not given, then it's assumed xlen = |xinc|
11457:  * ======================================================================= */
11458: /* --- entry point --- */
11459: subraster *rastline ( char **expression, int size, subraster *basesp,
11460:                         int arg1, int arg2, int arg3 )
11461: {
11462: /* -------------------------------------------------------------------------
11463: Allocations and Declarations
11464: -------------------------------------------------------------------------- */
11465: char    *texsubexpr(),linexpr[257], *xptr=linexpr; /*line(xinc,yinc){xlen}*/
11466: subraster *new_subraster(), *linesp=NULL; /* subraster for line */
11467: /*char  *origexpression = *expression;*/ /*original expression after \line*/
11468: int     pixsz = 1;                      /* pixels are one bit each */
11469: int     thickness = 1;                  /* line thickness */
11470: double  xinc=0.0, yinc=0.0,             /* x,y-increments for line, */
11471:         xlen=0.0, ylen=0.0;             /* x,y lengths for line */
11472: int     width=0,  height=0,             /* #pixels width,height of line */
11473:         maxwidth=1600, maxheight=1600,  /* max width,height in pixels */
11474:         rwidth=0, rheight=0;            /*alloc width,height plus thickness*/
11475: int     evalterm();                     /* evaluate [arg] and {arg}'s */
11476: int     istop=0,  isright=0,            /* origin at bot-left if x,yinc>=0 */
11477:         origin = 0;                     /* x,yinc: ++=00 +-=01 -+=10 --=11 */
11478: int     line_raster();                  /* draw line in linesp->image */
11479: /* -------------------------------------------------------------------------
11480: obtain (xinc,yinc) arguments immediately following \line command
11481: -------------------------------------------------------------------------- */
11482: /* --- parse for (xinc,yinc) arguments, and bump expression past it --- */
11483: *expression = texsubexpr(*expression,linexpr,253,"(",")",0,0);
11484: if ( *linexpr == '\000' ) goto end_of_job; /* couldn't get (xinc,yinc) */
11485: /* --- now interpret xinc,yinc;thickness returned in linexpr --- */
11486: if ( (xptr=strchr(linexpr,';')) != NULL ) /* look for ';' after xinc,yinc */
11487:   { *xptr = '\000';                     /* terminate linexpr at ; */
11488:     thickness = evalterm(mimestore,xptr+1); } /* get int thickness */
11489: if ( (xptr=strchr(linexpr,',')) != NULL ) /* look for ',' in xinc,yinc */
11490:   *xptr = '\000';                       /* found it, so replace ',' by '\0'*/
11491: if ( *linexpr != '\000' )               /* check against missing 1st arg */
11492:   xinc = xlen = (double)evalterm(mimestore,linexpr); /* xinc in user units */
11493: if ( xptr != NULL )                     /* 2nd arg, if present, is yinc */
11494:   yinc = ylen = (double)evalterm(mimestore,xptr+1); /* in user units */
11495: /* -------------------------------------------------------------------------
11496: obtain optional {xlen} following (xinc,yinc), and calculate ylen
11497: -------------------------------------------------------------------------- */
11498: /* --- check if {xlen} given --- */
11499: if ( *(*expression) == '{' )            /*have {xlen} if leading char is { */
11500:   {
11501:   /* --- parse {xlen} and bump expression past it, interpret as double --- */
11502:   *expression = texsubexpr(*expression,linexpr,253,"{","}",0,0);
11503:   if ( *linexpr == '\000' ) goto end_of_job; /* couldn't get {xlen} */
11504:   xlen = (double)evalterm(mimestore,linexpr); /* xlen in user units */
11505:   /* --- set other values accordingly --- */
11506:   if ( xlen  < 0.0 ) xinc = -xinc;      /* if xlen negative, flip xinc sign*/
11507:   if ( xinc != 0.0 ) ylen = xlen*yinc/xinc; /* set ylen from xlen and slope*/
11508:   else xlen  = 0.0;                     /* can't have xlen if xinc=0 */
11509:   } /* --- end-of-if(*(*expression)=='{') --- */
11510: /* -------------------------------------------------------------------------
11511: calculate width,height, etc, based on xlen,ylen, etc
11512: -------------------------------------------------------------------------- */
11513: /* --- force lengths positive --- */
11514: xlen = absval(xlen);                    /* force xlen positive */
11515: ylen = absval(ylen);                    /* force ylen positive */
11516: /* --- calculate corresponding lengths in pixels --- */
11517: width   = max2(1,iround(unitlength*xlen)); /*scale by unitlength and round,*/
11518: height  = max2(1,iround(unitlength*ylen)); /* and must be at least 1 pixel */
11519: rwidth  = width  + (ylen<0.001?0:max2(0,thickness-1));
11520: rheight = height + (xlen<0.001?0:max2(0,thickness-1));
11521: /* --- set origin corner, x,yinc's: ++=0=(0,0) +-=1=(0,1) -+=10=(1,0) --- */
11522: if ( xinc < 0.0 ) isright = 1;          /*negative xinc, so corner is (1,?)*/
11523: if ( yinc < 0.0 ) istop = 1;            /*negative yinc, so corner is (?,1)*/
11524: origin = isright*10 + istop;            /* interpret 0=(0,0), 11=(1,1), etc*/
11525: if ( msgfp!=NULL && msglevel>=29 )      /* debugging */
11526:   fprintf(msgfp,"rastline> width,height,origin;x,yinc=%d,%d,%d;%g,%g\n",
11527:   width,height,origin,xinc,yinc);
11528: /* -------------------------------------------------------------------------
11529: allocate subraster and raster for line
11530: -------------------------------------------------------------------------- */
11531: /* --- sanity check on width,height,thickness args --- */
11532: if ( width < 1 ||  width > maxwidth
11533: ||  height < 1 || height > maxheight
11534: ||  thickness<1||thickness>25 ) goto end_of_job;
11535: /* --- allocate and initialize subraster for constructed line --- */
11536: if ( (linesp=new_subraster(rwidth,rheight,pixsz)) /* alloc new subraster */
11537: ==   NULL )  goto end_of_job;           /* quit if failed */
11538: /* --- initialize line subraster parameters --- */
11539: linesp->type = IMAGERASTER;             /* image */
11540: linesp->symdef = NULL;                  /* not applicable for image */
11541: linesp->baseline = height/2 + 2         /* is a little above center good? */
11542:         + (rheight-height)/2;           /* account for line thickness too */
11543: linesp->size = size;                    /* size (probably unneeded) */
11544: /* -------------------------------------------------------------------------
11545: draw the line
11546: -------------------------------------------------------------------------- */
11547: line_raster ( linesp->image,            /* embedded raster image */
11548:         (istop?   0 : height-1),        /* row0, from bottom or top */
11549:         (isright?  width-1 : 0),        /* col0, from left or right */
11550:         (istop?   height-1 : 0),        /* row1, to top or bottom */
11551:         (isright? 0 :  width-1),        /* col1, to right or left */
11552:         thickness );                    /* line thickness (usually 1 pixel)*/
11553: /* -------------------------------------------------------------------------
11554: return constructed line to caller
11555: -------------------------------------------------------------------------- */
11556: end_of_job:
11557:   if ( workingparam != NULL )           /* caller wants origin */
11558:     *workingparam = origin;             /* return origin corner to caller */
11559:   return ( linesp );                    /* return line to caller */
11560: } /* --- end-of-function rastline() --- */
11561: 
11562: 
11563: /* ==========================================================================
11564:  * Function:    rastrule ( expression, size, basesp, arg1, arg2, arg3 )
11565:  * Purpose:     \rule handler, returns subraster corresponding to rule
11566:  *              parameters [lift]{width}{height}
11567:  * --------------------------------------------------------------------------
11568:  * Arguments:   expression (I/O) char **  to first char of null-terminated
11569:  *                              string immediately following \rule to be
11570:  *                              rasterized, and returning ptr immediately
11571:  *                              following last character processed.
11572:  *              size (I)        int containing 0-7 default font size
11573:  *              basesp (I)      subraster *  to character (or subexpression)
11574:  *                              immediately preceding \rule
11575:  *                              (unused, but passed for consistency)
11576:  *              arg1 (I)        int unused
11577:  *              arg2 (I)        int unused
11578:  *              arg3 (I)        int unused
11579:  * --------------------------------------------------------------------------
11580:  * Returns:     ( subraster * ) ptr to subraster corresponding to rule
11581:  *                              requested, or NULL for any parsing error
11582:  * --------------------------------------------------------------------------
11583:  * Notes:     o Summary of syntax...
11584:  *                \rule[lift]{width}{height}
11585:  *            o if [lift] not given, then bottom of rule on baseline
11586:  *            o if width=0 then you get an invisible strut 1 (one) pixel wide
11587:  * ======================================================================= */
11588: /* --- entry point --- */
11589: subraster *rastrule ( char **expression, int size, subraster *basesp,
11590:                         int arg1, int arg2, int arg3 )
11591: {
11592: /* -------------------------------------------------------------------------
11593: Allocations and Declarations
11594: -------------------------------------------------------------------------- */
11595: char    *texsubexpr(), rulexpr[257];    /* rule[lift]{wdth}{hgt} */
11596: subraster *new_subraster(), *rulesp=NULL; /* subraster for rule */
11597: int     pixsz = 1;                      /* pixels are one bit each */
11598: int     lift=0, width=0, height=0;      /* default rule parameters */
11599: double  dval;                           /* convert ascii params to doubles */
11600: int     rwidth=0, rheight=0,            /* alloc width, height plus lift */
11601:         maxwidth=1600, maxheight=1600;  /* max width,height in pixels */
11602: int     rule_raster();                  /* draw rule in rulesp->image */
11603: int     evalterm();                     /* evaluate args */
11604: /* -------------------------------------------------------------------------
11605: Obtain lift,width,height
11606: -------------------------------------------------------------------------- */
11607: /* --- check for optional lift arg  --- */
11608: if ( *(*expression) == '[' )            /*check for []-enclosed optional arg*/
11609:   { *expression = texsubexpr(*expression,rulexpr,255,"[","]",0,0);
11610:     dval = evalterm(mimestore,rulexpr); /* convert [lift] to int */
11611:     if ( dval <= 99 && dval >= (-99) )  /* sanity check */
11612:       lift = iround(unitlength*dval); } /* scale by unitlength and round */
11613: /* --- parse for width --- */
11614: *expression = texsubexpr(*expression,rulexpr,255,"{","}",0,0);
11615: if ( *rulexpr == '\000' ) goto end_of_job; /* quit if args missing */
11616: dval = evalterm(mimestore,rulexpr);     /* convert {width} to int */
11617: if ( dval <= 500 && dval >= 0 )         /* sanity check */
11618:   width = max2(0,iround(unitlength*dval)); /* scale by unitlength and round*/
11619: /* --- parse for height --- */
11620: *expression = texsubexpr(*expression,rulexpr,255,"{","}",0,0);
11621: if ( *rulexpr == '\000' ) goto end_of_job; /* quit if args missing */
11622: dval = evalterm(mimestore,rulexpr);     /* convert {height} to int */
11623: if ( dval <= 500 && dval > 0 )          /* sanity check */
11624:   height= max2(1,iround(unitlength*dval)); /* scale by unitlength and round*/
11625: /* --- raster width,height in pixels --- */
11626: rwidth  = max2(1,width);                /* raster must be at least 1 pixel*/
11627: rheight = height + (lift>=0?lift:       /* raster height plus lift */
11628:   (-lift<height?0:-lift-height+1));     /* may need empty space above rule */
11629: /* -------------------------------------------------------------------------
11630: allocate subraster and raster for rule
11631: -------------------------------------------------------------------------- */
11632: /* --- sanity check on width,height,thickness args --- */
11633: if ( rwidth < 1 ||  rwidth > maxwidth
11634: ||  rheight < 1 || rheight > maxheight ) goto end_of_job;
11635: /* --- allocate and initialize subraster for constructed rule --- */
11636: if ( (rulesp=new_subraster(rwidth,rheight,pixsz)) /* alloc new subraster */
11637: ==   NULL )  goto end_of_job;           /* quit if failed */
11638: /* --- initialize line subraster parameters --- */
11639: rulesp->type = IMAGERASTER;             /* image */
11640: rulesp->symdef = NULL;                  /* not applicable for image */
11641: rulesp->baseline = rheight-1 + (lift>=0?0:lift); /*adjust baseline for lift*/
11642: rulesp->size = size;                    /* size (probably unneeded) */
11643: /* -------------------------------------------------------------------------
11644: draw the rule
11645: -------------------------------------------------------------------------- */
11646: rule_raster ( rulesp->image,            /* embedded raster image */
11647:         (-lift<height?0:rheight-height), /* topmost row for top-left corner*/
11648:         0,                              /* leftmost col for top-left corner*/
11649:         width,                          /* rule width */
11650:         height,                         /* rule height */
11651:         ( width>0? 0:4 ) );             /* rule type */
11652: /* -------------------------------------------------------------------------
11653: return constructed rule to caller
11654: -------------------------------------------------------------------------- */
11655: end_of_job:
11656:   return ( rulesp );                    /* return rule to caller */
11657: } /* --- end-of-function rastrule() --- */
11658: 
11659: 
11660: /* ==========================================================================
11661:  * Function:    rastcircle ( expression, size, basesp, arg1, arg2, arg3 )
11662:  * Purpose:     \circle handler, returns subraster corresponding to ellipse
11663:  *              parameters (xdiam[,ydiam])
11664:  * --------------------------------------------------------------------------
11665:  * Arguments:   expression (I/O) char **  to first char of null-terminated
11666:  *                              string immediately following \circle to be
11667:  *                              rasterized, and returning ptr immediately
11668:  *                              following last character processed.
11669:  *              size (I)        int containing 0-7 default font size
11670:  *              basesp (I)      subraster *  to character (or subexpression)
11671:  *                              immediately preceding \circle
11672:  *                              (unused, but passed for consistency)
11673:  *              arg1 (I)        int unused
11674:  *              arg2 (I)        int unused
11675:  *              arg3 (I)        int unused
11676:  * --------------------------------------------------------------------------
11677:  * Returns:     ( subraster * ) ptr to subraster corresponding to ellipse
11678:  *                              requested, or NULL for any parsing error
11679:  * --------------------------------------------------------------------------
11680:  * Notes:     o Summary of syntax...
11681:  *                \circle(xdiam[,ydiam])
11682:  *            o
11683:  * ======================================================================= */
11684: /* --- entry point --- */
11685: subraster *rastcircle ( char **expression, int size, subraster *basesp,
11686:                         int arg1, int arg2, int arg3 )
11687: {
11688: /* -------------------------------------------------------------------------
11689: Allocations and Declarations
11690: -------------------------------------------------------------------------- */
11691: char    *texsubexpr(), circexpr[512],*xptr=circexpr; /*circle(xdiam[,ydiam])*/
11692: char    *qptr=NULL, quads[256]="1234";  /* default to draw all quadrants */
11693: double  theta0=0.0, theta1=0.0;         /* ;theta0,theta1 instead of ;quads*/
11694: subraster *new_subraster(), *circsp=NULL; /* subraster for ellipse */
11695: int     pixsz = 1;                      /* pixels are one bit each */
11696: double  xdiam=0.0, ydiam=0.0;           /* x,y major/minor axes/diameters */
11697: int     width=0,  height=0,             /* #pixels width,height of ellipse */
11698:         maxwidth=1600, maxheight=1600;  /* max width,height in pixels */
11699: int     thickness = 1;                  /* drawn lines are one pixel thick */
11700: int     evalterm();                     /* evaluate [arg],{arg} expressions*/
11701: int     origin = 55;                    /* force origin centered */
11702: int     circle_raster(),                /* draw ellipse in circsp->image */
11703:         circle_recurse();               /* for theta0,theta1 args */
11704: /* -------------------------------------------------------------------------
11705: obtain (xdiam[,ydiam]) arguments immediately following \circle command
11706: -------------------------------------------------------------------------- */
11707: /* --- parse for (xdiam[,ydiam]) args, and bump expression past it --- */
11708: *expression = texsubexpr(*expression,circexpr,500,"(",")",0,0);
11709: if ( *circexpr == '\000' ) goto end_of_job; /* couldn't get (xdiam[,ydiam])*/
11710: /* --- now interpret xdiam[,ydiam] returned in circexpr --- */
11711: if ( (qptr=strchr(circexpr,';')) != NULL ) /* semicolon signals quads data */
11712:   { *qptr = '\000';                     /* replace semicolon by '\0' */
11713:     strninit(quads,qptr+1,128);         /* save user-requested quads */
11714:     if ( (qptr=strchr(quads,',')) != NULL ) /* have theta0,theta1 instead */
11715:       { *qptr = '\000';                 /* replace , with null */
11716:         theta0 = (double)evalterm(mimestore,quads);  /* theta0 precedes , */
11717:         theta1 = (double)evalterm(mimestore,qptr+1); /* theta1 follows , */
11718:         qptr = NULL; }                  /* signal thetas instead of quads */
11719:     else
11720:         qptr = quads; }                 /* set qptr arg for circle_raster()*/
11721: else                                    /* no ;quads at all */
11722:   qptr = quads;                         /* default to all 4 quadrants */
11723: if ( (xptr=strchr(circexpr,',')) != NULL ) /* look for ',' in xdiam[,ydiam]*/
11724:   *xptr = '\000';                       /* found it, so replace ',' by '\0'*/
11725: xdiam = ydiam =                         /* xdiam=ydiam in user units */
11726:   (double)evalterm(mimestore,circexpr); /* evaluate expression */
11727: if ( xptr != NULL )                     /* 2nd arg, if present, is ydiam */
11728:   ydiam = (double)evalterm(mimestore,xptr+1); /* in user units */
11729: /* -------------------------------------------------------------------------
11730: calculate width,height, etc
11731: -------------------------------------------------------------------------- */
11732: /* --- calculate width,height in pixels --- */
11733: width  = max2(1,iround(unitlength*xdiam)); /*scale by unitlength and round,*/
11734: height = max2(1,iround(unitlength*ydiam)); /* and must be at least 1 pixel */
11735: if ( msgfp!=NULL && msglevel>=29 )      /* debugging */
11736:   fprintf(msgfp,"rastcircle> width,height;quads=%d,%d,%s\n",
11737:   width,height,(qptr==NULL?"default":qptr));
11738: /* -------------------------------------------------------------------------
11739: allocate subraster and raster for complete picture
11740: -------------------------------------------------------------------------- */
11741: /* --- sanity check on width,height args --- */
11742: if ( width < 1 ||  width > maxwidth
11743: ||  height < 1 || height > maxheight ) goto end_of_job;
11744: /* --- allocate and initialize subraster for constructed ellipse --- */
11745: if ( (circsp=new_subraster(width,height,pixsz)) /* allocate new subraster */
11746: ==   NULL )  goto end_of_job;           /* quit if failed */
11747: /* --- initialize ellipse subraster parameters --- */
11748: circsp->type = IMAGERASTER;             /* image */
11749: circsp->symdef = NULL;                  /* not applicable for image */
11750: circsp->baseline = height/2 + 2;        /* is a little above center good? */
11751: circsp->size = size;                    /* size (probably unneeded) */
11752: /* -------------------------------------------------------------------------
11753: draw the ellipse
11754: -------------------------------------------------------------------------- */
11755: if ( qptr != NULL )                     /* have quads */
11756:   circle_raster ( circsp->image,        /* embedded raster image */
11757:         0, 0,                           /* row0,col0 are upper-left corner */
11758:         height-1, width-1,              /* row1,col1 are lower-right */
11759:         thickness,                      /* line thickness is 1 pixel */
11760:         qptr );                         /* "1234" quadrants to be drawn */
11761: else                                    /* have theta0,theta1 */
11762:   circle_recurse ( circsp->image,       /* embedded raster image */
11763:         0, 0,                           /* row0,col0 are upper-left corner */
11764:         height-1, width-1,              /* row1,col1 are lower-right */
11765:         thickness,                      /* line thickness is 1 pixel */
11766:         theta0,theta1 );                /* theta0,theta1 arc to be drawn */
11767: /* -------------------------------------------------------------------------
11768: return constructed ellipse to caller
11769: -------------------------------------------------------------------------- */
11770: end_of_job:
11771:   if ( workingparam != NULL )           /* caller wants origin */
11772:     *workingparam = origin;             /* return center origin to caller */
11773:   return ( circsp );                    /* return ellipse to caller */
11774: } /* --- end-of-function rastcircle() --- */
11775: 
11776: 
11777: /* ==========================================================================
11778:  * Function:    rastbezier ( expression, size, basesp, arg1, arg2, arg3 )
11779:  * Purpose:     \bezier handler, returns subraster corresponding to bezier
11780:  *              parameters (col0,row0)(col1,row1)(colt,rowt)
11781:  * --------------------------------------------------------------------------
11782:  * Arguments:   expression (I/O) char **  to first char of null-terminated
11783:  *                              string immediately following \bezier to be
11784:  *                              rasterized, and returning ptr immediately
11785:  *                              following last character processed.
11786:  *              size (I)        int containing 0-7 default font size
11787:  *              basesp (I)      subraster *  to character (or subexpression)
11788:  *                              immediately preceding \bezier
11789:  *                              (unused, but passed for consistency)
11790:  *              arg1 (I)        int unused
11791:  *              arg2 (I)        int unused
11792:  *              arg3 (I)        int unused
11793:  * --------------------------------------------------------------------------
11794:  * Returns:     ( subraster * ) ptr to subraster corresponding to bezier
11795:  *                              requested, or NULL for any parsing error
11796:  * --------------------------------------------------------------------------
11797:  * Notes:     o Summary of syntax...
11798:  *                \bezier(col1,row1)(colt,rowt)
11799:  *            o col0=0,row0=0 assumed, i.e., given by
11800:  *              \picture(){~(col0,row0){\bezier(col1,row1)(colt,rowt)}~}
11801:  * ======================================================================= */
11802: /* --- entry point --- */
11803: subraster *rastbezier ( char **expression, int size, subraster *basesp,
11804:                         int arg1, int arg2, int arg3 )
11805: {
11806: /* -------------------------------------------------------------------------
11807: Allocations and Declarations
11808: -------------------------------------------------------------------------- */
11809: subraster *new_subraster(), *bezsp=NULL; /* subraster for bezier */
11810: char    *texsubexpr(), bezexpr[129],*xptr=bezexpr; /*\bezier(r,c)(r,c)(r,c)*/
11811: double  r0=0.0,c0=0.0, r1=0.0,c1=0.0, rt=0.0,ct=0.0, /* bezier points */
11812:         rmid=0.0, cmid=0.0,             /* coords at parameterized midpoint*/
11813:         rmin=0.0, cmin=0.0,             /* minimum r,c */
11814:         rmax=0.0, cmax=0.0,             /* maximum r,c */
11815:         rdelta=0.0, cdelta=0.0,         /* rmax-rmin, cmax-cmin */
11816:         r=0.0, c=0.0;                   /* some point */
11817: int     evalterm();                     /* evaluate [arg],{arg} expressions*/
11818: int     iarg=0;                         /* 0=r0,c0 1=r1,c1 2=rt,ct */
11819: int     width=0, height=0,              /* dimensions of bezier raster */
11820:         maxwidth=1600, maxheight=1600;  /* max width,height in pixels */
11821: int     pixsz = 1;                      /* pixels are one bit each */
11822: /*int   thickness = 1;*/                /* drawn lines are one pixel thick */
11823: int     origin = 0;                     /*c's,r's reset to lower-left origin*/
11824: int     bezier_raster();                /* draw bezier in bezsp->image */
11825: /* -------------------------------------------------------------------------
11826: obtain (c1,r1)(ct,rt) args immediately following \bezier command
11827: -------------------------------------------------------------------------- */
11828: for ( iarg=1; iarg<=2; iarg++ )         /* 0=c0,r0 1=c1,r1 2=ct,rt */
11829:   {
11830:   /* --- parse for (r,c) args, and bump expression past them all --- */
11831:   *expression = texsubexpr(*expression,bezexpr,127,"(",")",0,0);
11832:   if ( *bezexpr == '\000' ) goto end_of_job; /* couldn't get (r,c)*/
11833:   /* --- now interpret (r,c) returned in bezexpr --- */
11834:   c = r = 0.0;                          /* init x-coord=col, y-coord=row */
11835:   if ( (xptr=strchr(bezexpr,',')) != NULL ) /* comma separates row,col */
11836:     { *xptr = '\000';                   /* found it, so replace ',' by '\0'*/
11837:       /* --- row=y-coord in pixels --- */
11838:       r = unitlength*((double)evalterm(mimestore,xptr+1)); }
11839:   /* --- col=x-coord in pixels --- */
11840:   c = unitlength*((double)evalterm(mimestore,bezexpr));
11841:   /* --- store r,c --- */
11842:   switch ( iarg )
11843:     { case 0: r0=r; c0=c; break;
11844:       case 1: r1=r; c1=c; break;
11845:       case 2: rt=r; ct=c; break; }
11846:   } /* --- end-of-for(iarg) --- */
11847: /* --- determine midpoint and maximum,minimum points --- */
11848: rmid = 0.5*(rt + 0.5*(r0+r1));          /* y-coord at middle of bezier */
11849: cmid = 0.5*(ct + 0.5*(c0+c1));          /* x-coord at middle of bezier */
11850: rmin = min3(r0,r1,rmid);                /* lowest row */
11851: cmin = min3(c0,c1,cmid);                /* leftmost col */
11852: rmax = max3(r0,r1,rmid);                /* highest row */
11853: cmax = max3(c0,c1,cmid);                /* rightmost col */
11854: rdelta = rmax-rmin;                     /* height */
11855: cdelta = cmax-cmin;                     /* width */
11856: /* --- rescale coords so we start at 0,0 --- */
11857: r0 -= rmin;  c0 -= cmin;                /* rescale r0,c0 */
11858: r1 -= rmin;  c1 -= cmin;                /* rescale r1,c1 */
11859: rt -= rmin;  ct -= cmin;                /* rescale rt,ct */
11860: /* --- flip rows so 0,0 becomes lower-left corner instead of upper-left--- */
11861: r0 = rdelta - r0 + 1;                   /* map 0-->height-1, height-1-->0 */
11862: r1 = rdelta - r1 + 1;
11863: rt = rdelta - rt + 1;
11864: /* --- determine width,height of raster needed for bezier --- */
11865: width  = (int)(cdelta + 0.9999) + 1;    /* round width up */
11866: height = (int)(rdelta + 0.9999) + 1;    /* round height up */
11867: if ( msgfp!=NULL && msglevel>=29 )      /* debugging */
11868:   fprintf(msgfp,"rastbezier> width,height,origin=%d,%d,%d; c0,r0=%g,%g; "
11869:   "c1,r1=%g,%g\n rmin,mid,max=%g,%g,%g; cmin,mid,max=%g,%g,%g\n",
11870:   width,height,origin, c0,r0, c1,r1, rmin,rmid,rmax, cmin,cmid,cmax);
11871: /* -------------------------------------------------------------------------
11872: allocate raster
11873: -------------------------------------------------------------------------- */
11874: /* --- sanity check on width,height args --- */
11875: if ( width < 1 ||  width > maxwidth
11876: ||  height < 1 || height > maxheight ) goto end_of_job;
11877: /* --- allocate and initialize subraster for constructed bezier --- */
11878: if ( (bezsp=new_subraster(width,height,pixsz)) /* allocate new subraster */
11879: ==   NULL )  goto end_of_job;           /* quit if failed */
11880: /* --- initialize bezier subraster parameters --- */
11881: bezsp->type = IMAGERASTER;              /* image */
11882: bezsp->symdef = NULL;                   /* not applicable for image */
11883: bezsp->baseline = height/2 + 2;         /* is a little above center good? */
11884: bezsp->size = size;                     /* size (probably unneeded) */
11885: /* -------------------------------------------------------------------------
11886: draw the bezier
11887: -------------------------------------------------------------------------- */
11888: bezier_raster ( bezsp->image,           /* embedded raster image */
11889:         r0, c0,                         /* row0,col0 are lower-left corner */
11890:         r1, c1,                         /* row1,col1 are upper-right */
11891:         rt, ct );                       /* bezier tangent point */
11892: /* -------------------------------------------------------------------------
11893: return constructed bezier to caller
11894: -------------------------------------------------------------------------- */
11895: end_of_job:
11896:   if ( workingparam != NULL )           /* caller wants origin */
11897:     *workingparam = origin;             /* return center origin to caller */
11898:   return ( bezsp );                     /* return bezier to caller */
11899: } /* --- end-of-function rastbezier() --- */
11900: 
11901: 
11902: /* ==========================================================================
11903:  * Function:    rastraise ( expression, size, basesp, arg1, arg2, arg3 )
11904:  * Purpose:     \raisebox{lift}{subexpression} handler, returns subraster
11905:  *              containing subexpression with its baseline "lifted" by lift
11906:  *              pixels, scaled by \unitlength, or "lowered" if lift arg
11907:  *              negative
11908:  * --------------------------------------------------------------------------
11909:  * Arguments:   expression (I/O) char **  to first char of null-terminated
11910:  *                              string immediately following \raisebox to be
11911:  *                              rasterized, and returning ptr immediately
11912:  *                              following last character processed.
11913:  *              size (I)        int containing 0-7 default font size
11914:  *              basesp (I)      subraster *  to character (or subexpression)
11915:  *                              immediately preceding \raisebox
11916:  *                              (unused, but passed for consistency)
11917:  *              arg1 (I)        int unused
11918:  *              arg2 (I)        int unused
11919:  *              arg3 (I)        int unused
11920:  * --------------------------------------------------------------------------
11921:  * Returns:     ( subraster * ) ptr to subraster corresponding to \raisebox
11922:  *                              requested, or NULL for any parsing error
11923:  * --------------------------------------------------------------------------
11924:  * Notes:     o Summary of syntax...
11925:  *                \raisebox{lift}{subexpression}
11926:  *            o
11927:  * ======================================================================= */
11928: /* --- entry point --- */
11929: subraster *rastraise ( char **expression, int size, subraster *basesp,
11930:                         int arg1, int arg2, int arg3 )
11931: {
11932: /* -------------------------------------------------------------------------
11933: Allocations and Declarations
11934: -------------------------------------------------------------------------- */
11935: char    *texsubexpr(), subexpr[MAXSUBXSZ+1], *liftexpr=subexpr; /* args */
11936: subraster *rasterize(), *raisesp=NULL;  /* rasterize subexpr to be raised */
11937: int     lift=0;                         /* amount to raise/lower baseline */
11938: int     evalterm();                     /* evaluate [arg],{arg} expressions*/
11939: /* -------------------------------------------------------------------------
11940: obtain {lift} argument immediately following \raisebox command
11941: -------------------------------------------------------------------------- */
11942: rastlift = 0;                           /* reset global lift adjustment */
11943: /* --- parse for {lift} arg, and bump expression past it --- */
11944: *expression = texsubexpr(*expression,liftexpr,0,"{","}",0,0);
11945: if ( *liftexpr == '\000' ) goto end_of_job; /* couldn't get {lift} */
11946: lift = eround(liftexpr);                /* {lift} to integer */
11947: if ( abs(lift) > 200 ) lift=0;          /* sanity check */
11948: /* -------------------------------------------------------------------------
11949: obtain {subexpr} argument after {lift}, and rasterize it
11950: -------------------------------------------------------------------------- */
11951: /* --- parse for {subexpr} arg, and bump expression past it --- */
11952: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
11953: /* --- rasterize subexpression to be raised/lowered --- */
11954: if ( (raisesp = rasterize(subexpr,size)) /* rasterize subexpression */
11955: ==   NULL ) goto end_of_job;            /* and quit if failed */
11956: /* -------------------------------------------------------------------------
11957: raise/lower baseline and return it to caller
11958: -------------------------------------------------------------------------- */
11959: /* --- raise/lower baseline --- */
11960: raisesp->baseline += lift;              /* new baseline (no height checks) */
11961: rastlift = lift;                        /* set global to signal adjustment */
11962: /* --- return raised subexpr to caller --- */
11963: end_of_job:
11964:   return ( raisesp );                   /* return raised subexpr to caller */
11965: } /* --- end-of-function rastraise() --- */
11966: 
11967: 
11968: /* ==========================================================================
11969:  * Function:    rastrotate ( expression, size, basesp, arg1, arg2, arg3 )
11970:  * Purpose:     \rotatebox{degrees}{subexpression} handler, returns subraster
11971:  *              containing subexpression rotated by degrees (counterclockwise
11972:  *              if degrees positive)
11973:  * --------------------------------------------------------------------------
11974:  * Arguments:   expression (I/O) char **  to first char of null-terminated
11975:  *                              string immediately following \rotatebox to be
11976:  *                              rasterized, and returning ptr immediately
11977:  *                              following last character processed.
11978:  *              size (I)        int containing 0-7 default font size
11979:  *              basesp (I)      subraster *  to character (or subexpression)
11980:  *                              immediately preceding \rotatebox
11981:  *                              (unused, but passed for consistency)
11982:  *              arg1 (I)        int unused
11983:  *              arg2 (I)        int unused
11984:  *              arg3 (I)        int unused
11985:  * --------------------------------------------------------------------------
11986:  * Returns:     ( subraster * ) ptr to subraster corresponding to \rotatebox
11987:  *                              requested, or NULL for any parsing error
11988:  * --------------------------------------------------------------------------
11989:  * Notes:     o Summary of syntax...
11990:  *                \rotatebox{degrees}{subexpression}
11991:  *            o
11992:  * ======================================================================= */
11993: /* --- entry point --- */
11994: subraster *rastrotate ( char **expression, int size, subraster *basesp,
11995:                         int arg1, int arg2, int arg3 )
11996: {
11997: /* -------------------------------------------------------------------------
11998: Allocations and Declarations
11999: -------------------------------------------------------------------------- */
12000: char    *texsubexpr(), subexpr[MAXSUBXSZ+1], *degexpr=subexpr; /* args */
12001: subraster *rasterize(), *rotsp=NULL;    /* subraster for rotated subexpr */
12002: raster  *rastrot(), *rotrp=NULL;        /* rotate subraster->image 90 degs */
12003: int     delete_raster();                /* delete intermediate rasters */
12004: int     baseline=0;                     /* baseline of rasterized image */
12005: double  degrees=0.0, ipart,fpart;       /* degrees to be rotated */
12006: int     idegrees=0, isneg=0;            /* positive ipart, isneg=1 if neg */
12007: int     n90=0, isn90=1;                 /* degrees is n90 multiples of 90 */
12008: int     evalterm();                     /* evaluate [arg],{arg} expressions*/
12009: /* -------------------------------------------------------------------------
12010: obtain {degrees} argument immediately following \rotatebox command
12011: -------------------------------------------------------------------------- */
12012: /* --- parse for {degrees} arg, and bump expression past it --- */
12013: *expression = texsubexpr(*expression,degexpr,0,"{","}",0,0);
12014: if ( *degexpr == '\000' ) goto end_of_job; /* couldn't get {degrees} */
12015: degrees = (double)evalterm(mimestore,degexpr); /* degrees to be rotated */
12016: if ( degrees < 0.0 )                    /* clockwise rotation desired */
12017:   { degrees = -degrees;                 /* flip sign so degrees positive */
12018:     isneg = 1; }                        /* and set flag to indicate flip */
12019: fpart = modf(degrees,&ipart);           /* integer and fractional parts */
12020: ipart = (double)(((int)degrees)%360);   /* degrees mod 360 */
12021: degrees = ipart + fpart;                /* restore fractional part */
12022: if ( isneg )                            /* if clockwise rotation requested */
12023:   degrees = 360.0 - degrees;            /* do equivalent counterclockwise */
12024: idegrees = (int)(degrees+0.5);          /* integer degrees */
12025: n90 = idegrees/90;                      /* degrees is n90 multiples of 90 */
12026: isn90 = (90*n90==idegrees);             /*true if degrees is multiple of 90*/
12027: isn90 = 1;                              /* forced true for time being */
12028: /* -------------------------------------------------------------------------
12029: obtain {subexpr} argument after {degrees}, and rasterize it
12030: -------------------------------------------------------------------------- */
12031: /* --- parse for {subexpr} arg, and bump expression past it --- */
12032: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
12033: /* --- rasterize subexpression to be rotated --- */
12034: if ( (rotsp = rasterize(subexpr,size))  /* rasterize subexpression */
12035: ==   NULL ) goto end_of_job;            /* and quit if failed */
12036: /* --- return unmodified image if no rotation requested --- */
12037: if ( abs(idegrees) < 2 ) goto end_of_job; /* don't bother rotating image */
12038: /* --- extract params for image to be rotated --- */
12039: rotrp = rotsp->image;                   /* unrotated rasterized image */
12040: baseline = rotsp->baseline;             /* and baseline of that image */
12041: /* -------------------------------------------------------------------------
12042: rotate by multiples of 90 degrees
12043: -------------------------------------------------------------------------- */
12044: if ( isn90 )                            /* rotation by multiples of 90 */
12045:  if ( n90 > 0 )                         /* do nothing for 0 degrees */
12046:   {
12047:   n90 = 4-n90;                          /* rasrot() rotates clockwise */
12048:   while ( n90 > 0 )                     /* still have remaining rotations */
12049:     { raster *nextrp = rastrot(rotrp);  /* rotate raster image */
12050:       if ( nextrp == NULL ) break;      /* something's terribly wrong */
12051:       delete_raster(rotrp);             /* free previous raster image */
12052:       rotrp = nextrp;                   /* and replace it with rotated one */
12053:       n90--; }                          /* decrement remaining count */
12054:   } /* --- end-of-if(isn90) --- */
12055: /* -------------------------------------------------------------------------
12056: requested rotation not multiple of 90 degrees
12057: -------------------------------------------------------------------------- */
12058: if ( !isn90 )                           /* explicitly construct rotation */
12059:   { ; }                                 /* not yet implemented */
12060: /* -------------------------------------------------------------------------
12061: re-populate subraster envelope with rotated image
12062: -------------------------------------------------------------------------- */
12063: /* --- re-init various subraster parameters, embedding raster in it --- */
12064: if ( rotrp != NULL )                    /* rotated raster constructed okay */
12065:  { rotsp->type = IMAGERASTER;           /* signal constructed image */
12066:    rotsp->image = rotrp;                /* raster we just constructed */
12067:    /* --- now try to guess pleasing baseline --- */
12068:    if ( idegrees > 2 ) {                /* leave unchanged if unrotated */
12069:     if ( strlen(subexpr) < 3            /* we rotated a short expression */
12070:     ||   abs(idegrees-180) < 3 )        /* or just turned it upside-down */
12071:       baseline = rotrp->height - 1;     /* so set with nothing descending */
12072:     else                                /* rotated a long expression */
12073:       baseline = (65*(rotrp->height-1))/100; } /* roughly center long expr */
12074:    rotsp->baseline = baseline; }        /* set baseline as calculated above*/
12075: /* --- return rotated subexpr to caller --- */
12076: end_of_job:
12077:   return ( rotsp );                     /*return rotated subexpr to caller*/
12078: } /* --- end-of-function rastrotate() --- */
12079: 
12080: 
12081: /* ==========================================================================
12082:  * Function:    rastmagnify ( expression, size, basesp, arg1, arg2, arg3 )
12083:  * Purpose:     \magnify{magstep}{subexpression} handler, returns subraster
12084:  *              containing magnified subexpression
12085:  * --------------------------------------------------------------------------
12086:  * Arguments:   expression (I/O) char **  to first char of null-terminated
12087:  *                              string immediately following \reflectbox to
12088:  *                              be rasterized, and returning ptr immediately
12089:  *                              following last character processed.
12090:  *              size (I)        int containing 0-7 default font size
12091:  *              basesp (I)      subraster *  to character (or subexpression)
12092:  *                              immediately preceding \reflectbox
12093:  *                              (unused, but passed for consistency)
12094:  *              arg1 (I)        int unused
12095:  *              arg2 (I)        int unused
12096:  *              arg3 (I)        int unused
12097:  * --------------------------------------------------------------------------
12098:  * Returns:     ( subraster * ) ptr to subraster corresponding to \magnify
12099:  *                              requested, or NULL for any parsing error
12100:  * --------------------------------------------------------------------------
12101:  * Notes:     o Summary of syntax...
12102:  *                \magnify{magstep}{subexpression}
12103:  *            o
12104:  * ======================================================================= */
12105: /* --- entry point --- */
12106: subraster *rastmagnify ( char **expression, int size, subraster *basesp,
12107:                         int arg1, int arg2, int arg3 )
12108: {
12109: /* -------------------------------------------------------------------------
12110: Allocations and Declarations
12111: -------------------------------------------------------------------------- */
12112: char    *texsubexpr(), subexpr[MAXSUBXSZ+1], *magexpr=subexpr; /* args */
12113: subraster *rasterize(), *magsp=NULL;    /* subraster for magnified subexpr */
12114: raster  *rastmag(), *magrp=NULL;        /* magnify subraster->image */
12115: int     magstep = 1;                    /* default magnification */
12116: int     delete_raster();                /* delete intermediate raster */
12117: int     baseline=0;                     /* baseline of rasterized image */
12118: /* -------------------------------------------------------------------------
12119: obtain {magstep} argument immediately following \magnify command
12120: -------------------------------------------------------------------------- */
12121: /* --- parse for {magstep} arg, and bump expression past it --- */
12122: *expression = texsubexpr(*expression,magexpr,255,"{","}",0,0);
12123: magstep = atoi(magexpr);                /* convert {magstep} to int */
12124: if ( magstep<1 || magstep>10 )          /* check magstep input */
12125:   magstep = 1;                          /* back to default if illegal */
12126: /* -------------------------------------------------------------------------
12127: obtain {subexpr} argument after {magstep}, and rasterize it
12128: -------------------------------------------------------------------------- */
12129: /* --- parse for {subexpr} arg, and bump expression past it --- */
12130: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
12131: /* --- rasterize subexpression to be reflected --- */
12132: if ( (magsp = rasterize(subexpr,size))  /* rasterize subexpression */
12133: ==   NULL ) goto end_of_job;            /* and quit if failed */
12134: /* --- return unmodified image if no magnification requested --- */
12135: if ( magstep<=1 ) goto end_of_job;      /* don't bother magnifying image */
12136: /* --- extract params for image to be magnified --- */
12137: magrp = magsp->image;                   /* unmagnified rasterized image */
12138: baseline = magsp->baseline;             /* and baseline of that image */
12139: /* -------------------------------------------------------------------------
12140: magnify image and adjust its parameters
12141: -------------------------------------------------------------------------- */
12142: /* --- magnify image --- */
12143: magrp = rastmag(magsp->image,magstep);  /* magnify raster image */
12144: if ( magrp == NULL ) goto end_of_job;   /* failed to magnify image */
12145: delete_raster(magsp->image);            /* free original raster image */
12146: magsp->image = magrp;                   /*and replace it with magnified one*/
12147: /* --- adjust parameters --- */
12148: baseline *= magstep;                    /* scale baseline */
12149: if ( baseline > 0 ) baseline += 1;      /* adjust for no descenders */
12150: magsp->baseline = baseline;             /*reset baseline of magnified image*/
12151: /* --- return magnified subexpr to caller --- */
12152: end_of_job:
12153:   return ( magsp );                     /*back to caller with magnified expr*/
12154: } /* --- end-of-function rastmagnify() --- */
12155: 
12156: 
12157: /* ==========================================================================
12158:  * Function:    rastreflect ( expression, size, basesp, arg1, arg2, arg3 )
12159:  * Purpose:     \reflectbox[axis]{subexpression} handler, returns subraster
12160:  *              containing subexpression reflected horizontally (i.e., around
12161:  *              vertical axis, |_ becomes _|) if [axis] not given or axis=1,
12162:  *              or reflected vertically if axis=2 given.
12163:  * --------------------------------------------------------------------------
12164:  * Arguments:   expression (I/O) char **  to first char of null-terminated
12165:  *                              string immediately following \reflectbox to
12166:  *                              be rasterized, and returning ptr immediately
12167:  *                              following last character processed.
12168:  *              size (I)        int containing 0-7 default font size
12169:  *              basesp (I)      subraster *  to character (or subexpression)
12170:  *                              immediately preceding \reflectbox
12171:  *                              (unused, but passed for consistency)
12172:  *              arg1 (I)        int unused
12173:  *              arg2 (I)        int unused
12174:  *              arg3 (I)        int unused
12175:  * --------------------------------------------------------------------------
12176:  * Returns:     ( subraster * ) ptr to subraster corresponding to \reflectbox
12177:  *                              requested, or NULL for any parsing error
12178:  * --------------------------------------------------------------------------
12179:  * Notes:     o Summary of syntax...
12180:  *                \reflectbox[axis]{subexpression}
12181:  *            o
12182:  * ======================================================================= */
12183: /* --- entry point --- */
12184: subraster *rastreflect ( char **expression, int size, subraster *basesp,
12185:                         int arg1, int arg2, int arg3 )
12186: {
12187: /* -------------------------------------------------------------------------
12188: Allocations and Declarations
12189: -------------------------------------------------------------------------- */
12190: char    *texsubexpr(), subexpr[MAXSUBXSZ+1], *axisexpr=subexpr; /* args */
12191: subraster *rasterize(), *refsp=NULL;    /* subraster for reflected subexpr */
12192: raster  *rastref(), *refrp=NULL;        /* reflect subraster->image */
12193: int     axis = 1;                       /* default horizontal reflection */
12194: int     delete_raster();                /* delete intermediate raster */
12195: int     baseline=0;                     /* baseline of rasterized image */
12196: /* -------------------------------------------------------------------------
12197: obtain [axis] argument immediately following \reflectbox command, if given
12198: -------------------------------------------------------------------------- */
12199: /* --- check for optional [axis] arg  --- */
12200: if ( *(*expression) == '[' )            /*check for []-enclosed optional arg*/
12201:   { *expression = texsubexpr(*expression,axisexpr,255,"[","]",0,0);
12202:     axis = atoi(axisexpr);              /* convert [axis] to int */
12203:     if ( axis<1 || axis>2 )             /* check axis input */
12204:       axis = 1; }                       /* back to default if illegal */
12205: /* -------------------------------------------------------------------------
12206: obtain {subexpr} argument after optional [axis], and rasterize it
12207: -------------------------------------------------------------------------- */
12208: /* --- parse for {subexpr} arg, and bump expression past it --- */
12209: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
12210: /* --- rasterize subexpression to be reflected --- */
12211: if ( (refsp = rasterize(subexpr,size))  /* rasterize subexpression */
12212: ==   NULL ) goto end_of_job;            /* and quit if failed */
12213: /* --- return unmodified image if no reflection requested --- */
12214: if ( axis<1 || axis>2 ) goto end_of_job; /* don't bother reflecting image */
12215: /* --- extract params for image to be reflected --- */
12216: refrp = refsp->image;                   /* unreflected rasterized image */
12217: baseline = refsp->baseline;             /* and baseline of that image */
12218: /* -------------------------------------------------------------------------
12219: reflect image and adjust its parameters
12220: -------------------------------------------------------------------------- */
12221: /* --- reflect image --- */
12222: refrp = rastref(refsp->image,axis);     /* reflect raster image */
12223: if ( refrp == NULL ) goto end_of_job;   /* failed to reflect image */
12224: delete_raster(refsp->image);            /* free original raster image */
12225: refsp->image = refrp;                   /*and replace it with reflected one*/
12226: /* --- adjust parameters --- */
12227: if ( axis == 2 )                        /* for vertical reflection */
12228:   baseline = refrp->height - 1;         /* set with nothing descending */
12229: refsp->baseline = baseline;             /* reset baseline of reflected image*/
12230: /* --- return reflected subexpr to caller --- */
12231: end_of_job:
12232:   return ( refsp );                     /*back to caller with reflected expr*/
12233: } /* --- end-of-function rastreflect() --- */
12234: 
12235: 
12236: /* ==========================================================================
12237:  * Function:    rastfbox ( expression, size, basesp, arg1, arg2, arg3 )
12238:  * Purpose:     \fbox{subexpression} handler, returns subraster
12239:  *              containing subexpression with frame box drawn around it
12240:  * --------------------------------------------------------------------------
12241:  * Arguments:   expression (I/O) char **  to first char of null-terminated
12242:  *                              string immediately following \fbox to be
12243:  *                              rasterized, and returning ptr immediately
12244:  *                              following last character processed.
12245:  *              size (I)        int containing 0-7 default font size
12246:  *              basesp (I)      subraster *  to character (or subexpression)
12247:  *                              immediately preceding \fbox
12248:  *                              (unused, but passed for consistency)
12249:  *              arg1 (I)        int unused
12250:  *              arg2 (I)        int unused
12251:  *              arg3 (I)        int unused
12252:  * --------------------------------------------------------------------------
12253:  * Returns:     ( subraster * ) ptr to subraster corresponding to \fbox
12254:  *                              requested, or NULL for any parsing error
12255:  * --------------------------------------------------------------------------
12256:  * Notes:     o Summary of syntax...
12257:  *                \fbox[width][height]{subexpression}
12258:  *            o
12259:  * ======================================================================= */
12260: /* --- entry point --- */
12261: subraster *rastfbox ( char **expression, int size, subraster *basesp,
12262:                         int arg1, int arg2, int arg3 )
12263: {
12264: /* -------------------------------------------------------------------------
12265: Allocations and Declarations
12266: -------------------------------------------------------------------------- */
12267: char    *texsubexpr(), subexpr[MAXSUBXSZ+1], widtharg[512]; /* args */
12268: subraster *rasterize(), *framesp=NULL;  /* rasterize subexpr to be framed */
12269: raster  *border_raster(), *bp=NULL;     /* framed image raster */
12270: int     evalterm(), evalue=0;           /* interpret [width][height] */
12271: int     fwidth=6, fthick=1,             /*extra frame width, line thickness*/
12272:         fsides=0;               /* frame sides: 1=left,2=top,4=right,8=bot */
12273: int     width=(-1), height=(-1),        /* optional [width][height] args */
12274:         iscompose = 0;                  /* set true if optional args given */
12275: /* -------------------------------------------------------------------------
12276: obtain optional [width][height] arguments immediately following \fbox
12277: -------------------------------------------------------------------------- */
12278: /* --- first check for optional \fbox[width] --- */
12279: if ( *(*expression) == '[' ) {          /* check for []-enclosed width arg */
12280:   *expression = texsubexpr(*expression,widtharg,511,"[","]",0,0);
12281:   if ( !isempty(widtharg) ) {           /* got widtharg */
12282:     char *comma = strchr(widtharg,','); /* look for [width,sides] */
12283:     if ( comma == (char *)NULL )        /* no comma */
12284:       comma = strchr(widtharg,';');     /* permit semicolon [width;sides] */
12285:     if ( comma != (char *)NULL ) {      /* optional [width,fsides] found */
12286:       fsides = atoi(comma+1);           /* interpret fsides after comma */
12287:       if ( size < 5 )                   /* for smaller fonts */
12288:         { fwidth = 2;  fthick = 1; }    /* tighten frame, thinner accent */
12289:       else { fwidth = 3;  fthick = 2; } /* loosen frame, thicken accent */
12290:       *comma = '\000';                  /* null-terminate width at comma */
12291:       trimwhite(widtharg); }            /*remove leading/trailing whitespace*/
12292:     if ( comma==(char *)NULL || !isempty(widtharg) ) { /* have a width */
12293:       height = 1;                       /* default explicit height, too */
12294:       if ( fsides == 0 ) {              /* a normal framebox */
12295:         evalue = eround(widtharg);      /* interpret and scale width */
12296:         width = max2(1,evalue);         /* must be >0 */
12297:         fwidth = 2; iscompose = 1; }
12298:       else                              /* absolute pixels for "accents" */
12299:         width = evalterm(mimestore,widtharg); }
12300:     } /* --- end-of-if(!isempty(widtharg)) --- */
12301:   } /* --- end-of-if(**expression=='[') --- */
12302: if ( width > 0 || fsides > 0)           /* found leading [width], so... */
12303:  if ( *(*expression) == '[' )           /* check for []-enclosed height arg */
12304:   { *expression = texsubexpr(*expression,widtharg,511,"[","]",0,0);
12305:     if ( !isempty(widtharg) ) {         /* got widtharg */
12306:       if ( fsides == 0 ) {              /* a normal framebox */
12307:         evalue = eround(widtharg);      /* interpret and scale height */
12308:         height = max2(1,evalue);        /* must be >0 */
12309:         fwidth = 0; }                   /* no extra border */
12310:       else                              /* absolute pixels for "accents" */
12311:         height = evalterm(mimestore,widtharg); }
12312:   } /* --- end-of-if(**expression=='[') --- */
12313: /* -------------------------------------------------------------------------
12314: obtain {subexpr} argument
12315: -------------------------------------------------------------------------- */
12316: /* --- parse for {subexpr} arg, and bump expression past it --- */
12317: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
12318: /* --- rasterize subexpression to be framed --- */
12319: if ( width<0 || height<0 )              /* no explicit dimensions given */
12320:   { if ( (framesp = rasterize(subexpr,size)) /* rasterize subexpression */
12321:     ==   NULL ) goto end_of_job; }      /* and quit if failed */
12322: else
12323:   { char composexpr[8192];              /* compose subexpr with empty box */
12324:     sprintf(composexpr,"\\compose{\\hspace{%d}\\vspace{%d}}{%.8000s}",
12325:     width,height,subexpr);
12326:     if ( (framesp = rasterize(composexpr,size)) /* rasterize subexpression */
12327:     ==   NULL ) goto end_of_job; }      /* and quit if failed */
12328: /* -------------------------------------------------------------------------
12329: draw frame, reset params, and return it to caller
12330: -------------------------------------------------------------------------- */
12331: /* --- draw border --- */
12332: if ( fsides > 0 ) fthick += (100*fsides); /* embed fsides in fthick arg */
12333: if ( (bp = border_raster(framesp->image,-fwidth,-fwidth,fthick,1))
12334: ==   NULL ) goto end_of_job;            /* draw border and quit if failed */
12335: /* --- replace original image and raise baseline to accommodate frame --- */
12336: framesp->image = bp;                    /* replace image with framed one */
12337: if ( !iscompose )                       /* simple border around subexpr */
12338:   framesp->baseline += fwidth;          /* so just raise baseline */
12339: else
12340:   framesp->baseline = (framesp->image)->height - 1; /* set at bottom */
12341: /* --- return framed subexpr to caller --- */
12342: end_of_job:
12343:   return ( framesp );                   /* return framed subexpr to caller */
12344: } /* --- end-of-function rastfbox() --- */
12345: 
12346: 
12347: /* ==========================================================================
12348:  * Function:    rastovalbox ( expression, size, basesp, arg1, arg2, arg3 )
12349:  * Purpose:     \ovalbox{subexpression} handler, returns subraster
12350:  *              containing subexpression with ellipse drawn around it
12351:  * --------------------------------------------------------------------------
12352:  * Arguments:   expression (I/O) char **  to first char of null-terminated
12353:  *                              string immediately following \ovalbox to be
12354:  *                              rasterized, and returning ptr immediately
12355:  *                              following last character processed.
12356:  *              size (I)        int containing 0-7 default font size
12357:  *              basesp (I)      subraster *  to character (or subexpression)
12358:  *                              immediately preceding \ovalbox
12359:  *                              (unused, but passed for consistency)
12360:  *              arg1 (I)        int unused
12361:  *              arg2 (I)        int unused
12362:  *              arg3 (I)        int unused
12363:  * --------------------------------------------------------------------------
12364:  * Returns:     ( subraster * ) ptr to subraster corresponding to \ovalbox
12365:  *                              requested, or NULL for any parsing error
12366:  * --------------------------------------------------------------------------
12367:  * Notes:     o Summary of syntax...
12368:  *                \ovalbox[n;#ovals,wdelta,hdelta]{subexpression}
12369:  *            o Originally copied from rastfbox(), above,
12370:  *              but I've modified the [] optional argument parsing
12371:  *            o Thanks to answers from Jean-Marie Becker and "Narasimham" at
12372:  *                 http://math.stackexchange.com/questions/2149677/
12373:  *              for providing info about ellipse circumscribing a rectangle
12374:  * ======================================================================= */
12375: /* --- entry point --- */
12376: subraster *rastovalbox ( char **expression, int size, subraster *basesp,
12377:                         int arg1, int arg2, int arg3 )
12378: {
12379: /* -------------------------------------------------------------------------
12380: Allocations and Declarations
12381: -------------------------------------------------------------------------- */
12382: char    *texsubexpr(), subexpr[MAXSUBXSZ+1], narg[512], /* args */
12383:         composexpr[MAXSUBXSZ+2048],     /* compose subexpr[] with ellipse(s)*/
12384:         ovalexpr[2048];                 /* nested \compose{}{}'s */
12385: subraster *rasterize(), *framesp=NULL;  /* rasterize subexpr to be framed */
12386: int     delete_subraster();             /* just need width,height */
12387: int     fwidth = 3+size/999;            /* extra frame width */
12388: int     width=(-1), height=(-1),        /* width,height of subexpr[] */
12389:         origheight = (-1),              /* original subexpr[] height */
12390:         baseline = (-1);                /* and its original baseline */
12391: /*double sqrt2 = 1.414213562;*/         /*you think that's accurate enough?*/
12392: double  x0=0.0, y0=0.0,                 /*rectangle half-width, half-height*/
12393:         a=0.0,  b=0.0;                  /*ellipse semi-major, semi-minor axis*/
12394: double  n = 2.0;                        /*width/height=(semi-major/minor)^n*/
12395: int     ioval=0, novals=1,              /* #concentric ovals to draw */
12396:         wdelta=(3), hdelta=(-3);        /*width,height deltas for each oval*/
12397: /* -------------------------------------------------------------------------
12398: obtain {subexpr} argument
12399: -------------------------------------------------------------------------- */
12400: /* --- first check for optional \ovalbox[n] and bump expression past it--- */
12401: if ( *(*expression) == '[' ) {          /* check for []-enclosed n arg */
12402:   *expression = texsubexpr(*expression,narg,511,"[","]",0,0);
12403:   if ( !isempty(narg) ) {               /* got n */
12404:     char *semi = strchr(narg,';');      /* optionally followed by ;#ovals */
12405:     if ( semi != NULL ) {               /* found optional ;#ovals */
12406:       char *comma = strchr(semi+1,','); /* optionally followed by ,w,hdelta */
12407:       *semi = '\000';                   /* null-terminate 'n' argument at ; */
12408:       if ( comma != NULL ) *comma = '\000'; /* and #ovals arg at (first) , */
12409:       if ( (novals=atoi(semi+1))        /* convert #ovals argument after ; */
12410:       < 1 ) novals = 1;                 /* and make sure it's >0 */
12411:       if ( comma != NULL ) {            /*have ,wdelta and maybe ,hdelta args*/
12412:         char *comma2 = strchr(comma+1,','); /* ,wdelta and maybe ,hdelta */
12413:         if ( comma2 != NULL ) *comma2 = '\000'; /* null-terminate wdelta */
12414:         hdelta = wdelta = atoi(comma+1); /* init both wdelta,hdelta */
12415:         if ( comma2 != NULL )           /* but we have separate ,hdelta */
12416:           hdelta = atoi(comma2+1);      /* so set hdelta separately */
12417:         } /* --- end-of-if(comma!=NULL) --- */
12418:       } /* --- end-of-if(semi!=NULL) --- */
12419:     if ( (n = atof(narg))               /* convert narg to double */
12420:     <=   .001 ) n = 2.0;                /* sanity check (revert to default) */
12421:     } /* --- end-of-if(!isempty(narg)) --- */
12422:   } /* --- end-of-if(**expression=='[') --- */
12423: /* --- parse for {subexpr} arg, and bump expression past it --- */
12424: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
12425: /* -------------------------------------------------------------------------
12426: rasterize subexpression to be ellipse-framed
12427: -------------------------------------------------------------------------- */
12428: if ( (framesp = rasterize(subexpr,size)) /* rasterize subexpression */
12429: ==   NULL ) goto end_of_job;            /* and quit if failed */
12430: if ( framesp->image == NULL ) goto end_of_job; /* or quit if no image */
12431: /* --- pull out info we need --- */
12432: width  = (framesp->image)->width;       /* width  of subexpr image */
12433: height = (framesp->image)->height;      /* height of subexpr image */
12434: baseline = framesp->baseline;           /* and original baseline */
12435: delete_subraster(framesp);              /* just needed width,height,baseline */
12436: origheight = height;                    /* original "rectangle" height */
12437: /* --- old formulas for circumscribing ellipse (corresponds to n=1) --- */
12438: /*width  = (int)(0.5+sqrt2*(double)(width +fwidth));*/ /*major ellipse axis*/
12439: /*height = (int)(0.5+sqrt2*(double)(height+fwidth));*/ /*minor ellipse axis*/
12440: /* --- use general formulas for circumscribing ellipse --- */
12441: x0 = ((double)width)/2.0;  y0 = ((double)height)/2.0; /* half-width,height */
12442: a = sqrt(x0*x0 + pow(x0,2.0/n)*pow(y0,2.0-2.0/n));
12443: b = sqrt(y0*y0 + pow(y0,2.0/n)*pow(x0,2.0-2.0/n));
12444: width  = (int)(2.0*a + 0.5) + fwidth;
12445: height = (int)(2.0*b + 0.5) + fwidth;
12446: baseline += ((height-origheight)+1)/2;  /* baseline of subexpr[] chars */
12447: /* -------------------------------------------------------------------------
12448: construct nested \compose{\compose{\circle(,)}{\circle(,)}}{\circle(,)}
12449: -------------------------------------------------------------------------- */
12450: *ovalexpr = '\000';                     /* init as empty string */
12451: for ( ioval=0; ioval<novals; ioval++ ) { /* nest each oval */
12452:   char thisoval[2048],                  /* oval to be nested with ovalexpr */
12453:        ovalbuff[2048];                  /* temp ovalexpr buffer */
12454:   int thiswidth  =  width + (ioval*wdelta), /*  width for this oval */
12455:       thisheight = height + (ioval*hdelta); /* height for this oval */
12456:   if ( strlen(ovalexpr) > 1024 ) break; /* sanity check */
12457:   strcpy(ovalbuff,ovalexpr);            /* save current ovalexpr */
12458:   sprintf(thisoval,"\\circle(%d,%d)",thiswidth,thisheight); /*draw this oval*/
12459:   if ( ioval == 0 ) {                   /* initial oval */
12460:     strcpy(ovalexpr,thisoval); }        /* just copy \circle(,) directive */
12461:   else {                                /* nest with \compose{}{}'s */
12462:     sprintf(ovalexpr,"\\compose{%s}{%s}",ovalbuff,thisoval); }
12463:   } /* --- end-of-for(ioval) --- */
12464: /* -------------------------------------------------------------------------
12465: compose with circumscribed ellipse(s), reset params, and return it to caller
12466: -------------------------------------------------------------------------- */
12467: /* --- construct expression to be rasterized
12468:        (note: too bad we have to re-rasterize original subexpr[]) --- */
12469:       /* sprintf(composexpr,"\\compose{\\circle(%d,%d)}{%.8000s}",
12470:          width,height,subexpr); */
12471: sprintf(composexpr,"\\compose{%s}{%.8000s}", ovalexpr,subexpr);
12472: if ( (framesp = rasterize(composexpr,size)) /*rasterize ellipse with subexpr*/
12473: ==   NULL ) goto end_of_job;            /* and quit if failed */
12474: framesp->baseline = baseline;           /* reset baseline (I hope) */
12475: /* --- return ellipse-framed subexpr to caller --- */
12476: end_of_job:
12477:   return ( framesp );                   /* return framed subexpr to caller */
12478: } /* --- end-of-function rastovalbox() --- */
12479: 
12480: 
12481: /* ==========================================================================
12482:  * Function:    rastinput ( expression, size, basesp, arg1, arg2, arg3 )
12483:  * Purpose:     \input{filename} handler, reads filename and returns
12484:  *              subraster containing image of expression read from filename
12485:  * --------------------------------------------------------------------------
12486:  * Arguments:   expression (I/O) char **  to first char of null-terminated
12487:  *                              string immediately following \input to be
12488:  *                              rasterized, and returning ptr immediately
12489:  *                              following last character processed.
12490:  *              size (I)        int containing 0-7 default font size
12491:  *              basesp (I)      subraster *  to character (or subexpression)
12492:  *                              immediately preceding \input
12493:  *                              (unused, but passed for consistency)
12494:  *              arg1 (I)        int unused
12495:  *              arg2 (I)        int unused
12496:  *              arg3 (I)        int unused
12497:  * --------------------------------------------------------------------------
12498:  * Returns:     ( subraster * ) ptr to subraster corresponding to expression
12499:  *                              in filename, or NULL for any parsing error
12500:  * --------------------------------------------------------------------------
12501:  * Notes:     o Summary of syntax...
12502:  *                \input{filename}     reads entire file named filename
12503:  *                \input{filename:tag} reads filename, but returns only
12504:  *                those characters between <tag>...</tag> in that file.
12505:  *            o
12506:  * ======================================================================= */
12507: /* --- entry point --- */
12508: subraster *rastinput ( char **expression, int size, subraster *basesp,
12509:                         int arg1, int arg2, int arg3 )
12510: {
12511: /* -------------------------------------------------------------------------
12512: Allocations and Declarations
12513: -------------------------------------------------------------------------- */
12514: char    *texsubexpr(), tag[1024]="\000", filename[1024]="\000"; /* args */
12515: subraster *rasterize(), *inputsp=NULL, /* rasterized input image */
12516:         *read_pbm();            /* read .pbm file and make subraster image */
12517: int     status, rastreadfile(); /* read input file */
12518: FILE    *fp=NULL, *rastopenfile(); /* reading .pbm files locally */
12519: int     format=0, npts=0,       /* don't reformat (numerical) input */
12520:         ispbm = 0;              /* set true for \input[pbm]{filename.pbm} */
12521: double  sf = 0.0;               /* shrink factor (0 or 1 means don't shrink)*/
12522: int     isinput = (seclevel<=inputseclevel?1:0); /*true if \input permitted*/
12523: /*int   evalterm();*/           /* evaluate expressions */
12524: char    *inputpath = INPUTPATH; /* permitted \input{} paths for any user */
12525: int     isstrstr();             /* search for valid inputpath in filename */
12526: char    subexpr[MAXFILESZ+1] = "\000", /*concatanated lines from input file*/
12527:         *mimeprep(),            /* preprocess inputted data */
12528:         *dbltoa(), *reformat=NULL; /* reformat numerical input */
12529: /* -------------------------------------------------------------------------
12530: obtain [tag]{filename} argument
12531: -------------------------------------------------------------------------- */
12532: /* --- parse for optional [tag] or [fmt] arg, bump expression past it --- */
12533: if ( *(*expression) == '[' )            /* check for []-enclosed value */
12534:   { char argfld[MAXTOKNSZ+1];           /* optional argument field */
12535:     *expression = texsubexpr(*expression,argfld,MAXTOKNSZ-1,"[","]",0,0);
12536:     if ( (reformat=strstr(argfld,"dtoa")) != NULL ) /*dtoa/dbltoa requested*/
12537:       { format = 1;                     /* signal dtoa()/dbltoa() format */
12538:         if ( (reformat=strchr(reformat,'=')) != NULL ) /* have dtoa= */
12539:           npts = (int)strtol(reformat+1,NULL,0); } /* so set npts */
12540:     if ( (reformat=strstr(argfld,"pbm")) != NULL ) /*input a .pbm image file*/
12541:       { ispbm = 1;                      /* set ispbm flag */
12542:         if ( (reformat=strchr(reformat,',')) != NULL ) /* have pbm, */
12543:           sf = strtod(reformat+1,NULL); } /* so set sf(shrinkfactor) */
12544:     if ( format==0 && !ispbm ) {        /* not reformat request, and not pbm */
12545:       strninit(tag,argfld,1020); } }    /* so interpret arg as tag */
12546: /* --- parse for {filename} arg, and bump expression past it --- */
12547: *expression = texsubexpr(*expression,filename,1020,"{","}",0,0);
12548: /* --- check for alternate filename:tag --- */
12549: if ( !isempty(filename)                 /* got filename */
12550: /*&& isempty(tag)*/ )                   /* but no [tag] */
12551:  { char *delim = strchr(filename,':');  /* look for : in filename:tag */
12552:    if ( delim != (char *)NULL )         /* found it */
12553:     { *delim = '\000';                  /* null-terminate filename at : */
12554:       strninit(tag,delim+1,1020); } }   /* and stuff after : is tag */
12555: /* --- check filename for an inputpath valid for all users --- */
12556: if ( !isinput                           /* if this user can't \input{} */
12557: &&   !isempty(filename)                 /* and we got a filename */
12558: &&   !isempty(inputpath) )              /* and an inputpath */
12559:   if ( isstrstr(filename,inputpath,0) ) /* filename has allowed inputpath */
12560:     isinput = 1;                        /* okay to \input{} this filename */
12561: /* --- guard against recursive runaway (e.g., file \input's itself) --- */
12562: if ( ++ninputcmds > 8 )                 /* max \input's per expression */
12563:   isinput = 0;                          /* flip flag off after the max */
12564: /* --------------------------------------------------------------------------
12565: Read file (and convert to numeric if [dtoa] option was given)
12566: -------------------------------------------------------------------------- */
12567: if ( isinput ) {                        /* user permitted to use \input{} */
12568:   if ( ispbm ) {                        /* special case: input a .pbm file */
12569:     fp = rastopenfile(filename,"r");    /* open the .pbm file for "r"ead */
12570:     if ( fp != NULL ) {                 /* opened .pbm file successfully */
12571:       if(0) printf("rastinput> sf=%.3f to read_pbm(%s,sf)\n",sf,filename);
12572:       inputsp = read_pbm(fp,sf);        /* create subraster image from .pbm */
12573:       fclose(fp); }                     /* close file after reading */
12574:     goto end_of_job;                    /* all done, return inputsp to caller*/
12575:     } /* --- end-of-if(ispbm) --- */
12576:   status = rastreadfile(filename,0,tag,subexpr); /* read file */
12577:   if ( *subexpr == '\000' ) goto end_of_job;   /* quit if problem */
12578:   /* --- rasterize input subexpression  --- */
12579:   mimeprep(subexpr);                    /* preprocess subexpression */
12580:   if ( format == 1 ) {                  /* dtoa()/dbltoa() */
12581:     double d = strtod(subexpr,NULL);    /* interpret subexpr as double */
12582:     if ( d != 0.0 )                     /* conversion to double successful */
12583:       if ( (reformat=dbltoa(d,npts)) != NULL ) /* reformat successful */
12584:         strcpy(subexpr,reformat); }     /*replace subexpr with reformatted*/
12585:   } /* --- end-of-if(isinput) --- */
12586: /* --------------------------------------------------------------------------
12587: emit error message for unauthorized users trying to use \input{}
12588: -------------------------------------------------------------------------- */
12589: else {                                  /* inputseclevel > seclevel */
12590:   sprintf(subexpr,
12591:   "\\ \\text{[\\backslash input\\lbrace %.128s\\rbrace\\ not permitted]}\\ ",
12592:   (isempty(filename)?"???":filename));
12593:   } /* --- end-of-if/else(isinput) --- */
12594: /* --------------------------------------------------------------------------
12595: Rasterize constructed subexpression
12596: -------------------------------------------------------------------------- */
12597: inputsp = rasterize(subexpr,size);      /* rasterize subexpression */
12598: /* --- return input image to caller --- */
12599: end_of_job:
12600:   return ( inputsp );                   /* return input image to caller */
12601: } /* --- end-of-function rastinput() --- */
12602: 
12603: 
12604: /* ==========================================================================
12605:  * Function:    rastcounter ( expression, size, basesp, arg1, arg2, arg3 )
12606:  * Purpose:     \counter[value]{filename} handler, returns subraster
12607:  *              containing image of counter value read from filename
12608:  *              (or optional [value]), and increments counter
12609:  * --------------------------------------------------------------------------
12610:  * Arguments:   expression (I/O) char **  to first char of null-terminated
12611:  *                              string immediately following \counter to be
12612:  *                              rasterized, and returning ptr immediately
12613:  *                              following last character processed.
12614:  *              size (I)        int containing 0-7 default font size
12615:  *              basesp (I)      subraster *  to character (or subexpression)
12616:  *                              immediately preceding \counter
12617:  *                              (unused, but passed for consistency)
12618:  *              arg1 (I)        int unused
12619:  *              arg2 (I)        int unused
12620:  *              arg3 (I)        int unused
12621:  * --------------------------------------------------------------------------
12622:  * Returns:     ( subraster * ) ptr to subraster corresponding to \counter
12623:  *                              requested, or NULL for any parsing error
12624:  * --------------------------------------------------------------------------
12625:  * Notes:     o Summary of syntax...
12626:  *                \counter[value][logfile]{filename:tag}
12627:  *            o :tag is optional
12628:  * ======================================================================= */
12629: /* --- entry point --- */
12630: subraster *rastcounter ( char **expression, int size, subraster *basesp,
12631:                         int arg1, int arg2, int arg3 )
12632: {
12633: /* -------------------------------------------------------------------------
12634: Allocations and Declarations
12635: -------------------------------------------------------------------------- */
12636: char    *texsubexpr(), filename[1024]="\000", /* counter file */
12637:         logfile[1024]="\000", tag[1024]="\000"; /*optional log file and tag*/
12638: subraster *rasterize(), *countersp=NULL; /* rasterized counter image */
12639: FILE    /* *fp=NULL,*/ *logfp=NULL; /* counter and log file pointers */
12640: int     status=0,rastreadfile(),rastwritefile(), /*read,write counter file*/
12641:         iscounter = (seclevel<=counterseclevel?1:0), /*is \counter permitted*/
12642:         isstrict = 1;           /* true to only write to existing files */
12643: char    text[MAXFILESZ] = "1_", /* only line in counter file without tags */
12644:         *delim = NULL,          /* delimiter in text */
12645:         utext[128] = "1_",      /* default delimiter */
12646:         *udelim = utext+1;      /* underscore delimiter */
12647: char    *rasteditfilename(),    /* edit log file name */
12648:         *timestamp(),           /* timestamp for logging */
12649:         *dbltoa();              /* double to comma-separated ascii */
12650: int     counter = 1,            /* atoi(text) (after _ removed, if present) */
12651:         value = 1,              /* optional [value] argument */
12652:         gotvalue = 0,           /* set true if [value] supplied */
12653:         isdelta = 0,            /* set true if [+value] or [-value] is delta*/
12654:         ordindex = (-1);        /* ordinal[] index to append ordinal suffix */
12655: /*--- ordinal suffixes based on units digit of counter ---*/
12656: static  char *ordinal[]={"th","st","nd","rd","th","th","th","th","th","th"};
12657: static  char *logvars[]={"REMOTE_ADDR","HTTP_REFERER",NULL}; /* log vars*/
12658: static  int  commentvar = 1;    /* logvars[commentvar] replaced by comment */
12659: /* -------------------------------------------------------------------------
12660: first obtain optional [value][logfile] args immediately following \counter
12661: -------------------------------------------------------------------------- */
12662: /* --- first check for optional \counter[value] --- */
12663: if ( *(*expression) == '[' )            /* check for []-enclosed value */
12664:   { *expression = texsubexpr(*expression,text,1023,"[","]",0,0);
12665:     if ( *text != '\000' )              /* got counter value (or logfile) */
12666:      if ( strlen(text) >= 1 ) {         /* and it's not an empty string */
12667:       if ( isthischar(*text,"+-0123456789") ) /* check for leading +-digit */
12668:         gotvalue = 1;                   /* signal we got optional value */
12669:       else                              /* not +-digit, so must be logfile */
12670:         strcpy(logfile,text); }         /* so just copy it */
12671:   } /* --- end-of-if(**expression=='[') --- */
12672: /* --- next check for optional \counter[][logfile] --- */
12673: if ( *(*expression) == '[' )            /* check for []-enclosed logfile */
12674:   { *expression = texsubexpr(*expression,filename,1023,"[","]",0,0);
12675:     if ( *filename != '\000' )          /* got logfile (or counter value) */
12676:      if ( strlen(filename) >= 1 ) {     /* and it's not an empty string */
12677:       if ( !(isthischar(*text,"+-0123456789")) /* not a leading +-digit */
12678:       ||   gotvalue )                   /* or we already got counter value */
12679:         strcpy(logfile,filename);       /* so just copy it */
12680:       else                              /* leading +-digit must be value */
12681:         { strcpy(text,filename);        /* copy value to text line */
12682:           gotvalue = 1; } }             /* and signal we got optional value*/
12683:   } /* --- end-of-if(**expression=='[') --- */
12684: /* --- evaluate [value] if present --- */
12685: if ( gotvalue ) {                       /*leading +-digit should be in text*/
12686:  if ( *text == '+' ) isdelta = (+1);    /* signal adding */
12687:  if ( *text == '-' ) isdelta = (-1);    /* signal subtracting */
12688:  value = (int)(strtod((isdelta==0?text:text+1),&udelim)+0.1); /*abs(value)*/
12689:  if ( isdelta == (-1) ) value = (-value); /* set negative value if needed */
12690:  counter = value;                       /* re-init counter */
12691:  } /* --- end-of-if(gotvalue) --- */
12692: /* -------------------------------------------------------------------------
12693: obtain counter {filename} argument
12694: -------------------------------------------------------------------------- */
12695: /* --- parse for {filename} arg, and bump expression past it --- */
12696: *expression = texsubexpr(*expression,filename,1023,"{","}",0,0);
12697: /* --- check for counter filename:tag --- */
12698: if ( *filename != '\000' )              /* got filename */
12699:  if ( (delim=strchr(filename,':'))      /* look for : in filename:tag */
12700:  !=   (char *)NULL )                    /* found it */
12701:   { *delim = '\000';                    /* null-terminate filename at : */
12702:     strcpy(tag,delim+1); }              /* and stuff after : is tag */
12703: /* --------------------------------------------------------------------------
12704: emit error message for unauthorized users trying to use \counter{}
12705: -------------------------------------------------------------------------- */
12706: if ( !iscounter ) {                     /* counterseclevel > seclevel */
12707:  sprintf(text,
12708:  "\\ \\text{[\\backslash counter\\lbrace %.128s\\rbrace\\ not permitted]}\\ ",
12709:  (isempty(filename)?"???":filename));
12710:  goto rasterize_counter;                /* rasterize error message */
12711:  } /* --- end-of-if(!iscounter) --- */
12712: /* --------------------------------------------------------------------------
12713: Read and parse file, increment and rewrite counter (with optional underscore)
12714: -------------------------------------------------------------------------- */
12715: if ( strlen(filename) > 1 )             /* make sure we got {filename} arg */
12716:   {
12717:   /* --- read and interpret first (and only) line from counter file --- */
12718:   if ( !gotvalue || (isdelta!=0) )      /*if no [count] arg or if delta arg*/
12719:    if ( (status=rastreadfile(filename,1,tag,text)) > 0 ) /*try reading file*/
12720:     { char *vdelim = NULL;              /* underscore delim from file */
12721:       double fileval  = strtod(text,&vdelim); /* value and delim from file */
12722:       counter = (int)(fileval<0.0?fileval-0.1:fileval+0.1); /* integerized */
12723:       counter += value;                 /* bump count by 1 or add/sub delta*/
12724:       if ( !gotvalue ) udelim=vdelim; } /* default to file's current delim */
12725:   /* --- check for ordinal suffix --- */
12726:   if ( udelim != (char *)NULL )         /* have some delim after value */
12727:    if ( *udelim == '_' )                /* underscore signals ordinal */
12728:     { int abscount = (counter>=0?counter:(-counter)); /* abs(counter) */
12729:       ordindex = abscount%10;           /* least significant digit */
12730:       if ( abscount >= 10 )             /* counter is 10 or greater */
12731:        if ( (abscount/10)%10 == 1 )     /* and the last two are 10-19 */
12732:         ordindex = 0; }         /* use th for 11,12,13 rather than st,nd,rd */
12733:   /* --- rewrite counter file --- */
12734:   if ( status >= 0 )                    /* file was read okay */
12735:    { sprintf(text,"%d",counter);        /*build image of incremented counter*/
12736:      if ( ordindex >= 0 ) strcat(text,"_"); /* tack on _ */
12737:      if ( *tag == '\000' ) strcat(text,"\n"); /* and newline */
12738:      status = rastwritefile(filename,tag,text,isstrict); } /*rewrite counter*/
12739:   } /* --- end-of-if(strlen(filename)>1) --- */
12740: /* --------------------------------------------------------------------------
12741: log counter request
12742: -------------------------------------------------------------------------- */
12743: if ( strlen(logfile) > 1 )              /* optional [logfile] given */
12744:  {
12745:  char   comment[1024] = "\000",         /* embedded comment, logfile:comment*/
12746:         *commptr = strchr(logfile,':'); /* check for : signalling comment */
12747:  int    islogokay = 1;                  /* logfile must exist if isstrict */
12748:  if ( commptr != NULL )                 /* have embedded comment */
12749:   { strcpy(comment,commptr+1);          /* comment follows : */
12750:     *commptr = '\000'; }                /* null-terminate actual logfile */
12751:  strcpy(logfile,rasteditfilename(logfile)); /* edit log file name */
12752:  if ( *logfile == '\000' ) islogokay = 0; /* given an invalid file name */
12753:  else if ( isstrict ) {                 /*okay, but only write if it exists*/
12754:   if ( (logfp=fopen(logfile,"r")) == (FILE *)NULL ) /*doesn't already exist*/
12755:     islogokay = 0;                      /* so don't write log file */
12756:   else fclose(logfp); }                 /* close file opened for test read */
12757:  if ( islogokay )                       /* okay to write logfile */
12758:   if ( (logfp = fopen(logfile,"a"))     /* open logfile */
12759:   != (FILE *)NULL ) {                   /* opened successfully for append */
12760:    int  ilog=0;                         /* logvars[] index */
12761:    fprintf(logfp,"%s  ",timestamp(TZDELTA,0)); /* first emit timestamp */
12762:    if (*tag=='\000') fprintf(logfp,"%s",filename); /* emit counter filename */
12763:    else fprintf(logfp,"<%s>",tag);      /* or tag if we have one */
12764:    fprintf(logfp,"=%d",counter);        /* emit counter value */
12765:    if ( status < 1 )                    /* read or re-write failed */
12766:     fprintf(logfp,"(%s %d)","error status",status); /* emit error */
12767:    for ( ilog=0; logvars[ilog] != NULL; ilog++ ) /* log till end-of-table */
12768:     if ( ilog == commentvar             /* replace with comment... */
12769:     &&   commptr != NULL )              /* ...if available */
12770:      fprintf(logfp,"  %.256s",comment); /* log embedded comment */
12771:     else
12772:      { char *logval = getenv(logvars[ilog]); /*getenv(variable) to be logged*/
12773:        fprintf(logfp,"  %.64s",         /* log variable */
12774:         (logval!=NULL?logval:"<unknown>")); } /* emit value or <unknown> */
12775:    fprintf(logfp,"\n");                 /* terminating newline */
12776:    fclose(logfp);                       /* close logfile */
12777:    } /* --- end-of-if(islogokay&&logfp!=NULL) --- */
12778:  } /* --- end-of-if(strlen(logfile)>1) --- */
12779: /* --------------------------------------------------------------------------
12780: construct counter expression and rasterize it
12781: -------------------------------------------------------------------------- */
12782: /* --- construct expression --- */
12783: /*sprintf(text,"%d",counter);*/         /* start with counter */
12784: strcpy(text,dbltoa(((double)counter),0)); /* comma-separated counter value */
12785: if ( ordindex >= 0 )                    /* need to tack on ordinal suffix */
12786:   { strcat(text,"^{\\underline{\\rm~"); /* start with ^ and {\underline{\rm */
12787:     strcat(text,ordinal[ordindex]);     /* then st,nd,rd, or th */
12788:     strcat(text,"}}"); }                /* finish with }} */
12789: /* --- rasterize it --- */
12790: rasterize_counter:
12791:   countersp = rasterize(text,size);     /* rasterize counter subexpression */
12792: /* --- return counter image to caller --- */
12793: /*end_of_job:*/
12794:   return ( countersp );                 /* return counter image to caller */
12795: } /* --- end-of-function rastcounter() --- */
12796: 
12797: 
12798: /* ==========================================================================
12799:  * Function:    rasteval ( expression, size, basesp, arg1, arg2, arg3 )
12800:  * Purpose:     handle \eval
12801:  * --------------------------------------------------------------------------
12802:  * Arguments:   expression (I/O) char **  to first char of null-terminated
12803:  *                              string immediately following \eval,
12804:  *                              and returning ptr immediately
12805:  *                              following last character processed.
12806:  *              size (I)        int containing 0-7 default font size
12807:  *              basesp (I)      subraster *  to character (or subexpression)
12808:  *                              immediately preceding \eval
12809:  *                              (unused, but passed for consistency)
12810:  *              arg1 (I)        int unused
12811:  *              arg2 (I)        int unused
12812:  *              arg3 (I)        int unused
12813:  * --------------------------------------------------------------------------
12814:  * Returns:     ( subraster * ) subraster ptr to image of eval-uated
12815:  *                              expression
12816:  * --------------------------------------------------------------------------
12817:  * Notes:     o
12818:  * ======================================================================= */
12819: /* --- entry point --- */
12820: subraster *rasteval ( char **expression, int size, subraster *basesp,
12821:                         int arg1, int arg2, int arg3 )
12822: {
12823: /* -------------------------------------------------------------------------
12824: Allocations and Declarations
12825: -------------------------------------------------------------------------- */
12826: char    *texsubexpr(), subexpr[MAXSUBXSZ]; /* arg to be evaluated */
12827: subraster *rasterize(), *evalsp=NULL;   /* rasterize evaluated expression */
12828: int     evalterm(), value=0;            /* evaluate expression */
12829: /* -------------------------------------------------------------------------
12830: Parse for subexpr to be \eval-uated, and bump expression past it
12831: -------------------------------------------------------------------------- */
12832: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
12833: if ( *subexpr == '\000' )               /* couldn't get subexpression */
12834:   goto end_of_job;                      /* nothing to do, so quit */
12835: /* -------------------------------------------------------------------------
12836: Evaluate expression, ascii-ize integer result, rasterize it
12837: -------------------------------------------------------------------------- */
12838: /* --- evaluate expression --- */
12839: value = evalterm(mimestore,subexpr);    /* evaluate expression */
12840: /* --- ascii-ize it --- */
12841: sprintf(subexpr,"%d",value);            /* ascii version of value */
12842: /* --- rasterize ascii-ized expression value --- */
12843: evalsp = rasterize(subexpr,size);       /* rasterize evaluated expression */
12844: /* --- return evaluated expression raster to caller --- */
12845: end_of_job:
12846:   return ( evalsp );                    /* return evaluated expr to caller */
12847: } /* --- end-of-function rasteval() --- */
12848: 
12849: 
12850: /* ==========================================================================
12851:  * Function:    rastmathtex ( expression, size, basesp, arg1, arg2, arg3 )
12852:  * Purpose:     handle \mathtex...
12853:  *                ...obtain pixel image corresponding to expression
12854:  *              by "calling" mathtex, using wget,
12855:  *              for pbm-style bitmap of expression,
12856:  *              and then pixelizing it, and wrapping it in a subraster.
12857:  * --------------------------------------------------------------------------
12858:  * Arguments:   expression (I/O) char **  to first char of null-terminated
12859:  *                              string immediately following \mathtex,
12860:  *                              and returning ptr immediately
12861:  *                              following last character processed.
12862:  *              size (I)        int containing 0-11 default font size
12863:  *                              (unused, but passed for consistency)
12864:  *              basesp (I)      subraster *  to character (or subexpression)
12865:  *                              immediately preceding \mathtex
12866:  *                              (unused, but passed for consistency)
12867:  *              arg1 (I)        int unused
12868:  *              arg2 (I)        int unused
12869:  *              arg3 (I)        int unused
12870:  * --------------------------------------------------------------------------
12871:  * Returns:     ( subraster * ) subraster ptr to image of pixelized
12872:  *                              pbm bitmap of expression returned by mathtex
12873:  * --------------------------------------------------------------------------
12874:  * Notes:     o Adapted from function
12875:  *              BYTE *plainmimetext(char *expression, int *width, int *height)
12876:  *              in gifsave89.c
12877:  * ======================================================================= */
12878: /* ---
12879:  * entry point
12880:  * -------------- */
12881: subraster *rastmathtex ( char **expression, int size, subraster *basesp,
12882:                         int arg1, int arg2, int arg3 )
12883: {
12884: /* ---
12885:  * allocations and declarations
12886:  * ------------------------------- */
12887: /* --- for mimetex --- */
12888: char    *texsubexpr(), subexpr[MAXSUBXSZ]; /* arg to be evaluated */
12889: subraster *read_pbm(), *mathtexsp=NULL; /* rasterize mathtex'ed expression */
12890: /* --- for wget() pipe to mathtex --- */
12891: char    command[2048],                  /* complete popen(command,"r") */
12892:         execwget[1024];                 /* wget either mimetex or mathtex */
12893: FILE    *wget  = NULL;                  /* wget's stdout */
12894: char    *localhost="localhost://", *plocalhost=NULL; /*check if running local*/
12895: char    *mathtexpwd = MATHTEXPWD;       /* mathTeX \password{} */
12896: /* ---
12897:  * initialization
12898:  * ----------------- */
12899: /* --- Parse for subexpr for mathtex, and bump expression past it ---*/
12900: *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0);
12901: if ( *subexpr == '\000' )               /* couldn't get subexpression */
12902:   goto end_of_job;                      /* nothing to do, so quit */
12903: /* ---
12904:  * set up wget command for webservice
12905:  * ------------------------------------- */
12906: strcpy( execwget,                       /* --- mathTeX webservice --- */
12907:         WGET                            /*...begins with /path/to/wget */
12908:         " -q -O - "                     /*...followed by wget options */
12909:         "\"" MATHTEX "?\\pbm" );        /*..."mathtex.cgi?\pbm\dpi{400} */
12910: if ( !isempty(mathtexpwd) ) {           /* need \password{} ??? */
12911:   strcat(execwget,"\\password{");       /* start with \password{ */
12912:   strcat(execwget,mathtexpwd);          /* then actual mathtex password */
12913:   strcat(execwget,"}"); }               /* and closing } */
12914: strcat(execwget," ");                   /* blank space if message needs it */
12915: /* ---
12916:  * open pipe to wget (or run locally)
12917:  * ------------------------------------- */
12918: /* --- first construct popen() command using execwget --- */
12919: strcpy(command,execwget);               /* shell command plus mimetex url */
12920: strcat(command,subexpr);                /* followed by \mathtex{subexpr} */
12921: strcat(command,"\"");                   /* and closing " */
12922: /* --- then check for local mathTeX executable --- */
12923: if ( (plocalhost=strstr(MATHTEX,localhost)) /* does url have localhost://? */
12924: !=   NULL ) {                           /* if so, replace wget */
12925:   strcpy(command,plocalhost+strlen(localhost)); /*path follows localhost://*/
12926:   strcat(command," \"\\pbm\\stdout ");  /* start with "\pbm\stdout */
12927:   strcat(command,subexpr);              /* followed by \mathtex{subexpr} */
12928:   strcat(command,"\"");                 /* and closing " */
12929:   } /* --- end-of-if(plocalhost!=NULL) --- */
12930: /* --- issue wget command and capture its stdout --- */
12931: if ( (wget = popen(command,"r"))        /* issue command and capture stdout*/
12932: == NULL ) goto end_of_job;              /* or quit if failed */
12933: /* ---
12934:  * create subraster for pixelized image
12935:  * --------------------------------------- */
12936: mathtexsp = read_pbm(wget,0.0);
12937: /* ---
12938:  * end-of-job
12939:  * ------------- */
12940: end_of_job:
12941:   if ( wget != NULL ) pclose(wget);     /* close pipe (if open) */
12942:   return ( mathtexsp );                 /* back with mathtex subraster */
12943: } /* --- end-of-function rastmathtex() --- */
12944: 
12945: 
12946: /* ==========================================================================
12947:  * Function:    rasttoday ( expression, size, basesp, arg1, arg2, arg3 )
12948:  * Purpose:     handle \today
12949:  * --------------------------------------------------------------------------
12950:  * Arguments:   expression (I/O) char **  to first char of null-terminated
12951:  *                              string immediately following \today,
12952:  *                              and returning ptr immediately
12953:  *                              following last character processed.
12954:  *              size (I)        int containing 0-7 default font size
12955:  *              basesp (I)      subraster *  to character (or subexpression)
12956:  *                              immediately preceding \today
12957:  *                              (unused, but passed for consistency)
12958:  *              arg1 (I)        int unused
12959:  *              arg2 (I)        int unused
12960:  *              arg3 (I)        int unused
12961:  * --------------------------------------------------------------------------
12962:  * Returns:     ( subraster * ) subraster ptr to date stamp
12963:  * --------------------------------------------------------------------------
12964:  * Notes:     o
12965:  * ======================================================================= */
12966: /* --- entry point --- */
12967: subraster *rasttoday ( char **expression, int size, subraster *basesp,
12968:                         int arg1, int arg2, int arg3 )
12969: {
12970: /* -------------------------------------------------------------------------
12971: Allocations and Declarations
12972: -------------------------------------------------------------------------- */
12973: char    *texsubexpr(), optarg[2050];    /* optional [+/-tzdelta,ifmt] args */
12974: char    *timestamp(), *today=optarg;    /* timestamp to be rasterized */
12975: subraster *rasterize(), *todaysp=NULL;  /* rasterize timestamp */
12976: int     ifmt=1, tzdelta=0;              /* default timestamp() args */
12977: /* -------------------------------------------------------------------------
12978: Get optional args \today[+/-tzdelta,ifmt]
12979: -------------------------------------------------------------------------- */
12980: /* --- check for optional \today[+/-tzdelta,ifmt] --- */
12981: if ( *(*expression) == '[' )            /* check for []-enclosed value */
12982:   { *expression = texsubexpr(*expression,optarg,2047,"[","]",0,0);
12983:     if ( *optarg != '\000' )            /* got optional arg */
12984:      { char *comma = strchr(optarg,','); /* comma between +/-tzdelta,ifmt */
12985:        int iarg, nargs=(comma==NULL?1:2); /* #optional args between []'s */
12986:        if ( comma != NULL ) *comma = '\000'; /* null-terminate first arg */
12987:        for ( iarg=1; iarg<=nargs; iarg++ ) /* process one or both args */
12988:         { char *arg = (iarg==1?optarg:comma+1); /* choose 1st or 2nd arg */
12989:           if ( isthischar(*arg,"+-") )  /* leading +/- signals tzdelta */
12990:             tzdelta = atoi(arg);        /* so interpret arg as tzdelta */
12991:           else ifmt = atoi(arg); }      /* else interpret args as ifmt */
12992:      } /* --- end-of-if(*optarg!='\0') --- */
12993:   } /* --- end-of-if(**expression=='[') --- */
12994: /* -------------------------------------------------------------------------
12995: Get timestamp and rasterize it
12996: -------------------------------------------------------------------------- */
12997: strcpy(today,"\\text{");                /* rasterize timestamp as text */
12998: strcat(today,timestamp(tzdelta,ifmt));  /* get timestamp */
12999: strcat(today,"}");                      /* terminate \text{} braces */
13000: todaysp = rasterize(today,size);        /* rasterize timestamp */
13001: /* --- return timestamp raster to caller --- */
13002: /*end_of_job:*/
13003:   return ( todaysp );                   /* return timestamp to caller */
13004: } /* --- end-of-function rasttoday() --- */
13005: 
13006: 
13007: /* ==========================================================================
13008:  * Function:    rastcalendar ( expression, size, basesp, arg1, arg2, arg3 )
13009:  * Purpose:     handle \calendar
13010:  * --------------------------------------------------------------------------
13011:  * Arguments:   expression (I/O) char **  to first char of null-terminated
13012:  *                              string immediately following \calendar
13013:  *                              and returning ptr immediately
13014:  *                              following last character processed.
13015:  *              size (I)        int containing 0-7 default font size
13016:  *              basesp (I)      subraster *  to character (or subexpression)
13017:  *                              immediately preceding \calendar
13018:  *                              (unused, but passed for consistency)
13019:  *              arg1 (I)        int unused
13020:  *              arg2 (I)        int unused
13021:  *              arg3 (I)        int unused
13022:  * --------------------------------------------------------------------------
13023:  * Returns:     ( subraster * ) subraster ptr to rendered one-month calendar
13024:  * --------------------------------------------------------------------------
13025:  * Notes:     o
13026:  * ======================================================================= */
13027: /* --- entry point --- */
13028: subraster *rastcalendar ( char **expression, int size, subraster *basesp,
13029:                         int arg1, int arg2, int arg3 )
13030: {
13031: /* -------------------------------------------------------------------------
13032: Allocations and Declarations
13033: -------------------------------------------------------------------------- */
13034: char    *texsubexpr(), optarg[2050];    /* optional [year,month] args */
13035: char    *calendar(), *calstr=NULL;      /* calendar to be rasterized */
13036: subraster *rasterize(), *calendarsp=NULL; /* rasterize calendar string */
13037: int     year=0,month=0,day=0, argval=0; /* default calendar() args */
13038: /* -------------------------------------------------------------------------
13039: Get optional args \today[+/-tzdelta,ifmt]
13040: -------------------------------------------------------------------------- */
13041: /* --- check for optional \calendar[year,month] --- */
13042: if ( *(*expression) == '[' )            /* check for []-enclosed value */
13043:   { *expression = texsubexpr(*expression,optarg,2047,"[","]",0,0);
13044:     if ( *optarg != '\000' )            /* got optional arg */
13045:      { char *comma = strchr(optarg,','), /* comma between year,month */
13046:        *comma2 = NULL;                  /* second comma before day */
13047:        int iarg, nargs=(comma==NULL?1:2); /* #optional args between []'s */
13048:        if ( comma != NULL ) { *comma = '\000'; /*null-terminate first arg*/
13049:         if ( (comma2=strchr(comma+1,',')) != NULL ) /* have third arg */
13050:          { *comma2 = '\000'; nargs++; } } /* null-term 2nd arg, bump count */
13051:        for ( iarg=1; iarg<=nargs; iarg++ ) /* process one or both args */
13052:         { char *arg= (iarg==1?optarg:(iarg==2?comma+1:comma2+1)); /*get arg*/
13053:           argval = atoi(arg);           /* interpret arg as integer */
13054:           if ( iarg < 3 )               /* first two args are month,year */
13055:            {if ( argval>1972 && argval<2100 ) year = argval; /* year value */
13056:             else if ( argval>=1 && argval<=12 ) month = argval;} /*or month*/
13057:           else                          /* only 3rd arg can be day */
13058:            if ( argval>=1 && argval<=31 ) day = argval; } /* day value */
13059:      } /* --- end-of-if(*optarg!='\0') --- */
13060:   } /* --- end-of-if(**expression=='[') --- */
13061: /* -------------------------------------------------------------------------
13062: Get calendar string and rasterize it
13063: -------------------------------------------------------------------------- */
13064: if ( msgfp!= NULL && msglevel>=9 )
13065:   fprintf(msgfp,"rastcalendar> year=%d, month=%d, day=%d\n",
13066:   year,month,day);
13067: calstr = calendar(year,month,day);              /* get calendar string */
13068: calendarsp = rasterize(calstr,size);    /* rasterize calendar string */
13069: /* --- return calendar raster to caller --- */
13070: /*end_of_job:*/
13071:   return ( calendarsp );                /* return calendar to caller */
13072: } /* --- end-of-function rastcalendar() --- */
13073: 
13074: 
13075: /* ==========================================================================
13076:  * Function:    rastenviron ( expression, size, basesp, arg1, arg2, arg3 )
13077:  * Purpose:     handle \environment
13078:  * --------------------------------------------------------------------------
13079:  * Arguments:   expression (I/O) char **  to first char of null-terminated
13080:  *                              string immediately following \environment
13081:  *                              and returning ptr immediately
13082:  *                              following last character processed (in this
13083:  *                              case, \environment takes no arguments, so
13084:  *                              expression is returned unchanged).
13085:  *              size (I)        int containing 0-7 default font size
13086:  *              basesp (I)      subraster *  to character (or subexpression)
13087:  *                              immediately preceding \environment
13088:  *                              (unused, but passed for consistency)
13089:  *              arg1 (I)        int unused
13090:  *              arg2 (I)        int unused
13091:  *              arg3 (I)        int unused
13092:  * --------------------------------------------------------------------------
13093:  * Returns:     ( subraster * ) subraster ptr to rendered environment image
13094:  * --------------------------------------------------------------------------
13095:  * Notes:     o
13096:  * ======================================================================= */
13097: /* --- entry point --- */
13098: subraster *rastenviron ( char **expression, int size, subraster *basesp,
13099:                         int arg1, int arg2, int arg3 )
13100: {
13101: /* -------------------------------------------------------------------------
13102: Allocations and Declarations
13103: -------------------------------------------------------------------------- */
13104: char    *texsubexpr(), optarg[255];     /* optional [...] args (for future)*/
13105: char    environstr[8192] = "\000",      /* string for all environment vars */
13106:         environvar[1024] = "\000";      /* one environment variable */
13107: char    *strwrap(),                     /* wrap long lines */
13108:         *strdetex(),                    /* removes/replaces any math chars */
13109:         *environptr = NULL;             /* ptr to preprocessed environvar */
13110: char    *mimeprep();                    /* preprocess environvar string */
13111: int     unescape_url();                 /* convert all %xx's to chars */
13112: int     isenviron = (seclevel<=environseclevel?1:0); /*is \environ permitted*/
13113: int     maxvarlen = 512,                /* max chars in environment var */
13114:         maxenvlen = 6400,               /* max chars in entire string */
13115:         wraplen = 48;                   /* strwrap() wrap lines at 48 chars*/
13116: int     ienv = 0;                       /* environ[] index */
13117: subraster *rasterize(), *environsp=NULL; /* rasterize environment string */
13118: /* -------------------------------------------------------------------------
13119: Get args
13120: -------------------------------------------------------------------------- */
13121: /* --- check for optional \environment args --- */
13122: if ( 1 )                                /* there aren't any args (yet) */
13123:  if ( *(*expression) == '[' ) {         /* check for []-enclosed value */
13124:    *expression = texsubexpr(*expression,optarg,250,"[","]",0,0);
13125:    if ( *optarg != '\000' ) { ;         /* got optional arg, so process it */
13126:      wraplen = atoi(optarg);            /* interpret \environment[wraplen] */
13127:      if ( wraplen < 1 ) wraplen = 8;    /* set minimum */
13128:      } /* --- end-of-if(*optarg!='\0') --- */
13129:    } /* --- end-of-if(**expression=='[') --- */
13130: /* --------------------------------------------------------------------------
13131: emit error message for unauthorized users trying to use \environ
13132: -------------------------------------------------------------------------- */
13133: if ( !isenviron ) {                     /* environseclevel > seclevel */
13134:   sprintf(environstr,
13135:   "\\ \\text{[\\backslash environment\\ not permitted]}\\ ");
13136:   goto rasterize_environ;               /* rasterize error message */
13137:   } /* --- end-of-if(!isenviron) --- */
13138: /* -------------------------------------------------------------------------
13139: Accumulate environment variables and rasterize string containing them
13140: -------------------------------------------------------------------------- */
13141: *environstr = '\000';                   /* reset environment string */
13142: strcat(environstr,"\\nocaching\\fbox{\\normalsize\\text{"); /*init string*/
13143: for ( ienv=0; ; ienv++ ) {              /* loop over environ[] strings */
13144:   if ( environ[ienv] == (char *)NULL ) break; /* null terminates list */
13145:   if ( *(environ[ienv]) == '\000' ) break; /* double-check empty string */
13146:   strninit(environvar,environ[ienv],maxvarlen); /* max length displayed */
13147:   if ( strlen(environ[ienv]) > maxvarlen ) /* we truncated the variable */
13148:     strcat(environvar,"...");           /* so add an ellipsis */
13149:   unescape_url(environvar,0);           /* convert all %xx's to chars */
13150:   environptr = strdetex(environvar,1);  /* remove/replace any math chars */
13151:   strninit(environvar,environptr,maxvarlen); /*de-tex'ed/nomath environvar*/
13152:   environptr = strwrap(environvar,wraplen,-6); /* wrap long lines */
13153:   strninit(environvar,environptr,maxvarlen); /* line-wrapped environvar */
13154:   mimeprep(environvar);                 /* preprocess environvar string */
13155:   if ( strlen(environstr) + strlen(environvar) > maxenvlen ) break;
13156:   sprintf(environstr+strlen(environstr), /* display environment string */
13157:   " %2d. %s\\\\\n", ienv+1,environvar);
13158:   if ( msgfp!= NULL && msglevel>=9 )
13159:     fprintf(msgfp,"rastenviron> %2d. %.256s\n",
13160:     ienv+1,/*environ[ienv]*/environvar);
13161:   if ( strlen(environstr) >= 7200 ) break; /* don't overflow buffer */
13162:   } /* --- end-of-for(ienv) --- */
13163: strcat(environstr,"}}");                /* end {\text{...}} mode */
13164: rasterize_environ:
13165:   environsp = rasterize(environstr,size); /* rasterize environment string */
13166: /* --- return environment raster to caller --- */
13167: /*end_of_job:*/
13168:   return ( environsp );                 /* return environment to caller */
13169: } /* --- end-of-function rastenviron() --- */
13170: 
13171: 
13172: /* ==========================================================================
13173:  * Function:    rastmessage ( expression, size, basesp, arg1, arg2, arg3 )
13174:  * Purpose:     handle \message
13175:  * --------------------------------------------------------------------------
13176:  * Arguments:   expression (I/O) char **  to first char of null-terminated
13177:  *                              string immediately following \message
13178:  *                              and returning ptr immediately
13179:  *                              following last character processed.
13180:  *              size (I)        int containing 0-7 default font size
13181:  *              basesp (I)      subraster *  to character (or subexpression)
13182:  *                              immediately preceding \mesasge
13183:  *                              (unused, but passed for consistency)
13184:  *              arg1 (I)        int unused
13185:  *              arg2 (I)        int unused
13186:  *              arg3 (I)        int unused
13187:  * --------------------------------------------------------------------------
13188:  * Returns:     ( subraster * ) subraster ptr to rendered message image
13189:  * --------------------------------------------------------------------------
13190:  * Notes:     o
13191:  * ======================================================================= */
13192: /* --- entry point --- */
13193: subraster *rastmessage ( char **expression, int size, subraster *basesp,
13194:                         int arg1, int arg2, int arg3 )
13195: {
13196: /* -------------------------------------------------------------------------
13197: Allocations and Declarations
13198: -------------------------------------------------------------------------- */
13199: char    *texsubexpr(), amsg[256]="\000"; /* message number text */
13200: int     imsg = 0;                       /* default message number */
13201: char    msg[4096];
13202: subraster *rasterize(), *messagesp=NULL; /* rasterize requested message */
13203: int     strreplace();                   /*replace SERVER_NAME in refmsgnum*/
13204: int     reflevels = REFLEVELS;          /* #topmost levels to match */
13205: char    *urlprune();                    /*prune referer_match in refmsgnum*/
13206: char    *strdetex();                    /* remove math chars from messages */
13207: char    *http_host    = getenv("HTTP_HOST"), /* http host for mimeTeX */
13208:         *server_name  = getenv("SERVER_NAME"), /* server hosting mimeTeX */
13209:         *referer_match = (!isempty(http_host)?http_host: /*match http_host*/
13210:           (!isempty(server_name)?server_name:(NULL))); /* or server_name */
13211: /* -------------------------------------------------------------------------
13212: obtain message {amsg} argument
13213: -------------------------------------------------------------------------- */
13214: /* --- parse for {amsg} arg, and bump expression past it --- */
13215: *expression = texsubexpr(*expression,amsg,255,"{","}",0,0);
13216: /* --- interpret argument --- */
13217: if ( *amsg != '\000' ) {                /* got amsg arg */
13218:   imsg = atoi(amsg);                    /* interpret as an int */
13219:   if ( imsg < 0                         /* if too small */
13220:   ||   imsg > maxmsgnum )               /* or too big */
13221:     imsg = 0; }                         /* default to first message */
13222: /* --- retrieve requested message --- */
13223: strninit(msg,msgtable[imsg],4095);      /* local copy of message */
13224: /* --- process as necessary --- */
13225: if ( imsg == refmsgnum) {               /* urlncmp() failed to validate */
13226:   if ( reflevels > 0 )                  /* have #levels to validate */
13227:    strreplace(msg,"SERVER_NAME",        /* replace SERVER_NAME */
13228:     strdetex(urlprune(referer_match,reflevels),1),0); /*with referer_match*/
13229:   } /* --- end-of-switch(imsg) --- */
13230: /* --- rasterize requested message --- */
13231: messagesp = rasterize(msg,size);        /* rasterize message string */
13232: /* --- return message raster to caller --- */
13233: /*end_of_job:*/
13234:   return ( messagesp );                 /* return message to caller */
13235: } /* --- end-of-function rastmessage() --- */
13236: 
13237: 
13238: /* ==========================================================================
13239:  * Function:    rastnoop ( expression, size, basesp, nargs, arg2, arg3 )
13240:  * Purpose:     no op -- flush \escape without error
13241:  * --------------------------------------------------------------------------
13242:  * Arguments:   expression (I/O) char **  to first char of null-terminated
13243:  *                              string immediately following \escape to be
13244:  *                              flushed, and returning ptr immediately
13245:  *                              following last character processed.
13246:  *              size (I)        int containing 0-5 default font size
13247:  *              basesp (I)      subraster *  to character (or subexpression)
13248:  *                              immediately preceding \escape
13249:  *                              (unused, but passed for consistency)
13250:  *              nargs (I)       int containing number of {}-args after
13251:  *                              \escape to be flushed along with it
13252:  *              arg2 (I)        int unused
13253:  *              arg3 (I)        int unused
13254:  * --------------------------------------------------------------------------
13255:  * Returns:     ( subraster * ) NULL subraster ptr
13256:  * --------------------------------------------------------------------------
13257:  * Notes:     o
13258:  * ======================================================================= */
13259: /* --- entry point --- */
13260: subraster *rastnoop ( char **expression, int size, subraster *basesp,
13261:                         int nargs, int arg2, int arg3 )
13262: {
13263: /* -------------------------------------------------------------------------
13264: Allocations and Declarations
13265: -------------------------------------------------------------------------- */
13266: char    *texsubexpr(), subexpr[MAXSUBXSZ+1]; /*dummy args eaten by \escape*/
13267: subraster *rasterize(), *noopsp=NULL;   /* rasterize subexpr */
13268: /* --- flush accompanying args if necessary --- */
13269: if ( nargs != NOVALUE                   /* not unspecified */
13270: &&   nargs > 0 )                        /* and args to be flushed */
13271:   while ( --nargs >= 0 )                /* count down */
13272:     *expression = texsubexpr(*expression,subexpr,0,"{","}",0,0); /*flush arg*/
13273: /* --- return null ptr to caller --- */
13274: /*end_of_job:*/
13275:   return ( noopsp );                    /* return NULL ptr to caller */
13276: } /* --- end-of-function rastnoop() --- */
13277: 
13278: 
13279: /* ==========================================================================
13280:  * Function:    rastopenfile ( filename, mode )
13281:  * Purpose:     Opens filename[.tex] in mode, returning FILE *
13282:  * --------------------------------------------------------------------------
13283:  * Arguments:   filename (I/O)  char * to null-terminated string containing
13284:  *                              name of file to open (preceded by path
13285:  *                              relative to mimetex executable)
13286:  *                              If fopen() fails, .tex appeneded,
13287:  *                              and returned if that fopen() succeeds
13288:  *              mode (I)        char * to null-terminated string containing
13289:  *                              fopen() mode
13290:  * --------------------------------------------------------------------------
13291:  * Returns:     ( FILE * )      pointer to opened file, or NULL if error
13292:  * --------------------------------------------------------------------------
13293:  * Notes:     o
13294:  * ======================================================================= */
13295: /* --- entry point --- */
13296: FILE    *rastopenfile ( char *filename, char *mode )
13297: {
13298: /* -------------------------------------------------------------------------
13299: Allocations and Declarations
13300: -------------------------------------------------------------------------- */
13301: FILE    *fp = (FILE *)NULL /*,*fopen()*/; /*file pointer to opened filename*/
13302: char    texfile[2050] = "\000",         /* local, edited copy of filename */
13303:         *rasteditfilename(),            /* prepend pathprefix if necessary */
13304:         amode[512] = "r";               /* test open mode if arg mode=NULL */
13305: int     ismode = 0;                     /* true of mode!=NULL */
13306: /* --------------------------------------------------------------------------
13307: Check mode and open file
13308: -------------------------------------------------------------------------- */
13309: /* --- edit filename --- */
13310: strncpy(texfile,rasteditfilename(filename),2047); /*edited copy of filename*/
13311: texfile[2047] = '\000';                 /* make sure it's null terminated */
13312: /* --- check mode --- */
13313: if ( mode != (char *)NULL )             /* caller passed mode arg */
13314:  if ( *mode != '\000' )                 /* and it's not an empty string */
13315:   { ismode = 1;                         /* so flip mode flag true */
13316:     strncpy(amode,mode,254);            /* and replace "r" with caller's */
13317:     amode[254] = '\000';                /* make sure it's null terminated */
13318:     compress(amode,' '); }              /* remove embedded blanks */
13319: /* --- open filename or filename.tex --- */
13320: if ( strlen(texfile) > 1 )              /* make sure we got actual filename*/
13321:   if ( (fp = fopen(texfile,amode))      /* try opening given filename */
13322:   ==   NULL )                           /* failed to open given filename */
13323:   { strcpy(filename,texfile);           /* signal possible filename error */
13324:     strcat(texfile,".tex");             /* but first try adding .tex */
13325:     if ( (fp = fopen(texfile,amode))    /* now try opening filename.tex */
13326:     !=   NULL )                         /* filename.tex succeeded */
13327:       strcpy(filename,texfile); }       /* replace caller's filename */
13328: /* --- close file if only opened to check name --- */
13329: if ( !ismode && fp!=NULL )              /* no mode, so just checking */
13330:   fclose(fp);                           /* close file, fp signals success */
13331: /* --- return fp or NULL to caller --- */
13332: /*end_of_job:*/
13333:   if ( msglevel>=9 && msgfp!=NULL )     /* debuging */
13334:     { fprintf(msgfp,"rastopenfile> returning fopen(%s,%s) = %s\n",
13335:       filename,amode,(fp==NULL?"NULL":"Okay")); fflush(msgfp); }
13336:   return ( fp );                        /* return fp or NULL to caller */
13337: } /* --- end-of-function rastopenfile() --- */
13338: 
13339: 
13340: /* ==========================================================================
13341:  * Function:    rasteditfilename ( filename )
13342:  * Purpose:     edits filename to remove security problems,
13343:  *              e.g., removes all ../'s and ..\'s.
13344:  * --------------------------------------------------------------------------
13345:  * Arguments:   filename (I)    char * to null-terminated string containing
13346:  *                              name of file to be edited
13347:  * --------------------------------------------------------------------------
13348:  * Returns:     ( char * )      pointer to edited filename,
13349:  *                              or empty string "\000" if any problem
13350:  * --------------------------------------------------------------------------
13351:  * Notes:     o
13352:  * ======================================================================= */
13353: /* --- entry point --- */
13354: char    *rasteditfilename ( char *filename )
13355: {
13356: /* -------------------------------------------------------------------------
13357: Allocations and Declarations
13358: -------------------------------------------------------------------------- */
13359: static  char editname[2050];            /*edited filename returned to caller*/
13360: char    *strchange();                   /* prepend pathprefix if necessary */
13361: int     strreplace(),                   /* remove ../'s and ..\'s */
13362:         isprefix = (*pathprefix=='\000'?0:1); /* true if paths have prefix */
13363: /* --------------------------------------------------------------------------
13364: edit filename
13365: -------------------------------------------------------------------------- */
13366: /* --- first check filename arg --- */
13367: *editname = '\000';                     /* init edited name as empty string*/
13368: if ( filename == (char *)NULL ) goto end_of_job; /* no filename arg */
13369: if ( *filename == '\000' ) goto end_of_job; /* filename is an empty string */
13370: /* --- init edited filename --- */
13371: strcpy(editname,filename);              /* init edited name as input name */
13372: compress(editname,' ');                 /* remove embedded blanks */
13373: /* --- remove leading or embedded ....'s --- */
13374: while ( strreplace(editname,"....",NULL,0) > 0 ) ;  /* squeeze out ....'s */
13375: /* --- remove leading / and \ and dots (and blanks) --- */
13376: if ( *editname != '\000' )              /* still have chars in filename */
13377:  while ( isthischar(*editname," ./\\") ) /* absolute paths invalid */
13378:    {strsqueeze(editname,1);}            /* so flush leading / or \ (or ' ')*/
13379: if ( *editname == '\000' ) goto end_of_job; /* no chars left in filename */
13380: /* --- remove leading or embedded ../'s and ..\'s --- */
13381: while ( strreplace(editname,"../",NULL,0) > 0 ) ;  /* squeeze out ../'s */
13382: while ( strreplace(editname,"..\\",NULL,0) > 0 ) ; /* and ..\'s */
13383: while ( strreplace(editname,"../",NULL,0) > 0 ) ;  /* and ../'s again */
13384: /* --- prepend path prefix (if compiled with -DPATHPREFIX) --- */
13385: if ( isprefix && *editname!='\000' )    /* filename is preceded by prefix */
13386:   strchange(0,editname,pathprefix);     /* so prepend prefix */
13387: end_of_job:
13388:   return ( editname );                  /* back with edited filename */
13389: } /* --- end-of-function rasteditfilename() --- */
13390: 
13391: 
13392: /* ==========================================================================
13393:  * Function:    rastreadfile ( filename, islock, tag, value )
13394:  * Purpose:     Read filename, returning value as string
13395:  *              between <tag>...</tag> or entire file if tag=NULL passed.
13396:  * --------------------------------------------------------------------------
13397:  * Arguments:   filename (I)    char * to null-terminated string containing
13398:  *                              name of file to read (preceded by path
13399:  *                              relative to mimetex executable)
13400:  *              islock (I)      int containing 1 to lock file while reading
13401:  *                              (hopefully done by opening in "r+" mode)
13402:  *              tag (I)         char * to null-terminated string containing
13403:  *                              html-like tagname.  File contents between
13404:  *                              <tag> and </tag> will be returned, or
13405:  *                              entire file if tag=NULL passed.
13406:  *              value (O)       char * returning value between <tag>...</tag>
13407:  *                              or entire file if tag=NULL.
13408:  * --------------------------------------------------------------------------
13409:  * Returns:     ( int )         1=okay, 0=some error
13410:  * --------------------------------------------------------------------------
13411:  * Notes:     o
13412:  * ======================================================================= */
13413: /* --- entry point --- */
13414: int     rastreadfile ( char *filename, int islock, char *tag, char *value )
13415: {
13416: /* -------------------------------------------------------------------------
13417: Allocations and Declarations
13418: -------------------------------------------------------------------------- */
13419: FILE    *fp = (FILE *)NULL, *rastopenfile(); /* pointer to opened filename */
13420: char    texfile[1024] = "\000",         /* local copy of input filename */
13421:         text[MAXLINESZ+1];              /* line from input file */
13422: char    *tagp, tag1[1024], tag2[1024];  /* left <tag> and right <tag/> */
13423: int     vallen=0, maxvallen=MAXFILESZ;  /* #chars in value, max allowed */
13424: int     status = (-1);                  /* status returned, 1=okay */
13425: int     tagnum = 0;                     /* tag we're looking for */
13426: /*int   islock = 1;*/                   /* true to lock file */
13427: /* --------------------------------------------------------------------------
13428: Open file
13429: -------------------------------------------------------------------------- */
13430: /* --- first check output arg --- */
13431: if ( value == (char *)NULL ) goto end_of_job; /* no output buffer supplied */
13432: *value = '\000';                        /* init buffer with empty string */
13433: /* --- open filename or filename.tex --- */
13434: if ( filename != (char *)NULL )         /* make sure we got filename arg */
13435:   { strncpy(texfile,filename,1023);     /* local copy of filename */
13436:     texfile[1023] = '\000';             /* make sure it's null terminated */
13437:     fp = rastopenfile(texfile,(islock?"r+":"r")); } /* try opening it */
13438: /* --- check that file opened --- */
13439: if ( fp == (FILE *)NULL )               /* failed to open file */
13440:   { sprintf(value,"{\\normalsize\\rm[file %s?]}",texfile);
13441:     goto end_of_job; }                  /* return error message to caller */
13442: status = 0;                             /* file opened successfully */
13443: if ( islock ) rewind(fp);               /* start at beginning of file */
13444: /* --------------------------------------------------------------------------
13445: construct <tag>'s
13446: -------------------------------------------------------------------------- */
13447: if ( tag != (char *)NULL )              /* caller passed tag arg */
13448:  if ( *tag != '\000' )                  /* and it's not an empty string */
13449:   { strcpy(tag1,"<"); strcpy(tag2,"</"); /* begin with < and </ */
13450:     strcat(tag1,tag); strcat(tag2,tag); /* followed by caller's tag */
13451:     strcat(tag1,">"); strcat(tag2,">"); /* ending both tags with > */
13452:     compress(tag1,' '); compress(tag2,' '); /* remove embedded blanks */
13453:     tagnum = 1; }                       /* signal that we have tag */
13454: /* --------------------------------------------------------------------------
13455: Read file, concatnate lines
13456: -------------------------------------------------------------------------- */
13457: while ( fgets(text,MAXLINESZ-1,fp) != (char *)NULL ) { /*read input till eof*/
13458:   switch ( tagnum ) {                   /* look for left- or right-tag */
13459:     case 0: status = 1; break;          /* no tag to look for */
13460:     case 1:                             /* looking for opening left <tag> */
13461:       if ( (tagp=strstr(text,tag1)) == NULL ) break; /*haven't found it yet*/
13462:       tagp += strlen(tag1);             /* first char past tag */
13463:       strsqueezep(text,tagp);           /*shift out preceding text and tag*/
13464:       tagnum = 2;                       /*now looking for closing right tag*/
13465:     case 2:                             /* looking for closing right </tag> */
13466:       if ( (tagp=strstr(text,tag2)) == NULL ) break; /*haven't found it yet*/
13467:       *tagp = '\000';                   /* terminate line at tag */
13468:       tagnum = 3;                       /* done after this line */
13469:       status = 1;                       /* successfully read tag */
13470:       break;
13471:     } /* ---end-of-switch(tagnum) --- */
13472:   if ( tagnum != 1 ) {                  /* no tag or left tag already found*/
13473:     int textlen = strlen(text);         /* #chars in current line */
13474:     if ( vallen+textlen > maxvallen ) break; /* quit before overflow */
13475:     strcat(value,text);                 /* concat line to end of value */
13476:     vallen += textlen;                  /* bump length */
13477:     if ( tagnum > 2 ) break; }          /* found right tag, so we're done */
13478:   } /* --- end-of-while(fgets()!=NULL) --- */
13479: if ( tagnum<1 || tagnum>2 ) status=1;   /* okay if no tag or we found tag */
13480: fclose ( fp );                          /* close input file after reading */
13481: /* --- return value and status to caller --- */
13482: end_of_job:
13483:   return ( status );                    /* return status to caller */
13484: } /* --- end-of-function rastreadfile() --- */
13485: 
13486: 
13487: /* ==========================================================================
13488:  * Function:    rastwritefile ( filename, tag, value, isstrict )
13489:  * Purpose:     Re/writes filename, replacing string between <tag>...</tag>
13490:  *              with value, or writing entire file as value if tag=NULL.
13491:  * --------------------------------------------------------------------------
13492:  * Arguments:   filename (I)    char * to null-terminated string containing
13493:  *                              name of file to write (preceded by path
13494:  *                              relative to mimetex executable)
13495:  *              tag (I)         char * to null-terminated string containing
13496:  *                              html-like tagname.  File contents between
13497:  *                              <tag> and </tag> will be replaced, or
13498:  *                              entire file written if tag=NULL passed.
13499:  *              value (I)       char * containing string replacing value
13500:  *                              between <tag>...</tag> or replacing entire
13501:  *                              file if tag=NULL.
13502:  *              isstrict (I)    int containing 1 to only rewrite existing
13503:  *                              files, or 0 to create new file if necessary.
13504:  * --------------------------------------------------------------------------
13505:  * Returns:     ( int )         1=okay, 0=some error
13506:  * --------------------------------------------------------------------------
13507:  * Notes:     o
13508:  * ======================================================================= */
13509: /* --- entry point --- */
13510: int     rastwritefile( char *filename, char *tag, char *value, int isstrict )
13511: {
13512: /* -------------------------------------------------------------------------
13513: Allocations and Declarations
13514: -------------------------------------------------------------------------- */
13515: FILE    *fp = (FILE *)NULL, *rastopenfile(); /* pointer to opened filename */
13516: char    texfile[1024] = "\000",         /* local copy of input filename */
13517:         filebuff[MAXFILESZ+1] = "\000", /* entire contents of file */
13518:         tag1[1024], tag2[1024],         /* left <tag> and right <tag/> */
13519:         *strchange(),                   /* put value between <tag>...</tag>*/
13520:         *timestamp();                   /* log modification time */
13521: int     istag=0, rastreadfile(),        /* read file if tag!=NULL */
13522:         /*isstrict = (seclevel>5? 1:0),*/ /*true only writes existing files*/
13523:         isnewfile = 0,                  /* true if writing new file */
13524:         status = 0;                     /* status returned, 1=okay */
13525: int     istimestamp = 0;                /* true to update <timestamp> tag */
13526: /* --------------------------------------------------------------------------
13527: check args
13528: -------------------------------------------------------------------------- */
13529: /* --- check filename and value --- */
13530: if ( filename == (char *)NULL           /* quit if no filename arg supplied*/
13531: ||   value == (char *)NULL ) goto end_of_job; /* or no value arg supplied */
13532: if ( strlen(filename) < 2               /* quit if unreasonable filename */
13533: ||   *value == '\000' ) goto end_of_job; /* or empty value string supplied */
13534: /* --- establish filename[.tex] --- */
13535: strncpy(texfile,filename,1023);         /* local copy of input filename */
13536: texfile[1023] = '\000';                 /* make sure it's null terminated */
13537: if ( rastopenfile(texfile,NULL)         /* unchanged or .tex appended */
13538: ==   (FILE *)NULL )                     /* can't open, so write new file */
13539:   { if ( isstrict ) goto end_of_job;    /* fail if new files not permitted */
13540:     isnewfile = 1; }                    /* signal we're writing new file */
13541: /* --- check whether tag supplied by caller --- */
13542: if ( tag != (char *)NULL )              /* caller passed tag argument */
13543:  if ( *tag != '\000' )                  /* and it's not an empty string */
13544:   { istag = 1;                          /* so flip tag flag true */
13545:     strcpy(tag1,"<"); strcpy(tag2,"</");  /* begin tags with < and </ */
13546:     strcat(tag1,tag); strcat(tag2,tag);   /* followed by caller's tag */
13547:     strcat(tag1,">"); strcat(tag2,">"); /* ending both tags with > */
13548:     compress(tag1,' '); compress(tag2,' '); } /* remove embedded blanks */
13549: /* --------------------------------------------------------------------------
13550: read existing file if just rewriting a single tag
13551: -------------------------------------------------------------------------- */
13552: /* --- read original file if only replacing a tag within it --- */
13553: *filebuff = '\000';                     /* init as empty file */
13554: if ( !isnewfile )                       /* if file already exists */
13555:  if ( istag )                           /* and just rewriting one tag */
13556:   if ( rastreadfile(texfile,1,NULL,filebuff) /* read entire existing file */
13557:   <=   0 ) goto end_of_job;             /* signal error if failed to read */
13558: /* --------------------------------------------------------------------------
13559: construct new file data if needed (entire file replaced by value if no tag)
13560: -------------------------------------------------------------------------- */
13561: if ( istag )                            /* only replacing tag in file */
13562:  {
13563:  /* --- find <tag> and </tag> in file --- */
13564:  int    tlen1=strlen(tag1),  tlen2=strlen(tag2), flen;  /*tag,buff lengths*/
13565:  char   *tagp1 = (isnewfile? NULL:strstr(filebuff,tag1)), /* <tag> in file*/
13566:         *tagp2 = (isnewfile? NULL:strstr(filebuff,tag2)); /*</tag> in file*/
13567:  /* --- if adding new <tag> just concatanate at end of file --- */
13568:  if ( tagp1 == (char *)NULL )           /* add new tag to file */
13569:   {
13570:   /* --- preprocess filebuff --- */
13571:   if ( tagp2 != (char *)NULL )          /* apparently have ...</tag> */
13572:     {strsqueezep(filebuff,tagp2+tlen2);} /* remove ...</tag> */
13573:   if ( (flen = strlen(filebuff))        /* #chars currently in buffer */
13574:   > 0 )                                 /* we have non-empty buffer */
13575:    if (!isthischar(*(filebuff+flen-1),"\n\r")) /*no newline at end of file*/
13576:      if(0)strcat(filebuff,"\n");        /* so add one before new tag */
13577:   /* --- add new tag --- */
13578:   strcat(filebuff,tag1);                /* add opening <tag> */
13579:   strcat(filebuff,value);               /* then value */
13580:   strcat(filebuff,tag2);                /* finally closing </tag> */
13581:   strcat(filebuff,"\n");                /* newline at end of file */
13582:   } /* --- end-of-if(tagp1==NULL) --- */
13583:  else                                   /* found existing opening <tag> */
13584:   {
13585:   if ( tagp2 == NULL )                  /* apparently have <tag>... */
13586:     { *(tagp1+tlen1) = '\000';          /* so get rid of trailing ... */
13587:       strcat(filebuff,value);           /* then concatanate value */
13588:       strcat(filebuff,tag2); }          /* and finally closing </tag> */
13589:   else                                  /* else have <tag>...<tag/> */
13590:    if ( (flen=((int)(tagp2-tagp1))-tlen1) /* len of .'s in <tag>...</tag> */
13591:    >=   0 )                             /* usually <tag> precedes </tag> */
13592:     strchange(flen,tagp1+tlen1,value);  /* change ...'s to value */
13593:    else                                 /* weirdly, </tag> precedes <tag> */
13594:     { char fbuff[4096];                 /* field buff for <tag>value</tag> */
13595:       if ( (flen = ((int)(tagp1-tagp2))+tlen1) /* strlen(</tag>...<tag>) */
13596:       <=   0 ) goto end_of_job;         /* must be internal error */
13597:       strcpy(fbuff,tag1);               /* set opening <tag> */
13598:       strcat(fbuff,value);              /* then value */
13599:       strcat(fbuff,tag2);               /* finally closing </tag> */
13600:       strchange(flen,tagp2,fbuff); }    /* replace original </tag>...<tag> */
13601:   } /* --- end-of-if/else(tagp1==NULL) --- */
13602:  } /* --- end-of-if(istag) --- */
13603: /* --------------------------------------------------------------------------
13604: rewrite file and return to caller
13605: -------------------------------------------------------------------------- */
13606: /* --- first open file for write --- */
13607: if ( (fp=rastopenfile(texfile,"w"))     /* open for write */
13608: ==   (FILE *)NULL ) goto end_of_job;    /* signal error if can't open */
13609: /* --- rewrite and close file --- */
13610: if ( fputs((istag?filebuff:value),fp)   /* write filebuff or value */
13611: !=  EOF ) status = 1;                   /* signal success if succeeded */
13612: fclose ( fp );                          /* close output file after writing */
13613: /* --- modify timestamp --- */
13614: if ( status > 0 )                       /*forget timestamp if write failed*/
13615:  if ( istimestamp )                     /* if we're updating timestamp */
13616:   if ( istag )                          /* only log time in tagged file */
13617:    if ( strstr(tag,"timestamp") == (char *)NULL ) /* but avoid recursion */
13618:     { char fbuff[2048];                 /* field buff <timestamp> value */
13619:       strcpy(fbuff,tag);                /* tag modified */
13620:       strcat(fbuff," modified at ");    /* spacer */
13621:       strcat(fbuff,timestamp(TZDELTA,0)); /* start with timestamp */
13622:       status = rastwritefile(filename,"timestamp",fbuff,1); }
13623: /* --- return status to caller --- */
13624: end_of_job:
13625:   return ( status );                    /* return status to caller */
13626: } /* --- end-of-function rastwritefile() --- */
13627: 
13628: 
13629: /* ==========================================================================
13630:  * Function:    calendar ( year, month, day )
13631:  * Purpose:     returns null-terminated character string containing
13632:  *              \begin{array}...\end{array} for the one-month calendar
13633:  *              specified by year=1973...2099 and month=1...12.
13634:  *              If either arg out-of-range, today's value is used.
13635:  * --------------------------------------------------------------------------
13636:  * Arguments:   year (I)        int containing 1973...2099 or 0 for current
13637:  *                              year
13638:  *              month (I)       int containing 1...12 or 0 for current month
13639:  *              day (I)         int containing day to emphasize or 0
13640:  * --------------------------------------------------------------------------
13641:  * Returns:     ( char * )      char ptr to null-terminated buffer
13642:  *                              containing \begin{array}...\end{array}
13643:  *                              string that will render calendar for
13644:  *                              requested month, or NULL for any error.
13645:  * --------------------------------------------------------------------------
13646:  * Notes:     o
13647:  * ======================================================================= */
13648: /* --- entry point --- */
13649: char    *calendar( int year, int month, int day )
13650: {
13651: /* -------------------------------------------------------------------------
13652: Allocations and Declarations
13653: -------------------------------------------------------------------------- */
13654: static char calbuff[4096];              /* calendar returned to caller */
13655: time_t  time_val = (time_t)(0);         /* binary value returned by time() */
13656: struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */
13657: int     yy=0, mm=0, dd=0;               /* today (emphasize today's dd) */
13658: int     idd=1, iday=0, daynumber();     /* day-of-week for idd=1...31 */
13659: char    aval[64];                       /* ascii day or 4-digit year */
13660: /* --- calendar data --- */
13661: static  char *monthnames[] = { "?", "January", "February", "March", "April",
13662:          "May", "June", "July", "August", "September", "October",
13663:         "November", "December", "?" } ;
13664: static  int modays[] =
13665:         { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0 };
13666: /* -------------------------------------------------------------------------
13667: initialization
13668: -------------------------------------------------------------------------- */
13669: /* --- get current date/time --- */
13670: time((time_t *)(&time_val));            /* get date and time */
13671: tmstruct = localtime((time_t *)(&time_val)); /* interpret time_val */
13672: yy  =  1900 + (int)(tmstruct->tm_year); /* current four-digit year */
13673: mm  =  1 + (int)(tmstruct->tm_mon);     /* current month, 1-12 */
13674: dd  =  (int)(tmstruct->tm_mday);        /* current day, 1-31 */
13675: /* --- check args --- */
13676: if ( year<1973 || year>2099 ) year  = yy; /* current year if out-of-bounds */
13677: if ( month<1 || month>12 ) month = mm;  /* current month if out-of-bounds */
13678: if ( month==mm && year==yy && day==0 )  /* current month and default day */
13679:   day = dd;                             /* emphasize current day */
13680: modays[2] = (year%4==0?29:28);          /* Feb has 29 days in leap years */
13681: /* --- initialize calendar string --- */
13682: strcpy(calbuff,"{\\begin{gather}");     /* center `month year` above cal */
13683: strcat(calbuff,"\\small\\text{");       /* month set in roman */
13684: strcat(calbuff,monthnames[month]);      /* insert month name */
13685: strcat(calbuff," }");                   /* add a space */
13686: sprintf(aval,"%d",year);                /* convert year to ascii */
13687: strcat(calbuff,aval);                   /* add year */
13688: strcat(calbuff,"\\\\");                 /* end top row */
13689: strcat(calbuff,                         /* now begin calendar arrayr */
13690:         "\\begin{array}{|c|c|c|c|c|c|c|CCCCCC} \\hline"
13691:         "\\tiny\\text{Sun} & \\tiny\\text{Mon} & \\tiny\\text{Tue} &"
13692:         "\\tiny\\text{Wed} & \\tiny\\text{Thu} & \\tiny\\text{Fri} &"
13693:         "\\tiny\\text{Sat} \\\\ \\hline " );
13694: /* -------------------------------------------------------------------------
13695: generate calendar
13696: -------------------------------------------------------------------------- */
13697: for ( idd=1; idd<=modays[month]; idd++ ) /* run through days of month */
13698:   {
13699:   /* --- get day-of-week for this day --- */
13700:   iday = 1 + (daynumber(year,month,idd)%7); /* 1=Monday...7=Sunday */
13701:   if ( iday == 7 ) iday = 0;            /* now 0=Sunday...6=Saturday */
13702:   /* --- may need empty cells at beginning of month --- */
13703:   if ( idd == 1 )                       /* first day of month */
13704:    if ( iday > 0 )                      /* need to skip cells */
13705:     { strcpy(aval,"\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\ &\\"); /*cells to skip*/
13706:       aval[3*iday] = '\000';            /*skip cells preceding 1st of month*/
13707:       strcat(calbuff,aval); }           /* add skip string to buffer */
13708:   /* --- add idd to current cell --- */
13709:   sprintf(aval,"%d",idd);               /* convert idd to ascii */
13710:   if ( idd == day                       /* emphasize today's date */
13711:   /*&&   month==mm && year==yy*/ )      /* only if this month's calendar */
13712:    { strcat(calbuff,"{\\fs{-1}\\left\\langle "); /*emphasize, 1 size smaller*/
13713:      strcat(calbuff,aval);              /* put in idd */
13714:      strcat(calbuff,"\\right\\rangle}"); } /* finish emphasis */
13715:   else                                  /* not today's date */
13716:     strcat(calbuff,aval);               /* so just put in idd */
13717:   /* --- terminate cell --- */
13718:   if ( idd < modays[month] ) {          /* not yet end-of-month */
13719:    if ( iday < 6 )                      /* still have days left in week */
13720:     strcat(calbuff,"&");                /* new cell in same week */
13721:    else                                 /* reached end-of-week */
13722:     strcat(calbuff,"\\\\ \\hline"); }   /* so start new week */
13723:   } /* --- end-of-for(idd) --- */
13724: strcat(calbuff,"\\\\ \\hline");         /* final underline at end-of-month */
13725: /* --- return calendar to caller --- */
13726: strcat(calbuff,"\\end{array}\\end{gather}}"); /* terminate array */
13727: return ( calbuff );                     /* back to caller with calendar */
13728: } /* --- end-of-function calendar() --- */
13729: 
13730: 
13731: /* ==========================================================================
13732:  * Function:    timestamp ( tzdelta, ifmt )
13733:  * Purpose:     returns null-terminated character string containing
13734:  *              current date:time stamp as ccyy-mm-dd:hh:mm:ss{am,pm}
13735:  * --------------------------------------------------------------------------
13736:  * Arguments:   tzdelta (I)     integer, positive or negative, containing
13737:  *                              containing number of hours to be added or
13738:  *                              subtracted from system time (to accommodate
13739:  *                              your desired time zone).
13740:  *              ifmt (I)        integer containing 0 for default format
13741:  * --------------------------------------------------------------------------
13742:  * Returns:     ( char * )      ptr to null-terminated buffer
13743:  *                              containing current date:time stamp
13744:  * --------------------------------------------------------------------------
13745:  * Notes:     o
13746:  * ======================================================================= */
13747: /* --- entry point --- */
13748: char    *timestamp( int tzdelta, int ifmt )
13749: {
13750: /* -------------------------------------------------------------------------
13751: Allocations and Declarations
13752: -------------------------------------------------------------------------- */
13753: static  char timebuff[256];             /* date:time buffer back to caller */
13754: /*long  time_val = 0L;*/                /* binary value returned by time() */
13755: time_t  time_val = (time_t)(0);         /* binary value returned by time() */
13756: struct tm *tmstruct=(struct tm *)NULL, *localtime(); /* interpret time_val */
13757: int     year=0, hour=0,ispm=1,          /* adjust year, and set am/pm hour */
13758:         month=0, day=0,                 /* adjust day and month for delta  */
13759:         minute=0,second=0;              /* minute and second not adjusted  */
13760: int     tzadjust();                     /* time zone adjustment function */
13761: int     daynumber();                    /* #days since Jan 1, 1973 */
13762: static  char *daynames[] = { "Monday", "Tuesday", "Wednesday",
13763:          "Thursday", "Friday", "Saturday", "Sunday" } ;
13764: static  char *monthnames[] = { "?", "January", "February", "March", "April",
13765:          "May", "June", "July", "August", "September", "October",
13766:         "November", "December", "?" } ;
13767: /* -------------------------------------------------------------------------
13768: get current date:time, adjust values, and and format stamp
13769: -------------------------------------------------------------------------- */
13770: /* --- first init returned timebuff in case of any error --- */
13771: *timebuff = '\000';
13772: /* --- get current date:time --- */
13773: time((time_t *)(&time_val));            /* get date and time */
13774: tmstruct = localtime((time_t *)(&time_val)); /* interpret time_val */
13775: /* --- extract fields --- */
13776: year  = (int)(tmstruct->tm_year);       /* local copy of year,  0=1900 */
13777: month = (int)(tmstruct->tm_mon) + 1;    /* local copy of month, 1-12 */
13778: day   = (int)(tmstruct->tm_mday);       /* local copy of day,   1-31 */
13779: hour  = (int)(tmstruct->tm_hour);       /* local copy of hour,  0-23 */
13780: minute= (int)(tmstruct->tm_min);        /* local copy of minute,0-59 */
13781: second= (int)(tmstruct->tm_sec);        /* local copy of second,0-59 */
13782: /* --- adjust year --- */
13783: year += 1900;                           /* set century in year */
13784: /* --- adjust for timezone --- */
13785: tzadjust(tzdelta,&year,&month,&day,&hour);
13786: /* --- check params --- */
13787: if ( hour<0  || hour>23
13788: ||   day<1   || day>31
13789: ||   month<1 || month>12
13790: ||   year<1973 ) goto end_of_job;
13791: /* --- adjust hour for am/pm --- */
13792: switch ( ifmt )
13793:   {
13794:   default:
13795:   case 0:
13796:     if ( hour < 12 )                    /* am check */
13797:      { ispm=0;                          /* reset pm flag */
13798:        if ( hour == 0 ) hour = 12; }    /* set 00hrs = 12am */
13799:     if ( hour > 12 ) hour -= 12;        /* pm check sets 13hrs to 1pm, etc */
13800:     break;
13801:   } /* --- end-of-switch(ifmt) --- */
13802: /* --- format date:time stamp --- */
13803: switch ( ifmt )
13804:   {
13805:   default:
13806:   case 0:  /* --- 2005-03-05:11:49:59am --- */
13807:     sprintf(timebuff,"%04d-%02d-%02d:%02d:%02d:%02d%s", year,month,day,
13808:     hour,minute,second,((ispm)?"pm":"am"));
13809:     break;
13810:   case 1:  /* --- Saturday, March 5, 2005 --- */
13811:     sprintf(timebuff,"%s, %s %d, %d",
13812:     daynames[daynumber(year,month,day)%7],monthnames[month],day,year);
13813:     break;
13814:   case 2: /* --- Saturday, March 5, 2005, 11:49:59am --- */
13815:     sprintf(timebuff,"%s, %s %d, %d, %d:%02d:%02d%s",
13816:     daynames[daynumber(year,month,day)%7],monthnames[month],day,year,
13817:     hour,minute,second,((ispm)?"pm":"am"));
13818:     break;
13819:   case 3: /* --- 11:49:59am --- */
13820:     sprintf(timebuff,"%d:%02d:%02d%s",
13821:     hour,minute,second,((ispm)?"pm":"am"));
13822:     break;
13823:   case 4: /* --- 1231235959 (mmddhhmmss time as integer) --- */
13824:     sprintf(timebuff,"%d%02d%02d%02d%02d",
13825:     month,day,hour,minute,second);
13826:     break;
13827:   } /* --- end-of-switch(ifmt) --- */
13828: end_of_job:
13829:   return ( timebuff );                  /* return stamp to caller */
13830: } /* --- end-of-function timestamp() --- */
13831: 
13832: 
13833: /* ==========================================================================
13834:  * Function:    tzadjust ( tzdelta, year, month, day, hour )
13835:  * Purpose:     Adjusts hour, and day,month,year if necessary,
13836:  *              by delta increment to accommodate your time zone.
13837:  * --------------------------------------------------------------------------
13838:  * Arguments:   tzdelta (I)     integer, positive or negative, containing
13839:  *                              containing number of hours to be added or
13840:  *                              subtracted from given time (to accommodate
13841:  *                              your desired time zone).
13842:  *              year (I)        addr of int containing        4-digit year
13843:  *              month (I)       addr of int containing month  1=Jan - 12=Dec.
13844:  *              day (I)         addr of int containing day    1-31 for Jan.
13845:  *              hour (I)        addr of int containing hour   0-23
13846:  * Returns:     ( int )         1 for success, or 0 for error
13847:  * --------------------------------------------------------------------------
13848:  * Notes:     o
13849:  * ======================================================================= */
13850: /* --- entry point --- */
13851: int     tzadjust ( int tzdelta, int *year, int *month, int *day, int *hour )
13852: {
13853: /* --------------------------------------------------------------------------
13854: Allocations and Declarations
13855: -------------------------------------------------------------------------- */
13856: int     yy = *year, mm = *month, dd = *day, hh = *hour; /*dereference args*/
13857: /* --- calendar data --- */
13858: static  int modays[] =
13859:         { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0 };
13860: /* --------------------------------------------------------------------------
13861: check args
13862: -------------------------------------------------------------------------- */
13863: if ( mm<1 || mm>12 ) return(-1);        /* bad month */
13864: if ( dd<1 || dd>modays[mm] ) return(-1); /* bad day */
13865: if ( hh<0 || hh>23 ) return(-1);        /* bad hour */
13866: if ( tzdelta>23 || tzdelta<(-23) ) return(-1); /* bad tzdelta */
13867: /* --------------------------------------------------------------------------
13868: make adjustments
13869: -------------------------------------------------------------------------- */
13870: /* --- adjust hour --- */
13871: hh += tzdelta;                          /* apply caller's delta */
13872: /* --- adjust for feb 29 --- */
13873: modays[2] = (yy%4==0?29:28);            /* Feb has 29 days in leap years */
13874: /* --- adjust day --- */
13875: if ( hh < 0 )                           /* went to preceding day */
13876:   { dd--;  hh += 24; }
13877: if ( hh > 23 )                          /* went to next day */
13878:   { dd++;  hh -= 24; }
13879: /* --- adjust month --- */
13880: if ( dd < 1 )                           /* went to preceding month */
13881:   { mm--;  dd = modays[mm]; }
13882: if ( dd > modays[mm] )                  /* went to next month */
13883:   { mm++;  dd = 1; }
13884: /* --- adjust year --- */
13885: if ( mm < 1 )                           /* went to preceding year */
13886:   { yy--;  mm = 12;  dd = modays[mm]; }
13887: if ( mm > 12 )                          /* went to next year */
13888:   { yy++;  mm = 1;   dd = 1; }
13889: /* --- back to caller --- */
13890: *year=yy; *month=mm; *day=dd; *hour=hh; /* reset adjusted args */
13891: return ( 1 );
13892: } /* --- end-of-function tzadjust() --- */
13893: 
13894: 
13895: /* ==========================================================================
13896:  * Function:    daynumber ( year, month, day )
13897:  * Purpose:     Returns number of actual calendar days from Jan 1, 1973
13898:  *              to the given date (e.g., bvdaynumber(1974,1,1)=365).
13899:  * --------------------------------------------------------------------------
13900:  * Arguments:   year (I)        int containing year -- may be either 1995 or
13901:  *                              95, or may be either 2010 or 110 for those
13902:  *                              years.
13903:  *              month (I)       int containing month, 1=Jan thru 12=Dec.
13904:  *              day (I)         int containing day of month, 1-31 for Jan, etc.
13905:  * Returns:     ( int )         Number of days from Jan 1, 1973 to given date,
13906:  *                              or -1 for error (e.g., year<1973).
13907:  * --------------------------------------------------------------------------
13908:  * Notes:     o
13909:  * ======================================================================= */
13910: /* --- entry point --- */
13911: int     daynumber ( int year, int month, int day )
13912: {
13913: /* --------------------------------------------------------------------------
13914: Allocations and Declarations
13915: -------------------------------------------------------------------------- */
13916: /* --- returned value (note: returned as a default "int") --- */
13917: int     ndays;                          /* #days since jan 1, year0 */
13918: /* --- initial conditions --- */
13919: static  int year0 = 73,                 /* jan 1 was a monday, 72 was a leap */
13920:         days4yrs = 1461,                /* #days in 4 yrs = 365*4 + 1 */
13921:         days1yr  = 365;
13922: /* --- table of accumulated days per month (last index not used) --- */
13923: static  int modays[] =
13924:         { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
13925: /* --- variables for #days since day0 --- */
13926: int     nyears, nfouryrs;               /*#years, #4-yr periods since year0*/
13927: /* --------------------------------------------------------------------------
13928: Check input
13929: -------------------------------------------------------------------------- */
13930: if ( month < 1 || month > 12 )          /*month used as index, so must be ok*/
13931:         return ( -1 );                  /* otherwise, forget it */
13932: if ( year >= 1900 ) year -= 1900;       /*use two-digit years (3 after 2000)*/
13933: /* --------------------------------------------------------------------------
13934: Find #days since jan 1, 1973
13935: -------------------------------------------------------------------------- */
13936: /* --- figure #complete 4-year periods and #remaining yrs till current --- */
13937: nyears = year - year0;                  /* #years since year0 */
13938: if ( nyears < 0 ) return ( -1 );        /* we're not working backwards */
13939: nfouryrs = nyears/4;                    /* #complete four-year periods */
13940: nyears -= (4*nfouryrs);                 /* remainder excluding current year*/
13941: /* --- #days from jan 1, year0 till jan 1, this year --- */
13942: ndays = (days4yrs*nfouryrs)             /* #days in 4-yr periods */
13943:       +  (days1yr*nyears);              /* +remaining days */
13944: /*if ( year > 100 ) ndays--;*/          /* subtract leap year for 2000AD */
13945: /* --- add #days within current year --- */
13946: ndays += (modays[month-1] + (day-1));
13947: /* --- may need an extra day if current year is a leap year --- */
13948: if ( nyears == 3 )                      /*three preceding yrs so this is 4th*/
13949:     { if ( month > 2 )                  /* past feb so need an extra day */
13950:         /*if ( year != 100 )*/          /* unless it's 2000AD */
13951:           ndays++; }                    /* so add it in */
13952: return ( (int)(ndays) );                /* #days back to caller */
13953: } /* --- end-of-function daynumber() --- */
13954: 
13955: 
13956: /* ==========================================================================
13957:  * Function:    strwrap ( s, linelen, tablen )
13958:  * Purpose:     Inserts \n's and spaces in (a copy of) s to wrap lines
13959:  *              at linelen and indent them by tablen.
13960:  * --------------------------------------------------------------------------
13961:  * Arguments:   s (I)           char * to null-terminated string
13962:  *                              to be wrapped.
13963:  *              linelen (I)     int containing maximum linelen
13964:  *                              between \\'s.
13965:  *              tablen (I)      int containing number of spaces to indent
13966:  *                              lines.  0=no indent.  Positive means
13967:  *                              only indent first line and not others.
13968:  *                              Negative means indent all lines except first.
13969:  * --------------------------------------------------------------------------
13970:  * Returns:     ( char * )      ptr to "line-wrapped" copy of s
13971:  *                              or "" (empty string) for any error.
13972:  * --------------------------------------------------------------------------
13973:  * Notes:     o The returned copy of s has embedded \\'s as necessary
13974:  *              to wrap lines at linelen.  Any \\'s in the input copy
13975:  *              are removed first.  If (and only if) the input s contains
13976:  *              a terminating \\ then so does the returned copy.
13977:  *            o The returned pointer addresses a static buffer,
13978:  *              so don't call strwrap() again until you're finished
13979:  *              with output from the preceding call.
13980:  *            o Modified for mimetex from original version written
13981:  *              for mathtex (where \n in verbatim mode instead of \\
13982:  *              produced linebreaks).
13983:  * ======================================================================= */
13984: /* --- entry point --- */
13985: char    *strwrap ( char *s, int linelen, int tablen )
13986: {
13987: /* -------------------------------------------------------------------------
13988: Allocations and Declarations
13989: -------------------------------------------------------------------------- */
13990: static  char sbuff[4096];               /* line-wrapped copy of s */
13991: char    *sol = sbuff;                   /* ptr to start of current line*/
13992: char    tab[32] = "                 ";  /* tab string */
13993: int     strreplace();                   /* remove \n's */
13994: char    *strchange();                   /* add \n's and indent space */
13995: int     finalnewline = (lastchar(s)=='\n'?1:0); /*newline at end of string?*/
13996: int     istab = (tablen>0?1:0),         /* init true to indent first line */
13997:         iswhite = 0;                    /* true if line break on whitespace*/
13998: int     rhslen  = 0,                    /* remaining right hand side length*/
13999:         thislen = 0,                    /* length of current line segment */
14000:         thistab = 0,                    /* length of tab on current line */
14001:         wordlen = 0;                    /* length to next whitespace char */
14002: /* -------------------------------------------------------------------------
14003: Make a clean copy of s
14004: -------------------------------------------------------------------------- */
14005: /* --- check input --- */
14006: *sbuff = '\000';                        /* initialize in case of error */
14007: if ( isempty(s) ) goto end_of_job;      /* no input */
14008: if ( tablen < 0 ) tablen = (-tablen);   /* set positive tablen */
14009: if ( tablen >= linelen ) tablen = linelen-1; /* tab was longer than line */
14010: tab[min2(tablen,16)] = '\000';          /* null-terminate tab string */
14011: tablen = strlen(tab);                   /* reset to actual tab length */
14012: finalnewline = 0;                       /* turned off for mimetex version */
14013: /* --- start with copy of s --- */
14014: strninit(sbuff,s,3000);                 /* leave room for \n's and tabs */
14015: if ( linelen < 1 ) goto end_of_job;     /* can't do anything */
14016: trimwhite(sbuff);                       /*remove leading/trailing whitespace*/
14017: strreplace(sbuff,"\n"," ",0);           /* remove any original \n's */
14018: strreplace(sbuff,"\r"," ",0);           /* remove any original \r's */
14019: strreplace(sbuff,"\t"," ",0);           /* remove any original \t's */
14020: strreplace(sbuff,"\f"," ",0);           /* remove any original \f's */
14021: strreplace(sbuff,"\v"," ",0);           /* remove any original \v's */
14022: strreplace(sbuff,"\\\\"," ",0);         /* remove any original \\'s */
14023: /* -------------------------------------------------------------------------
14024: Insert \\'s and spaces as needed
14025: -------------------------------------------------------------------------- */
14026: while ( 1 ) {                           /* till end-of-line */
14027:   /* --- init --- */
14028:   trimwhite(sol);                       /*remove leading/trailing whitespace*/
14029:   thislen = thistab = 0;                /* no chars in current line yet */
14030:   if ( istab && tablen>0 ) {            /* need to indent this line */
14031:     strchange(0,sol,tab);               /* insert indent at start of line */
14032:     thistab = tablen; }                 /* line starts with whitespace tab */
14033:   if ( sol == sbuff ) istab = 1-istab;  /* flip tab flag after first line */
14034:   sol += thistab;                       /* skip tab */
14035:   rhslen = strlen(sol);                 /* remaining right hand side chars */
14036:   if ( rhslen+thistab <= linelen ) break; /* no more \\'s needed */
14037:   if ( 0 && msgfp!=NULL && msglevel >= 99 ) {
14038:     fprintf(msgfp,"strwrap> rhslen=%d, sol=\"\"%s\"\"\n",rhslen,sol);
14039:     fflush(msgfp); }
14040:   /* --- look for last whitespace preceding linelen --- */
14041:   while ( 1 ) {                         /* till we exceed linelen */
14042:     wordlen = strcspn(sol+thislen," \t\n\r\f\v :;.,"); /*ptr to next white/break*/
14043:     if ( sol[thislen+wordlen] == '\000' ) /* no more whitespace in string */
14044:       goto end_of_job;                  /* so nothing more we can do */
14045:     if ( thislen+thistab+wordlen >= linelen ) /* next word won't fit */
14046:       if ( thislen > 0 ) break;         /* but make sure line has one word */
14047:     thislen += (wordlen+1); }           /* ptr past next whitespace char */
14048:   if ( thislen < 1 ) break;             /* line will have one too-long word*/
14049:   /*sol[thislen-1] = '\n';*/            /* replace last space with newline */
14050:   /*sol += thislen;*/                   /* next line starts after newline */
14051:   iswhite = (isthischar(sol[thislen-1],":;.,")?0:1); /*linebreak on space?*/
14052:   strchange(iswhite,sol+thislen-iswhite,"\\\\"); /* put \\ at end of line */
14053:   sol += (thislen+2-iswhite);           /* next line starts after \\ */
14054:   } /* --- end-of-while(1) --- */
14055: end_of_job:
14056:   if ( finalnewline ) strcat(sbuff,"\\\\"); /* replace final newline */
14057:   return ( sbuff );                     /* back with clean copy of s */
14058: } /* --- end-of-function strwrap() --- */
14059: 
14060: 
14061: /* ==========================================================================
14062:  * Function:    strnlower ( s, n )
14063:  * Purpose:     lowercase the first n chars of string s
14064:  * --------------------------------------------------------------------------
14065:  * Arguments:   s (I/O)         (char *)pointer to null-terminated string
14066:  *                              whose chars are to be lowercased
14067:  *              n (I)           int containing max number of chars to be
14068:  *                              lowercased (less than n will be lowercased
14069:  *                              if terminating '\000' found first)
14070:  *                              If n<=0 (or n>=strlen(s)) then the entire
14071:  *                              string s will be lowercased
14072:  * --------------------------------------------------------------------------
14073:  * Returns:     ( char * )      s (always same as input)
14074:  * --------------------------------------------------------------------------
14075:  * Notes:     o
14076:  * ======================================================================= */
14077: /* --- entry point --- */
14078: char    *strnlower ( char *s, int n )
14079: {
14080: /* -------------------------------------------------------------------------
14081: lowercase s
14082: -------------------------------------------------------------------------- */
14083: char    *p = s;                         /* save s for return to caller */
14084: if ( !isempty(s) )                      /* check for valid input */
14085:   while ( *p != '\000' ) {              /* lowercase each char till end */
14086:     *p = tolower(*p);                   /* lowercase this char */
14087:     if ( n > 0 )                        /* only lowercase first n chars */
14088:       if ( --n < 1 ) break;             /* quit when we're done */
14089:     p++; }                              /* proceed to next char */
14090: return ( s );                           /* back to caller with s */
14091: } /* --- end-of-function strnlower() --- */
14092: 
14093: 
14094: /* ==========================================================================
14095:  * Function:    urlprune ( url, n )
14096:  * Purpose:     Prune http://abc.def.ghi.com/etc into abc.def.ghi.com
14097:  *              (if n=2 only ghi.com is returned, or if n=-1 only "ghi")
14098:  * --------------------------------------------------------------------------
14099:  * Arguments:   url (I)         char * to null-terminated string
14100:  *                              containing url to be pruned
14101:  *              n (i)           int containing number of levels retained
14102:  *                              in pruned url.  If n<0 its abs() is used,
14103:  *                              but the topmost level (usually .com, .org,
14104:  *                              etc) is omitted.  That is, if n=2 would
14105:  *                              return "ghi.com" then n=-1 returns "ghi".
14106:  *                              n=0 retains all levels.
14107:  * --------------------------------------------------------------------------
14108:  * Returns:     ( char * )      pointer to (static) null-terminated string
14109:  *                              containing pruned url with the first n
14110:  *                              top-level domain, e.g., for n=2,
14111:  *                              http://abc.def.ghi.com/etc returns ghi.com,
14112:  *                              or an empty string "\000" for any error
14113:  * --------------------------------------------------------------------------
14114:  * Notes:     o
14115:  * ======================================================================= */
14116: /* --- entry point --- */
14117: char    *urlprune ( char *url, int n )
14118: {
14119: /* -------------------------------------------------------------------------
14120: Allocations and Declarations
14121: -------------------------------------------------------------------------- */
14122: static  char pruned[2048];              /* pruned url returned to caller */
14123: char    *purl = /*NULL*/pruned;         /* ptr to pruned, init for error */
14124: char    *delim = NULL;                  /* delimiter separating components */
14125: char    *strnlower();                   /* lowercase a string */
14126: int     istruncate = (n<0?1:0);         /*true to truncate .com from pruned*/
14127: int     ndots = 0;                      /* number of dots found in url */
14128: /* -------------------------------------------------------------------------
14129: prune the url
14130: -------------------------------------------------------------------------- */
14131: /* --- first check input --- */
14132: *pruned = '\000';                       /* init for error */
14133: if ( isempty(url) ) goto end_of_job;    /* missing input, so return NULL */
14134: if ( n < 0 ) n = (-n);                  /* flip n positive */
14135: if ( n == 0 ) n = 999;                  /* retain all levels of url */
14136: /* --- preprocess url --- */
14137: strninit(pruned,url,2032);              /* copy url to our static buffer */
14138: strlower(pruned);                       /* lowercase it and... */
14139: trimwhite(pruned);                      /*remove leading/trailing whitespace*/
14140: /* --- first remove leading http:// --- */
14141: if ( (delim=strstr(pruned,"://")) != NULL ) /* found http:// or ftp:// etc */
14142:   if ( ((int)(delim-pruned)) <= 8 ) {   /* make sure it's a prefix */
14143:     strsqueezep(pruned,delim+3);        /* squeeze out leading http:// */
14144:     trimwhite(pruned); }                /*remove leading/trailing whitespace*/
14145: /* --- next remove leading www. --- */
14146: if ( (delim=strstr(pruned,"www.")) != NULL ) /* found www. */
14147:   if ( ((int)(delim-pruned)) == 0 ) {   /* make sure it's the leading chars*/
14148:     strsqueezep(pruned,delim+4);        /* squeeze out leading www. */
14149:     trimwhite(pruned); }                /*remove leading/trailing whitespace*/
14150: /* --- finally remove leading / and everything following it --- */
14151: if ( (delim=strchr(pruned,'/')) != NULL ) /* found first / */
14152:   *delim = '\000';                      /* null-terminate url at first / */
14153: if ( isempty(pruned) ) goto end_of_job; /* nothing left in url */
14154: /* --- count dots from back of url --- */
14155: delim = pruned + strlen(pruned);        /*ptr to '\000' terminating pruned*/
14156: while ( ((int)(delim-pruned)) > 0 ) {   /* don't back up before first char */
14157:   delim--;                              /* ptr to preceding character */
14158:   if ( *delim != '.' ) continue;        /* not a dot, so keep looking */
14159:   ndots++;                              /* count another dot found */
14160:   if ( istruncate ) {                   /* remove trailing .com */
14161:     istruncate = 0;                     /* don't truncate any more dots */
14162:     *delim = '\000';                    /* truncate pruned url */
14163:     ndots = 0; }                        /* and reset dot count */
14164:   if ( ndots >= n ) {                   /* have all requested levels */
14165:     strsqueezep(pruned,delim+1);        /* squeeze out leading levels */
14166:     break; }                            /* and we're done */
14167:   } /* --- end-of-while() --- */
14168: purl = pruned;                          /*completed okay, return pruned url*/
14169: end_of_job:
14170:   return ( purl );                      /* back with pruned url */
14171: } /* --- end-of-function urlprune() --- */
14172: 
14173: 
14174: /* ==========================================================================
14175:  * Function:    urlncmp ( url1, url2, n )
14176:  * Purpose:     Compares the n topmost levels of two urls
14177:  * --------------------------------------------------------------------------
14178:  * Arguments:   url1 (I)        char * to null-terminated string
14179:  *                              containing url to be compared with url2
14180:  *              url2 (I)        char * to null-terminated string
14181:  *                              containing url to be compared with url1
14182:  *              n (I)           int containing number of top levels
14183:  *                              to compare, or 0 to compare them all.
14184:  *                              n<0 compares that many top levels excluding
14185:  *                              the last, i.e., for n=-1, xxx.com and xxx.org
14186:  *                              would be considered a match
14187:  * --------------------------------------------------------------------------
14188:  * Returns:     ( int )         1 if url's match, or
14189:  *                              0 if not.
14190:  * --------------------------------------------------------------------------
14191:  * Notes:     o
14192:  * ======================================================================= */
14193: /* --- entry point --- */
14194: int     urlncmp ( char *url1, char *url2, int n )
14195: {
14196: /* -------------------------------------------------------------------------
14197: Allocations and Declarations
14198: -------------------------------------------------------------------------- */
14199: char    *urlprune(), *prune=NULL,       /* prune url's */
14200:         prune1[4096], prune2[4096];     /* pruned copies of url1,url2 */
14201: int     ismatch = 0;                    /* true if url's match */
14202: /* -------------------------------------------------------------------------
14203: prune url's and compare the pruned results
14204: -------------------------------------------------------------------------- */
14205: /* --- check input --- */
14206: if ( isempty(url1)                      /*make sure both url1,url2 supplied*/
14207: ||   isempty(url2) ) goto end_of_job;   /* missing input, so return 0 */
14208: /* --- prune url's --- */
14209: prune = urlprune(url1,n);               /* ptr to pruned version of url1 */
14210: if ( isempty(prune) ) goto end_of_job;  /* some problem with url1 */
14211: strninit(prune1,prune,4064);            /* local copy of pruned url1 */
14212: prune = urlprune(url2,n);               /* ptr to pruned version of url2 */
14213: if ( isempty(prune) ) goto end_of_job;  /* some problem with url2 */
14214: strninit(prune2,prune,4064);            /* local copy of pruned url2 */
14215: /* --- compare pruned url's --- */
14216: if ( strcmp(prune1,prune2) == 0 )       /* pruned url's are identical */
14217:   ismatch = 1;                          /* signal match to caller */
14218: end_of_job:
14219:   return ( ismatch );                   /*back with #matching url components*/
14220: } /* --- end-of-function urlncmp() --- */
14221: 
14222: 
14223: /* ==========================================================================
14224:  * Function:    dbltoa ( dblval, npts )
14225:  * Purpose:     Converts double to ascii, in financial format
14226:  *              (e.g., comma-separated and negatives enclosed in ()'s).
14227:  * -------------------------------------------------------------------------
14228:  * Arguments:   dblval (I)      double containing value to be converted.
14229:  *              npts (I)        int containing #places after decimal point
14230:  *                              to be displayed in returned string.
14231:  * Returns:     ( char * )      null-terminated string containing
14232:  *                              double converted to financial format.
14233:  * -------------------------------------------------------------------------
14234:  * Notes:     o
14235:  * ======================================================================= */
14236: /* --- entry point --- */
14237: char    *dbltoa ( double dblval, int npts )
14238: /* double dblval;
14239:    int  npts; */
14240: {
14241: /* -------------------------------------------------------------------------
14242: Allocations and Declarations
14243: ------------------------------------------------------------------------- */
14244: static  char finval[256];               /* buffer returned to caller */
14245: static  char digittbl[32]="0123456789*"; /* table of ascii decimal digits */
14246: char    *finptr = finval;               /* ptr to next char being converted*/
14247: double  floor();                        /* integer which is glb(double) */
14248: double  dbldigit;                       /* to shift out digits from dblval */
14249: int     digit;                          /* one digit from dblval */
14250: int     isneg = 0;                      /* reset true if dblval negative */
14251: int     ifrac = 0;                      /* npts fractional digits of dblval*/
14252: char    digits[64]; int ndigits=0;      /* all the digits [0]=least signif */
14253: /* -------------------------------------------------------------------------
14254: Check sign
14255: ------------------------------------------------------------------------- */
14256: if ( dblval < 0.0 )                     /* got a negative value to convert */
14257:     { isneg=1; dblval=(-dblval); }      /* set flag and make it positive */
14258: /* -------------------------------------------------------------------------
14259: Get fractional part of dblval if required
14260: ------------------------------------------------------------------------- */
14261: if ( npts > 0 )
14262:     { int ipts = npts;                  /* loop index */
14263:       dbldigit = dblval-floor(dblval);  /* fractional part as double */
14264:       digit = 1;                        /* check if rounded frac > 1 */
14265:       while ( --ipts >= 0 )             /* count down */
14266:         { dbldigit *= 10.0;             /* shift left one digit at a time */
14267:           digit *= 10; }                /* and keep max up-to-date */
14268:       ifrac = (int)(dbldigit + 0.5);    /* store fractional part as integer*/
14269:       if ( ifrac >= digit )             /* round to next whole number */
14270:         { dblval++; ifrac=0; }          /* bump val, reset frac to zero */
14271:     } /* --- end-of-if(npts>0) --- */
14272: else dblval += 0.5;                     /* no frac, round to nearest whole */
14273: /* -------------------------------------------------------------------------
14274: Get whole digits
14275: ------------------------------------------------------------------------- */
14276: dblval = floor(dblval);                 /* get rid of fractional part */
14277: while ( dblval > 0.0 )                  /* still have data digits remaining*/
14278:     { dbldigit = floor(dblval/10.0);    /* shift out next digit */
14279:       digit = (int)(dblval - 10.0*dbldigit + 0.01); /* least signif digit */
14280:       if ( digit<0 || digit>9 ) digit=10; /* index check */
14281:       digits[ndigits++] = digittbl[digit]; /* store ascii digit */
14282:       dblval = dbldigit; }              /* ready for next digit */
14283: if ( ndigits < 1 ) digits[ndigits++] = '0'; /* store a single '0' for 0.0 */
14284: /* -------------------------------------------------------------------------
14285: Format whole part from digits[] array
14286: ------------------------------------------------------------------------- */
14287: if ( isneg ) *finptr++ = '(';           /* leading paren for negative value*/
14288: for ( digit=ndigits-1; digit>=0; digit-- ) /* start with most significant */
14289:     { *finptr++ = digits[digit];        /* store digit */
14290:       if ( digit>0 && digit%3==0 )      /* need a comma */
14291:         *finptr++ = ','; }              /* put in separating comma */
14292: /* -------------------------------------------------------------------------
14293: Format fractional part using ifrac
14294: ------------------------------------------------------------------------- */
14295: if ( npts > 0 )
14296:     { *finptr++ = '.';                  /* start with decimal point */
14297:       sprintf(finptr,"%0*d",npts,ifrac); /* convert to string */
14298:       finptr += npts; }                 /* bump ptr past fractional digits */
14299: /* -------------------------------------------------------------------------
14300: End-of-Job
14301: ------------------------------------------------------------------------- */
14302: if ( isneg ) *finptr++ = ')';           /*trailing paren for negative value*/
14303: *finptr = '\000';                       /* null-terminate converted double */
14304: return ( finval );                      /* converted double back to caller */
14305: } /* --- end-of-function dbltoa() --- */
14306: 
14307: 
14308: /* ==========================================================================
14309:  * Function:    rotmatrix ( axis, theta )
14310:  *              Constructs rotation matrix for theta degrees around axis
14311:  *              (see Notes below for discussion).
14312:  * --------------------------------------------------------------------------
14313:  * Arguments:   axis (I)        point3d * to u=(u_x,u_y,u_z) components
14314:  *                              of rotation axis (u's tail point at origin),
14315:  *                              or NULL ptr returns previous matrix
14316:  *              theta (I)       double containing rotation in degrees
14317:  *                              (positive theta rotation according to
14318:  *                              right-hand screw rule),
14319:  *                              or 0.0 returns previous matrix
14320:  * --------------------------------------------------------------------------
14321:  * Returns:     ( matrix3d * )  pointer to constructed rotation matrix,
14322:  *                              or NULL for any error
14323:  * --------------------------------------------------------------------------
14324:  * Notes:     o For discussion, see
14325:  *              http://wikipedia.org/wiki/Rotation_matrix#In_three_dimensions
14326:  *
14327:  * ======================================================================= */
14328: /* --- entry point --- */
14329: matrix3d *rotmatrix ( point3d *axis, double theta )
14330: {
14331: /* -------------------------------------------------------------------------
14332: Allocations and Declarations
14333: -------------------------------------------------------------------------- */
14334: static  matrix3d rot =                  /* returned rotation matrix */
14335:         { {0.,0.,0,}, {0.,0.,0,}, {0.,0.,0,} }; /* just init as zero's */
14336: matrix3d *prot = &(rot);                /* init returned rot ptr */
14337: double  pi = 3.14159265359;             /* pi */
14338: double  u,ux,uy,uz,                     /* axis length and components */
14339:         tsin,tcos;                      /* sin,cos (theta) */
14340: /* -------------------------------------------------------------------------
14341: Initialization
14342: -------------------------------------------------------------------------- */
14343: if ( axis == NULL                       /*  no rotation axis, or no theta */
14344: ||   absval(theta) < 1.0e-10 ) goto end_of_job;/*just return preceding matrix*/
14345: if ( 1 ) theta *= (pi/180.);            /*convert theta in degrees to radians*/
14346: tsin = sin(theta);  tcos = cos(theta);  /* sin,cos (theta) */
14347: ux = axis->x;  uy = axis->y;  uz = axis->z; /* unnormalized axis components */
14348: u = sqrt((ux*ux)+(uy*uy)+(uz*uz));      /* axis length */
14349: if ( u < 1.0e-10 ) goto end_of_job;     /* zero-vector is an error */
14350: ux /= u;  uy /= u;  uz /= u;            /* normalized axis components */
14351: /* -------------------------------------------------------------------------
14352: rotation matrix components, stored row-wise
14353: -------------------------------------------------------------------------- */
14354: /* --- x-row --- */
14355: rot.xrow.x = tcos + ux*ux*(1.0-tcos);
14356: rot.xrow.y =        ux*uy*(1.0-tcos) - uz*tsin;
14357: rot.xrow.z =        ux*uz*(1.0-tcos) + uy*tsin;
14358: /* --- y-row --- */
14359: rot.yrow.x =        uy*ux*(1.0-tcos) + uz*tsin;
14360: rot.yrow.y = tcos + uy*uy*(1.0-tcos);
14361: rot.yrow.z =        uy*uz*(1.0-tcos) - ux*tsin;
14362: /* --- z-row --- */
14363: rot.zrow.x =        uz*ux*(1.0-tcos) - uy*tsin;
14364: rot.zrow.y =        uz*uy*(1.0-tcos) + ux*tsin;
14365: rot.zrow.z = tcos + uz*uz*(1.0-tcos);
14366: prot = &(rot);                          /* point return value to matrix */
14367: end_of_job:
14368:   return ( prot );                      /*back to caller with rotation matrix*/
14369: } /* --- end-of-function rotmatrix() --- */
14370: 
14371: 
14372: /* ==========================================================================
14373:  * Function:    matmult ( mat, vec )
14374:  *              returns result of mat(rix) x vec(tor)
14375:  * --------------------------------------------------------------------------
14376:  * Arguments:   mat (I)         matrix3d * to matrix
14377:  *              vec (I)         point3d *  to vector
14378:  * --------------------------------------------------------------------------
14379:  * Returns:     ( point3d * )   pointer to result of mat(rix) x vec(tor),
14380:  *                              or NULL for any error
14381:  * --------------------------------------------------------------------------
14382:  * Notes:     o
14383:  * ======================================================================= */
14384: /* --- entry point --- */
14385: point3d *matmult ( matrix3d *mat, point3d *vec )
14386: {
14387: /* -------------------------------------------------------------------------
14388: Allocations and Declarations
14389: -------------------------------------------------------------------------- */
14390: static  point3d mult;                   /* returned matrix x vector product */
14391: point3d *pmult = NULL;                  /* init mult ptr for error */
14392: /* -------------------------------------------------------------------------
14393: Initialization
14394: -------------------------------------------------------------------------- */
14395: if ( mat == NULL                        /* mat and vec required input */
14396: ||   vec == NULL ) goto end_of_job;     /* missing required input */
14397: /* -------------------------------------------------------------------------
14398: matrix multiplication
14399: -------------------------------------------------------------------------- */
14400: /* --- x,y,z-components of mat(rix) x vec(tor) product --- */
14401: mult.x = (mat->xrow.x)*vec->x + (mat->xrow.y)*vec->y + (mat->xrow.z)*vec->z;
14402: mult.y = (mat->yrow.x)*vec->x + (mat->yrow.y)*vec->y + (mat->yrow.z)*vec->z;
14403: mult.z = (mat->zrow.x)*vec->x + (mat->zrow.y)*vec->y + (mat->zrow.z)*vec->z;
14404: pmult = &(mult);                        /* point return value to product */
14405: end_of_job:
14406:   return ( pmult );                     /*back to caller with matrix x vector*/
14407: } /* --- end-of-function matmult() --- */
14408: 
14409: 
14410: /* ==========================================================================
14411:  * Function:    aalowpass ( rp, bytemap, grayscale )
14412:  * Purpose:     calculates a lowpass anti-aliased bytemap
14413:  *              for rp->bitmap, with each byte 0...grayscale-1
14414:  * --------------------------------------------------------------------------
14415:  * Arguments:   rp (I)          raster *  to raster whose bitmap
14416:  *                              is to be anti-aliased
14417:  *              bytemap (O)     intbyte * to bytemap, calculated
14418:  *                              by applying lowpass filter to rp->bitmap,
14419:  *                              and returned (as you'd expect) in 1-to-1
14420:  *                              addressing correspondence with rp->bitmap
14421:  *              grayscale (I)   int containing number of grayscales
14422:  *                              to be calculated, 0...grayscale-1
14423:  *                              (should typically be given as 256)
14424:  * --------------------------------------------------------------------------
14425:  * Returns:     ( int )         1=success, 0=any error
14426:  * --------------------------------------------------------------------------
14427:  * Notes:     o If the center point of the box being averaged is black,
14428:  *              then the entire "average" is forced black (grayscale-1)
14429:  *              regardless of the surrounding points.  This is my attempt
14430:  *              to avoid a "washed-out" appearance of thin (one-pixel-wide)
14431:  *              lines, which would otherwise turn from black to a gray shade.
14432:  *           o  Also, while the weights for neighbor points are fixed,
14433:  *              you may adjust the center point weight on the compile line.
14434:  *              A higher weight sharpens the resulting anti-aliased image;
14435:  *              lower weights blur it out more (but keep the "center" black
14436:  *              as per the preceding note).
14437:  * ======================================================================= */
14438: /* --- entry point --- */
14439: int     aalowpass (raster *rp, intbyte *bytemap, int grayscale)
14440: {
14441: /* -------------------------------------------------------------------------
14442: Allocations and Declarations
14443: -------------------------------------------------------------------------- */
14444: int     status = 1;                     /* 1=success, 0=failure to caller */
14445: pixbyte *bitmap= (rp==NULL?NULL:rp->pixmap); /*local rp->pixmap ptr*/
14446: int     irow=0, icol=0;                 /* rp->height, rp->width indexes */
14447: int     weights[9] = { 1,3,1, 3,0,3, 1,3,1 }; /* matrix of weights */
14448: int     adjindex[9]= { 0,1,2, 7,-1,3, 6,5,4 }; /*clockwise from upper-left*/
14449: int     totwts = 0;                     /* sum of all weights in matrix */
14450: int     isforceavg = 1,                 /*force avg black if center black?*/
14451:         isminmaxwts = 1,                /*use wts or #pts for min/max test */
14452:         blackscale = 0; /*(grayscale+1)/4;*/ /*force black if wgted avg>bs */
14453: /* -------------------------------------------------------------------------
14454: Initialization
14455: -------------------------------------------------------------------------- */
14456: /* --- calculate total weights --- */
14457: weights[4]= centerwt;                   /* weight for center point */
14458: weights[1]= weights[3]= weights[5]= weights[7]= adjacentwt; /*adjacent pts*/
14459: totwts = centerwt + 4*(1+adjacentwt);   /* tot is center plus neighbors */
14460: /* -------------------------------------------------------------------------
14461: Calculate bytemap as 9-point weighted average over bitmap
14462: -------------------------------------------------------------------------- */
14463: for ( irow=0; irow<rp->height; irow++ )
14464:  for ( icol=0; icol<rp->width; icol++ )
14465:   {
14466:   int   ipixel = icol + irow*(rp->width); /* center pixel index */
14467:   int   jrow=0, jcol=0,                 /* box around ipixel */
14468:         bitval = 0,                     /* value of bit/pixel at jrow,jcol */
14469:         iscenter = 0,                   /* set true if center pixel black */
14470:         nadjacent=0, wadjacent=0,       /* #adjacent black pixels, their wts*/
14471:         ngaps = 0,                      /* #gaps in 8 pixels around center */
14472:         iwt=(-1), sumwts=0;             /* weights index, sum in-bound wts */
14473:   char  adjmatrix[8];                   /* adjacency "matrix" */
14474:   memset(adjmatrix,0,8);                /* zero out adjacency matrix */
14475:   bytemap[ipixel] = 0;                  /* init pixel white */
14476:   /*--- for ipixel at irow,icol, get weighted average of adjacent pixels ---*/
14477:   for ( jrow=irow-1; jrow<=irow+1; jrow++ )  /* jrow = irow-1...irow+1 */
14478:    for ( jcol=icol-1; jcol<=icol+1; jcol++ ) /* jcol = icol-1...icol+1 */
14479:     {
14480:     int jpixel = jcol + jrow*(rp->width); /* averaging index */
14481:     iwt++;                              /*always bump weight index*/
14482:     if ( jrow<0 || jrow>=rp->height     /* if row out pf bounds */
14483:     ||   jcol<0 || jcol>=rp->width )    /* or col out of bounds */
14484:         continue;                       /* ignore this point */
14485:     bitval = (int)getlongbit(bitmap,jpixel); /* value of bit at jrow,jcol */
14486:     if ( bitval )                       /* this is a black pixel */
14487:       { if ( jrow==irow && jcol==icol ) /* and this is center point */
14488:           iscenter = 1;                 /* set flag for center point black */
14489:         else                            /* adjacent point black */
14490:           { nadjacent++;                /* bump adjacent black count */
14491:             adjmatrix[adjindex[iwt]] = 1; } /*set "bit" in adjacency matrix*/
14492:         wadjacent += weights[iwt]; }    /* sum weights for black pixels */
14493:     sumwts += weights[iwt];             /* and sum weights for all pixels */
14494:     } /* --- end-of-for(jrow,jcol) --- */
14495:   /* --- count gaps --- */
14496:   ngaps = (adjmatrix[7]!=adjmatrix[0]?1:0); /* init count */
14497:   for ( iwt=0; iwt<7; iwt++ )           /* clockwise around adjacency */
14498:     if ( adjmatrix[iwt] != adjmatrix[iwt+1] ) ngaps++; /* black/white flip */
14499:   ngaps /= 2;                           /*each gap has 2 black/white flips*/
14500:   /* --- anti-alias pixel, but leave it black if it was already black --- */
14501:   if ( isforceavg && iscenter )         /* force avg if center point black */
14502:       bytemap[ipixel] = grayscale-1;    /* so force grayscale-1=black */
14503:   else                                  /* center point not black */
14504:    if ( ngaps <= 2 )                    /*don't darken checkerboarded pixel*/
14505:     { bytemap[ipixel] =                 /* 0=white ... grayscale-1=black */
14506:         ((totwts/2 - 1) + (grayscale-1)*wadjacent)/totwts; /* not /sumwts; */
14507:       if ( blackscale > 0               /* blackscale kludge turned on */
14508:       &&   bytemap[ipixel] > blackscale ) /* weighted avg > blackscale */
14509:         bytemap[ipixel] = grayscale-1; } /* so force it entirely black */
14510:   /*--- only anti-alias pixels whose adjacent pixels fall within bounds ---*/
14511:   if ( !iscenter ) {                    /* apply min/maxadjacent test */
14512:    if ( isminmaxwts )                   /* min/max refer to adjacent weights*/
14513:     { if ( wadjacent < minadjacent      /* wts of adjacent points too low */
14514:       ||   wadjacent > maxadjacent )    /* or too high */
14515:         bytemap[ipixel] = 0; }          /* so leave point white */
14516:    else                                 /* min/max refer to #adjacent points*/
14517:     { if ( nadjacent < minadjacent      /* too few adjacent points black */
14518:       ||   nadjacent > maxadjacent )    /* or too many */
14519:         bytemap[ipixel] = 0; } }        /* so leave point white */
14520:   } /* --- end-of-for(irow,icol) --- */
14521: /* -------------------------------------------------------------------------
14522: Back to caller with gray-scale anti-aliased bytemap
14523: -------------------------------------------------------------------------- */
14524: /*end_of_job:*/
14525:   return ( status );
14526: } /* --- end-of-function aalowpass() --- */
14527: 
14528: 
14529: /* ==========================================================================
14530:  * Function:    aapnm ( rp, bytemap, grayscale )
14531:  * Purpose:     calculates a lowpass anti-aliased bytemap
14532:  *              for rp->bitmap, with each byte 0...grayscale-1,
14533:  *              based on the pnmalias.c algorithm
14534:  * --------------------------------------------------------------------------
14535:  * Arguments:   rp (I)          raster *  to raster whose bitmap
14536:  *                              is to be anti-aliased
14537:  *              bytemap (O)     intbyte * to bytemap, calculated
14538:  *                              by applying pnm-based filter to rp->bitmap,
14539:  *                              and returned (as you'd expect) in 1-to-1
14540:  *                              addressing correspondence with rp->bitmap
14541:  *              grayscale (I)   int containing number of grayscales
14542:  *                              to be calculated, 0...grayscale-1
14543:  *                              (should typically be given as 256)
14544:  * --------------------------------------------------------------------------
14545:  * Returns:     ( int )         1=success, 0=any error
14546:  * --------------------------------------------------------------------------
14547:  * Notes:    o  Based on the pnmalias.c algorithm in the netpbm package
14548:  *              on sourceforge.
14549:  * ======================================================================= */
14550: /* --- entry point --- */
14551: int     aapnm (raster *rp, intbyte *bytemap, int grayscale)
14552: {
14553: /* -------------------------------------------------------------------------
14554: Allocations and Declarations
14555: -------------------------------------------------------------------------- */
14556: pixbyte *bitmap = rp->pixmap;           /* local rp->pixmap ptr */
14557: int     width=rp->width, height=rp->height, /* width, height of raster */
14558:         icol = 0,        irow = 0,      /* width, height indexes */
14559:         imap = (-1);                    /* pixel index = icol + irow*width */
14560: int     bgbitval=0, fgbitval=1;         /* background, foreground bitval */
14561: int     isfirstaa = 1;                  /*debugging switch signals 1st pixel*/
14562: #if 0
14563: int     totwts=12, wts[9]={1,1,1, 1,4,1, 1,1,1}; /* pnmalias default wts */
14564: int     totwts=16, wts[9]={1,2,1, 2,4,2, 1,2,1}; /* weights */
14565: #endif
14566: int     totwts=18, wts[9]={1,2,1, 2,6,2, 1,2,1}; /* pnmalias default wts */
14567: int     isresetparams = 1,              /* true to set antialiasing params */
14568:         isfgalias  = 1,                 /* true to antialias fg bits */
14569:         isfgonly   = 0,                 /* true to only antialias fg bits */
14570:         isbgalias  = 0,                 /* true to antialias bg bits */
14571:         isbgonly   = 0;                 /* true to only antialias bg bits */
14572: /* -------------------------------------------------------------------------
14573: Initialization
14574: -------------------------------------------------------------------------- */
14575: /* --- check for bold light --- */
14576: if ( 0 )
14577:  { if ( weightnum > 2 ) { isbgalias=1; }        /* simulate bold */
14578:    if ( weightnum < 1 ) { isbgonly=1; isfgalias=0; } } /* simulate light */
14579: /* --- reset wts[], etc, and calculate total weights --- */
14580: if ( isresetparams )                    /* wts[], etc taken from params */
14581:   { int iwt=0;                          /* wts[iwt] index */
14582:     wts[4]= centerwt;                   /* weight for center point */
14583:     wts[1]=wts[3]=wts[5]=wts[7] = adjacentwt; /* and adjacent points */
14584:     wts[0]=wts[2]=wts[6]=wts[8] = cornerwt;   /* and corner points */
14585:     for ( totwts=0,iwt=0; iwt<9; iwt++ ) totwts += wts[iwt]; /* sum wts */
14586:     isfgalias = fgalias;                /* set isfgalias */
14587:     isfgonly = fgonly;                  /* set isfgonly */
14588:     isbgalias = bgalias;                /* set isbgalias */
14589:     isbgonly = bgonly; }                /* set isbgonly */
14590: /* -------------------------------------------------------------------------
14591: Calculate bytemap as 9-point weighted average over bitmap
14592: -------------------------------------------------------------------------- */
14593: for ( irow=0; irow<height; irow++ )
14594:  for ( icol=0; icol<width; icol++ )
14595:   {
14596:   /* --- local allocations and declarations --- */
14597:   int   bitval=0,                       /* value of rp bit at irow,icol */
14598:         nnbitval=0, nebitval=0, eebitval=0, sebitval=0, /*adjacent vals*/
14599:         ssbitval=0, swbitval=0, wwbitval=0, nwbitval=0; /*compass pt names*/
14600:   int   isbgedge=0, isfgedge=0;         /*does pixel border a bg or fg edge*/
14601:   int   aabyteval=0;                    /* antialiased (or unchanged) value*/
14602:   /* --- bump imap index and get center bit value --- */
14603:   imap++;                               /* imap = icol + irow*width */
14604:   bitval = getlongbit(bitmap,imap);     /* value of rp input bit at imap */
14605:   aabyteval = (intbyte)(bitval==bgbitval?0:grayscale-1); /* default aa val */
14606:   bytemap[imap] = (intbyte)(aabyteval); /* init antialiased pixel */
14607:   /* --- check if we're antialiasing this pixel --- */
14608:   if ( (isbgonly && bitval==fgbitval)   /* only antialias background bit */
14609:   ||   (isfgonly && bitval==bgbitval) ) /* only antialias foreground bit */
14610:     continue;                           /* leave default and do next bit */
14611:   /* --- get surrounding bits --- */
14612:   if ( irow > 0 )                       /* nn (north) bit available */
14613:      nnbitval = getlongbit(bitmap,imap-width); /* nn bit value */
14614:   if ( irow < height-1 )                /* ss (south) bit available */
14615:      ssbitval = getlongbit(bitmap,imap+width); /* ss bit value */
14616:   if ( icol > 0 )                       /* ww (west) bit available */
14617:    { wwbitval = getlongbit(bitmap,imap-1); /* ww bit value */
14618:      if ( irow > 0 )                    /* nw bit available */
14619:        nwbitval = getlongbit(bitmap,imap-width-1); /* nw bit value */
14620:      if ( irow < height-1 )             /* sw bit available */
14621:        swbitval = getlongbit(bitmap,imap+width-1); } /* sw bit value */
14622:   if ( icol < width-1 )                 /* ee (east) bit available */
14623:    { eebitval = getlongbit(bitmap,imap+1); /* ee bit value */
14624:      if ( irow > 0 )                    /* ne bit available */
14625:        nebitval = getlongbit(bitmap,imap-width+1); /* ne bit value */
14626:      if ( irow < height-1 )             /* se bit available */
14627:        sebitval = getlongbit(bitmap,imap+width+1); } /* se bit value */
14628:   /* --- check for edges --- */
14629:   isbgedge =                            /* current pixel borders a bg edge */
14630:         (nnbitval==bgbitval && eebitval==bgbitval) ||   /*upper-right edge*/
14631:         (eebitval==bgbitval && ssbitval==bgbitval) ||   /*lower-right edge*/
14632:         (ssbitval==bgbitval && wwbitval==bgbitval) ||   /*lower-left  edge*/
14633:         (wwbitval==bgbitval && nnbitval==bgbitval) ;    /*upper-left  edge*/
14634:   isfgedge =                            /* current pixel borders an fg edge*/
14635:         (nnbitval==fgbitval && eebitval==fgbitval) ||   /*upper-right edge*/
14636:         (eebitval==fgbitval && ssbitval==fgbitval) ||   /*lower-right edge*/
14637:         (ssbitval==fgbitval && wwbitval==fgbitval) ||   /*lower-left  edge*/
14638:         (wwbitval==fgbitval && nnbitval==fgbitval) ;    /*upper-left  edge*/
14639:   /* ---check top/bot left/right edges for corners (added by j.forkosh)--- */
14640:   if ( 1 ) {                            /* true to perform test */
14641:     int isbghorz=0, isfghorz=0, isbgvert=0, isfgvert=0; /* horz/vert edges */
14642:     isbghorz =                          /* top or bottom edge is all bg */
14643:         (nwbitval+nnbitval+nebitval == 3*bgbitval) ||   /* top edge bg */
14644:         (swbitval+ssbitval+sebitval == 3*bgbitval) ;    /* bottom edge bg */
14645:     isfghorz =                          /* top or bottom edge is all fg */
14646:         (nwbitval+nnbitval+nebitval == 3*fgbitval) ||   /* top edge fg */
14647:         (swbitval+ssbitval+sebitval == 3*fgbitval) ;    /* bottom edge fg */
14648:     isbgvert =                          /* left or right edge is all bg */
14649:         (nwbitval+wwbitval+swbitval == 3*bgbitval) ||   /* left edge bg */
14650:         (nebitval+eebitval+sebitval == 3*bgbitval) ;    /* right edge bg */
14651:     isfgvert =                          /* left or right edge is all bg */
14652:         (nwbitval+wwbitval+swbitval == 3*fgbitval) ||   /* left edge fg */
14653:         (nebitval+eebitval+sebitval == 3*fgbitval) ;    /* right edge fg */
14654:     if ( (isbghorz && isbgvert && (bitval==fgbitval))   /* we're at an...*/
14655:     ||   (isfghorz && isfgvert && (bitval==bgbitval)) ) /*...inside corner */
14656:         continue;                                       /* don't antialias */
14657:     } /* --- end-of-if(1) --- */
14658:   /* --- check #gaps for checkerboard (added by j.forkosh) --- */
14659:   if ( 0 ) {                            /* true to perform test */
14660:     int ngaps=0, mingaps=1,maxgaps=2;   /* count #fg/bg flips (max=4 noop) */
14661:     if ( nwbitval!=nnbitval ) ngaps++;  /* upper-left =? upper */
14662:     if ( nnbitval!=nebitval ) ngaps++;  /* upper =? upper-right */
14663:     if ( nebitval!=eebitval ) ngaps++;  /* upper-right =? right */
14664:     if ( eebitval!=sebitval ) ngaps++;  /* right =? lower-right */
14665:     if ( sebitval!=ssbitval ) ngaps++;  /* lower-right =? lower */
14666:     if ( ssbitval!=swbitval ) ngaps++;  /* lower =? lower-left */
14667:     if ( swbitval!=wwbitval ) ngaps++;  /* lower-left =? left */
14668:     if ( wwbitval!=nwbitval ) ngaps++;  /* left =? upper-left */
14669:     if ( ngaps > 0 ) ngaps /= 2;        /* each gap has 2 bg/fg flips */
14670:     if ( ngaps<mingaps || ngaps>maxgaps ) continue;
14671:     } /* --- end-of-if(1) --- */
14672:   /* --- antialias if necessary --- */
14673:   if ( (isbgalias && isbgedge)          /* alias pixel surrounding bg */
14674:   ||   (isfgalias && isfgedge)          /* alias pixel surrounding fg */
14675:   ||   (isbgedge  && isfgedge) )        /* neighboring fg and bg pixel */
14676:     {
14677:     int aasumval =                      /* sum wts[]*bitmap[] */
14678:         wts[0]*nwbitval + wts&